From ea6a1752e7d3af244dfd9714b0ded1aca89c8d08 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Thu, 21 Aug 2025 14:18:19 +0800 Subject: [PATCH] feat: reasoning effort cache (#9357) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(useAssistant): 修改模型切换时推理努力值回退逻辑 当模型切换时,确保推理努力值回退到模型支持的第一个有效值,并默认开启思考模式。使用useRef优化设置引用,避免不必要的依赖。 * feat(assistant): 添加 reasoning_effort_cache 以保留思考模型设置 当从非思考模型切换回思考模型时,恢复上次使用的 reasoning_effort 值 * fix(assistant): 修复思考模式切换时缓存未正确更新的问题 * fix(useAssistant): 修复模型选项回退逻辑以支持推理模式 当启用推理模式时,回退到支持推理的选项,否则回退到默认选项 * docs(types): 完善 AssistantSettings 类型注释中的 TODO 说明 --- src/renderer/src/hooks/useAssistant.ts | 49 ++++++++++++++----- .../pages/home/Inputbar/ThinkingButton.tsx | 2 + src/renderer/src/types/index.ts | 7 +++ 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/renderer/src/hooks/useAssistant.ts b/src/renderer/src/hooks/useAssistant.ts index b029f96b06..02a327c345 100644 --- a/src/renderer/src/hooks/useAssistant.ts +++ b/src/renderer/src/hooks/useAssistant.ts @@ -3,7 +3,8 @@ import { getThinkModelType, isSupportedReasoningEffortModel, isSupportedThinkingTokenModel, - MODEL_SUPPORTED_OPTIONS + MODEL_SUPPORTED_OPTIONS, + MODEL_SUPPORTED_REASONING_EFFORT } from '@renderer/config/models' import { db } from '@renderer/databases' import { getDefaultTopic } from '@renderer/services/AssistantService' @@ -24,9 +25,9 @@ import { updateTopics } from '@renderer/store/assistants' import { setDefaultModel, setQuickModel, setTranslateModel } from '@renderer/store/llm' -import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types' +import { Assistant, AssistantSettings, Model, ThinkingOption, Topic } from '@renderer/types' import { uuid } from '@renderer/utils' -import { useCallback, useEffect, useMemo } from 'react' +import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { TopicManager } from './useTopic' @@ -84,6 +85,12 @@ export function useAssistant(id: string) { const assistantWithModel = useMemo(() => ({ ...assistant, model }), [assistant, model]) + const settingsRef = useRef(assistant?.settings) + + useEffect(() => { + settingsRef.current = assistant.settings + }, [assistant?.settings]) + const updateAssistantSettings = useCallback( (settings: Partial) => { assistant?.id && dispatch(_updateAssistantSettings({ assistantId: assistant.id, settings })) @@ -93,28 +100,46 @@ export function useAssistant(id: string) { // 当model变化时,同步reasoning effort为模型支持的合法值 useEffect(() => { - if (assistant?.settings) { + const settings = settingsRef.current + if (settings) { + const currentReasoningEffort = settings.reasoning_effort 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] + const modelType = getThinkModelType(model) + const supportedOptions = MODEL_SUPPORTED_OPTIONS[modelType] + if (supportedOptions.every((option) => option !== currentReasoningEffort)) { + const cache = settings.reasoning_effort_cache + let fallbackOption: ThinkingOption + + // 选项不支持时,首先尝试恢复到上次使用的值 + if (cache && supportedOptions.includes(cache)) { + fallbackOption = cache + } else { + // 灵活回退到支持的值 + // 注意:这里假设可用的options不会为空 + const enableThinking = currentReasoningEffort !== undefined + fallbackOption = enableThinking + ? MODEL_SUPPORTED_REASONING_EFFORT[modelType][0] + : MODEL_SUPPORTED_OPTIONS[modelType][0] + } updateAssistantSettings({ reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption, - qwenThinkMode: fallbackOption === 'off' + reasoning_effort_cache: fallbackOption === 'off' ? undefined : fallbackOption, + qwenThinkMode: fallbackOption === 'off' ? undefined : true }) + } else { + // 对于支持的选项, 不再更新 cache. } } else { + // 切换到非思考模型时保留cache updateAssistantSettings({ reasoning_effort: undefined, + reasoning_effort_cache: currentReasoningEffort, qwenThinkMode: undefined }) } } - }, [assistant?.settings, model, updateAssistantSettings]) + }, [model, updateAssistantSettings]) return { assistant: assistantWithModel, diff --git a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx index d3765c4067..4ad9269380 100644 --- a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx @@ -77,12 +77,14 @@ const ThinkingButton: FC = ({ ref, model, assistant, ToolbarButton }): Re if (!isEnabled) { updateAssistantSettings({ reasoning_effort: undefined, + reasoning_effort_cache: undefined, qwenThinkMode: false }) return } updateAssistantSettings({ reasoning_effort: option, + reasoning_effort_cache: option, qwenThinkMode: true }) return diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 147408d83c..e3663b966d 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -99,6 +99,13 @@ export type AssistantSettings = { defaultModel?: Model customParameters?: AssistantSettingCustomParameters[] reasoning_effort?: ReasoningEffortOption + /** 保留上一次使用思考模型时的 reasoning effort, 在从非思考模型切换到思考模型时恢复. + * + * TODO: 目前 reasoning_effort === undefined 有两个语义,有的场景是显式关闭思考,有的场景是不传参。 + * 未来应该重构思考控制,将启用/关闭思考和思考选项分离,这样就不用依赖 cache 了。 + * + */ + reasoning_effort_cache?: ReasoningEffortOption qwenThinkMode?: boolean toolUseMode: 'function' | 'prompt' }