UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

674 lines (673 loc) • 29.3 kB
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { a as asOptionalRecord } from "./record-coerce-TIsquoVR.mjs"; import { r as normalizeProviderId } from "./provider-id-D1VImdKk.mjs"; import "./defaults-Jag53zwP.mjs"; import { n as buildManifestModelProviderConfig, o as findCatalogTemplate } from "./provider-catalog-Io9OWhUB.mjs"; import "./string-coerce-runtime-50qKeWeL.mjs"; import { n as buildFamilyForwardCompatModel, t as matchesExactOrPrefix } from "./provider-model-id-match-BaRr80pd.mjs"; import { n as createProviderApiKeyAuthMethod } from "./provider-api-key-auth-CoMirT4h.mjs"; import "./provider-entry-B6zHcYqA.mjs"; import "./provider-model-metadata-fSBArSt7.mjs"; import { a as isOpenAIApiBaseUrl, i as classifyOpenAIBaseUrl, n as OPENAI_CODEX_RESPONSES_BASE_URL, o as isOpenAICodexBaseUrl, s as resolveOpenAIDefaultBaseUrl } from "./base-url-BUR_8zW8.mjs"; import { a as OPENAI_DEFAULT_MODEL, c as applyOpenAIConfig, t as OPENAI_CODEX_DEFAULT_MODEL } from "./default-models-CYEf1QuY.mjs"; import { C as isOpenAISubscriptionOnlyRouteModelId, S as isOpenAIPlatformOnlyRouteModelId, T as resolveOpenAICodexReasoningEfforts, b as buildOpenAIUnknownModelHint, c as OPENAI_GPT_54_NANO_MODEL_ID, d as OPENAI_GPT_55_PRO_MODEL_ID, f as OPENAI_GPT_56_LUNA_MODEL_ID, h as OPENAI_GPT_56_TERRA_MODEL_ID, l as OPENAI_GPT_54_PRO_MODEL_ID, m as OPENAI_GPT_56_SOL_MODEL_ID, n as OPENAI_CHAT_LATEST_MODEL_ID, o as OPENAI_GPT_54_MINI_MODEL_ID, p as OPENAI_GPT_56_MODEL_ID, r as OPENAI_DAYBREAK_MODEL_IDS, s as OPENAI_GPT_54_MODEL_ID, u as OPENAI_GPT_55_MODEL_ID, v as OPENAI_GPT_6_MODEL_IDS, w as normalizeOpenAIModelRouteId, y as OPENAI_PROVIDER_MODERN_MODEL_IDS } from "./model-route-contract-hw5lQEww.mjs"; import { t as modelCatalog } from "./openclaw.plugin-_vm__Dty.mjs"; import { t as resolveUnifiedOpenAIThinkingProfile } from "./thinking-policy-DzOL4GWr.mjs"; import { l as isSIWCAuthFlow, s as TOKEN_SHARING_RESOURCE } from "./token-sharing-CmBELw8t.mjs"; import { o as resolveAuthoredOpenAIProviderConfig } from "./provider-policy-api-DaHQqwNU.mjs"; import { n as buildOpenAIResponsesProviderHooks, t as OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS } from "./shared-C588ZwAA.mjs"; import { n as buildOpenAIAccountOnlyModels, t as OPENAI_UNKNOWN_MODEL_COST } from "./account-models-DatBd9YC.mjs"; import { i as shouldIncludeCodexModelRow, n as readCodexReasoningLevels, r as resolveCodexModelInput, t as readCodexModelRows } from "./codex-model-rows-BGjGXco2.mjs"; import { n as readOpenAICodexServiceTiers, t as projectOpenAICatalog } from "./model-service-tiers-nivcMQ5Y.mjs"; import { n as buildOpenAICodexProviderHooks, t as buildOpenAIChatGPTAuthMethodRuns } from "./openai-chatgpt-provider-DSoHd8aC.mjs"; import { t as createOpenAIProvider } from "./provider-contract-api-BEqjkoEA.mjs"; //#region extensions/openai/openai-provider.ts const PROVIDER_ID = "openai"; function classifyOpenAiFailoverCode(code) { switch (code?.trim().toUpperCase()) { case "SERVER_ERROR": return "server_error"; case "INSUFFICIENT_QUOTA": return "billing"; default: return; } } const OPENAI_MODELS_ENDPOINT = "https://api.openai.com/v1/models"; const OPENAI_CODEX_MODELS_ENDPOINT = `${OPENAI_CODEX_RESPONSES_BASE_URL}/models?client_version=0.160.0`; const OPENAI_MODELS_CACHE_TTL_MS = 6e4; const OPENAI_CODEX_MODELS_CACHE_TTL_MS = 6e4; const OPENAI_CODEX_GPT_56_CONTEXT_WINDOW = 372e3; const OPENAI_GPT_55_PRO_CONTEXT_WINDOW = 105e4; const OPENAI_GPT_54_CONTEXT_TOKENS = 105e4; const OPENAI_GPT_54_PRO_CONTEXT_TOKENS = 105e4; const OPENAI_GPT_54_MINI_CONTEXT_TOKENS = 4e5; const OPENAI_GPT_54_NANO_CONTEXT_TOKENS = 4e5; const OPENAI_GPT_54_MAX_TOKENS = 128e3; const OPENAI_CHAT_LATEST_COST = { input: 5, output: 30, cacheRead: .5, cacheWrite: 0 }; const OPENAI_GPT_55_PRO_TEMPLATE_MODEL_IDS = [OPENAI_GPT_54_PRO_MODEL_ID, OPENAI_GPT_54_MODEL_ID]; const OPENAI_GPT_54_TEMPLATE_MODEL_IDS = [OPENAI_GPT_54_MODEL_ID, OPENAI_GPT_55_MODEL_ID]; const OPENAI_GPT_54_PRO_TEMPLATE_MODEL_IDS = [OPENAI_GPT_54_PRO_MODEL_ID, OPENAI_GPT_55_PRO_MODEL_ID]; const OPENAI_GPT_54_MINI_TEMPLATE_MODEL_IDS = [OPENAI_GPT_54_MINI_MODEL_ID, "gpt-5-mini"]; const OPENAI_GPT_54_NANO_TEMPLATE_MODEL_IDS = [ OPENAI_GPT_54_NANO_MODEL_ID, "gpt-5-nano", "gpt-5-mini" ]; const OPENAI_CHAT_LATEST_TEMPLATE_MODEL_IDS = [OPENAI_GPT_55_MODEL_ID, OPENAI_GPT_54_MODEL_ID]; const OPENAI_GPT_56_TEMPLATE_MODEL_IDS = [OPENAI_GPT_55_MODEL_ID]; const OPENAI_MANIFEST_PROVIDER = buildManifestModelProviderConfig({ providerId: PROVIDER_ID, catalog: modelCatalog.providers.openai }); function buildOpenAIManifestModelsForBaseUrl(baseUrl) { return OPENAI_MANIFEST_PROVIDER.models.map((model) => model.api === "openai-chatgpt-responses" || isOpenAICodexBaseUrl(model.baseUrl) ? { ...model } : { ...model, api: model.api ?? OPENAI_MANIFEST_PROVIDER.api ?? "openai-responses", baseUrl }); } function buildOpenAIStaticPlatformProviderConfig(apiKey, baseUrl = resolveOpenAIDefaultBaseUrl()) { return { baseUrl, api: "openai-responses", ...apiKey ? { apiKey } : {}, models: buildOpenAIManifestModelsForBaseUrl(baseUrl) }; } async function buildOpenAILiveProviderConfig(params) { const baseUrl = normalizeOptionalString(params.baseUrl) ?? resolveOpenAIDefaultBaseUrl(params.env); const fallback = buildOpenAIStaticPlatformProviderConfig(params.apiKey, baseUrl); const models = fallback.models; if (!isOpenAIApiBaseUrl(baseUrl)) return { provider: fallback }; const [{ getCachedLiveProviderModelRows, LiveModelCatalogHttpError }, { isNonSecretApiKeyMarker }] = await Promise.all([import("./plugin-sdk/provider-catalog-live-runtime.js"), import("./plugin-sdk/provider-auth.js")]); const rejectionScope = params.apiKey && !params.discoveryApiKey && isNonSecretApiKeyMarker(params.apiKey) ? "catalog" : void 0; try { const rows = await getCachedLiveProviderModelRows({ providerId: PROVIDER_ID, endpoint: OPENAI_MODELS_ENDPOINT, apiKey: params.apiKey, discoveryApiKey: params.discoveryApiKey, fetchGuard: params.fetchGuard, signal: params.signal, ttlMs: OPENAI_MODELS_CACHE_TTL_MS, auditContext: "openai-model-discovery" }); const discoveredIds = new Set(rows.flatMap((row) => { const candidate = asOptionalRecord(row); if (candidate?.object !== void 0 && candidate.object !== "model") return []; const modelId = typeof candidate?.id === "string" ? candidate.id.trim() : ""; return modelId ? [modelId] : []; })); const selectedIds = /* @__PURE__ */ new Set(); const catalogModels = [...models, { id: OPENAI_CHAT_LATEST_MODEL_ID, name: "Chat Latest", reasoning: false, cost: OPENAI_CHAT_LATEST_COST, contextWindow: 4e5, api: "openai-responses", baseUrl, input: ["text", "image"], maxTokens: OPENAI_GPT_54_MAX_TOKENS }]; return { provider: { ...fallback, models: [...catalogModels.filter((model) => { if (!discoveredIds.has(model.id) || selectedIds.has(model.id)) return false; selectedIds.add(model.id); return true; }), ...buildOpenAIAccountOnlyModels({ discoveredIds, catalogModels, baseUrl })] }, outcome: { provider: PROVIDER_ID, status: "ready" } }; } catch (error) { if (error instanceof LiveModelCatalogHttpError && (error.status === 401 || error.status === 403)) return { provider: { ...fallback, models: [] }, outcome: { provider: PROVIDER_ID, ...rejectionScope ? { rejectionScope } : {}, status: "auth-rejected" } }; return { provider: fallback, outcome: { provider: PROVIDER_ID, status: "unavailable" } }; } } function normalizeOpenAICodexCatalogModel(model) { const modelId = normalizeLowercaseStringOrEmpty(model.id); if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra" || modelId === "gpt-5.6-luna") { const supportedReasoningEfforts = resolveOpenAICodexReasoningEfforts(modelId, model.compat?.supportedReasoningEfforts?.filter((effort) => effort !== "none")); return { ...model, contextWindow: OPENAI_CODEX_GPT_56_CONTEXT_WINDOW, contextTokens: OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS, thinkingLevelMap: { ...model.thinkingLevelMap, off: null }, ...model.compat ? { compat: { ...model.compat, ...supportedReasoningEfforts ? { supportedReasoningEfforts } : {} } } : {} }; } return model; } function resolveCodexModelFallback(modelId) { const fallbackModel = OPENAI_MANIFEST_PROVIDER.models.find((candidate) => normalizeLowercaseStringOrEmpty(candidate.id) === normalizeLowercaseStringOrEmpty(modelId)); return fallbackModel ? normalizeOpenAICodexCatalogModel(fallbackModel) : void 0; } function buildOpenAICodexModelFromLiveRow(row, readers) { const { readLiveModelCatalogStringField, readLiveModelCatalogPositiveSafeIntegerField } = readers; if (!shouldIncludeCodexModelRow(row, readers)) return; const modelId = readLiveModelCatalogStringField(row, "slug") ?? readLiveModelCatalogStringField(row, "id"); if (!modelId) return; if (isOpenAIPlatformOnlyRouteModelId(modelId)) return; const normalizedModelId = normalizeLowercaseStringOrEmpty(modelId); const fallback = resolveCodexModelFallback(modelId); const reasoningLevels = readCodexReasoningLevels(row); const observedContextTokens = readLiveModelCatalogPositiveSafeIntegerField(row, ["context_window", "contextWindow"]); const contextTokens = matchesExactOrPrefix(normalizedModelId, ["gpt-5.6"]) ? Math.min(observedContextTokens ?? fallback?.contextTokens ?? 272e3, OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS) : observedContextTokens; const contextWindow = readLiveModelCatalogPositiveSafeIntegerField(row, ["max_context_window", "maxContextWindow"]) ?? fallback?.contextWindow ?? observedContextTokens ?? 2e5; const maxTokens = readLiveModelCatalogPositiveSafeIntegerField(row, [ "max_output_tokens", "maxOutputTokens", "max_completion_tokens", "maxCompletionTokens" ]) ?? fallback?.maxTokens ?? OPENAI_GPT_54_MAX_TOKENS; const compat = reasoningLevels !== void 0 ? { ...fallback?.compat, supportsReasoningEffort: true, supportedReasoningEfforts: [...reasoningLevels] } : fallback?.compat; const thinkingLevelMap = { ...reasoningLevels === void 0 ? fallback?.thinkingLevelMap : {}, ...fallback?.thinkingLevelMap?.off === null || normalizedModelId.startsWith("gpt-5.6") ? { off: null } : {}, ...reasoningLevels?.includes("xhigh") ? { xhigh: "xhigh" } : {}, ...reasoningLevels?.includes("max") ? { max: "max" } : {} }; return normalizeOpenAICodexCatalogModel({ id: modelId, name: readLiveModelCatalogStringField(row, "display_name") ?? fallback?.name ?? modelId, api: "openai-chatgpt-responses", baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL, reasoning: (reasoningLevels?.length ?? 0) > 0 || fallback?.reasoning || false, input: resolveCodexModelInput(row, fallback), cost: fallback?.cost ?? OPENAI_UNKNOWN_MODEL_COST, contextWindow, maxTokens, ...contextTokens ?? fallback?.contextTokens ? { contextTokens: contextTokens ?? fallback?.contextTokens } : {}, ...fallback?.mediaInput ? { mediaInput: fallback.mediaInput } : {}, ...compat ? { compat } : {}, ...Object.keys(thinkingLevelMap).length > 0 ? { thinkingLevelMap } : {} }); } function buildOpenAICodexStaticProviderConfig() { return { baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL, api: "openai-chatgpt-responses", auth: "oauth", models: OPENAI_MANIFEST_PROVIDER.models.flatMap((model) => { const modelId = normalizeLowercaseStringOrEmpty(model.id); if (isOpenAIPlatformOnlyRouteModelId(modelId)) return []; if (OPENAI_GPT_6_MODEL_IDS.some((id) => id === modelId) || OPENAI_DAYBREAK_MODEL_IDS.some((id) => id === modelId) || modelId.startsWith("gpt-5.6") && modelId !== "gpt-5.6-sol") return []; return [normalizeOpenAICodexCatalogModel(model)]; }) }; } async function buildOpenAICodexLiveProviderConfig(params) { const catalogRuntime = await import("./plugin-sdk/provider-catalog-live-runtime.js"); const { getCachedLiveProviderModelRows, LiveModelCatalogHttpError } = catalogRuntime; try { const rows = await getCachedLiveProviderModelRows({ providerId: PROVIDER_ID, endpoint: OPENAI_CODEX_MODELS_ENDPOINT, discoveryApiKey: params.discoveryApiKey, fetchGuard: params.fetchGuard, signal: params.signal, ttlMs: OPENAI_CODEX_MODELS_CACHE_TTL_MS, auditContext: "openai-model-discovery", readRows: readCodexModelRows, buildRequestHeaders: ({ discoveryApiKey }) => ({ Accept: "application/json", ...discoveryApiKey ? { Authorization: `Bearer ${discoveryApiKey}` } : {}, ...params.accountId ? { "ChatGPT-Account-ID": params.accountId } : {} }), cacheKeyParts: [ PROVIDER_ID, "codex-model-rows", OPENAI_CODEX_MODELS_ENDPOINT, params.discoveryApiKey, params.accountId ?? "" ] }); const models = rows.map((row) => buildOpenAICodexModelFromLiveRow(row, catalogRuntime)).filter((model) => Boolean(model)); const modelServiceTiers = readOpenAICodexServiceTiers(rows); return { provider: { baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL, api: "openai-chatgpt-responses", auth: "oauth", models }, outcome: { provider: PROVIDER_ID, status: "ready", ...modelServiceTiers.length ? { modelServiceTiers } : {} } }; } catch (error) { if (error instanceof LiveModelCatalogHttpError && (error.status === 401 || error.status === 403)) return { provider: { ...buildOpenAICodexStaticProviderConfig(), models: [] }, outcome: { provider: PROVIDER_ID, status: "auth-rejected" } }; } return { provider: buildOpenAICodexStaticProviderConfig(), outcome: { provider: PROVIDER_ID, status: "unavailable" } }; } function isCodexCatalogAuthMode(mode) { return mode === "oauth" || mode === "token"; } function resolveOpenAICatalogBaseUrl(ctx) { const configuredProvider = Object.entries(ctx.config?.models?.providers ?? {}).find(([providerId]) => normalizeProviderId(providerId) === PROVIDER_ID)?.[1]; return normalizeOptionalString(configuredProvider?.baseUrl) ?? resolveOpenAIDefaultBaseUrl(ctx.env ?? process.env); } function shouldUseOpenAIResponsesTransport(params) { if (params.api !== "openai-completions") return false; const isOwnerProvider = normalizeProviderId(params.provider) === PROVIDER_ID; const isPlatformEndpoint = typeof params.baseUrl === "string" && classifyOpenAIBaseUrl(params.baseUrl) === "platform"; if (isOwnerProvider) { if (resolveAuthoredOpenAICompletionsRoute(params)) return false; return !params.baseUrl || isPlatformEndpoint; } return isPlatformEndpoint; } /** Resolves the effective authored OpenAI config route for one model. */ function resolveAuthoredOpenAIConfigRoute(params) { const providerConfig = resolveAuthoredOpenAIProviderConfig(params); if (!providerConfig) return; const modelId = normalizeOpenAIModelRouteId(params.modelId); let modelConfig; for (const model of providerConfig.models ?? []) { if (normalizeOpenAIModelRouteId(model.id) !== modelId) continue; modelConfig = modelConfig ? { ...model, ...modelConfig } : model; } return { ...modelConfig ? { configuredModel: modelConfig } : {}, configuredProvider: providerConfig }; } /** Authored Completions is a current transport contract; only catalog defaults are upgraded. */ function resolveAuthoredOpenAICompletionsRoute(params) { const configuredRoute = resolveAuthoredOpenAIConfigRoute(params); if (!configuredRoute) return; if ((normalizeOptionalString(configuredRoute.configuredModel?.api) ?? normalizeOptionalString(configuredRoute.configuredProvider.api)) !== "openai-completions") return; return { api: "openai-completions", baseUrl: normalizeOptionalString(configuredRoute.configuredModel?.baseUrl) ?? normalizeOptionalString(configuredRoute.configuredProvider.baseUrl) ?? resolveOpenAIDefaultBaseUrl(process.env) }; } function shouldUseCodexResponsesHooks(params) { if (params.api === "openai-chatgpt-responses") return true; return typeof params.baseUrl === "string" && isOpenAICodexBaseUrl(params.baseUrl); } function shouldResolveDynamicModelThroughCodex(ctx) { if (shouldUseCodexResponsesHooks({ provider: ctx.provider, api: ctx.providerConfig?.api, baseUrl: ctx.providerConfig?.baseUrl })) return true; if (ctx.providerConfig?.api === "openai-responses" || ctx.providerConfig?.api === "openai-completions" || ctx.providerConfig?.baseUrl && !isOpenAICodexBaseUrl(ctx.providerConfig.baseUrl)) return false; if (isOpenAIPlatformOnlyRouteModelId(ctx.modelId)) return false; if (isOpenAISubscriptionOnlyRouteModelId(ctx.modelId)) return true; return ctx.agentRuntimeId === "codex"; } const OPENAI_GPT_FORWARD_COMPAT_CASES = [ { match: OPENAI_DAYBREAK_MODEL_IDS, templateIds: [OPENAI_GPT_56_SOL_MODEL_ID] }, { match: OPENAI_GPT_6_MODEL_IDS, templateIds: [OPENAI_GPT_56_SOL_MODEL_ID, OPENAI_GPT_55_MODEL_ID] }, { match: [OPENAI_CHAT_LATEST_MODEL_ID], templateIds: OPENAI_CHAT_LATEST_TEMPLATE_MODEL_IDS, patch: { reasoning: false, cost: OPENAI_CHAT_LATEST_COST, contextWindow: 4e5 } }, { match: [ OPENAI_GPT_56_MODEL_ID, OPENAI_GPT_56_SOL_MODEL_ID, OPENAI_GPT_56_TERRA_MODEL_ID, OPENAI_GPT_56_LUNA_MODEL_ID ], templateIds: OPENAI_GPT_56_TEMPLATE_MODEL_IDS }, { match: [OPENAI_GPT_55_MODEL_ID], templateIds: [OPENAI_GPT_55_MODEL_ID, OPENAI_GPT_54_MODEL_ID] }, { match: [OPENAI_GPT_55_PRO_MODEL_ID], templateIds: OPENAI_GPT_55_PRO_TEMPLATE_MODEL_IDS }, { match: [OPENAI_GPT_54_MODEL_ID], templateIds: OPENAI_GPT_54_TEMPLATE_MODEL_IDS }, { match: [OPENAI_GPT_54_PRO_MODEL_ID], templateIds: OPENAI_GPT_54_PRO_TEMPLATE_MODEL_IDS }, { match: [OPENAI_GPT_54_MINI_MODEL_ID], templateIds: OPENAI_GPT_54_MINI_TEMPLATE_MODEL_IDS }, { match: [OPENAI_GPT_54_NANO_MODEL_ID], templateIds: OPENAI_GPT_54_NANO_TEMPLATE_MODEL_IDS } ]; function resolveOpenAIGptForwardCompatModel(ctx) { const trimmedModelId = ctx.modelId.trim(); const modelId = normalizeLowercaseStringOrEmpty(trimmedModelId); const exactModel = ctx.modelRegistry.find(PROVIDER_ID, trimmedModelId); if (OPENAI_DAYBREAK_MODEL_IDS.some((id) => id === modelId) || OPENAI_GPT_6_MODEL_IDS.some((id) => id === modelId) || modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra" || modelId === "gpt-5.6-luna") { if (exactModel) return exactModel; } const catalogModel = OPENAI_MANIFEST_PROVIDER.models.find(({ id }) => id === (modelId === "gpt-5.6" ? OPENAI_GPT_56_SOL_MODEL_ID : modelId)); return buildFamilyForwardCompatModel({ providerId: PROVIDER_ID, ctx, cases: OPENAI_GPT_FORWARD_COMPAT_CASES, patch: { reasoning: true, maxTokens: OPENAI_GPT_54_MAX_TOKENS, ...catalogModel, id: trimmedModelId, name: trimmedModelId, api: "openai-responses", provider: PROVIDER_ID, baseUrl: resolveOpenAIDefaultBaseUrl(), input: catalogModel ? catalogModel.input.filter((item) => item === "text" || item === "image") : ["text", "image"], compat: exactModel ? exactModel.compat : catalogModel?.compat }, synthesize: true }); } function buildOpenAIProvider() { const providerDefinition = createOpenAIProvider(); const apiKeyDefinition = providerDefinition.auth.find((method) => method.id === "api-key"); if (!apiKeyDefinition) throw new Error("OpenAI provider contract is missing API-key auth"); const chatGPTAuthRuns = buildOpenAIChatGPTAuthMethodRuns(); const apiKeyRuntime = createProviderApiKeyAuthMethod({ providerId: PROVIDER_ID, methodId: apiKeyDefinition.id, label: apiKeyDefinition.label, hint: apiKeyDefinition.hint, optionKey: "openaiApiKey", flagName: "--openai-api-key", envVar: "OPENAI_API_KEY", promptMessage: "Enter OpenAI API key", profileId: "openai:api-key", defaultModel: OPENAI_DEFAULT_MODEL, preserveExistingPrimary: true, expectedProviders: [PROVIDER_ID], applyConfig: (cfg) => applyOpenAIConfig(cfg), wizard: apiKeyDefinition.wizard }); for (const method of providerDefinition.auth) { if (method.id === "siwc") { method.starterModel = OPENAI_DEFAULT_MODEL; method.run = async (ctx) => (await import("./extensions/openai/token-sharing-oauth.runtime.js")).loginTokenSharing(ctx); continue; } if (method.id === "oauth" || method.id === "device-code") { method.starterModel = OPENAI_CODEX_DEFAULT_MODEL; method.run = chatGPTAuthRuns[method.id]; continue; } if (method.id !== "api-key") throw new Error(`OpenAI provider contract has unknown auth method: ${method.id}`); method.starterModel = apiKeyRuntime.starterModel; method.run = apiKeyRuntime.run; method.runNonInteractive = apiKeyRuntime.runNonInteractive; method.validateNonInteractive = apiKeyRuntime.validateNonInteractive; } const codexHooks = buildOpenAICodexProviderHooks(); const nativeResponsesHooks = buildOpenAIResponsesProviderHooks(); const responsesHooks = buildOpenAIResponsesProviderHooks({ transport: "sse" }); return { ...providerDefinition, catalog: { order: "simple", run: async (ctx) => { if (ctx.providerIds && !ctx.providerIds.includes(PROVIDER_ID)) return null; const auth = ctx.resolveProviderAuth(PROVIDER_ID); if (isSIWCAuthFlow(auth.authFlow)) { const { buildTokenSharingCatalog } = await import("./extensions/openai/token-sharing-catalog.js"); return await buildTokenSharingCatalog({ auth, models: buildOpenAIStaticPlatformProviderConfig(void 0, TOKEN_SHARING_RESOURCE).models, signal: ctx.signal }); } if (auth.preparationFailed) return null; const [{ resolveApiKeyForProvider, resolveProviderAuthProfileMetadata }, { isNonSecretApiKeyMarker }] = await Promise.all([import("./plugin-sdk/provider-auth-runtime.js"), import("./plugin-sdk/provider-auth.js")]); let runtimeAuth; if (auth.profileId || auth.mode === "none") try { runtimeAuth = await resolveApiKeyForProvider({ provider: PROVIDER_ID, cfg: ctx.config, ...ctx.agentDir ? { agentDir: ctx.agentDir } : {}, ...ctx.workspaceDir ? { workspaceDir: ctx.workspaceDir } : {}, ...auth.profileId ? { profileId: auth.profileId, lockedProfile: true } : {} }); } catch { runtimeAuth = void 0; } if (runtimeAuth && isCodexCatalogAuthMode(runtimeAuth.mode) && runtimeAuth.apiKey) { const metadata = resolveProviderAuthProfileMetadata({ provider: PROVIDER_ID, cfg: ctx.config, ...ctx.agentDir ? { agentDir: ctx.agentDir } : {}, ...runtimeAuth.profileId ?? auth.profileId ? { profileId: runtimeAuth.profileId ?? auth.profileId } : {} }); return projectOpenAICatalog(await buildOpenAICodexLiveProviderConfig({ discoveryApiKey: runtimeAuth.apiKey, accountId: metadata.accountId }), runtimeAuth.profileId ?? auth.profileId); } if (!auth.profileId && isCodexCatalogAuthMode(auth.mode) && auth.apiKey) { const discoveryApiKey = auth.discoveryApiKey ?? (isNonSecretApiKeyMarker(auth.apiKey) ? void 0 : auth.apiKey); if (!discoveryApiKey) return { providers: { [PROVIDER_ID]: buildOpenAICodexStaticProviderConfig() }, outcomes: [{ provider: PROVIDER_ID, status: "unavailable" }] }; return projectOpenAICatalog(await buildOpenAICodexLiveProviderConfig({ discoveryApiKey })); } if (auth.profileId && isCodexCatalogAuthMode(auth.mode)) return { providers: { [PROVIDER_ID]: buildOpenAICodexStaticProviderConfig() }, outcomes: [{ provider: PROVIDER_ID, profileId: auth.profileId, status: "unavailable" }] }; if (auth.mode === "api_key" && auth.apiKey) { const discoveryApiKey = auth.discoveryApiKey ?? (runtimeAuth?.mode === "api-key" ? runtimeAuth.apiKey : void 0); if (!discoveryApiKey && isNonSecretApiKeyMarker(auth.apiKey)) return { providers: { [PROVIDER_ID]: buildOpenAIStaticPlatformProviderConfig(auth.apiKey, resolveOpenAICatalogBaseUrl(ctx)) }, outcomes: [{ provider: PROVIDER_ID, ...auth.profileId ? { profileId: auth.profileId } : {}, rejectionScope: "catalog", status: "unavailable" }] }; return projectOpenAICatalog(await buildOpenAILiveProviderConfig({ apiKey: auth.apiKey, baseUrl: resolveOpenAICatalogBaseUrl(ctx), discoveryApiKey }), auth.profileId); } return null; } }, staticCatalog: { order: "simple", run: async () => ({ providers: { [PROVIDER_ID]: OPENAI_MANIFEST_PROVIDER } }) }, resolveDynamicModel: (ctx) => shouldResolveDynamicModelThroughCodex(ctx) ? codexHooks.resolveDynamicModel?.(ctx) : resolveOpenAIGptForwardCompatModel(ctx), preferRuntimeResolvedModel: (ctx) => codexHooks.preferRuntimeResolvedModel?.(ctx) ?? false, normalizeResolvedModel: (ctx) => { if (normalizeProviderId(ctx.provider ?? "") !== PROVIDER_ID) return; const authoredCompletionsRoute = resolveAuthoredOpenAICompletionsRoute(ctx); if (authoredCompletionsRoute) return { ...ctx.model, ...authoredCompletionsRoute }; if (shouldUseCodexResponsesHooks({ provider: ctx.provider, api: ctx.model.api, baseUrl: ctx.model.baseUrl })) return codexHooks.normalizeResolvedModel?.(ctx); return shouldUseOpenAIResponsesTransport({ ...ctx.model, modelId: ctx.modelId, config: ctx.config }) ? { ...ctx.model, api: "openai-responses" } : ctx.model; }, normalizeTransport: (ctx) => { const authoredCompletionsRoute = resolveAuthoredOpenAICompletionsRoute(ctx); if (authoredCompletionsRoute) return ctx.api === authoredCompletionsRoute.api && ctx.baseUrl === authoredCompletionsRoute.baseUrl ? void 0 : authoredCompletionsRoute; if (shouldUseCodexResponsesHooks(ctx)) return codexHooks.normalizeTransport?.(ctx); return shouldUseOpenAIResponsesTransport(ctx) ? { api: "openai-responses", baseUrl: ctx.baseUrl } : void 0; }, ...responsesHooks, prepareExtraParams: (ctx) => { if (ctx.auth?.mode === "oauth" && ctx.auth.authFlow === "chatgpt-token-sharing") return { ...ctx.extraParams, transport: "sse", store: false, responsesServerCompaction: false }; const providerConfig = ctx.config?.models?.providers?.[PROVIDER_ID]; return (shouldUseCodexResponsesHooks({ provider: ctx.provider, api: ctx.model?.api, baseUrl: ctx.model?.baseUrl }) || normalizeProviderId(ctx.provider) === PROVIDER_ID && (!providerConfig?.baseUrl || isOpenAIApiBaseUrl(providerConfig.baseUrl)) && isCodexCatalogAuthMode(providerConfig?.auth ?? "") ? nativeResponsesHooks : responsesHooks).prepareExtraParams?.(ctx); }, resolveUsageAuth: codexHooks.resolveUsageAuth, fetchUsageSnapshot: codexHooks.fetchUsageSnapshot, refreshOAuth: async (credential) => isSIWCAuthFlow(credential.authFlow) ? (await import("./extensions/openai/token-sharing-oauth.runtime.js")).refreshTokenSharingCredential(credential) : codexHooks.refreshOAuth(credential), buildUnknownModelHint: ({ modelId }) => buildOpenAIUnknownModelHint(modelId), buildMissingAuthMessage: (ctx) => { if (normalizeProviderId(ctx.provider) !== PROVIDER_ID) return; if (ctx.listProfileIds(PROVIDER_ID).length === 0) return; return `No API key found for provider "openai". You are authenticated with OpenAI ChatGPT/Codex OAuth. Use ${OPENAI_CODEX_DEFAULT_MODEL} with the ChatGPT/Codex OAuth profile, or set OPENAI_API_KEY for direct OpenAI API access.`; }, matchesContextOverflowError: ({ errorMessage }) => /content_filter.*(?:prompt|input).*(?:too long|exceed)/i.test(errorMessage), classifyFailoverReason: ({ code }) => classifyOpenAiFailoverCode(code), resolveReasoningOutputMode: () => "native", resolveThinkingProfile: ({ provider, modelId, agentRuntime, api, compat, thinkingLevelMap }) => normalizeProviderId(provider) === PROVIDER_ID ? resolveUnifiedOpenAIThinkingProfile(modelId, agentRuntime, compat, api, thinkingLevelMap) : null, isModernModelRef: ({ modelId }) => matchesExactOrPrefix(modelId, OPENAI_PROVIDER_MODERN_MODEL_IDS), augmentModelCatalog: (ctx) => { return [ { id: OPENAI_GPT_55_PRO_MODEL_ID, templateIds: OPENAI_GPT_55_PRO_TEMPLATE_MODEL_IDS, contextWindow: OPENAI_GPT_55_PRO_CONTEXT_WINDOW, contextTokens: OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS }, { id: OPENAI_GPT_54_MODEL_ID, templateIds: OPENAI_GPT_54_TEMPLATE_MODEL_IDS, contextWindow: OPENAI_GPT_54_CONTEXT_TOKENS }, { id: OPENAI_GPT_54_PRO_MODEL_ID, templateIds: OPENAI_GPT_54_PRO_TEMPLATE_MODEL_IDS, contextWindow: OPENAI_GPT_54_PRO_CONTEXT_TOKENS }, { id: OPENAI_GPT_54_MINI_MODEL_ID, templateIds: OPENAI_GPT_54_MINI_TEMPLATE_MODEL_IDS, contextWindow: OPENAI_GPT_54_MINI_CONTEXT_TOKENS }, { id: OPENAI_GPT_54_NANO_MODEL_ID, templateIds: OPENAI_GPT_54_NANO_TEMPLATE_MODEL_IDS, contextWindow: OPENAI_GPT_54_NANO_CONTEXT_TOKENS } ].flatMap(({ templateIds, ...model }) => { const template = findCatalogTemplate({ entries: ctx.entries, providerId: PROVIDER_ID, templateIds }); const input = ["text", "image"]; return template ? [{ ...template, ...model, name: model.id, reasoning: true, input }] : []; }); } }; } //#endregion export { buildOpenAIProvider as t };