Cap imported Codex GPT-5 context windows

This commit is contained in:
musistudio
2026-07-28 15:15:14 +08:00
parent 755b00f829
commit d305f87e67
4 changed files with 206 additions and 12 deletions
@@ -6,7 +6,7 @@ import {
readCatalogCapability,
type ModelCatalogEntry
} from "@ccr/core/gateway/model-catalog";
import { codexDefaultBaseUrl, readCodexLocalModelCatalog } from "@ccr/core/agents/local-providers/codex";
import { codexDefaultBaseUrl, codexImportedModelContextWindow, readCodexLocalModelCatalog } from "@ccr/core/agents/local-providers/codex";
import { localAgentProviderApiKey } from "@ccr/core/agents/local-providers/shared";
import { normalizeProviderBaseUrl } from "@ccr/core/providers/url";
import { resolveUsageModelAttribution } from "@ccr/core/usage/model-attribution";
@@ -211,7 +211,7 @@ function codexModelCapabilityProfile(
: findConfiguredProviderForModel(config, attribution.model ?? model);
const providerModel = attribution.model ?? selector?.model ?? model;
const providerModelMetadata = provider
? providerModelMetadataFor(provider, providerModel) ?? localCodexModelMetadataFor(provider, providerModel)
? codexProviderModelMetadataFor(provider, providerModel)
: undefined;
const physicalModelSelector = provider ? `${provider.name}/${providerModel}` : providerModel;
const catalogEntry = findModelCatalogEntry(physicalModelSelector);
@@ -281,6 +281,22 @@ function providerModelMetadataFor(provider: GatewayProviderConfig, model: string
return match?.[1];
}
function codexProviderModelMetadataFor(provider: GatewayProviderConfig, model: string): ProviderModelMetadata | undefined {
const metadata = providerModelMetadataFor(provider, model) ?? localCodexModelMetadataFor(provider, model);
if (!isLocalCodexProvider(provider)) {
return metadata;
}
const contextWindow = codexImportedModelContextWindow(model);
if (!contextWindow) {
return metadata;
}
return {
...(metadata ?? {}),
contextWindow,
maxContextWindow: contextWindow
};
}
function localCodexModelMetadataFor(provider: GatewayProviderConfig, model: string): ProviderModelMetadata | undefined {
if (!isLocalCodexProvider(provider)) {
return undefined;
@@ -37,6 +37,8 @@ export const codexDefaultBaseUrl = "https://chatgpt.com/backend-api/codex";
const codexAccountBaseUrl = "https://chatgpt.com/backend-api";
const codexDefaultModels = ["gpt-5-codex"];
const codexImportedGpt56ContextWindow = 368_000;
const codexImportedGpt5ContextWindow = 256_000;
const codexProbeTimeoutMs = 8_000;
export type LocalAgentModelCatalog = {
@@ -382,19 +384,30 @@ export function codexRateLimitResetCreditDetails(payload: unknown): ProviderAcco
}
export function normalizeCodexProviderAccountConfig(provider: GatewayProviderConfig): GatewayProviderConfig {
if (!isLocalCodexProvider(provider) || !shouldUseCurrentCodexAccountConfig(provider.account)) {
return provider;
const normalizedProvider = normalizeCodexImportedProviderModelMetadata(provider);
if (!isLocalCodexProvider(normalizedProvider) || !shouldUseCurrentCodexAccountConfig(normalizedProvider.account)) {
return normalizedProvider;
}
const account = codexProviderAccountConfig();
return {
...provider,
...normalizedProvider,
account: {
...account,
refreshIntervalMs: provider.account?.refreshIntervalMs ?? account.refreshIntervalMs
refreshIntervalMs: normalizedProvider.account?.refreshIntervalMs ?? account.refreshIntervalMs
}
};
}
function normalizeCodexImportedProviderModelMetadata(provider: GatewayProviderConfig): GatewayProviderConfig {
if (!isLocalCodexProvider(provider)) {
return provider;
}
const modelMetadata = codexImportedModelMetadataWithContextOverrides(provider.modelMetadata, provider.models ?? []);
return modelMetadata === provider.modelMetadata
? provider
: { ...provider, modelMetadata };
}
function isLocalCodexProvider(provider: GatewayProviderConfig): boolean {
return (
providerApiKey(provider) === localAgentProviderApiKey &&
@@ -612,7 +625,7 @@ export function readCodexLocalModelCatalog(): LocalAgentModelCatalog {
const uniqueModels = uniqueStrings([...catalog.models, ...codexDefaultModels]);
return {
modelDisplayNames: modelDisplayNamesForModels(catalog.modelDisplayNames, uniqueModels),
modelMetadata: modelMetadataForModels(catalog.modelMetadata, uniqueModels),
modelMetadata: codexImportedModelMetadataForModels(catalog.modelMetadata, uniqueModels),
models: uniqueModels
};
}
@@ -718,11 +731,50 @@ function codexModelCatalogFromPayload(payload: unknown): LocalAgentModelCatalog
const uniqueModels = uniqueStrings(models);
return {
modelDisplayNames: modelDisplayNamesForModels(modelDisplayNames, uniqueModels),
modelMetadata: modelMetadataForModels(modelMetadata, uniqueModels),
modelMetadata: codexImportedModelMetadataForModels(modelMetadata, uniqueModels),
models: uniqueModels
};
}
function codexImportedModelMetadataForModels(
value: Record<string, ProviderModelMetadata> | undefined,
models: string[]
): Record<string, ProviderModelMetadata> | undefined {
return modelMetadataForModels(codexImportedModelMetadataWithContextOverrides(value, models), models);
}
function codexImportedModelMetadataWithContextOverrides(
value: Record<string, ProviderModelMetadata> | undefined,
models: string[]
): Record<string, ProviderModelMetadata> | undefined {
const metadata: Record<string, ProviderModelMetadata> = { ...(value ?? {}) };
let changed = false;
for (const model of models) {
const contextWindow = codexImportedModelContextWindow(model);
if (!contextWindow) {
continue;
}
metadata[model] = {
...(metadata[model] ?? {}),
contextWindow,
maxContextWindow: contextWindow
};
changed = true;
}
return changed ? metadata : value;
}
export function codexImportedModelContextWindow(model: string): number | undefined {
const name = model.trim().toLowerCase().split("/").at(-1) ?? "";
if (/^gpt-5\.6(?:[.-]|$)/.test(name)) {
return codexImportedGpt56ContextWindow;
}
if (/^gpt-5(?:[.-]|$)/.test(name)) {
return codexImportedGpt5ContextWindow;
}
return undefined;
}
function codexModelMetadataFromItem(item: Record<string, unknown>): ProviderModelMetadata | undefined {
const additionalSpeedTiers = readArray(item.additional_speed_tiers) ?? readArray(item.additionalSpeedTiers) ?? readArray(item.speed_tiers) ?? readArray(item.speedTiers);
const contextWindow = readPositiveInteger(item.context_window ?? item.contextWindow);
@@ -314,6 +314,37 @@ test("codex catalog uses provider model metadata for reasoning effort and speed
assert.equal(model.supports_reasoning_summaries, true);
});
test("codex catalog caps persisted local Codex GPT-5 context metadata", () => {
const cases = [
["gpt-5-codex", 256000],
["gpt-5.5", 256000],
["gpt-5.6-sol", 368000]
];
for (const [modelName, expectedContextWindow] of cases) {
const model = catalogModelFor({
Providers: [
{
api_base_url: "https://chatgpt.com/backend-api/codex",
api_key: "ccr-local-agent-login",
modelMetadata: {
[modelName]: {
contextWindow: 272000,
maxContextWindow: 300000
}
},
models: [modelName],
name: "Codex API",
type: "openai_responses"
}
]
}, `Codex API/${modelName}`);
assert.equal(model.context_window, expectedContextWindow);
assert.equal(model.max_context_window, expectedContextWindow);
}
});
test("codex catalog falls back to local Codex model cache metadata", () => {
const previousCcrHome = process.env.CCR_INTERNAL_HOME_DIR;
const previousHome = process.env.HOME;
@@ -355,10 +386,10 @@ test("codex catalog falls back to local Codex model cache metadata", () => {
}, "Codex API/gpt-5-codex");
assert.deepEqual(model.additional_speed_tiers, [{ id: "fast", label: "Fast" }]);
assert.equal(model.context_window, 272000);
assert.equal(model.context_window, 256000);
assert.equal(model.default_reasoning_level, "high");
assert.equal(model.effective_context_window_percent, 92);
assert.equal(model.max_context_window, 300000);
assert.equal(model.max_context_window, 256000);
assert.deepEqual(model.service_tiers, [{ id: "auto" }]);
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), ["low", "high"]);
} finally {
@@ -80,6 +80,58 @@ test("Codex local provider account config upgrades persisted usage mapping", ()
assert.ok(upgradedManualResetMeter.remaining.includes("$.rate_limit_reset_credits.available_count"));
});
test("Codex local provider config caps imported GPT-5 context metadata", () => {
const provider = normalizeCodexProviderAccountConfig({
api_base_url: codexDefaultBaseUrl,
api_key: localAgentProviderApiKey,
modelMetadata: {
"custom-model": {
contextWindow: 64000,
maxContextWindow: 64000
},
"gpt-5.6-sol": {
contextWindow: 1050000,
maxContextWindow: 1050000
}
},
models: ["gpt-5-codex", "gpt-5.6-sol", "custom-model"],
name: "Codex API",
protocol: "openai_responses"
});
assert.deepEqual(provider.modelMetadata["gpt-5-codex"], {
contextWindow: 256000,
maxContextWindow: 256000
});
assert.deepEqual(provider.modelMetadata["gpt-5.6-sol"], {
contextWindow: 368000,
maxContextWindow: 368000
});
assert.deepEqual(provider.modelMetadata["custom-model"], {
contextWindow: 64000,
maxContextWindow: 64000
});
const nonLocal = normalizeCodexProviderAccountConfig({
api_base_url: codexDefaultBaseUrl,
api_key: "real-api-key",
modelMetadata: {
"gpt-5.6-sol": {
contextWindow: 1050000,
maxContextWindow: 1050000
}
},
models: ["gpt-5.6-sol"],
name: "OpenAI",
protocol: "openai_responses"
});
assert.deepEqual(nonLocal.modelMetadata["gpt-5.6-sol"], {
contextWindow: 1050000,
maxContextWindow: 1050000
});
});
test("Codex reset credit details include available effective and expiry times", () => {
const details = codexRateLimitResetCreditDetails({
rate_limit_reset_credits: {
@@ -240,10 +292,10 @@ test("Codex model catalog parser accepts live model endpoint shapes", () => {
});
assert.deepEqual(catalog.modelMetadata["gpt-5-codex"], {
additionalSpeedTiers: [{ id: "fast", label: "Fast" }],
contextWindow: 272000,
contextWindow: 256000,
defaultReasoningLevel: "high",
effectiveContextWindowPercent: 95,
maxContextWindow: 300000,
maxContextWindow: 256000,
serviceTiers: [{ id: "auto" }],
supportedReasoningLevels: [
{ description: "Low", effort: "low" },
@@ -251,6 +303,49 @@ test("Codex model catalog parser accepts live model endpoint shapes", () => {
],
supportsReasoningSummaries: true
});
assert.deepEqual(catalog.modelMetadata["gpt-5.1-codex"], {
contextWindow: 256000,
maxContextWindow: 256000
});
assert.deepEqual(catalog.modelMetadata["gpt-5.2-codex"], {
contextWindow: 256000,
maxContextWindow: 256000
});
});
test("Codex model catalog parser overrides imported GPT-5 context windows", () => {
const catalog = codexModelCatalogFromPayloadForTest({
models: [
"gpt-5-codex",
"gpt-5.5",
"gpt-5.6",
"gpt-5.6-sol",
"gpt-5.6-terra-2026-07-28",
"gpt-50"
]
});
assert.deepEqual(catalog.modelMetadata["gpt-5-codex"], {
contextWindow: 256000,
maxContextWindow: 256000
});
assert.deepEqual(catalog.modelMetadata["gpt-5.5"], {
contextWindow: 256000,
maxContextWindow: 256000
});
assert.deepEqual(catalog.modelMetadata["gpt-5.6"], {
contextWindow: 368000,
maxContextWindow: 368000
});
assert.deepEqual(catalog.modelMetadata["gpt-5.6-sol"], {
contextWindow: 368000,
maxContextWindow: 368000
});
assert.deepEqual(catalog.modelMetadata["gpt-5.6-terra-2026-07-28"], {
contextWindow: 368000,
maxContextWindow: 368000
});
assert.equal(catalog.modelMetadata?.["gpt-50"], undefined);
});
test("Codex model catalog parser ignores invalid context metadata", () => {