feat: reasoning effort cache (#9357)

* feat(useAssistant): 修改模型切换时推理努力值回退逻辑

当模型切换时,确保推理努力值回退到模型支持的第一个有效值,并默认开启思考模式。使用useRef优化设置引用,避免不必要的依赖。

* feat(assistant): 添加 reasoning_effort_cache 以保留思考模型设置

当从非思考模型切换回思考模型时,恢复上次使用的 reasoning_effort 值

* fix(assistant): 修复思考模式切换时缓存未正确更新的问题

* fix(useAssistant): 修复模型选项回退逻辑以支持推理模式

当启用推理模式时,回退到支持推理的选项,否则回退到默认选项

* docs(types): 完善 AssistantSettings 类型注释中的 TODO 说明
This commit is contained in:
Phantom 2025-08-21 14:18:19 +08:00 committed by GitHub
parent 062b3b0a33
commit ea6a1752e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 12 deletions

View File

@ -3,7 +3,8 @@ import {
getThinkModelType, getThinkModelType,
isSupportedReasoningEffortModel, isSupportedReasoningEffortModel,
isSupportedThinkingTokenModel, isSupportedThinkingTokenModel,
MODEL_SUPPORTED_OPTIONS MODEL_SUPPORTED_OPTIONS,
MODEL_SUPPORTED_REASONING_EFFORT
} from '@renderer/config/models' } from '@renderer/config/models'
import { db } from '@renderer/databases' import { db } from '@renderer/databases'
import { getDefaultTopic } from '@renderer/services/AssistantService' import { getDefaultTopic } from '@renderer/services/AssistantService'
@ -24,9 +25,9 @@ import {
updateTopics updateTopics
} from '@renderer/store/assistants' } from '@renderer/store/assistants'
import { setDefaultModel, setQuickModel, setTranslateModel } from '@renderer/store/llm' 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 { uuid } from '@renderer/utils'
import { useCallback, useEffect, useMemo } from 'react' import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { TopicManager } from './useTopic' import { TopicManager } from './useTopic'
@ -84,6 +85,12 @@ export function useAssistant(id: string) {
const assistantWithModel = useMemo(() => ({ ...assistant, model }), [assistant, model]) const assistantWithModel = useMemo(() => ({ ...assistant, model }), [assistant, model])
const settingsRef = useRef(assistant?.settings)
useEffect(() => {
settingsRef.current = assistant.settings
}, [assistant?.settings])
const updateAssistantSettings = useCallback( const updateAssistantSettings = useCallback(
(settings: Partial<AssistantSettings>) => { (settings: Partial<AssistantSettings>) => {
assistant?.id && dispatch(_updateAssistantSettings({ assistantId: assistant.id, settings })) assistant?.id && dispatch(_updateAssistantSettings({ assistantId: assistant.id, settings }))
@ -93,28 +100,46 @@ export function useAssistant(id: string) {
// 当model变化时同步reasoning effort为模型支持的合法值 // 当model变化时同步reasoning effort为模型支持的合法值
useEffect(() => { useEffect(() => {
if (assistant?.settings) { const settings = settingsRef.current
if (settings) {
const currentReasoningEffort = settings.reasoning_effort
if (isSupportedThinkingTokenModel(model) || isSupportedReasoningEffortModel(model)) { if (isSupportedThinkingTokenModel(model) || isSupportedReasoningEffortModel(model)) {
const currentReasoningEffort = assistant?.settings?.reasoning_effort const modelType = getThinkModelType(model)
const supportedOptions = MODEL_SUPPORTED_OPTIONS[getThinkModelType(model)] const supportedOptions = MODEL_SUPPORTED_OPTIONS[modelType]
if (currentReasoningEffort && !supportedOptions.includes(currentReasoningEffort)) { if (supportedOptions.every((option) => option !== currentReasoningEffort)) {
// 选项不支持时,回退到第一个支持的值 const cache = settings.reasoning_effort_cache
// 注意这里假设可用的options不会为空 let fallbackOption: ThinkingOption
const fallbackOption = supportedOptions[0]
// 选项不支持时,首先尝试恢复到上次使用的值
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({ updateAssistantSettings({
reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption, reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption,
qwenThinkMode: fallbackOption === 'off' reasoning_effort_cache: fallbackOption === 'off' ? undefined : fallbackOption,
qwenThinkMode: fallbackOption === 'off' ? undefined : true
}) })
} else {
// 对于支持的选项, 不再更新 cache.
} }
} else { } else {
// 切换到非思考模型时保留cache
updateAssistantSettings({ updateAssistantSettings({
reasoning_effort: undefined, reasoning_effort: undefined,
reasoning_effort_cache: currentReasoningEffort,
qwenThinkMode: undefined qwenThinkMode: undefined
}) })
} }
} }
}, [assistant?.settings, model, updateAssistantSettings]) }, [model, updateAssistantSettings])
return { return {
assistant: assistantWithModel, assistant: assistantWithModel,

View File

@ -77,12 +77,14 @@ const ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): Re
if (!isEnabled) { if (!isEnabled) {
updateAssistantSettings({ updateAssistantSettings({
reasoning_effort: undefined, reasoning_effort: undefined,
reasoning_effort_cache: undefined,
qwenThinkMode: false qwenThinkMode: false
}) })
return return
} }
updateAssistantSettings({ updateAssistantSettings({
reasoning_effort: option, reasoning_effort: option,
reasoning_effort_cache: option,
qwenThinkMode: true qwenThinkMode: true
}) })
return return

View File

@ -99,6 +99,13 @@ export type AssistantSettings = {
defaultModel?: Model defaultModel?: Model
customParameters?: AssistantSettingCustomParameters[] customParameters?: AssistantSettingCustomParameters[]
reasoning_effort?: ReasoningEffortOption reasoning_effort?: ReasoningEffortOption
/** 使 reasoning effort, .
*
* TODO: 目前 reasoning_effort === undefined
* / cache
*
*/
reasoning_effort_cache?: ReasoningEffortOption
qwenThinkMode?: boolean qwenThinkMode?: boolean
toolUseMode: 'function' | 'prompt' toolUseMode: 'function' | 'prompt'
} }