mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-05 04:19:02 +08:00
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:
parent
33ec5c5c6b
commit
aed9566409
@ -300,6 +300,7 @@ export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = {
|
|||||||
qwen: ['low', 'medium', 'high'] as const,
|
qwen: ['low', 'medium', 'high'] as const,
|
||||||
qwen_thinking: ['low', 'medium', 'high'] as const,
|
qwen_thinking: ['low', 'medium', 'high'] as const,
|
||||||
doubao: ['auto', 'high'] as const,
|
doubao: ['auto', 'high'] as const,
|
||||||
|
doubao_no_auto: ['high'] as const,
|
||||||
hunyuan: ['auto'] as const,
|
hunyuan: ['auto'] as const,
|
||||||
zhipu: ['auto'] as const,
|
zhipu: ['auto'] as const,
|
||||||
perplexity: ['low', 'medium', 'high'] 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: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.qwen] as const,
|
||||||
qwen_thinking: MODEL_SUPPORTED_REASONING_EFFORT.qwen_thinking,
|
qwen_thinking: MODEL_SUPPORTED_REASONING_EFFORT.qwen_thinking,
|
||||||
doubao: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao] as const,
|
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,
|
hunyuan: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.hunyuan] as const,
|
||||||
zhipu: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.zhipu] as const,
|
zhipu: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.zhipu] as const,
|
||||||
perplexity: MODEL_SUPPORTED_REASONING_EFFORT.perplexity
|
perplexity: MODEL_SUPPORTED_REASONING_EFFORT.perplexity
|
||||||
@ -339,8 +341,13 @@ export const getThinkModelType = (model: Model): ThinkingModelType => {
|
|||||||
thinkingModelType = 'qwen_thinking'
|
thinkingModelType = 'qwen_thinking'
|
||||||
}
|
}
|
||||||
thinkingModelType = 'qwen'
|
thinkingModelType = 'qwen'
|
||||||
} else if (isSupportedThinkingTokenDoubaoModel(model)) thinkingModelType = 'doubao'
|
} else if (isSupportedThinkingTokenDoubaoModel(model)) {
|
||||||
else if (isSupportedThinkingTokenHunyuanModel(model)) thinkingModelType = 'hunyuan'
|
if (isDoubaoThinkingAutoModel(model)) {
|
||||||
|
thinkingModelType = 'doubao'
|
||||||
|
} else {
|
||||||
|
thinkingModelType = 'doubao_no_auto'
|
||||||
|
}
|
||||||
|
} else if (isSupportedThinkingTokenHunyuanModel(model)) thinkingModelType = 'hunyuan'
|
||||||
else if (isSupportedReasoningEffortPerplexityModel(model)) thinkingModelType = 'perplexity'
|
else if (isSupportedReasoningEffortPerplexityModel(model)) thinkingModelType = 'perplexity'
|
||||||
else if (isSupportedThinkingTokenZhipuModel(model)) thinkingModelType = 'zhipu'
|
else if (isSupportedThinkingTokenZhipuModel(model)) thinkingModelType = 'zhipu'
|
||||||
return thinkingModelType
|
return thinkingModelType
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
|
import {
|
||||||
|
getThinkModelType,
|
||||||
|
isSupportedReasoningEffortModel,
|
||||||
|
isSupportedThinkingTokenModel,
|
||||||
|
MODEL_SUPPORTED_OPTIONS
|
||||||
|
} 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'
|
||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
@ -12,7 +18,7 @@ import {
|
|||||||
setModel,
|
setModel,
|
||||||
updateAssistant,
|
updateAssistant,
|
||||||
updateAssistants,
|
updateAssistants,
|
||||||
updateAssistantSettings,
|
updateAssistantSettings as _updateAssistantSettings,
|
||||||
updateDefaultAssistant,
|
updateDefaultAssistant,
|
||||||
updateTopic,
|
updateTopic,
|
||||||
updateTopics
|
updateTopics
|
||||||
@ -20,7 +26,7 @@ import {
|
|||||||
import { setDefaultModel, setTopicNamingModel, setTranslateModel } from '@renderer/store/llm'
|
import { setDefaultModel, setTopicNamingModel, setTranslateModel } from '@renderer/store/llm'
|
||||||
import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types'
|
import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
import { uuid } from '@renderer/utils'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useEffect, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { TopicManager } from './useTopic'
|
import { TopicManager } from './useTopic'
|
||||||
@ -78,6 +84,36 @@ export function useAssistant(id: string) {
|
|||||||
|
|
||||||
const assistantWithModel = useMemo(() => ({ ...assistant, model }), [assistant, model])
|
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 {
|
return {
|
||||||
assistant: assistantWithModel,
|
assistant: assistantWithModel,
|
||||||
model,
|
model,
|
||||||
@ -110,9 +146,7 @@ export function useAssistant(id: string) {
|
|||||||
[assistant, dispatch]
|
[assistant, dispatch]
|
||||||
),
|
),
|
||||||
updateAssistant: (assistant: Assistant) => dispatch(updateAssistant(assistant)),
|
updateAssistant: (assistant: Assistant) => dispatch(updateAssistant(assistant)),
|
||||||
updateAssistantSettings: (settings: Partial<AssistantSettings>) => {
|
updateAssistantSettings
|
||||||
assistant?.id && dispatch(updateAssistantSettings({ assistantId: assistant.id, settings }))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { useAssistant } from '@renderer/hooks/useAssistant'
|
|||||||
import { getReasoningEffortOptionsLabel } from '@renderer/i18n/label'
|
import { getReasoningEffortOptionsLabel } from '@renderer/i18n/label'
|
||||||
import { Assistant, Model, ThinkingOption } from '@renderer/types'
|
import { Assistant, Model, ThinkingOption } from '@renderer/types'
|
||||||
import { Tooltip } from 'antd'
|
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'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
export interface ThinkingButtonRef {
|
export interface ThinkingButtonRef {
|
||||||
@ -26,16 +26,6 @@ interface Props {
|
|||||||
ToolbarButton: any
|
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 ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): ReactElement => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const quickPanel = useQuickPanel()
|
const quickPanel = useQuickPanel()
|
||||||
@ -59,19 +49,6 @@ const ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): Re
|
|||||||
return MODEL_SUPPORTED_OPTIONS[modelType]
|
return MODEL_SUPPORTED_OPTIONS[modelType]
|
||||||
}, [model, 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 createThinkingIcon = useCallback((option?: ThinkingOption, isActive: boolean = false) => {
|
||||||
const iconColor = isActive ? 'var(--color-primary)' : 'var(--color-icon)'
|
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(() => {
|
const getThinkingIcon = useCallback(() => {
|
||||||
// 如果当前选项不支持,显示回退选项的图标
|
// 不再判断选项是否支持,依赖 useAssistant 更新选项为支持选项的行为
|
||||||
if (currentReasoningEffort && !supportedOptions.includes(currentReasoningEffort)) {
|
|
||||||
const fallbackOption = OPTION_FALLBACK[currentReasoningEffort as ThinkingOption]
|
|
||||||
return createThinkingIcon(fallbackOption, true)
|
|
||||||
}
|
|
||||||
return createThinkingIcon(currentReasoningEffort, currentReasoningEffort !== 'off')
|
return createThinkingIcon(currentReasoningEffort, currentReasoningEffort !== 'off')
|
||||||
}, [createThinkingIcon, currentReasoningEffort, supportedOptions])
|
}, [createThinkingIcon, currentReasoningEffort])
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
openQuickPanel
|
openQuickPanel
|
||||||
|
|||||||
@ -52,38 +52,29 @@ export type AssistantSettingCustomParameters = {
|
|||||||
type: 'string' | 'number' | 'boolean' | 'json'
|
type: 'string' | 'number' | 'boolean' | 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReasoningEffortOption = NonNullable<OpenAI.ReasoningEffort> | 'auto'
|
const ThinkModelTypes = [
|
||||||
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[] = [
|
|
||||||
'default',
|
'default',
|
||||||
|
'o',
|
||||||
|
'gpt5',
|
||||||
'grok',
|
'grok',
|
||||||
'gemini',
|
'gemini',
|
||||||
'gemini_pro',
|
'gemini_pro',
|
||||||
'qwen',
|
'qwen',
|
||||||
'qwen_thinking',
|
'qwen_thinking',
|
||||||
'doubao',
|
'doubao',
|
||||||
|
'doubao_no_auto',
|
||||||
'hunyuan',
|
'hunyuan',
|
||||||
'zhipu',
|
'zhipu',
|
||||||
'perplexity'
|
'perplexity'
|
||||||
] as const
|
] 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 {
|
export function isThinkModelType(type: string): type is ThinkingModelType {
|
||||||
return ThinkModelTypes.some((t) => t === type)
|
return ThinkModelTypes.some((t) => t === type)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user