openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
783 lines (782 loc) • 31.8 kB
JavaScript
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import "./defaults-CdX9UGcX.js";
import { c as isNonSecretApiKeyMarker } from "./model-auth-markers-jBKQn38x.js";
import { n as buildManifestModelProviderConfig, o as findCatalogTemplate } from "./provider-catalog-8ARoyQkL.js";
import "./provider-auth-BeZ7NZUU.js";
import { t as createProviderApiKeyAuthMethod } from "./provider-api-key-auth-mNtReYE0.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import { d as normalizeProviderId, h as resolveFamilyForwardCompatModel, m as matchesExactOrPrefix } from "./provider-model-shared-fX-Ywqb3.js";
import "./provider-auth-api-key-DSeBB_JQ.js";
import { c as getCachedLiveProviderModelRows, h as readLiveModelCatalogStringField, m as readLiveModelCatalogPositiveSafeIntegerField, p as readLiveModelCatalogBooleanField, t as LiveModelCatalogHttpError } from "./provider-catalog-live-runtime-EZZSw18v.js";
import "./provider-catalog-shared-B4jeC2Nd.js";
import { c as resolveOpenAIDefaultBaseUrl, i as classifyOpenAIBaseUrl, n as OPENAI_CODEX_RESPONSES_BASE_URL, o as isOpenAICodexBaseUrl, s as isOpenAIHttpsApiBaseUrl } from "./base-url-DVAaAeRo.js";
import { a as OPENAI_DEFAULT_MODEL, c as applyOpenAIConfig, t as OPENAI_CODEX_DEFAULT_MODEL } from "./default-models-Dn67kTkm.js";
import { n as buildOpenAIResponsesProviderHooks, r as buildOpenAISyntheticCatalogEntry, t as OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS } from "./shared-BAfroQe-.js";
import { S as resolveOpenAICodexReasoningEfforts, _ as OPENAI_PROVIDER_MODERN_MODEL_IDS, a as OPENAI_GPT_54_MINI_MODEL_ID, b as isOpenAISubscriptionOnlyRouteModelId, c as OPENAI_GPT_54_PRO_MODEL_ID, d as OPENAI_GPT_56_LUNA_MODEL_ID, f as OPENAI_GPT_56_MODEL_ID, g as OPENAI_GPT_6_ASTRA_MODEL_ID, l as OPENAI_GPT_55_MODEL_ID, m as OPENAI_GPT_56_TERRA_MODEL_ID, n as OPENAI_CHAT_LATEST_MODEL_ID, o as OPENAI_GPT_54_MODEL_ID, p as OPENAI_GPT_56_SOL_MODEL_ID, s as OPENAI_GPT_54_NANO_MODEL_ID, u as OPENAI_GPT_55_PRO_MODEL_ID, x as normalizeOpenAIModelRouteId, y as isOpenAIPlatformOnlyRouteModelId } from "./model-route-contract-pNgq17sM.js";
import { n as resolveUnifiedOpenAIThinkingProfile, r as modelCatalog } from "./thinking-policy-C1E_gzmv.js";
import { n as buildOpenAICodexProviderHooks, t as buildOpenAIChatGPTAuthMethodRuns } from "./openai-chatgpt-provider-DMrfR8t_.js";
import { t as createOpenAIProvider } from "./provider-contract-api-iXo0BzTG.js";
import { a as resolveAuthoredOpenAIProviderConfig } from "./provider-policy-api-CAxxTyEq.js";
//#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.153.4`;
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_54_COST = {
input: 2.5,
output: 15,
cacheRead: .25,
cacheWrite: 0
};
const OPENAI_GPT_54_PRO_COST = {
input: 30,
output: 180,
cacheRead: 0,
cacheWrite: 0
};
const OPENAI_GPT_54_MINI_COST = {
input: .75,
output: 4.5,
cacheRead: .075,
cacheWrite: 0
};
const OPENAI_GPT_54_NANO_COST = {
input: .2,
output: 1.25,
cacheRead: .02,
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_UNKNOWN_MODEL_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
};
const OPENAI_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: PROVIDER_ID,
catalog: modelCatalog.providers.openai
});
function shouldFetchOpenAILiveModels(baseUrl) {
return isOpenAIHttpsApiBaseUrl(baseUrl);
}
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 buildOpenAIDiscoverablePlatformModels(baseUrl) {
return [
{
id: OPENAI_CHAT_LATEST_MODEL_ID,
name: "Chat Latest",
reasoning: false,
cost: OPENAI_CHAT_LATEST_COST,
contextWindow: 4e5
},
{
id: OPENAI_GPT_54_MODEL_ID,
name: "GPT-5.4",
reasoning: true,
cost: OPENAI_GPT_54_COST,
contextWindow: OPENAI_GPT_54_CONTEXT_TOKENS
},
{
id: OPENAI_GPT_54_PRO_MODEL_ID,
name: "GPT-5.4 Pro",
reasoning: true,
cost: OPENAI_GPT_54_PRO_COST,
contextWindow: OPENAI_GPT_54_PRO_CONTEXT_TOKENS
},
{
id: OPENAI_GPT_54_MINI_MODEL_ID,
name: "GPT-5.4 Mini",
reasoning: true,
cost: OPENAI_GPT_54_MINI_COST,
contextWindow: OPENAI_GPT_54_MINI_CONTEXT_TOKENS
},
{
id: OPENAI_GPT_54_NANO_MODEL_ID,
name: "GPT-5.4 Nano",
reasoning: true,
cost: OPENAI_GPT_54_NANO_COST,
contextWindow: OPENAI_GPT_54_NANO_CONTEXT_TOKENS
}
].map(({ id, name, reasoning, cost, contextWindow }) => ({
id,
name,
reasoning,
cost,
contextWindow,
api: "openai-responses",
baseUrl,
input: ["text", "image"],
maxTokens: OPENAI_GPT_54_MAX_TOKENS
}));
}
function scopeOpenAICatalogOutcome(catalog, profileId) {
const scopedProfileId = profileId?.trim();
if (!catalog.outcome || !scopedProfileId) return catalog;
return {
...catalog,
outcome: {
...catalog.outcome,
profileId: scopedProfileId
}
};
}
async function buildOpenAILiveProviderConfig(params) {
const baseUrl = normalizeOptionalString(params.baseUrl) ?? resolveOpenAIDefaultBaseUrl(params.env);
const models = buildOpenAIManifestModelsForBaseUrl(baseUrl);
const fallback = {
baseUrl,
api: "openai-responses",
...params.apiKey ? { apiKey: params.apiKey } : {},
models
};
if (!shouldFetchOpenAILiveModels(baseUrl)) return { provider: fallback };
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) => {
if (!row || typeof row !== "object" || Array.isArray(row)) return [];
const candidate = 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();
return {
provider: {
...fallback,
models: [...models, ...buildOpenAIDiscoverablePlatformModels(baseUrl)].filter((model) => {
if (!discoveredIds.has(model.id) || selectedIds.has(model.id)) return false;
selectedIds.add(model.id);
return true;
})
},
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,
...params.rejectionScope ? { rejectionScope: "catalog" } : {},
status: "auth-rejected"
}
};
return {
provider: fallback,
outcome: {
provider: PROVIDER_ID,
status: "unavailable"
}
};
}
}
function readCodexModelStringArray(row, keys) {
if (!row || typeof row !== "object" || Array.isArray(row)) return [];
const record = row;
for (const key of keys) {
const value = record[key];
if (Array.isArray(value)) return value.filter((entry) => typeof entry === "string");
}
return [];
}
function readCodexReasoningLevels(row) {
if (!row || typeof row !== "object" || Array.isArray(row)) return;
const record = row;
const value = record.supported_reasoning_levels ?? record.supportedReasoningLevels;
if (!Array.isArray(value)) return;
return value.flatMap((entry) => {
if (typeof entry === "string" && entry.trim().length > 0) return [entry.trim()];
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
const effort = entry.effort;
return typeof effort === "string" && effort.trim().length > 0 ? [effort.trim()] : [];
}
return [];
});
}
function readCodexModelRows(body) {
if (!body || typeof body !== "object" || Array.isArray(body)) throw new Error("OpenAI Codex model discovery response must be { models: [] }");
const models = body.models;
if (!Array.isArray(models)) throw new Error("OpenAI Codex model discovery response must be { models: [] }");
return models;
}
function shouldIncludeCodexModelRow(row) {
const visibility = normalizeLowercaseStringOrEmpty(readLiveModelCatalogStringField(row, "visibility") ?? "");
if (visibility && visibility !== "list") return false;
return (readLiveModelCatalogBooleanField(row, "show_in_picker") ?? readLiveModelCatalogBooleanField(row, "showInPicker")) !== false;
}
function resolveCodexModelInput(row, fallback) {
const rawModalities = readCodexModelStringArray(row, ["input_modalities", "inputModalities"]);
if (rawModalities.length === 0) return fallback?.input ?? ["text", "image"];
const modalities = new Set(rawModalities.map((modality) => normalizeLowercaseStringOrEmpty(modality)));
const input = /* @__PURE__ */ new Set();
if (modalities.has("text")) input.add("text");
if (modalities.has("image") || modalities.has("vision")) input.add("image");
if (modalities.has("audio")) input.add("audio");
if (modalities.has("video")) input.add("video");
return input.size > 0 ? [...input] : fallback?.input ?? ["text", "image"];
}
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) {
if (!shouldIncludeCodexModelRow(row)) 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 : {},
...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 (modelId === "gpt-6-astra" || modelId.startsWith("gpt-5.6") && modelId !== "gpt-5.6-sol") return [];
return [normalizeOpenAICodexCatalogModel(model)];
})
};
}
async function buildOpenAICodexLiveProviderConfig(params) {
try {
const models = (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 ?? ""
]
})).map(buildOpenAICodexModelFromLiveRow).filter((model) => Boolean(model));
return {
provider: {
baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,
api: "openai-chatgpt-responses",
auth: "oauth",
models
},
outcome: {
provider: PROVIDER_ID,
status: "ready"
}
};
} 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 resolveOpenAICatalogRejectionScope(auth) {
return auth.apiKey && !auth.discoveryApiKey && isNonSecretApiKeyMarker(auth.apiKey) ? "catalog" : void 0;
}
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 isOpenAIProvider(provider) {
return normalizeProviderId(provider ?? "") === PROVIDER_ID;
}
function normalizeOpenAITransport(model, context) {
if (!shouldUseOpenAIResponsesTransport({
provider: model.provider,
modelId: context?.modelId,
api: model.api,
baseUrl: model.baseUrl,
config: context?.config
})) return model;
return {
...model,
api: "openai-responses"
};
}
function shouldUseCodexResponsesHooks(params) {
if (params.api === "openai-chatgpt-responses") return true;
return typeof params.baseUrl === "string" && isOpenAICodexBaseUrl(params.baseUrl);
}
function resolveConfiguredProviderAuthTransport(providerConfig) {
const authMode = providerConfig?.auth;
if (authMode === "oauth" || authMode === "token") return "codex";
if (authMode === "api-key") return "responses";
}
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";
}
function buildOpenAIUnknownModelHint(modelId) {
if (normalizeLowercaseStringOrEmpty(modelId) !== "gpt-5.3-codex-spark") return;
return "gpt-5.3-codex-spark is available only through ChatGPT/Codex OAuth. Run `openclaw models auth login --provider openai` and use openai/gpt-5.3-codex-spark with that OAuth profile; OpenAI API-key auth cannot use this model.";
}
const OPENAI_GPT_FORWARD_COMPAT_CASES = [
{
match: [OPENAI_GPT_6_ASTRA_MODEL_ID],
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 (modelId === "gpt-6-astra" || 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 resolveFamilyForwardCompatModel({
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 === "oauth" || method.id === "device-code") {
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);
try {
const { resolveApiKeyForProvider, resolveProviderAuthProfileMetadata } = await import("./plugin-sdk/provider-auth-runtime.js");
const 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
} : {}
});
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 } : {}
});
const catalog = scopeOpenAICatalogOutcome(await buildOpenAICodexLiveProviderConfig({
discoveryApiKey: runtimeAuth.apiKey,
accountId: metadata.accountId
}), runtimeAuth.profileId ?? auth.profileId);
return {
providers: { [PROVIDER_ID]: catalog.provider },
...catalog.outcome ? { outcomes: [catalog.outcome] } : {}
};
}
} catch {}
if (auth.mode === "api_key" && auth.apiKey) {
const catalog = scopeOpenAICatalogOutcome(await buildOpenAILiveProviderConfig({
apiKey: auth.apiKey,
baseUrl: resolveOpenAICatalogBaseUrl(ctx),
discoveryApiKey: auth.discoveryApiKey,
rejectionScope: resolveOpenAICatalogRejectionScope(auth)
}), auth.profileId);
return {
providers: { [PROVIDER_ID]: catalog.provider },
...catalog.outcome ? { outcomes: [catalog.outcome] } : {}
};
}
const apiKey = ctx.resolveProviderApiKey(PROVIDER_ID);
if (!apiKey.apiKey) return null;
const catalog = await buildOpenAILiveProviderConfig({
apiKey: apiKey.apiKey,
baseUrl: resolveOpenAICatalogBaseUrl(ctx),
discoveryApiKey: apiKey.discoveryApiKey,
rejectionScope: resolveOpenAICatalogRejectionScope(apiKey)
});
return {
providers: { [PROVIDER_ID]: catalog.provider },
...catalog.outcome ? { outcomes: [catalog.outcome] } : {}
};
}
},
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 (!isOpenAIProvider(ctx.provider)) 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 normalizeOpenAITransport(ctx.model, ctx);
},
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) => {
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 || isOpenAIHttpsApiBaseUrl(providerConfig.baseUrl)) && resolveConfiguredProviderAuthTransport(providerConfig) === "codex" ? nativeResponsesHooks : responsesHooks).prepareExtraParams?.(ctx);
},
resolveUsageAuth: codexHooks.resolveUsageAuth,
fetchUsageSnapshot: codexHooks.fetchUsageSnapshot,
refreshOAuth: codexHooks.refreshOAuth,
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 }) => normalizeProviderId(provider) === PROVIDER_ID ? resolveUnifiedOpenAIThinkingProfile(modelId, agentRuntime, compat, api) : null,
isModernModelRef: ({ modelId }) => matchesExactOrPrefix(modelId, OPENAI_PROVIDER_MODERN_MODEL_IDS),
augmentModelCatalog: (ctx) => {
const openAiGpt55ProTemplate = findCatalogTemplate({
entries: ctx.entries,
providerId: PROVIDER_ID,
templateIds: OPENAI_GPT_55_PRO_TEMPLATE_MODEL_IDS
});
const openAiGpt54Template = findCatalogTemplate({
entries: ctx.entries,
providerId: PROVIDER_ID,
templateIds: OPENAI_GPT_54_TEMPLATE_MODEL_IDS
});
const openAiGpt54ProTemplate = findCatalogTemplate({
entries: ctx.entries,
providerId: PROVIDER_ID,
templateIds: OPENAI_GPT_54_PRO_TEMPLATE_MODEL_IDS
});
const openAiGpt54MiniTemplate = findCatalogTemplate({
entries: ctx.entries,
providerId: PROVIDER_ID,
templateIds: OPENAI_GPT_54_MINI_TEMPLATE_MODEL_IDS
});
const openAiGpt54NanoTemplate = findCatalogTemplate({
entries: ctx.entries,
providerId: PROVIDER_ID,
templateIds: OPENAI_GPT_54_NANO_TEMPLATE_MODEL_IDS
});
return [
buildOpenAISyntheticCatalogEntry(openAiGpt55ProTemplate, {
id: OPENAI_GPT_55_PRO_MODEL_ID,
reasoning: true,
input: ["text", "image"],
contextWindow: OPENAI_GPT_55_PRO_CONTEXT_WINDOW,
contextTokens: OPENAI_DEFAULT_RUNTIME_CONTEXT_TOKENS
}),
buildOpenAISyntheticCatalogEntry(openAiGpt54Template, {
id: OPENAI_GPT_54_MODEL_ID,
reasoning: true,
input: ["text", "image"],
contextWindow: OPENAI_GPT_54_CONTEXT_TOKENS
}),
buildOpenAISyntheticCatalogEntry(openAiGpt54ProTemplate, {
id: OPENAI_GPT_54_PRO_MODEL_ID,
reasoning: true,
input: ["text", "image"],
contextWindow: OPENAI_GPT_54_PRO_CONTEXT_TOKENS
}),
buildOpenAISyntheticCatalogEntry(openAiGpt54MiniTemplate, {
id: OPENAI_GPT_54_MINI_MODEL_ID,
reasoning: true,
input: ["text", "image"],
contextWindow: OPENAI_GPT_54_MINI_CONTEXT_TOKENS
}),
buildOpenAISyntheticCatalogEntry(openAiGpt54NanoTemplate, {
id: OPENAI_GPT_54_NANO_MODEL_ID,
reasoning: true,
input: ["text", "image"],
contextWindow: OPENAI_GPT_54_NANO_CONTEXT_TOKENS
})
].filter((entry) => entry !== void 0);
}
};
}
//#endregion
export { buildOpenAIProvider as t };