diff --git a/packages/i18n/src/locales/en/settings.yaml b/packages/i18n/src/locales/en/settings.yaml index 4577cac51..89c5e6817 100644 --- a/packages/i18n/src/locales/en/settings.yaml +++ b/packages/i18n/src/locales/en/settings.yaml @@ -1245,6 +1245,10 @@ pages: nano_banana: Nano Banana (Gemini 2.5 Flash Image) deepseek: description: deepseek.com + fields: + field: + thinking-mode: + description: Controls whether DeepSeek reasoning models use thinking in chat requests. title: DeepSeek elevenlabs: description: elevenlabs.io diff --git a/packages/i18n/src/locales/zh-Hans/settings.yaml b/packages/i18n/src/locales/zh-Hans/settings.yaml index 680321bd8..f00bd07ba 100644 --- a/packages/i18n/src/locales/zh-Hans/settings.yaml +++ b/packages/i18n/src/locales/zh-Hans/settings.yaml @@ -1191,6 +1191,10 @@ pages: nano_banana: Nano Banana(Gemini 2.5 Flash Image) deepseek: description: DeepSeek.com + fields: + field: + thinking-mode: + description: 控制 DeepSeek 推理模型是否在聊天请求中启用深度思考。 title: 深度求索 DeepSeek elevenlabs: description: Elevenlabs.io diff --git a/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue b/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue index 3802d2b61..271b3478b 100644 --- a/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue +++ b/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue @@ -14,6 +14,7 @@ import { useProviderValidation } from '@proj-airi/stage-ui/composables/use-provi import { getDefinedProvider } from '@proj-airi/stage-ui/libs' import { useConsciousnessStore } from '@proj-airi/stage-ui/stores/modules/consciousness' import { useProviderConfigStore } from '@proj-airi/stage-ui/stores/providers/config' +import { FieldCombobox } from '@proj-airi/ui' import { storeToRefs } from 'pinia' import { computed } from 'vue' import { useRoute } from 'vue-router' @@ -44,6 +45,17 @@ const baseUrl = computed({ }, }) +const thinkingMode = computed({ + get: () => providers.value[providerId]?.thinkingMode || 'auto', + set: (value) => { + if (!providers.value[providerId]) + providers.value[providerId] = {} + providers.value[providerId].thinkingMode = value + }, +}) + +const supportsDeepSeekThinkingMode = computed(() => providerId === 'deepseek') + // Use the composable to get validation logic and state const { t, @@ -107,6 +119,18 @@ function goToModelSelection() { v-model="baseUrl" :placeholder="providerMetadata?.defaultConfig.baseUrl as string || 'Base URL of your provider'" /> + + { + it('should not set thinking when thinkingMode is auto', () => { + const provider = createDeepSeekChatProvider('auto') + + expect(provider.chat('deepseek-chat')).not.toHaveProperty('thinking') + }) + + it('should set thinking disabled when thinkingMode is disable', () => { + const provider = createDeepSeekChatProvider('disable') + + expect(provider.chat('deepseek-chat')).toMatchObject({ + thinking: { type: 'disabled' }, + }) + }) + + it('should set thinking enabled when thinkingMode is enable', () => { + const provider = createDeepSeekChatProvider('enable') + + expect(provider.chat('deepseek-chat')).toMatchObject({ + thinking: { type: 'enabled' }, + }) + }) +}) diff --git a/packages/stage-ui/src/libs/providers/providers/deepseek/index.ts b/packages/stage-ui/src/libs/providers/providers/deepseek/index.ts index 15c1f64dc..b854d6cca 100644 --- a/packages/stage-ui/src/libs/providers/providers/deepseek/index.ts +++ b/packages/stage-ui/src/libs/providers/providers/deepseek/index.ts @@ -5,6 +5,8 @@ import { ProviderValidationCheck } from '../../types' import { createOpenAICompatibleValidators } from '../../validators' import { defineProvider } from '../registry' +type DeepSeekThinkingMode = 'auto' | 'disable' | 'enable' + const deepSeekConfigSchema = z.object({ apiKey: z .string('API Key'), @@ -12,10 +14,38 @@ const deepSeekConfigSchema = z.object({ .string('Base URL') .optional() .default('https://api.deepseek.com/'), + thinkingMode: z.enum(['auto', 'disable', 'enable']) + .default('auto'), }) type DeepSeekConfig = z.input +function normalizeDeepSeekThinkingMode(value: unknown): DeepSeekThinkingMode { + switch (value) { + case 'auto': + case 'disable': + case 'enable': + return value + default: + return 'auto' + } +} + +function resolveDeepSeekThinking(modeRaw: unknown): { type: 'disabled' | 'enabled' } | undefined { + const mode = normalizeDeepSeekThinkingMode(modeRaw) + + switch (mode) { + case 'auto': + return undefined + case 'disable': + return { type: 'disabled' } + case 'enable': + return { type: 'enabled' } + default: + return undefined + } +} + export const providerDeepSeek = defineProvider({ id: 'deepseek', order: 4, @@ -39,9 +69,42 @@ export const providerDeepSeek = defineProvider({ descriptionLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.base-url.description'), placeholderLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.base-url.placeholder'), }), + thinkingMode: deepSeekConfigSchema.shape.thinkingMode.meta({ + labelLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.thinking-mode.label'), + descriptionLocalized: t('settings.pages.providers.provider.deepseek.fields.field.thinking-mode.description'), + section: 'advanced', + type: 'select', + options: [ + { + label: t('settings.pages.providers.catalog.edit.config.common.fields.field.thinking-mode.options.auto'), + value: 'auto', + }, + { + label: t('settings.pages.providers.catalog.edit.config.common.fields.field.thinking-mode.options.disable'), + value: 'disable', + }, + { + label: t('settings.pages.providers.catalog.edit.config.common.fields.field.thinking-mode.options.enable'), + value: 'enable', + }, + ], + }), }), createProvider(config) { - return createDeepSeek(config.apiKey, config.baseUrl) + const baseProvider = createDeepSeek(config.apiKey, config.baseUrl) + + return { + ...baseProvider, + chat(model: string) { + const chatOptions = baseProvider.chat(model) + const thinking = resolveDeepSeekThinking(config.thinkingMode) + + if (thinking === undefined) + return chatOptions + + return { ...chatOptions, thinking } + }, + } }, validationRequiredWhen(config) {