fix: thinking button doesn't update assistant reasoning effort (#9247)

* feat(思考模式): 添加doubao_no_auto模型支持并重构选项回退逻辑

将思考模式选项回退逻辑从组件移至配置文件中统一管理
添加doubao_no_auto模型类型及其支持的选项配置

* refactor(assistant): 将模型兼容性检查逻辑移至useAssistant钩子

将ThinkingButton组件中的模型兼容性检查逻辑重构到useAssistant钩子中,使逻辑更集中
移除不再需要的useEffect导入

* refactor(useAssistant): 移除不必要的类型断言

* refactor: 移除THINKING_OPTION_FALLBACK并使用支持选项的第一个值作为回退

不再使用预定义的选项回退映射表,改为直接使用模型支持选项列表中的第一个值作为回退选项
This commit is contained in:
Phantom 2025-08-17 19:43:56 +08:00 committed by GitHub
parent 33ec5c5c6b
commit aed9566409
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 62 additions and 57 deletions

View File

@ -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

View File

@ -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<AssistantSettings>) => {
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<AssistantSettings>) => {
assistant?.id && dispatch(updateAssistantSettings({ assistantId: assistant.id, settings }))
}
updateAssistantSettings
}
}

View File

@ -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<ThinkingOption, ThinkingOption> = {
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<Props> = ({ ref, model, assistant, ToolbarButton }): ReactElement => {
const { t } = useTranslation()
const quickPanel = useQuickPanel()
@ -59,19 +49,6 @@ const ThinkingButton: FC<Props> = ({ 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<Props> = ({ 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

View File

@ -52,38 +52,29 @@ export type AssistantSettingCustomParameters = {
type: 'string' | 'number' | 'boolean' | 'json'
}
export type ReasoningEffortOption = NonNullable<OpenAI.ReasoningEffort> | '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<ThinkingModelType, ThinkingOption[]>
export type ReasoningEffortConfig = Record<ThinkingModelType, ReasoningEffortOption[]>
export type EffortRatio = Record<ReasoningEffortOption, number>
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<OpenAI.ReasoningEffort> | 'auto'
export type ThinkingOption = ReasoningEffortOption | 'off'
export type ThinkingModelType = (typeof ThinkModelTypes)[number]
export type ThinkingOptionConfig = Record<ThinkingModelType, ThinkingOption[]>
export type ReasoningEffortConfig = Record<ThinkingModelType, ReasoningEffortOption[]>
export type EffortRatio = Record<ReasoningEffortOption, number>
export function isThinkModelType(type: string): type is ThinkingModelType {
return ThinkModelTypes.some((t) => t === type)
}