diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index ed94d524..48e5af4f 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed OpenAI Codex OAuth model list (removed aliases), aligned context window/maxTokens with observed backend limits, and refined reasoning effort clamping. + ## [0.37.8] - 2026-01-07 ## [0.37.7] - 2026-01-07 diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index 4f52d8a2..431c8678 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -443,37 +443,16 @@ async function generateModels() { } // OpenAI Codex (ChatGPT OAuth) models + // NOTE: These are not fetched from models.dev; we keep a small, explicit list to avoid aliases. + // Context window is based on observed server limits (400s above ~272k), not marketing numbers. const CODEX_BASE_URL = "https://chatgpt.com/backend-api"; - const CODEX_CONTEXT = 400000; - const CODEX_MAX_TOKENS = 128000; + const CODEX_CONTEXT = 272000; + // Use the same max output token budget as Codex CLI. + const CODEX_MAX_TOKENS = 10000; const codexModels: Model<"openai-codex-responses">[] = [ { - id: "gpt-5.2-codex", - name: "GPT-5.2 Codex", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5.2", - name: "GPT-5.2", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5.1-codex-max", - name: "GPT-5.1 Codex Max", + id: "gpt-5.1", + name: "GPT-5.1", api: "openai-codex-responses", provider: "openai-codex", baseUrl: CODEX_BASE_URL, @@ -484,8 +463,8 @@ async function generateModels() { maxTokens: CODEX_MAX_TOKENS, }, { - id: "gpt-5.1-codex", - name: "GPT-5.1 Codex", + id: "gpt-5.1-codex-max", + name: "GPT-5.1 Codex Max", api: "openai-codex-responses", provider: "openai-codex", baseUrl: CODEX_BASE_URL, @@ -508,98 +487,26 @@ async function generateModels() { maxTokens: CODEX_MAX_TOKENS, }, { - id: "codex-mini-latest", - name: "Codex Mini Latest", + id: "gpt-5.2", + name: "GPT-5.2", api: "openai-codex-responses", provider: "openai-codex", baseUrl: CODEX_BASE_URL, reasoning: true, input: ["text", "image"], - cost: { input: 1.5, output: 6, cacheRead: 0.375, cacheWrite: 0 }, + cost: { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }, contextWindow: CODEX_CONTEXT, maxTokens: CODEX_MAX_TOKENS, }, { - id: "gpt-5-codex-mini", - name: "gpt-5-codex-mini", + id: "gpt-5.2-codex", + name: "GPT-5.2 Codex", api: "openai-codex-responses", provider: "openai-codex", baseUrl: CODEX_BASE_URL, reasoning: true, input: ["text", "image"], - cost: { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5-codex", - name: "gpt-5-codex", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5.1", - name: "GPT-5.1", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5.1-chat-latest", - name: "gpt-5.1-chat-latest", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5", - name: "gpt-5", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5-mini", - name: "gpt-5-mini", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0 }, - contextWindow: CODEX_CONTEXT, - maxTokens: CODEX_MAX_TOKENS, - }, - { - id: "gpt-5-nano", - name: "gpt-5-nano", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: CODEX_BASE_URL, - reasoning: true, - input: ["text", "image"], - cost: { input: 0.05, output: 0.4, cacheRead: 0.005, cacheWrite: 0 }, + cost: { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }, contextWindow: CODEX_CONTEXT, maxTokens: CODEX_MAX_TOKENS, }, diff --git a/packages/ai/src/models.generated.ts b/packages/ai/src/models.generated.ts index 82bebbc7..4e86ee0d 100644 --- a/packages/ai/src/models.generated.ts +++ b/packages/ai/src/models.generated.ts @@ -415,6 +415,23 @@ export const MODELS = { contextWindow: 131072, maxTokens: 40960, } satisfies Model<"openai-completions">, + "zai-glm-4.7": { + id: "zai-glm-4.7", + name: "Z.AI GLM-4.7", + api: "openai-completions", + provider: "cerebras", + baseUrl: "https://api.cerebras.ai/v1", + reasoning: false, + input: ["text"], + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + }, + contextWindow: 131072, + maxTokens: 40000, + } satisfies Model<"openai-completions">, }, "github-copilot": { "claude-haiku-4.5": { @@ -2774,108 +2791,6 @@ export const MODELS = { } satisfies Model<"openai-responses">, }, "openai-codex": { - "codex-mini-latest": { - id: "codex-mini-latest", - name: "Codex Mini Latest", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 1.5, - output: 6, - cacheRead: 0.375, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, - "gpt-5": { - id: "gpt-5", - name: "gpt-5", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 1.25, - output: 10, - cacheRead: 0.125, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, - "gpt-5-codex": { - id: "gpt-5-codex", - name: "gpt-5-codex", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 1.25, - output: 10, - cacheRead: 0.125, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, - "gpt-5-codex-mini": { - id: "gpt-5-codex-mini", - name: "gpt-5-codex-mini", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 0.25, - output: 2, - cacheRead: 0.025, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, - "gpt-5-mini": { - id: "gpt-5-mini", - name: "gpt-5-mini", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 0.25, - output: 2, - cacheRead: 0.025, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, - "gpt-5-nano": { - id: "gpt-5-nano", - name: "gpt-5-nano", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 0.05, - output: 0.4, - cacheRead: 0.005, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, "gpt-5.1": { id: "gpt-5.1", name: "GPT-5.1", @@ -2890,42 +2805,8 @@ export const MODELS = { cacheRead: 0.125, cacheWrite: 0, }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, - "gpt-5.1-chat-latest": { - id: "gpt-5.1-chat-latest", - name: "gpt-5.1-chat-latest", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 1.25, - output: 10, - cacheRead: 0.125, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, - } satisfies Model<"openai-codex-responses">, - "gpt-5.1-codex": { - id: "gpt-5.1-codex", - name: "GPT-5.1 Codex", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text", "image"], - cost: { - input: 1.25, - output: 10, - cacheRead: 0.125, - cacheWrite: 0, - }, - contextWindow: 400000, - maxTokens: 128000, + contextWindow: 272000, + maxTokens: 10000, } satisfies Model<"openai-codex-responses">, "gpt-5.1-codex-max": { id: "gpt-5.1-codex-max", @@ -2941,8 +2822,8 @@ export const MODELS = { cacheRead: 0.125, cacheWrite: 0, }, - contextWindow: 400000, - maxTokens: 128000, + contextWindow: 272000, + maxTokens: 10000, } satisfies Model<"openai-codex-responses">, "gpt-5.1-codex-mini": { id: "gpt-5.1-codex-mini", @@ -2958,8 +2839,8 @@ export const MODELS = { cacheRead: 0.025, cacheWrite: 0, }, - contextWindow: 400000, - maxTokens: 128000, + contextWindow: 272000, + maxTokens: 10000, } satisfies Model<"openai-codex-responses">, "gpt-5.2": { id: "gpt-5.2", @@ -2975,8 +2856,8 @@ export const MODELS = { cacheRead: 0.175, cacheWrite: 0, }, - contextWindow: 400000, - maxTokens: 128000, + contextWindow: 272000, + maxTokens: 10000, } satisfies Model<"openai-codex-responses">, "gpt-5.2-codex": { id: "gpt-5.2-codex", @@ -2992,8 +2873,8 @@ export const MODELS = { cacheRead: 0.175, cacheWrite: 0, }, - contextWindow: 400000, - maxTokens: 128000, + contextWindow: 272000, + maxTokens: 10000, } satisfies Model<"openai-codex-responses">, }, "openrouter": { diff --git a/packages/ai/src/providers/openai-codex-responses.ts b/packages/ai/src/providers/openai-codex-responses.ts index 0f74b635..74cb2206 100644 --- a/packages/ai/src/providers/openai-codex-responses.ts +++ b/packages/ai/src/providers/openai-codex-responses.ts @@ -37,7 +37,6 @@ import { buildCodexPiBridge } from "./openai-codex/prompts/pi-codex-bridge.js"; import { buildCodexSystemPrompt } from "./openai-codex/prompts/system-prompt.js"; import { type CodexRequestOptions, - normalizeModel, type RequestBody, transformRequestBody, } from "./openai-codex/request-transformer.js"; @@ -111,8 +110,7 @@ export const streamOpenAICodexResponses: StreamFunction<"openai-codex-responses" params.tools = convertTools(context.tools); } - const normalizedModel = normalizeModel(params.model); - const codexInstructions = await getCodexInstructions(normalizedModel); + const codexInstructions = await getCodexInstructions(params.model); const bridgeText = buildCodexPiBridge(context.tools); const systemPrompt = buildCodexSystemPrompt({ codexInstructions, @@ -120,7 +118,6 @@ export const streamOpenAICodexResponses: StreamFunction<"openai-codex-responses" userSystemPrompt: context.systemPrompt, }); - params.model = normalizedModel; params.instructions = systemPrompt.instructions; const codexOptions: CodexRequestOptions = { diff --git a/packages/ai/src/providers/openai-codex/prompts/codex.ts b/packages/ai/src/providers/openai-codex/prompts/codex.ts index db94e780..869add2e 100644 --- a/packages/ai/src/providers/openai-codex/prompts/codex.ts +++ b/packages/ai/src/providers/openai-codex/prompts/codex.ts @@ -44,17 +44,17 @@ export type CacheMetadata = { url: string; }; -export function getModelFamily(normalizedModel: string): ModelFamily { - if (normalizedModel.includes("gpt-5.2-codex") || normalizedModel.includes("gpt 5.2 codex")) { +export function getModelFamily(model: string): ModelFamily { + if (model.includes("gpt-5.2-codex") || model.includes("gpt 5.2 codex")) { return "gpt-5.2-codex"; } - if (normalizedModel.includes("codex-max")) { + if (model.includes("codex-max")) { return "codex-max"; } - if (normalizedModel.includes("codex") || normalizedModel.startsWith("codex-")) { + if (model.includes("codex") || model.startsWith("codex-")) { return "codex"; } - if (normalizedModel.includes("gpt-5.2")) { + if (model.includes("gpt-5.2")) { return "gpt-5.2"; } return "gpt-5.1"; @@ -96,8 +96,8 @@ async function getLatestReleaseTag(): Promise { throw new Error("Failed to determine latest release tag from GitHub"); } -export async function getCodexInstructions(normalizedModel = "gpt-5.1-codex"): Promise { - const modelFamily = getModelFamily(normalizedModel); +export async function getCodexInstructions(model = "gpt-5.1-codex"): Promise { + const modelFamily = getModelFamily(model); const promptFile = PROMPT_FILES[modelFamily]; const cacheDir = getCacheDir(); const cacheFile = join(cacheDir, CACHE_FILES[modelFamily]); diff --git a/packages/ai/src/providers/openai-codex/request-transformer.ts b/packages/ai/src/providers/openai-codex/request-transformer.ts index 32b21e59..bd3f9926 100644 --- a/packages/ai/src/providers/openai-codex/request-transformer.ts +++ b/packages/ai/src/providers/openai-codex/request-transformer.ts @@ -41,155 +41,26 @@ export interface RequestBody { [key: string]: unknown; } -const MODEL_MAP: Record = { - "gpt-5.1-codex": "gpt-5.1-codex", - "gpt-5.1-codex-low": "gpt-5.1-codex", - "gpt-5.1-codex-medium": "gpt-5.1-codex", - "gpt-5.1-codex-high": "gpt-5.1-codex", - "gpt-5.1-codex-max": "gpt-5.1-codex-max", - "gpt-5.1-codex-max-low": "gpt-5.1-codex-max", - "gpt-5.1-codex-max-medium": "gpt-5.1-codex-max", - "gpt-5.1-codex-max-high": "gpt-5.1-codex-max", - "gpt-5.1-codex-max-xhigh": "gpt-5.1-codex-max", - "gpt-5.2": "gpt-5.2", - "gpt-5.2-none": "gpt-5.2", - "gpt-5.2-low": "gpt-5.2", - "gpt-5.2-medium": "gpt-5.2", - "gpt-5.2-high": "gpt-5.2", - "gpt-5.2-xhigh": "gpt-5.2", - "gpt-5.2-codex": "gpt-5.2-codex", - "gpt-5.2-codex-low": "gpt-5.2-codex", - "gpt-5.2-codex-medium": "gpt-5.2-codex", - "gpt-5.2-codex-high": "gpt-5.2-codex", - "gpt-5.2-codex-xhigh": "gpt-5.2-codex", - "gpt-5.1-codex-mini": "gpt-5.1-codex-mini", - "gpt-5.1-codex-mini-medium": "gpt-5.1-codex-mini", - "gpt-5.1-codex-mini-high": "gpt-5.1-codex-mini", - "gpt-5.1": "gpt-5.1", - "gpt-5.1-none": "gpt-5.1", - "gpt-5.1-low": "gpt-5.1", - "gpt-5.1-medium": "gpt-5.1", - "gpt-5.1-high": "gpt-5.1", - "gpt-5.1-chat-latest": "gpt-5.1", - "gpt-5-codex": "gpt-5.1-codex", - "codex-mini-latest": "gpt-5.1-codex-mini", - "gpt-5-codex-mini": "gpt-5.1-codex-mini", - "gpt-5-codex-mini-medium": "gpt-5.1-codex-mini", - "gpt-5-codex-mini-high": "gpt-5.1-codex-mini", - "gpt-5": "gpt-5.1", - "gpt-5-mini": "gpt-5.1", - "gpt-5-nano": "gpt-5.1", -}; - -function getNormalizedModel(modelId: string): string | undefined { - if (MODEL_MAP[modelId]) return MODEL_MAP[modelId]; - const lowerModelId = modelId.toLowerCase(); - const match = Object.keys(MODEL_MAP).find((key) => key.toLowerCase() === lowerModelId); - return match ? MODEL_MAP[match] : undefined; -} - -export function normalizeModel(model: string | undefined): string { - if (!model) return "gpt-5.1"; - +function clampReasoningEffort(model: string, effort: ReasoningConfig["effort"]): ReasoningConfig["effort"] { + // Codex backend expects exact model IDs. Do not normalize model names here. const modelId = model.includes("/") ? model.split("/").pop()! : model; - const mappedModel = getNormalizedModel(modelId); - if (mappedModel) return mappedModel; - const normalized = modelId.toLowerCase(); - - if (normalized.includes("gpt-5.2-codex") || normalized.includes("gpt 5.2 codex")) { - return "gpt-5.2-codex"; - } - if (normalized.includes("gpt-5.2") || normalized.includes("gpt 5.2")) { - return "gpt-5.2"; - } - if (normalized.includes("gpt-5.1-codex-max") || normalized.includes("gpt 5.1 codex max")) { - return "gpt-5.1-codex-max"; - } - if (normalized.includes("gpt-5.1-codex-mini") || normalized.includes("gpt 5.1 codex mini")) { - return "gpt-5.1-codex-mini"; - } - if ( - normalized.includes("codex-mini-latest") || - normalized.includes("gpt-5-codex-mini") || - normalized.includes("gpt 5 codex mini") - ) { - return "codex-mini-latest"; - } - if (normalized.includes("gpt-5.1-codex") || normalized.includes("gpt 5.1 codex")) { - return "gpt-5.1-codex"; - } - if (normalized.includes("gpt-5.1") || normalized.includes("gpt 5.1")) { - return "gpt-5.1"; - } - if (normalized.includes("codex")) { - return "gpt-5.1-codex"; - } - if (normalized.includes("gpt-5") || normalized.includes("gpt 5")) { - return "gpt-5.1"; + // gpt-5.1 does not support xhigh. + if (modelId === "gpt-5.1" && effort === "xhigh") { + return "high"; } - return "gpt-5.1"; + // gpt-5.1-codex-mini only supports medium/high. + if (modelId === "gpt-5.1-codex-mini") { + return effort === "high" || effort === "xhigh" ? "high" : "medium"; + } + + return effort; } -function getReasoningConfig(modelName: string | undefined, options: CodexRequestOptions = {}): ReasoningConfig { - const normalizedName = modelName?.toLowerCase() ?? ""; - - const isGpt52Codex = normalizedName.includes("gpt-5.2-codex") || normalizedName.includes("gpt 5.2 codex"); - const isGpt52General = (normalizedName.includes("gpt-5.2") || normalizedName.includes("gpt 5.2")) && !isGpt52Codex; - const isCodexMax = normalizedName.includes("codex-max") || normalizedName.includes("codex max"); - const isCodexMini = - normalizedName.includes("codex-mini") || - normalizedName.includes("codex mini") || - normalizedName.includes("codex_mini") || - normalizedName.includes("codex-mini-latest"); - const isCodex = normalizedName.includes("codex") && !isCodexMini; - const isLightweight = !isCodexMini && (normalizedName.includes("nano") || normalizedName.includes("mini")); - const isGpt51General = - (normalizedName.includes("gpt-5.1") || normalizedName.includes("gpt 5.1")) && - !isCodex && - !isCodexMax && - !isCodexMini; - - const supportsXhigh = isGpt52General || isGpt52Codex || isCodexMax; - const supportsNone = isGpt52General || isGpt51General; - - const defaultEffort: ReasoningConfig["effort"] = isCodexMini - ? "medium" - : supportsXhigh - ? "high" - : isLightweight - ? "minimal" - : "medium"; - - let effort = options.reasoningEffort || defaultEffort; - - if (isCodexMini) { - if (effort === "minimal" || effort === "low" || effort === "none") { - effort = "medium"; - } - if (effort === "xhigh") { - effort = "high"; - } - if (effort !== "high" && effort !== "medium") { - effort = "medium"; - } - } - - if (!supportsXhigh && effort === "xhigh") { - effort = "high"; - } - - if (!supportsNone && effort === "none") { - effort = "low"; - } - - if (isCodex && effort === "minimal") { - effort = "low"; - } - +function getReasoningConfig(model: string, options: CodexRequestOptions): ReasoningConfig { return { - effort, + effort: clampReasoningEffort(model, options.reasoningEffort as ReasoningConfig["effort"]), summary: options.reasoningSummary ?? "auto", }; } @@ -213,9 +84,6 @@ export async function transformRequestBody( options: CodexRequestOptions = {}, prompt?: { instructions: string; developerMessages: string[] }, ): Promise { - const normalizedModel = normalizeModel(body.model); - - body.model = normalizedModel; body.store = false; body.stream = true; @@ -270,7 +138,7 @@ export async function transformRequestBody( } if (options.reasoningEffort !== undefined) { - const reasoningConfig = getReasoningConfig(normalizedModel, options); + const reasoningConfig = getReasoningConfig(body.model, options); body.reasoning = { ...body.reasoning, ...reasoningConfig, diff --git a/packages/ai/test/openai-codex.test.ts b/packages/ai/test/openai-codex.test.ts index c2b91dd8..9252656b 100644 --- a/packages/ai/test/openai-codex.test.ts +++ b/packages/ai/test/openai-codex.test.ts @@ -3,11 +3,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getCodexInstructions } from "../src/providers/openai-codex/prompts/codex.js"; -import { - normalizeModel, - type RequestBody, - transformRequestBody, -} from "../src/providers/openai-codex/request-transformer.js"; +import { type RequestBody, transformRequestBody } from "../src/providers/openai-codex/request-transformer.js"; import { parseCodexError } from "../src/providers/openai-codex/response-handler.js"; const DEFAULT_PROMPT_PREFIX = @@ -59,9 +55,21 @@ describe("openai-codex request transformer", () => { }); }); -describe("openai-codex model normalization", () => { - it("maps space-separated codex-mini names to codex-mini-latest", () => { - expect(normalizeModel("gpt 5 codex mini")).toBe("codex-mini-latest"); +describe("openai-codex reasoning effort clamping", () => { + it("clamps gpt-5.1 xhigh to high", async () => { + const body: RequestBody = { model: "gpt-5.1", input: [] }; + const transformed = await transformRequestBody(body, { reasoningEffort: "xhigh" }); + expect(transformed.reasoning?.effort).toBe("high"); + }); + + it("clamps gpt-5.1-codex-mini to medium/high only", async () => { + const body: RequestBody = { model: "gpt-5.1-codex-mini", input: [] }; + + const low = await transformRequestBody({ ...body }, { reasoningEffort: "low" }); + expect(low.reasoning?.effort).toBe("medium"); + + const xhigh = await transformRequestBody({ ...body }, { reasoningEffort: "xhigh" }); + expect(xhigh.reasoning?.effort).toBe("high"); }); });