文件:/extensions/openai/openai-provider.ts

核心职责是:

这是 OpenAI 模型的 Provider Plugin,OpenClaw 用它来管理所有 OpenAI 模型(API Key 直连 + ChatGPT/Codex OAuth 两种方式)。

不做 LLM 调用,而是告诉上层系统:"用户想用 X 模型 → 应该走哪个 API 端点、哪种传输协议、什么认证方式"。run-loop.ts 是执行层("怎么跑"),openai-provider.ts 是配置层("用什么跑")。run-loop 调用 provider 获取模型配置,然后发起 LLM 请求。

文件整体架构(6 大块)

1: 常量定义  (模型 ID、上下文窗口、成本、端点 URL)

1.1 常量区(L1-L161):模型参数表

├── PROVIDER_ID = "openai"
├── API Endpoints
│   ├── https://api.openai.com/v1/models          ← API Key 模型发现
│   └── https://api.openai.com/.../models?...     ← Codex OAuth 模型发现
├── Context Tokens(每个模型的上下文上限)
│   ├── GPT-5.6: 1,050,000 (直连) / 372,000 (Codex)
│   ├── GPT-5.5: 1,000,000 (窗口) / 272,000 (实际)
│   ├── GPT-5.5 Pro: 1,000,000
│   ├── GPT-5.4: 1,050,000
│   └── GPT-5.4 Mini/Nano: 400,000
└── 定价表($/百万token)
    每个模型 { input, output, cacheRead, cacheWrite }

关键发现: 同一个模型(如 GPT-5.6)走不同通道时上下文窗口不同:

  • 直连 OpenAI API → 1,050,000 tokens
  • 走 Codex 通道 → 372,000 tokens

这解释了为什么需要区分路由。

1.2 成本定义(美元/百万 token)

注意: GPT-5.6 有三个变体(Sol/Terra/Luna),价格梯度明显。这是 OpenClaw 做 cost-aware 模型选择 的基础。

1.3 Manifest 静态目录

从 openclaw.plugin.json 文件读取离线静态模型目录。这是"兜底"配置,当网络不可用时使用。

2: 模型发现(L189-294,L381-438):从 API 动态拉模型列表

整体结构如下:

buildOpenAILiveProviderConfig()     ← API Key 方式,调 /v1/models
buildOpenAICodexLiveProviderConfig() ← Codex OAuth 方式,调 Codex 专用端点
    │
    ├── readCodexModelRows()         解析 API 返回的 { models: [...] }
    ├── readCodexModelString/Boolean/PositiveInteger()
    │   从每个 model row 中提取字段(slug, context_window, 等)
    └── buildOpenAICodexModelFromLiveRow()
        将一行 API 数据 → ModelDefinitionConfig
        关键逻辑:如果 API 没说某个字段,fallback 到 manifest 模板

2.1 buildOpenAILiveProviderConfig L189-218

async function buildOpenAILiveProviderConfig(
  params: BuildOpenAILiveProviderConfigParams,
): Promise<ModelProviderConfig> {
  const baseUrl =  // 1. 解析 baseUrl(用户配置 > 环境变量 > 默认值)
    normalizeOptionalString(params.baseUrl) ?? resolveOpenAIDefaultBaseUrl(params.env);
  const models = buildOpenAIManifestModelsForBaseUrl(baseUrl); // 2. 从 manifest 构建基础模型列表,并根据 baseUrl 调整
  if (!shouldFetchOpenAILiveModels(baseUrl)) {  // 3. 判断:是否需要从 OpenAI API 实时拉取模型列表?
    return {
      baseUrl,
      api: "openai-responses",
      apiKey: params.apiKey,
      models,
    };
  }
  return await buildLiveModelProviderConfig({  // 4. 是官方 API,调用 /v1/models 端点实时获取模型列表
    providerId: PROVIDER_ID,
    endpoint: OPENAI_MODELS_ENDPOINT,
    providerConfig: {
      baseUrl,
      api: "openai-responses",
    },
    models,
    apiKey: params.apiKey,
    discoveryApiKey: params.discoveryApiKey,
    fetchGuard: params.fetchGuard,
    signal: params.signal,
    ttlMs: OPENAI_MODELS_CACHE_TTL_MS,
    auditContext: "openai-model-discovery",
  });
}

核心逻辑:

  • 如果 baseUrl 指向 api.openai.com → 实时拉取模型列表(保证新模型自动可用)
  • 如果 baseUrl 是自定义的(如 Azure、代理)→ 只用 manifest 中的静态列表

2.2 Codex 模型目录构建   (readCodexModel* / buildOpenAICodexModel* 系列函数)

Codex 是什么?在这个文件中,Codex 是 OpenAI 的一种认证/传输模式,具体来说:

维度 标准 OpenAI API Codex 通道
认证方式 API Key OAuth(ChatGPT 账号登录)
API 端点 api.openai.com/v1/... OPENAI_CODEX_RESPONSES_BASE_URL
传输协议标识 "openai-responses" "openai-chatgpt-responses"
模型发现端点 /v1/models /models?client_version=0.144.5
上下文窗口 完整(如 1,050,000) 缩减(如 372,000)
适用场景 开发者直接调用 Codex CLI / Agent 运行时

一句话总结:Codex 不是一个模型,而是 OpenAI 为 Codex 产品(CLI Agent)提供的专用 OAuth 认证通道,走这个通道时模型参数(上下文窗口等)会有所不同。

2.3 readCodexModel* 系列函数 L220-283

这些函数是从 Codex 模型发现 API 的响应中安全提取字段的工具函数

// 从响应行中读取字符串字段(如 model 的 slug、display_name)
function readCodexModelString(row: unknown, key: string): string | undefined 

// 读取正整数字段(如 context_window、max_output_tokens)
function readCodexModelPositiveInteger(row: unknown, keys: readonly string[]): number | undefined 

// 读取字符串数组(如 input_modalities: ["text", "image"])
function readCodexModelStringArray(row: unknown, keys: readonly string[]): readonly string[] 

// 读取推理级别(如 ["low", "medium", "high", "xhigh", "max"])
function readCodexReasoningLevels(row: unknown): readonly string[] | undefined 

// 读取布尔字段(如 show_in_picker)
function readCodexModelBoolean(row: unknown, key: string): boolean | undefined 

为什么需要这些函数? 因为 Codex 模型发现 API 返回的 JSON 结构不固定(可能是 snake_case 也可能是 camelCase),这些函数做了防御性解析,兼容两种命名风格。

2.4 buildOpenAICodexModelFromLiveRow(核心函数)L381-438

这是将 Codex API 返回的一行数据转换为 OpenClaw 内部 ModelDefinitionConfig 的函数:

function buildOpenAICodexModelFromLiveRow(row: unknown): ModelDefinitionConfig | undefined {
  // 1. 过滤:只保留 visibility="list" 且 show_in_picker !== false 的模型
  if (!shouldIncludeCodexModelRow(row)) return undefined;
  
  // 2. 提取模型 ID(优先 slug,其次 id)
  const modelId = readCodexModelString(row, "slug") ?? readCodexModelString(row, "id");
  
  // 3. 查找 manifest 中的回退配置(如果 API 没返回某些字段,用 manifest 兜底)
  const fallback = resolveCodexModelFallback(modelId);
  
  // 4. 提取推理级别 → 决定 thinkingLevelMap
  const reasoningLevels = readCodexReasoningLevels(row);
  
  // 5. 提取上下文窗口、最大输出 token(API 值 > fallback 值 > 默认值)
  const contextWindow = readCodexModelPositiveInteger(row, ["max_context_window", "maxContextWindow"])
    ?? fallback?.contextWindow ?? contextTokens ?? DEFAULT_CONTEXT_TOKENS;
  
  // 6. 组装最终的 ModelDefinitionConfig
  return {
    id: modelId,
    api: "openai-chatgpt-responses",           // ← 关键:标记为 Codex 传输协议
    baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,  // ← 关键:指向 Codex 专用端点
    reasoning: (reasoningLevels?.length ?? 0) > 0,
    contextWindow,
    maxTokens,
    cost: fallback?.cost ?? OPENAI_UNKNOWN_MODEL_COST,
    ...
  };
}

3.4 buildOpenAICodexLiveProviderConfig L458-503

async function buildOpenAICodexLiveProviderConfig(params): Promise<ModelProviderConfig> {
  try {
    // 1. 从 Codex 模型发现端点拉取实时模型列表(带 60 秒缓存)
    const rows = await getCachedLiveProviderModelRows({
      endpoint: OPENAI_CODEX_MODELS_ENDPOINT,
      // 请求头带 OAuth token 和 Account ID
      buildRequestHeaders: ({ discoveryApiKey }) => ({
        Authorization: `Bearer ${discoveryApiKey}`,
        "ChatGPT-Account-ID": params.accountId,
      }),
    });
    
    // 2. 将每一行转换为 ModelDefinitionConfig
    const models = rows.map(buildOpenAICodexModelFromLiveRow).filter(Boolean);
    
    // 3. 如果成功获取到模型,返回 Codex Provider 配置
    if (models.length > 0) {
      return {
        baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,
        api: "openai-chatgpt-responses",
        auth: "oauth",        // ← 注意:认证方式是 OAuth,不是 API Key
        models,
      };
    }
  } catch {
    // 4. 如果 Codex 发现失败(网络错误、OAuth 过期等),静默降级
  }
  
  // 5. 降级:返回静态 Codex 配置(从 manifest 读取)
  return buildOpenAICodexStaticProviderConfig();
}

关键设计:Codex 发现是"advisory"(建议性的),失败不会阻断系统,会静默降级到静态配置。

3.5 normalizeOpenAICodexCatalogModel(GPT-5.6 特殊处理)L333-371

function normalizeOpenAICodexCatalogModel(model): ModelDefinitionConfig | undefined {
  const modelId = normalizeLowercaseStringOrEmpty(model.id);
  
  // GPT-5.6 基础版在 Codex 通道中不可用(返回 undefined = 过滤掉)
  if (modelId === OPENAI_GPT_56_MODEL_ID) return undefined;
  
  // GPT-5.6 Sol/Terra/Luna 在 Codex 通道中:
  if (modelId === OPENAI_GPT_56_SOL_MODEL_ID || ...) {
    return {
      ...model,
      // 上下文窗口从 1,050,000 缩减到 372,000
      contextWindow: OPENAI_CODEX_GPT_56_CONTEXT_TOKENS,
      contextTokens: OPENAI_CODEX_GPT_56_CONTEXT_TOKENS,
      // thinking 模式的 "off" 映射为 null(GPT-5.6 不支持关闭 thinking)
      thinkingLevelMap: { ...model.thinkingLevelMap, off: null },
      // Sol 和 Terra 额外支持 "ultra" 推理级别
      ...(supportsNativeUltra ? { compat: { supportedReasoningEfforts: [..., "ultra"] } } : {}),
    };
  }
  
  return model; // 其他模型原样返回
}

3: 传输协议路由决策 (L522-844):决定用哪个 API

这是核心部分——OpenAI 现在有两种 API 协议

3.1 三种传输协议

协议标识 含义 适用场景
"openai-completions" 传统 Chat Completions API 用户显式配置使用旧 API
"openai-responses" OpenAI Responses API(新) 标准 API Key 认证
"openai-chatgpt-responses" Codex/ChatGPT 专用 Responses API OAuth 认证 / Codex 运行时

3.2 概览

shouldUseOpenAIResponsesTransport() 
  → 如果用户配了 completions 但没有显式指定,自动"升级"到 responses

shouldUseCodexResponsesHooks()
  → 判断要不要走 Codex 通道(baseUrl 是 chatgpt.com / OAuth 认证)

resolveOpenAIGptForwardCompatModel()  ★ 重点函数
  → 当用户请求 gpt-5.6 / gpt-5.5 / chat-latest 等"别名"时
  → 从模板模型 clone,覆盖 context/tokens/cost 等参数
  → 这就是为什么 openai/gpt-5.6 可以直接用

3.3 shouldUseOpenAIResponsesTransport L522-542

function shouldUseOpenAIResponsesTransport(params): boolean {
  // 只有标记为 "openai-completions" 的才需要判断是否升级
  if (params.api !== "openai-completions") return false;
  
  // 如果用户显式配置了 completions 路由,尊重用户选择,不升级
  if (resolveAuthoredOpenAICompletionsRoute(params)) return false;
  
  // OpenAI 官方 provider + 官方端点 → 自动升级到 Responses API
  if (isOwnerProvider) return !params.baseUrl || isPlatformEndpoint;
  
  // 非官方 provider 但指向 OpenAI 平台端点 → 也升级
  return isPlatformEndpoint;
}

设计意图: OpenAI 正在从 Completions API 迁移到 Responses API,OpenClaw 自动帮用户升级,但如果用户显式配置了 completions,则尊重用户。

3.4 shouldResolveDynamicModelThroughCodex L670-697

function shouldResolveDynamicModelThroughCodex(ctx): boolean {
  // 如果 api 是 "openai-chatgpt-responses" 或 baseUrl 是 Codex 端点 → 走 Codex
  if (shouldUseCodexResponsesHooks(...)) return true;
  
  // 如果明确配置了 openai-responses 或 openai-completions → 不走 Codex
  if (ctx.providerConfig?.api === "openai-responses" || ...) return false;
  
  // 如果模型是"仅平台可用"(如 gpt-4o)→ 不走 Codex
  if (isOpenAIPlatformOnlyRouteModelId(ctx.modelId)) return false;
  
  // 如果模型是"仅订阅可用"(如 gpt-5.3-codex-spark)→ 走 Codex
  if (isOpenAISubscriptionOnlyRouteModelId(ctx.modelId)) return true;
  
  // 最终判断:如果 agentRuntimeId 是 "codex" → 走 Codex
  return ctx.agentRuntimeId === "codex";
}

3.5 模型前向兼容解析  (resolveOpenAIGptForwardCompatModel)  L707-844

这个函数解决一个问题:用户请求了一个 manifest 中不存在的模型 ID 怎么办?

function resolveOpenAIGptForwardCompatModel(ctx) {
  // 根据模型 ID 匹配已知模板
  if (lower === OPENAI_GPT_55_MODEL_ID) {
    templateIds = [OPENAI_GPT_55_MODEL_ID, OPENAI_GPT_54_MODEL_ID];
    patch = { api: "openai-responses", reasoning: true, cost: OPENAI_GPT_55_COST, ... };
  } else if (...) { ... }
  
  // 尝试从模板克隆(复用已有模型的配置,覆盖差异字段)
  return cloneFirstTemplateModel({ providerId, modelId, templateIds, ctx, patch })
    // 如果模板也找不到,用 patch 中的默认值构建
    ?? normalizeModelCompat({ id, name, ...patch, ... });
}

设计意图: 当 OpenAI 发布新模型时,即使 manifest 还没更新,只要模型 ID 匹配已知模式,就能自动推断出合理的配置。

4: ProviderPlugin 组装导出 (buildOpenAIProvider) L846-1085

这是整个文件的入口函数,返回一个完整的ProviderPlugin 对象,向 OpenClaw 注册:

export function buildOpenAIProvider(): ProviderPlugin {
  const codexHooks = buildOpenAICodexProviderHooks();          // Codex 通道的钩子
  const codexResponsesHooks = buildOpenAIResponsesProviderHooks(); // Codex Responses 钩子
  const responsesHooks = buildOpenAIResponsesProviderHooks({ transport: "sse" }); // 标准 SSE 钩子
  
  return {
    id: "openai",
    label: "OpenAI",
    hookAliases: ["azure-openai", "azure-openai-responses"],  // 兼容 Azure
    
    // ===== 认证方式 =====
    auth: [
      ...buildOpenAIChatGPTAuthMethods(),  // 登录 ChatGPT 账号
      createProviderApiKeyAuthMethod({...}), // 直接用 OPENAI_API_KEY
    ],
    
    // ===== 动态拉模型列表(在线) =====
    catalog: {  
      run: async (ctx) => {
        // 优先级:OAuth/Codex > API Key > 环境变量
        // 1. 尝试 OAuth 认证 → 走 Codex 模型发现
        // 2. 尝试 API Key → 走标准模型发现
        // 3. 都没有 → 返回 null
      },
    },
    
    // ===== 静态 Manifest 列表(离线兜底) =====
    staticCatalog: { run: async () => ({ providers: { openai: OPENAI_MANIFEST_PROVIDER } }) },
    
    // ===== 根据 auth 模式走 Codex 还是 API Key 路径 =====
    resolveDynamicModel: (ctx) =>
      shouldResolveDynamicModelThroughCodex(ctx)
        ? codexHooks.resolveDynamicModel?.(ctx)       // Codex 通道
        : resolveOpenAIGptForwardCompatModel(ctx),    // 标准通道
    
    // ===== 统一输出格式 =====
    normalizeResolvedModel: (ctx) => { /* 传输协议升级/降级 */ },
    
    // ===== completions → responses 自动升级 =====
    normalizeTransport: (ctx) => { /* completions → responses 升级 */ },
    
    // ===== 给请求附加 thinking/reasoning 参数 =====
    prepareExtraParams: (ctx) => {
      // 根据是 Codex 还是标准通道,选择不同的参数准备逻辑
      return (useCodexTransport ? codexResponsesHooks : responsesHooks).prepareExtraParams?.(ctx);
    },

    // =====   =====
    // ===== 用量查询(ChatGPT 订阅)  =====
    fetchUsageSnapshot: codexHooks.fetchUsageSnapshot,

    // ===== OAuth token 刷新  =====
    refreshOAuth: codexHooks.refreshOAuth,

    // ===== 错误分类(billing/server_error)  =====
    classifyFailoverReason: ({ code }) => classifyOpenAiFailoverCode(code),
    
    // ===== 错误处理 =====
    matchesContextOverflowError: ({ errorMessage }) => /content_filter.*too long/i.test(errorMessage),
    classifyFailoverReason: ({ code }) => classifyOpenAiFailoverCode(code),
    
    // ===== 思考模式 =====
    resolveReasoningOutputMode: () => "native",
    // =====Thinking 等级映射 (off/xhigh/max)=======
    resolveThinkingProfile: (...) => resolveUnifiedOpenAIThinkingProfile(...),
    
    // ===== 注入"合成"模型(GPT-5.5 Pro 等没有 API 的动态模型) =====
    augmentModelCatalog: (ctx) => { /* 为缺失的模型生成合成目录条目 */ },
  };
}

这个文件的核心设计思想

设计点 说明 答辩
双通道架构 同一个 OpenAI Provider 支持 API Key(标准)和 OAuth(Codex)两种认证通道 "Provider 层将认证方式和传输协议解耦,同一模型可以通过不同通道访问,系统自动选择最优路径"
三级模型发现 实时 API > 静态 Manifest > 前向兼容推断 "模型目录有三级降级策略,确保在任何网络条件下都能工作"
传输协议自动升级 Completions → Responses 自动迁移,但尊重用户显式配置 "框架做了协议演进的自动适配,同时保证用户配置的优先级最高"
Codex 是通道不是模型 Codex 是 OAuth 认证 + 专用端点 + 缩减上下文的组合 "Codex 不是一个独立模型,而是 OpenAI 为 Agent 运行时提供的专用接入通道"
成本感知 每个模型都有精确的 cost 定义(input/output/cacheRead/cacheWrite) "框架内置了成本元数据,上层 Agent 编排可以基于 cost 做模型选择"

更多推荐