Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fixes/codex-max-context-window.md
Original file line number Diff line number Diff line change
@@ -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
11 changes: 9 additions & 2 deletions open-sse/config/providers/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,14 +283,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;

Expand Down
7 changes: 6 additions & 1 deletion src/app/api/providers/[id]/models/discovery/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/codex-gpt56-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
31 changes: 31 additions & 0 deletions tests/unit/provider-models-discovery-split.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
Loading