From acf1c1c5b21b7bcedd2fc7a0beff77e104d889c1 Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Tue, 22 Sep 2026 09:06:55 +0200 Subject: [PATCH] fix: scope deepseek budget and align supported model aliases --- .../copilot/lib.anthropicRouting.test.ts | 34 ++++++++++++------- .../src/lib/components/copilot/lib.test.ts | 1 + frontend/src/lib/components/copilot/lib.ts | 6 +++- .../src/lib/components/copilot/modelConfig.ts | 1 + 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/frontend/src/lib/components/copilot/lib.anthropicRouting.test.ts b/frontend/src/lib/components/copilot/lib.anthropicRouting.test.ts index fca7bd93a6..c2a231dedb 100644 --- a/frontend/src/lib/components/copilot/lib.anthropicRouting.test.ts +++ b/frontend/src/lib/components/copilot/lib.anthropicRouting.test.ts @@ -124,21 +124,29 @@ afterEach(() => { }) describe('DeepSeek output budget', () => { - it.each(['deepseek-flash', 'deepseek-v4-flash', 'deepseek-v4-pro'])( - 'sends the full reasoning and answer budget for %s', - async (model) => { - const { getCompletion, getNonStreamingMetadataCompletion, METADATA_MAX_TOKENS } = - await import('./lib') - h.currentModel = { provider: 'deepseek', model } + it.each([ + 'deepseek-flash', + 'deepseek-v4-flash', + 'deepseek-v4-pro', + 'deepseek-chat', + 'deepseek-reasoner' + ])('sends the full reasoning and answer budget for %s', async (model) => { + const { getCompletion } = await import('./lib') + h.currentModel = { provider: 'deepseek', model } - await getCompletion(messages, new AbortController()) - expect(openaiCreate.mock.calls[0][0].max_tokens).toBe(393216) + await getCompletion(messages, new AbortController()) + expect(openaiCreate.mock.calls[0][0].max_tokens).toBe(393216) + }) - openaiCreate.mockClear() - await getNonStreamingMetadataCompletion(messages, new AbortController()) - expect(openaiCreate.mock.calls[0][0].max_tokens).toBe(METADATA_MAX_TOKENS) - } - ) + it('preserves the metadata cap and third-party provider defaults', async () => { + const { getNonStreamingMetadataCompletion, getModelMaxTokens, METADATA_MAX_TOKENS } = + await import('./lib') + h.currentModel = { provider: 'deepseek', model: 'deepseek-flash' } + await getNonStreamingMetadataCompletion(messages, new AbortController()) + expect(openaiCreate.mock.calls[0][0].max_tokens).toBe(METADATA_MAX_TOKENS) + expect(getModelMaxTokens('customai', 'deepseek-flash')).toBe(8192) + expect(getModelMaxTokens('azure_foundry', 'deepseek-v4-pro')).toBe(8192) + }) }) describe('Anthropic Messages API routing', () => { diff --git a/frontend/src/lib/components/copilot/lib.test.ts b/frontend/src/lib/components/copilot/lib.test.ts index 964bf6e703..ad197b6b5b 100644 --- a/frontend/src/lib/components/copilot/lib.test.ts +++ b/frontend/src/lib/components/copilot/lib.test.ts @@ -263,6 +263,7 @@ describe('model context windows', () => { expect(getKnownModelContextWindow('gemini-3-flash')).toBe(1000000) expect(getKnownModelContextWindow('gemini-2.5-flash')).toBe(1000000) expect(getKnownModelContextWindow('deepseek-v4-pro')).toBe(1000000) + expect(getKnownModelContextWindow('deepseek-flash')).toBe(1000000) expect(getKnownModelContextWindow('deepseek-chat')).toBe(1000000) expect(getKnownModelContextWindow('deepseek-reasoner')).toBe(1000000) }) diff --git a/frontend/src/lib/components/copilot/lib.ts b/frontend/src/lib/components/copilot/lib.ts index 239880b780..1e4396da8b 100644 --- a/frontend/src/lib/components/copilot/lib.ts +++ b/frontend/src/lib/components/copilot/lib.ts @@ -15,6 +15,7 @@ import { get, type Writable } from 'svelte/store' import { OpenAPI, ResourceService, type Script } from '../../gen' import { EDIT_CONFIG, FIX_CONFIG, GEN_CONFIG } from './prompts' import { + parseModelId, requiresMaxCompletionTokens, usesAnthropicMessagesApi, usesOpenRouterPromptCaching @@ -310,7 +311,10 @@ export async function fetchAvailableModels( } export function getModelMaxTokens(provider: AIProvider, model: string) { - if (/^(deepseek-flash|deepseek-v4-(flash|pro))$/.test(model.split('/').pop() ?? '')) { + if ( + provider === 'deepseek' && + /^(deepseek-(flash|chat|reasoner)|deepseek-v4-(flash|pro))$/.test(parseModelId(model).base) + ) { // DeepSeek counts reasoning against the output budget as well as the answer. return 393216 } else if (model.includes('gpt-5')) { diff --git a/frontend/src/lib/components/copilot/modelConfig.ts b/frontend/src/lib/components/copilot/modelConfig.ts index cc45a23dac..18ac02de31 100644 --- a/frontend/src/lib/components/copilot/modelConfig.ts +++ b/frontend/src/lib/components/copilot/modelConfig.ts @@ -100,6 +100,7 @@ const MODEL_CONTEXT_WINDOWS: [name: string, contextWindow: number][] = [ // deepseek-reasoner aliases were retired 2026-07-24 but can still sit in a // saved selection, so they keep resolving to the window they had. ['deepseek-v4', 1_000_000], + ['deepseek-flash', 1_000_000], ['deepseek-chat', 1_000_000], ['deepseek-reasoner', 1_000_000], ['deepseek', 128_000],