From 7af14d269613b05e5fae03987acf3afad781dec7 Mon Sep 17 00:00:00 2001 From: Jonathan Bailey <127773378+excessivechaos@users.noreply.github.com> Date: Sun, 23 Aug 2026 05:47:03 -0700 Subject: [PATCH] fix(codex): prefer max_context_window over context_window as the usable input limit (#11179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validated on the combined batch board over tip 0b41259f: static gates clean (changelog, file-size 160 frozen, complexity 2628<=2774, cognitive 1187<=1223, dead-code 408<=416, docs-counts green at 351 providers, provider-consistency 268/351/0), typecheck:core clean, 430+ focused tests green across 5 groups. max_context_window now wins over the pricing-tier context_window in Codex catalog parsing — context-aware fallback stops demoting codex behind smaller targets. Live evidence (390K served past 272K) plus the live-shape regression test. Thank you @excessivechaos! --- changelog.d/fixes/codex-max-context-window.md | 1 + open-sse/config/providers/shared.ts | 11 +++++-- .../providers/[id]/models/discovery/codex.ts | 7 ++++- tests/unit/codex-gpt56-catalog.test.ts | 4 +-- .../provider-models-discovery-split.test.ts | 31 +++++++++++++++++++ 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 changelog.d/fixes/codex-max-context-window.md diff --git a/changelog.d/fixes/codex-max-context-window.md b/changelog.d/fixes/codex-max-context-window.md new file mode 100644 index 0000000000..391d894e46 --- /dev/null +++ b/changelog.d/fixes/codex-max-context-window.md @@ -0,0 +1 @@ +- fix(codex): prefer `max_context_window` over the `context_window` pricing tier as the usable input limit in discovery, and raise the static Codex OAuth catalog to the same usable window so the conservative discovery merge no longer caps live values at the 272K pricing tier diff --git a/open-sse/config/providers/shared.ts b/open-sse/config/providers/shared.ts index 62909a528d..db4b9a4d5b 100644 --- a/open-sse/config/providers/shared.ts +++ b/open-sse/config/providers/shared.ts @@ -284,14 +284,21 @@ export const GPT_5_6_API_CAPABILITIES = { maxOutputTokens: 128000, } as const; +// Codex OAuth catalog limits. The live OAuth `/codex/models` endpoint reports +// `context_window` (~272K, the first pricing tier) alongside +// `max_context_window` (~872K, the real usable window); requests past the +// pricing tier succeed upstream (verified: gpt-5.6-luna-xhigh served 380-390K +// input tokens with HTTP 200). The static catalog must advertise the usable +// window so the conservative discovery merge (`Math.min`) does not cap the +// live value at the pricing tier. export const GPT_5_6_CODEX_CAPABILITIES = { targetFormat: "openai-responses", toolCalling: true, supportsReasoning: true, supportsVision: true, supportsXHighEffort: true, - contextLength: 272000, - maxInputTokens: 272000, + contextLength: 872000, + maxInputTokens: 872000, maxOutputTokens: 128000, } as const; diff --git a/src/app/api/providers/[id]/models/discovery/codex.ts b/src/app/api/providers/[id]/models/discovery/codex.ts index 4d113863f3..7f8ec93689 100644 --- a/src/app/api/providers/[id]/models/discovery/codex.ts +++ b/src/app/api/providers/[id]/models/discovery/codex.ts @@ -165,14 +165,19 @@ function buildCodexDiscoveryModel(record: JsonRecord): CodexDiscoveryModel | nul apiFormat: "responses", supportedEndpoints: ["responses"], }; + // The live Codex OAuth catalog reports BOTH `context_window` (the first + // pricing tier, ~272K) and `max_context_window` (the real usable window, + // ~872K). Requests well past the pricing tier succeed upstream, so the max + // window must win whenever it is present; `context_window` is only a + // fallback for catalogs that omit the max. const inputTokenLimit = firstPositiveNumber( record.inputTokenLimit, record.maxInputTokens, record.max_input_tokens, record.contextLength, record.context_length, - record.context_window, record.max_context_window, + record.context_window, topProvider.context_length, limits.input_tokens, limits.inputTokenLimit, diff --git a/tests/unit/codex-gpt56-catalog.test.ts b/tests/unit/codex-gpt56-catalog.test.ts index b4eb0ab293..c7d8075ffb 100644 --- a/tests/unit/codex-gpt56-catalog.test.ts +++ b/tests/unit/codex-gpt56-catalog.test.ts @@ -36,8 +36,8 @@ test("Codex catalog exposes the GPT-5.6 lineup in configured priority order", () for (const modelId of expectedIds) { const model = models.find((entry) => entry.id === modelId); assert.ok(model, `codex must expose ${modelId}`); - assert.equal(model.contextLength, 272000); - assert.equal(model.maxInputTokens, 272000); + assert.equal(model.contextLength, 872000); + assert.equal(model.maxInputTokens, 872000); assert.equal(model.maxOutputTokens, 128000); assert.equal(model.targetFormat, "openai-responses"); assert.equal(model.toolCalling, true); diff --git a/tests/unit/provider-models-discovery-split.test.ts b/tests/unit/provider-models-discovery-split.test.ts index f954c4c7f0..a98019ca64 100644 --- a/tests/unit/provider-models-discovery-split.test.ts +++ b/tests/unit/provider-models-discovery-split.test.ts @@ -272,6 +272,37 @@ test("codex.normalizeCodexModelsResponse parses the Codex live catalog shape", ( assert.equal(parsed.find((model) => model.id === "gpt-5.5")?.outputTokenLimit, 64000); }); +test("codex.normalizeCodexModelsResponse prefers max_context_window over the context_window pricing tier", () => { + // The live Codex OAuth catalog reports BOTH fields: `context_window` is the + // first pricing tier (~272K) while `max_context_window` is the real usable + // window (~872K). Requests well above 272K succeed upstream (verified: + // gpt-5.6-luna-xhigh served 380-390K input tokens with HTTP 200), so the + // usable window must win when both are present. + const parsed = normalizeCodexModelsResponse({ + models: [ + { + slug: "gpt-5.6-luna", + display_name: "GPT 5.6 Luna", + visibility: "list", + supported_in_api: true, + context_window: 272000, + max_context_window: 872000, + }, + { + slug: "gpt-5.4", + display_name: "GPT-5.4", + visibility: "list", + supported_in_api: true, + context_window: 272000, + max_context_window: 1000000, + }, + ], + }); + + assert.equal(parsed.find((model) => model.id === "gpt-5.6-luna")?.inputTokenLimit, 872000); + assert.equal(parsed.find((model) => model.id === "gpt-5.4")?.inputTokenLimit, 1000000); +}); + test("codex.normalizeCodexGithubCatalogResponse parses current client catalog metadata", () => { const parsed = normalizeCodexGithubCatalogResponse({ models: [