From caec11513823f821a2cdcaf02c9d43bd506e24d8 Mon Sep 17 00:00:00 2001 From: xinweigao Date: Sat, 8 Aug 2026 00:21:06 +0800 Subject: [PATCH 1/4] fix(catalog): restore DeepSeek V4 context window on routed rebuilds DeepSeek routed models were rebuilt with a 128k context window after every sync because the registry entry had no jawcodeBundle, so the catalog metadata restore step could not resolve the provider and the strict-fields fallback overwrote the window. Users who saved the provider under a title-cased key ("DeepSeek") also missed the alias table, which was case-sensitive. Add jawcodeBundle: "deepseek" to the registry entry and fold provider case in resolveJawcodeProvider, then regenerate the metadata snapshot with the official 1,048,576-token window for both V4 models. Routed deepseek entries now restore 1,048,576 instead of the 128k fallback. --- scripts/generate-model-metadata.ts | 2 +- src/providers/registry.ts | 8 +++++++- tests/codex-catalog.test.ts | 20 ++++++++++++++++++++ tests/provider-registry-parity.test.ts | 8 ++++++-- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/scripts/generate-model-metadata.ts b/scripts/generate-model-metadata.ts index edd8d3560..af13fed71 100644 --- a/scripts/generate-model-metadata.ts +++ b/scripts/generate-model-metadata.ts @@ -105,7 +105,7 @@ for (const provider of allowedProviders) { lines.push("};"); lines.push(""); lines.push("export function resolveMetadataProvider(provider: string): string | undefined {"); -lines.push(" return PROVIDER_ALIASES[provider];"); +lines.push(" return PROVIDER_ALIASES[provider] ?? PROVIDER_ALIASES[provider.toLowerCase()];"); lines.push("}"); lines.push(""); lines.push("export function getModelMetadata(provider: string, modelId: string): ModelMetadata | undefined {"); diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 375c4ada7..590c0ffcc 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -1299,11 +1299,17 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ adapter: "openai-chat", authKind: "key", dashboardUrl: "https://platform.deepseek.com/api_keys", + // Route DeepSeek's own catalog bundle so routed rebuilds restore the official + // context window from jawcode metadata instead of falling back to the 128k + // strict-fields default (jawcode models.json, verified 2026-08-08). + jawcodeBundle: "deepseek", // deepseek-chat/deepseek-reasoner are upstream-deprecated at 2026-07-24 15:59 UTC; // kept until then. Evidence: devlog/_plan/260710_provider_hardening/002_research_cn.md. models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS], defaultModel: "deepseek-v4-flash", - modelContextWindows: { "deepseek-v4-flash": 1_000_000, "deepseek-v4-pro": 1_000_000 }, + // Official DeepSeek Codex setup (codex-deepseek-setup.sh) advertises 1,048,576 + // for both V4 models; the older 1,000,000 figure was a rounded approximation. + modelContextWindows: { "deepseek-v4-flash": 1_048_576, "deepseek-v4-pro": 1_048_576 }, // DeepSeek documents V4-Flash as a native Responses API model adapted for Codex. The // API id is `deepseek-v4-flash`; `DeepSeek-V4-Flash-0731` is a release/version label. modelWireDefaults: { diff --git a/tests/codex-catalog.test.ts b/tests/codex-catalog.test.ts index a65fe0417..12cba1b04 100644 --- a/tests/codex-catalog.test.ts +++ b/tests/codex-catalog.test.ts @@ -2826,6 +2826,26 @@ describe("Codex catalog routed normalization", () => { expect(routed?.input_modalities).toEqual(["text"]); }); + test("DeepSeek routed entries restore the official context window from jawcode metadata", () => { + const entries = buildCatalogEntries(nativeTemplate(), [], [ + { provider: "deepseek", id: "deepseek-v4-flash" }, + { provider: "deepseek", id: "deepseek-v4-pro" }, + ]); + const flash = entries.find(e => e.slug === "deepseek/deepseek-v4-flash"); + const pro = entries.find(e => e.slug === "deepseek/deepseek-v4-pro"); + + for (const routed of [flash, pro]) { + expect(routed?.context_window).toBe(1_048_576); + expect(routed?.max_context_window).toBe(1_048_576); + expect(routed?.auto_compact_token_limit).toBe(943_718); // floor(1048576 * 0.9) + expect(routed?.input_modalities).toEqual(["text"]); + } + // The catalog rebuild falls back to the strict 128k default when metadata is missing, + // so this assertion proves the jawcode bundle lookup actually ran. + expect(resolveMetadataProvider("deepseek")).toBe("deepseek"); + expect(getModelMetadata("deepseek", "deepseek-v4-flash")?.contextWindow).toBe(1_048_576); + }); + test("provider context-cap applies before jawcode catalog metadata reaches Codex", () => { const entries = buildCatalogEntries(nativeTemplate(), [], [ { provider: "opencode-go", id: "deepseek-v4-pro", contextCap: 350_000, contextCapped: false }, diff --git a/tests/provider-registry-parity.test.ts b/tests/provider-registry-parity.test.ts index 2bef5e696..0b24e63a4 100644 --- a/tests/provider-registry-parity.test.ts +++ b/tests/provider-registry-parity.test.ts @@ -234,8 +234,8 @@ describe("provider registry parity", () => { baseUrl: "https://api.deepseek.com", defaultModel: "deepseek-v4-flash", modelContextWindows: { - "deepseek-v4-flash": 1_000_000, - "deepseek-v4-pro": 1_000_000, + "deepseek-v4-flash": 1_048_576, + "deepseek-v4-pro": 1_048_576, }, }); @@ -761,6 +761,7 @@ describe("provider registry parity", () => { "google-antigravity": "google", "antigravity": "google", "gemini-antigravity": "google", + deepseek: "deepseek", moonshot: "moonshot", minimax: "minimax", "minimax-cn": "minimax", @@ -769,6 +770,9 @@ describe("provider registry parity", () => { }); expect(resolveMetadataProvider("gemini")).toBe("google"); expect(resolveMetadataProvider("minimax-cn")).toBe("minimax"); + expect(resolveMetadataProvider("deepseek")).toBe("deepseek"); + // User-saved provider keys can be title-cased ("DeepSeek"); alias lookup folds case. + expect(resolveMetadataProvider("DeepSeek")).toBe("deepseek"); }); test("legacy azure adapter spelling remains accepted", () => { From 7a8756cc6d0c92830b77461107e78f3f0d46616f Mon Sep 17 00:00:00 2001 From: xinweigao Date: Sat, 8 Aug 2026 10:17:08 +0800 Subject: [PATCH 2/4] fix(providers): reconcile DeepSeek deprecation note with retired aliases deepseek-chat/deepseek-reasoner were deprecated upstream on 2026-07-24 15:59 UTC and official identifiers are now deepseek-v4-flash/deepseek-v4-pro. The aliases stay in the registry only as compatibility aliases so existing saved configs and requests keep validating and routing; the comment now documents that post-deprecation reason instead of a stale future-dated note. --- src/providers/registry.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 590c0ffcc..b9d6d50cb 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -1303,8 +1303,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // context window from jawcode metadata instead of falling back to the 128k // strict-fields default (jawcode models.json, verified 2026-08-08). jawcodeBundle: "deepseek", - // deepseek-chat/deepseek-reasoner are upstream-deprecated at 2026-07-24 15:59 UTC; - // kept until then. Evidence: devlog/_plan/260710_provider_hardening/002_research_cn.md. + // deepseek-chat/deepseek-reasoner were deprecated upstream on 2026-07-24 15:59 UTC; + // official identifiers are now deepseek-v4-flash / deepseek-v4-pro. They stay in + // the list only as compatibility aliases so existing saved configs and requests + // keep validating and routing (they previously mapped to v4-flash; devlog + // _plan/260710_provider_hardening/002_research_cn.md). The current offerings are + // the V4 ids — defaultModel and the model-specific wiring above use them. models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS], defaultModel: "deepseek-v4-flash", // Official DeepSeek Codex setup (codex-deepseek-setup.sh) advertises 1,048,576 From 8a804281b3fab678e0f728185e5be9133bbb6e04 Mon Sep 17 00:00:00 2001 From: xinweigao Date: Sat, 8 Aug 2026 18:36:31 +0800 Subject: [PATCH 3/4] fix(catalog): restore DeepSeek V4 context window via vendored metadata pipeline The metadata pipeline was de-jawcoded upstream: the bundled snapshot now lives at scripts/model-metadata.source.json and generates src/generated/model-metadata.ts. Move the DeepSeek V4 context-window fix onto that pipeline (1048576 for flash and pro per DeepSeek's official Codex setup docs) and regenerate the committed output so routed rebuilds stop falling back to the 128k strict-fields default. --- scripts/model-metadata.source.json | 6 +++--- src/generated/model-metadata.ts | 5 +++-- src/providers/registry.ts | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/model-metadata.source.json b/scripts/model-metadata.source.json index 7fdc72322..e59a4e525 100644 --- a/scripts/model-metadata.source.json +++ b/scripts/model-metadata.source.json @@ -9439,7 +9439,7 @@ "cacheRead": 0.0028, "cacheWrite": 0 }, - "contextWindow": 1000000, + "contextWindow": 1048576, "maxTokens": 384000, "compat": { "supportsDeveloperRole": false, @@ -9485,7 +9485,7 @@ "cacheRead": 0.003625, "cacheWrite": 0 }, - "contextWindow": 1000000, + "contextWindow": 1048576, "maxTokens": 384000, "compat": { "supportsDeveloperRole": false, @@ -85724,4 +85724,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/generated/model-metadata.ts b/src/generated/model-metadata.ts index 78f305dd8..d58b66d09 100644 --- a/src/generated/model-metadata.ts +++ b/src/generated/model-metadata.ts @@ -27,6 +27,7 @@ const PROVIDER_ALIASES: Record = { "google-antigravity": "google", "antigravity": "google", "gemini-antigravity": "google", + "deepseek": "deepseek", "moonshot": "moonshot", "zhipu-bigmodel": "zai", "zhipu-bigmodel-coding": "zai", @@ -40,7 +41,7 @@ const DATA: Record = { "anthropic": [["claude-3-5-sonnet-20240620",200000,8192,"text,image",0,null,3,15,0.3,3.75],["claude-3-5-sonnet-20241022",200000,8192,"text,image",0,null,3,15,0.3,3.75],["claude-3-haiku-20240307",200000,4096,"text,image",0,null,0.25,1.25,0.03,0.3],["claude-fable-5",1000000,128000,"text,image",1,null,10,50,1,12.5],["claude-haiku-4-5",200000,64000,"text,image",1,null,1,5,0.1,1.25],["claude-haiku-4-5-20251001",200000,64000,"text,image",1,null,1,5,0.1,1.25],["claude-opus-4-0",200000,32000,"text,image",1,null,15,75,1.5,18.75],["claude-opus-4-1",200000,32000,"text,image",1,null,15,75,1.5,18.75],["claude-opus-4-1-20250805",200000,32000,"text,image",1,null,15,75,1.5,18.75],["claude-opus-4-20250514",200000,32000,"text,image",1,null,15,75,1.5,18.75],["claude-opus-4-5",200000,64000,"text,image",1,null,5,25,0.5,6.25],["claude-opus-4-5-20251101",200000,64000,"text,image",1,null,5,25,0.5,6.25],["claude-opus-4-6",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["claude-opus-4-6[1m]",1000000,128000,"text,image",1,"claude-opus-4-6",5,25,0.5,6.25],["claude-opus-4-7",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["claude-opus-4-7[1m]",1000000,128000,"text,image",1,"claude-opus-4-7",5,25,0.5,6.25],["claude-opus-4-8",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["claude-opus-4-8[1m]",1000000,128000,"text,image",1,"claude-opus-4-8",5,25,0.5,6.25],["claude-opus-5",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["claude-sonnet-4-0",200000,64000,"text,image",1,null,3,15,0.3,3.75],["claude-sonnet-4-20250514",200000,64000,"text,image",1,null,3,15,0.3,3.75],["claude-sonnet-4-5",1000000,64000,"text,image",1,null,3,15,0.3,3.75],["claude-sonnet-4-5-20250929",1000000,64000,"text,image",1,null,3,15,0.3,3.75],["claude-sonnet-4-6",1000000,128000,"text,image",1,null,3,15,0.3,3.75],["claude-sonnet-4-6[1m]",1000000,64000,"text,image",1,"claude-sonnet-4-6",3,15,0.3,3.75],["claude-sonnet-5",1000000,128000,"text,image",1,null,2,10,0.2,2.5]], "azure-openai": [["gpt-4.1",1047576,32768,"text,image",0,null,2,8,0.5,0],["gpt-4o",128000,16384,"text,image",0,null,2.5,10,1.25,0],["gpt-4o-mini",128000,16384,"text,image",0,null,0.15,0.6,0.075,0],["o3",200000,100000,"text,image",1,null,2,8,0.5,0],["o3-mini",200000,100000,"text",1,null,1.1,4.4,0.55,0]], "cerebras": [["gemma-4-31b",131072,40960,"text,image",1,null,0.99,1.49,0,0],["gpt-oss-120b",131072,40960,"text",1,null,0.35,0.75,0,0],["llama3.1-8b",32000,8000,"text",0,null,0.1,0.1,0,0],["qwen-3-235b-a22b-instruct-2507",131000,32000,"text",0,null,0.6,1.2,0,0],["qwen-3-coder-480b",131072,32768,"text",0,null,0,0,0,0],["zai-glm-4.6",131072,32768,"text",0,null,0,0,0,0],["zai-glm-4.7",131072,40960,"text",1,null,2.25,2.75,2.25,0]], - "deepseek": [["deepseek-v4-flash",1000000,384000,"text",1,null,0.14,0.28,0.0028,0],["deepseek-v4-pro",1000000,384000,"text",1,null,0.435,0.87,0.003625,0]], + "deepseek": [["deepseek-v4-flash",1048576,384000,"text",1,null,0.14,0.28,0.0028,0],["deepseek-v4-pro",1048576,384000,"text",1,null,0.435,0.87,0.003625,0]], "google": [["deep-research-max-preview-04-2026",131072,65536,"text,image",1,null,2,12,0.2,0],["deep-research-preview-04-2026",131072,65536,"text,image",1,null,2,12,0.2,0],["gemini-1.5-flash",1000000,8192,"text,image",0,null,0.075,0.3,0.01875,0],["gemini-1.5-flash-8b",1000000,8192,"text,image",0,null,0.0375,0.15,0.01,0],["gemini-1.5-pro",1000000,8192,"text,image",0,null,1.25,5,0.3125,0],["gemini-2.0-flash",1048576,8192,"text,image",0,null,0.1,0.4,0.025,0],["gemini-2.0-flash-lite",1048576,8192,"text,image",0,null,0.075,0.3,0,0],["gemini-2.5-computer-use-preview-10-2025",131072,65536,"text,image",1,null,1.25,10,0,0],["gemini-2.5-flash",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0],["gemini-2.5-flash-lite",1048576,65536,"text,image",1,null,0.1,0.4,0.01,0],["gemini-2.5-flash-lite-preview-06-17",1048576,65536,"text,image",1,null,0.1,0.4,0.025,0],["gemini-2.5-flash-lite-preview-09-2025",1048576,65536,"text,image",1,null,0.1,0.4,0.025,0],["gemini-2.5-flash-preview-04-17",1048576,65536,"text,image",1,null,0.15,0.6,0.0375,0],["gemini-2.5-flash-preview-05-20",1048576,65536,"text,image",1,null,0.15,0.6,0.0375,0],["gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1,null,0.3,2.5,0.075,0],["gemini-2.5-pro",1048576,65536,"text,image",1,null,1.25,10,0.125,0],["gemini-2.5-pro-preview-05-06",1048576,65536,"text,image",1,null,1.25,10,0.31,0],["gemini-2.5-pro-preview-06-05",1048576,65536,"text,image",1,null,1.25,10,0.31,0],["gemini-3-flash-preview",1048576,65536,"text,image",1,null,0.5,3,0.05,0],["gemini-3-pro-preview",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.1-flash-lite",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-3.1-flash-lite-image",65536,65536,"text,image",1,null,0.25,30,0,0],["gemini-3.1-flash-lite-preview",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-3.1-flash-live-preview",131072,65536,"text,image",1,null,0.75,4.5,0,0],["gemini-3.1-pro-preview",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.1-pro-preview-customtools",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.5-flash",1048576,65536,"text,image",1,null,1.5,9,0.15,0],["gemini-3.5-flash-lite",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0],["gemini-3.6-flash",1048576,65536,"text,image",1,null,1.5,7.5,0.15,0],["gemini-flash-latest",1048576,65536,"text,image",1,null,1.5,9,0.15,0],["gemini-flash-lite-latest",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-live-2.5-flash",128000,8000,"text,image",1,null,0.5,2,0,0],["gemini-live-2.5-flash-preview-native-audio",131072,65536,"text",1,null,0.5,2,0,0],["gemini-robotics-er-1.6-preview",131072,65536,"text,image",1,null,1,5,0,0],["gemma-3-27b-it",131072,8192,"text,image",0,null,0,0,0,0],["gemma-4-26b",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-26b-a4b-it",262144,32768,"text,image",1,null,0,0,0,0],["gemma-4-26b-it",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-31b",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-31b-it",262144,32768,"text,image",1,null,0,0,0,0],["gemma-4-E2B-it",131072,8192,"text,image",1,null,0,0,0,0],["gemma-4-E4B-it",131072,8192,"text,image",1,null,0,0,0,0]], "minimax": [["MiniMax-M2",196608,128000,"text",1,null,0.3,1.2,0,0],["MiniMax-M2.1",204800,131072,"text",1,null,0.3,1.2,0,0],["MiniMax-M2.5",204800,131072,"text",1,null,0.3,1.2,0.03,0.375],["MiniMax-M2.5-highspeed",204800,131072,"text",1,null,0.6,2.4,0.06,0.375],["MiniMax-M2.5-lightning",204800,32000,"text",1,null,0.3,2.4,0,0],["MiniMax-M2.7",204800,131072,"text",1,null,0.3,1.2,0.06,0.375],["MiniMax-M2.7-highspeed",204800,131072,"text",1,null,0.6,2.4,0.06,0.375],["minimax-m3",512000,128000,"text,image",1,null,0.6,2.4,0.12,0],["MiniMax-M3",1000000,128000,"text,image,video",1,null,0.3,1.2,0.06,0]], "mistral": [["codestral-latest",256000,4096,"text",0,null,0.3,0.9,0,0],["devstral-2512",262144,262144,"text",0,null,0.4,2,0,0],["devstral-latest",262144,262144,"text",0,null,0.4,2,0,0],["devstral-medium-2507",128000,128000,"text",0,null,0.4,2,0,0],["devstral-medium-latest",262144,262144,"text",0,null,0.4,2,0,0],["devstral-small-2505",128000,128000,"text",0,null,0.1,0.3,0,0],["devstral-small-2507",128000,128000,"text",0,null,0.1,0.3,0,0],["labs-devstral-small-2512",256000,256000,"text,image",0,null,0,0,0,0],["magistral-medium-latest",128000,16384,"text",1,null,2,5,0,0],["magistral-small",128000,128000,"text",1,null,0.5,1.5,0,0],["ministral-3b-latest",128000,128000,"text",0,null,0.04,0.04,0,0],["ministral-8b-latest",128000,128000,"text",0,null,0.1,0.1,0,0],["mistral-large-2411",131072,16384,"text",0,null,2,6,0,0],["mistral-large-2512",262144,262144,"text,image",0,null,0.5,1.5,0,0],["mistral-large-latest",262144,262144,"text,image",0,null,0.5,1.5,0,0],["mistral-medium-2505",131072,131072,"text,image",0,null,0.4,2,0,0],["mistral-medium-2508",262144,262144,"text,image",0,null,0.4,2,0,0],["mistral-medium-2604",262144,262144,"text,image",1,null,1.5,7.5,0,0],["mistral-medium-latest",262144,262144,"text,image",1,null,1.5,7.5,0,0],["mistral-nemo",128000,128000,"text",0,null,0.15,0.15,0,0],["mistral-small-2506",128000,16384,"text,image",0,null,0.1,0.3,0,0],["mistral-small-2603",256000,256000,"text,image",1,null,0.15,0.6,0,0],["mistral-small-latest",256000,256000,"text,image",1,null,0.15,0.6,0,0],["open-mistral-7b",8000,8000,"text",0,null,0.25,0.25,0,0],["open-mistral-nemo",128000,128000,"text",0,null,0.15,0.15,0,0],["open-mixtral-8x22b",64000,64000,"text",0,null,2,6,0,0],["open-mixtral-8x7b",32000,32000,"text",0,null,0.7,0.7,0,0],["pixtral-12b",128000,128000,"text,image",0,null,0.15,0.15,0,0],["pixtral-large-latest",128000,128000,"text,image",0,null,2,6,0,0]], @@ -54,7 +55,7 @@ const DATA: Record = { }; export function resolveMetadataProvider(provider: string): string | undefined { - return PROVIDER_ALIASES[provider]; + return PROVIDER_ALIASES[provider] ?? PROVIDER_ALIASES[provider.toLowerCase()]; } export function getModelMetadata(provider: string, modelId: string): ModelMetadata | undefined { diff --git a/src/providers/registry.ts b/src/providers/registry.ts index b9d6d50cb..656816253 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -1300,14 +1300,15 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ authKind: "key", dashboardUrl: "https://platform.deepseek.com/api_keys", // Route DeepSeek's own catalog bundle so routed rebuilds restore the official - // context window from jawcode metadata instead of falling back to the 128k - // strict-fields default (jawcode models.json, verified 2026-08-08). + // context window from the vendored model-metadata bundle instead of falling + // back to the 128k strict-fields default (scripts/model-metadata.source.json, + // verified 2026-08-08). jawcodeBundle: "deepseek", // deepseek-chat/deepseek-reasoner were deprecated upstream on 2026-07-24 15:59 UTC; // official identifiers are now deepseek-v4-flash / deepseek-v4-pro. They stay in // the list only as compatibility aliases so existing saved configs and requests // keep validating and routing (they previously mapped to v4-flash; devlog - // _plan/260710_provider_hardening/002_research_cn.md). The current offerings are + // _fin/260710_provider_hardening/002_research_cn.md). The current offerings are // the V4 ids — defaultModel and the model-specific wiring above use them. models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS], defaultModel: "deepseek-v4-flash", From 7f536489186e7b811d7cd0ea97097d72b16e8d2c Mon Sep 17 00:00:00 2001 From: xinweigao Date: Sat, 8 Aug 2026 18:49:59 +0800 Subject: [PATCH 4/4] fix(catalog): restore DeepSeek V4 rows when /v1/models returns an empty list Add deepseek to the catalog augmentation allowlist so metadata-sourced V4 rows are appended when live discovery returns nothing, and cover the empty-discovery path with a regression test asserting the official 1,048,576 context window, auto-compact limit, and text-only input. --- src/codex/catalog/parsing.ts | 2 +- tests/codex-catalog.test.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/codex/catalog/parsing.ts b/src/codex/catalog/parsing.ts index a4cc9e073..fa0028759 100644 --- a/src/codex/catalog/parsing.ts +++ b/src/codex/catalog/parsing.ts @@ -126,7 +126,7 @@ export type RawEntry = Record; export type RawCatalog = { models?: RawEntry[]; [k: string]: unknown }; -export const JAWCODE_CATALOG_AUGMENT_PROVIDERS = new Set(["opencode-go"]); +export const JAWCODE_CATALOG_AUGMENT_PROVIDERS = new Set(["opencode-go", "deepseek"]); export const ROUTED_MODEL_COMPATIBILITY_EXCLUSIONS = new Set([ // Issue #82: Zen Go /models advertises HY3, but Console Go rejects it as outside the lite list. diff --git a/tests/codex-catalog.test.ts b/tests/codex-catalog.test.ts index 12cba1b04..a46835463 100644 --- a/tests/codex-catalog.test.ts +++ b/tests/codex-catalog.test.ts @@ -2846,6 +2846,27 @@ describe("Codex catalog routed normalization", () => { expect(getModelMetadata("deepseek", "deepseek-v4-flash")?.contextWindow).toBe(1_048_576); }); + test("DeepSeek catalog sync appends V4 rows missing from /v1/models", () => { + const models = augmentRoutedModelsWithMetadata([], ["deepseek"]); + const slugs = new Set(models.map(m => `${m.provider}/${m.id}`)); + + expect(slugs.has("deepseek/deepseek-v4-flash")).toBe(true); + expect(slugs.has("deepseek/deepseek-v4-pro")).toBe(true); + for (const model of models) { + expect(model.contextWindow).toBe(1_048_576); + expect(model.inputModalities).toEqual(["text"]); + } + + const entries = buildCatalogEntries(nativeTemplate(), [], models); + for (const id of ["deepseek-v4-flash", "deepseek-v4-pro"]) { + const routed = entries.find(e => e.slug === `deepseek/${id}`); + expect(routed?.context_window).toBe(1_048_576); + expect(routed?.max_context_window).toBe(1_048_576); + expect(routed?.auto_compact_token_limit).toBe(943_718); // floor(1048576 * 0.9) + expect(routed?.input_modalities).toEqual(["text"]); + } + }); + test("provider context-cap applies before jawcode catalog metadata reaches Codex", () => { const entries = buildCatalogEntries(nativeTemplate(), [], [ { provider: "opencode-go", id: "deepseek-v4-pro", contextCap: 350_000, contextCapped: false },