diff --git a/src/modules/llms/server/listModels.dispatch.ts b/src/modules/llms/server/listModels.dispatch.ts index e513c922f..056bb4227 100644 --- a/src/modules/llms/server/listModels.dispatch.ts +++ b/src/modules/llms/server/listModels.dispatch.ts @@ -29,7 +29,7 @@ import { openAIAccess } from './openai/openai.access'; import { alibabaModelFilter, alibabaModelSort, alibabaModelToModelDescription } from './openai/models/alibaba.models'; import { azureDeploymentFilter, azureDeploymentToModelDescription, azureParseFromDeploymentsAPI } from './openai/models/azure.models'; import { chutesAIHeuristic, chutesAIModelsToModelDescriptions } from './openai/models/chutesai.models'; -import { deepseekModelFilter, deepseekModelSort, deepseekModelToModelDescription } from './openai/models/deepseek.models'; +import { deepseekInjectVariants, deepseekModelFilter, deepseekModelSort, deepseekModelToModelDescription } from './openai/models/deepseek.models'; import { fastAPIHeuristic, fastAPIModels } from './openai/models/fastapi.models'; import { fireworksAIHeuristic, fireworksAIModelsToModelDescriptions } from './openai/models/fireworksai.models'; import { groqModelFilter, groqModelSortFn, groqModelToModelDescription } from './openai/models/groq.models'; @@ -345,9 +345,11 @@ function _listModelsCreateDispatch(access: AixAPI_Access, signal?: AbortSignal): .sort(openAISortModels); case 'deepseek': + // [DeepSeek, 2025-12-01] Inject V3.2-Speciale variant via reduce return maybeModels .filter(({ id }) => deepseekModelFilter(id)) .map(({ id }) => deepseekModelToModelDescription(id)) + .reduce(deepseekInjectVariants, [] as ModelDescriptionSchema[]) .sort(deepseekModelSort); case 'groq': diff --git a/src/modules/llms/server/openai/models/deepseek.models.ts b/src/modules/llms/server/openai/models/deepseek.models.ts index e6a6c1ac7..3e2d3740c 100644 --- a/src/modules/llms/server/openai/models/deepseek.models.ts +++ b/src/modules/llms/server/openai/models/deepseek.models.ts @@ -8,10 +8,10 @@ import { fromManualMapping, ManualMappings } from '../../models.mappings'; const _knownDeepseekChatModels: ManualMappings = [ // [Models and Pricing](https://api-docs.deepseek.com/quick_start/pricing) // [List Models](https://api-docs.deepseek.com/api/list-models) - // [Release Notes - V3.2-Exp](https://api-docs.deepseek.com/news/news250929) - Released 2025-09-29 + // [Release Notes - V3.2](https://api-docs.deepseek.com/news/news251201) - Released 2025-12-01 { idPrefix: 'deepseek-reasoner', - label: 'DeepSeek V3.2-Exp (Reasoner)', + label: 'DeepSeek V3.2 (Reasoner)', description: 'Reasoning model with Chain-of-Thought capabilities, 128K context length. Supports JSON output and function calling.', contextWindow: 131072, // 128K interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_Reasoning], @@ -21,7 +21,7 @@ const _knownDeepseekChatModels: ManualMappings = [ }, { idPrefix: 'deepseek-chat', - label: 'DeepSeek V3.2-Exp', + label: 'DeepSeek V3.2', description: 'General-purpose model with 128K context length. Supports JSON output and function calling.', contextWindow: 131072, // 128K interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json], @@ -59,3 +59,28 @@ export function deepseekModelSort(a: ModelDescriptionSchema, b: ModelDescription return aIndex - bIndex; return a.id.localeCompare(b.id); } + + +// [DeepSeek, 2025-12-01] V3.2-Speciale: Temporary endpoint until Dec 15, 2025 15:59 UTC +// Thinking mode only (deepseek-reasoner), 128K max output, no JSON/tool calling +export const DEEPSEEK_SPECIALE_HOST = 'https://api.deepseek.com/v3.2_speciale_expires_on_20251215'; +export const DEEPSEEK_SPECIALE_SUFFIX = '@speciale'; + +const _hardcodedDeepseekVariants: { [modelId: string]: Partial } = { + 'deepseek-reasoner': { + id: 'deepseek-reasoner' + DEEPSEEK_SPECIALE_SUFFIX, // [DeepSeek, 2025-12-01] marker for dispatch routing (no idVariant - the @speciale suffix serves as both) + label: 'DeepSeek V3.2 Speciale', + description: 'V3.2-Speciale reasoning model. 128K max output, no JSON/tool calling. Expires Dec 15, 2025.', + interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Reasoning], // NO Fn, NO Json + // contextWindow: null, + // maxCompletionTokens: undefined, // default 64K, max 128K (higher than regular reasoner's 32K default) + }, +}; + +export function deepseekInjectVariants(models: ModelDescriptionSchema[], model: ModelDescriptionSchema): ModelDescriptionSchema[] { + // [DeepSeek, 2025-12-01] Inject Speciale variant for deepseek-reasoner + if (_hardcodedDeepseekVariants[model.id]) + models.push({ ...model, ..._hardcodedDeepseekVariants[model.id] }); + models.push(model); + return models; +}