fix(codex): prefer max_context_window over context_window as the usable input limit (#11179)

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!
This commit is contained in:
Jonathan Bailey
2026-08-23 05:47:03 -07:00
committed by GitHub
parent 1135cbea80
commit 7af14d2696
5 changed files with 49 additions and 5 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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);

View File

@@ -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: [