diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index 6e9c2326ee..d424cf1402 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -300,6 +300,7 @@ export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = { qwen: ['low', 'medium', 'high'] as const, qwen_thinking: ['low', 'medium', 'high'] as const, doubao: ['auto', 'high'] as const, + doubao_no_auto: ['high'] as const, hunyuan: ['auto'] as const, zhipu: ['auto'] as const, perplexity: ['low', 'medium', 'high'] as const @@ -316,6 +317,7 @@ export const MODEL_SUPPORTED_OPTIONS: ThinkingOptionConfig = { qwen: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.qwen] as const, qwen_thinking: MODEL_SUPPORTED_REASONING_EFFORT.qwen_thinking, doubao: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao] as const, + doubao_no_auto: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao_no_auto] as const, hunyuan: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.hunyuan] as const, zhipu: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.zhipu] as const, perplexity: MODEL_SUPPORTED_REASONING_EFFORT.perplexity @@ -339,8 +341,13 @@ export const getThinkModelType = (model: Model): ThinkingModelType => { thinkingModelType = 'qwen_thinking' } thinkingModelType = 'qwen' - } else if (isSupportedThinkingTokenDoubaoModel(model)) thinkingModelType = 'doubao' - else if (isSupportedThinkingTokenHunyuanModel(model)) thinkingModelType = 'hunyuan' + } else if (isSupportedThinkingTokenDoubaoModel(model)) { + if (isDoubaoThinkingAutoModel(model)) { + thinkingModelType = 'doubao' + } else { + thinkingModelType = 'doubao_no_auto' + } + } else if (isSupportedThinkingTokenHunyuanModel(model)) thinkingModelType = 'hunyuan' else if (isSupportedReasoningEffortPerplexityModel(model)) thinkingModelType = 'perplexity' else if (isSupportedThinkingTokenZhipuModel(model)) thinkingModelType = 'zhipu' return thinkingModelType diff --git a/src/renderer/src/hooks/useAssistant.ts b/src/renderer/src/hooks/useAssistant.ts index 24d192501e..370635f97d 100644 --- a/src/renderer/src/hooks/useAssistant.ts +++ b/src/renderer/src/hooks/useAssistant.ts @@ -1,4 +1,10 @@ import { loggerService } from '@logger' +import { + getThinkModelType, + isSupportedReasoningEffortModel, + isSupportedThinkingTokenModel, + MODEL_SUPPORTED_OPTIONS +} from '@renderer/config/models' import { db } from '@renderer/databases' import { getDefaultTopic } from '@renderer/services/AssistantService' import { useAppDispatch, useAppSelector } from '@renderer/store' @@ -12,7 +18,7 @@ import { setModel, updateAssistant, updateAssistants, - updateAssistantSettings, + updateAssistantSettings as _updateAssistantSettings, updateDefaultAssistant, updateTopic, updateTopics @@ -20,7 +26,7 @@ import { import { setDefaultModel, setTopicNamingModel, setTranslateModel } from '@renderer/store/llm' import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types' import { uuid } from '@renderer/utils' -import { useCallback, useMemo } from 'react' +import { useCallback, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { TopicManager } from './useTopic' @@ -78,6 +84,36 @@ export function useAssistant(id: string) { const assistantWithModel = useMemo(() => ({ ...assistant, model }), [assistant, model]) + const updateAssistantSettings = useCallback( + (settings: Partial) => { + assistant?.id && dispatch(_updateAssistantSettings({ assistantId: assistant.id, settings })) + }, + [assistant.id, dispatch] + ) + + // 当model变化时,同步reasoning effort为模型支持的合法值 + useEffect(() => { + if (isSupportedThinkingTokenModel(model) || isSupportedReasoningEffortModel(model)) { + const currentReasoningEffort = assistant.settings?.reasoning_effort + const supportedOptions = MODEL_SUPPORTED_OPTIONS[getThinkModelType(model)] + if (currentReasoningEffort && !supportedOptions.includes(currentReasoningEffort)) { + // 选项不支持时,回退到第一个支持的值 + // 注意:这里假设可用的options不会为空 + const fallbackOption = supportedOptions[0] + + updateAssistantSettings({ + reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption, + qwenThinkMode: fallbackOption === 'off' + }) + } + } else { + updateAssistantSettings({ + reasoning_effort: undefined, + qwenThinkMode: undefined + }) + } + }, [assistant.settings?.reasoning_effort, model, updateAssistantSettings]) + return { assistant: assistantWithModel, model, @@ -110,9 +146,7 @@ export function useAssistant(id: string) { [assistant, dispatch] ), updateAssistant: (assistant: Assistant) => dispatch(updateAssistant(assistant)), - updateAssistantSettings: (settings: Partial) => { - assistant?.id && dispatch(updateAssistantSettings({ assistantId: assistant.id, settings })) - } + updateAssistantSettings } } diff --git a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx index 1ceb2cc52e..d3765c4067 100644 --- a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx @@ -12,7 +12,7 @@ import { useAssistant } from '@renderer/hooks/useAssistant' import { getReasoningEffortOptionsLabel } from '@renderer/i18n/label' import { Assistant, Model, ThinkingOption } from '@renderer/types' import { Tooltip } from 'antd' -import { FC, ReactElement, useCallback, useEffect, useImperativeHandle, useMemo } from 'react' +import { FC, ReactElement, useCallback, useImperativeHandle, useMemo } from 'react' import { useTranslation } from 'react-i18next' export interface ThinkingButtonRef { @@ -26,16 +26,6 @@ interface Props { ToolbarButton: any } -// 选项转换映射表:当选项不支持时使用的替代选项 -const OPTION_FALLBACK: Record = { - off: 'low', // off -> low (for Gemini Pro models) - minimal: 'low', // minimal -> low (for gpt-5 and after) - low: 'high', - medium: 'high', // medium -> high (for Grok models) - high: 'high', - auto: 'high' // auto -> high (for non-Gemini models) -} - const ThinkingButton: FC = ({ ref, model, assistant, ToolbarButton }): ReactElement => { const { t } = useTranslation() const quickPanel = useQuickPanel() @@ -59,19 +49,6 @@ const ThinkingButton: FC = ({ ref, model, assistant, ToolbarButton }): Re return MODEL_SUPPORTED_OPTIONS[modelType] }, [model, modelType]) - // 检查当前设置是否与当前模型兼容 - useEffect(() => { - if (currentReasoningEffort && !supportedOptions.includes(currentReasoningEffort)) { - // 使用表中定义的替代选项 - const fallbackOption = OPTION_FALLBACK[currentReasoningEffort as ThinkingOption] - - updateAssistantSettings({ - reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption, - qwenThinkMode: fallbackOption === 'off' - }) - } - }, [currentReasoningEffort, supportedOptions, updateAssistantSettings, model.id]) - const createThinkingIcon = useCallback((option?: ThinkingOption, isActive: boolean = false) => { const iconColor = isActive ? 'var(--color-primary)' : 'var(--color-icon)' @@ -154,13 +131,9 @@ const ThinkingButton: FC = ({ ref, model, assistant, ToolbarButton }): Re // 获取当前应显示的图标 const getThinkingIcon = useCallback(() => { - // 如果当前选项不支持,显示回退选项的图标 - if (currentReasoningEffort && !supportedOptions.includes(currentReasoningEffort)) { - const fallbackOption = OPTION_FALLBACK[currentReasoningEffort as ThinkingOption] - return createThinkingIcon(fallbackOption, true) - } + // 不再判断选项是否支持,依赖 useAssistant 更新选项为支持选项的行为 return createThinkingIcon(currentReasoningEffort, currentReasoningEffort !== 'off') - }, [createThinkingIcon, currentReasoningEffort, supportedOptions]) + }, [createThinkingIcon, currentReasoningEffort]) useImperativeHandle(ref, () => ({ openQuickPanel diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 4401e14d6e..f14dbd1d25 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -52,38 +52,29 @@ export type AssistantSettingCustomParameters = { type: 'string' | 'number' | 'boolean' | 'json' } -export type ReasoningEffortOption = NonNullable | 'auto' -export type ThinkingOption = ReasoningEffortOption | 'off' -export type ThinkingModelType = - | 'default' - | 'o' - | 'gpt5' - | 'grok' - | 'gemini' - | 'gemini_pro' - | 'qwen' - | 'qwen_thinking' - | 'doubao' - | 'hunyuan' - | 'zhipu' - | 'perplexity' -export type ThinkingOptionConfig = Record -export type ReasoningEffortConfig = Record -export type EffortRatio = Record - -const ThinkModelTypes: ThinkingModelType[] = [ +const ThinkModelTypes = [ 'default', + 'o', + 'gpt5', 'grok', 'gemini', 'gemini_pro', 'qwen', 'qwen_thinking', 'doubao', + 'doubao_no_auto', 'hunyuan', 'zhipu', 'perplexity' ] as const +export type ReasoningEffortOption = NonNullable | 'auto' +export type ThinkingOption = ReasoningEffortOption | 'off' +export type ThinkingModelType = (typeof ThinkModelTypes)[number] +export type ThinkingOptionConfig = Record +export type ReasoningEffortConfig = Record +export type EffortRatio = Record + export function isThinkModelType(type: string): type is ThinkingModelType { return ThinkModelTypes.some((t) => t === type) }