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/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/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/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 375c4ada7..656816253 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -1299,11 +1299,22 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ adapter: "openai-chat", authKind: "key", dashboardUrl: "https://platform.deepseek.com/api_keys", - // 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. + // Route DeepSeek's own catalog bundle so routed rebuilds restore the official + // 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 + // _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", - 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..a46835463 100644 --- a/tests/codex-catalog.test.ts +++ b/tests/codex-catalog.test.ts @@ -2826,6 +2826,47 @@ 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("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 }, 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", () => {