mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 22:10:21 +08:00
fix: fallback when invalid reasoning effort (#8857)
* refactor(types): 扩展思考模型类型并优化相关类型定义 添加 ThinkingModelType 和 ThinkingOption 类型以支持更多模型 引入 ThinkingOptionConfig 和 ReasoningEffortConfig 类型配置 重构 ReasoningEffortOptions 为 ReasoningEffortOption 并更新相关引用 * feat(模型配置): 添加模型推理配置映射表并重构ThinkingButton组件 将模型支持的推理选项配置集中管理,新增MODEL_SUPPORTED_REASONING_EFFORT和MODEL_SUPPORTED_OPTIONS映射表 提取getThinkModelType方法用于统一判断模型类型,简化ThinkingButton组件逻辑 * fix(OpenAIApiClient): 添加对reasoning_effort参数的有效性检查 当模型不支持指定的reasoning_effort值时,回退到第一个支持的值 * fix: 修正判断模型类型的条件函数 * refactor(types): 使用 Record 类型替代映射类型语法 简化类型定义,提升代码可读性和一致性
This commit is contained in:
parent
201fcf9f45
commit
b38b2f16fc
@ -4,6 +4,7 @@ import {
|
|||||||
findTokenLimit,
|
findTokenLimit,
|
||||||
GEMINI_FLASH_MODEL_REGEX,
|
GEMINI_FLASH_MODEL_REGEX,
|
||||||
getOpenAIWebSearchParams,
|
getOpenAIWebSearchParams,
|
||||||
|
getThinkModelType,
|
||||||
isDoubaoThinkingAutoModel,
|
isDoubaoThinkingAutoModel,
|
||||||
isGrokReasoningModel,
|
isGrokReasoningModel,
|
||||||
isNotSupportSystemMessageModel,
|
isNotSupportSystemMessageModel,
|
||||||
@ -20,7 +21,8 @@ import {
|
|||||||
isSupportedThinkingTokenModel,
|
isSupportedThinkingTokenModel,
|
||||||
isSupportedThinkingTokenQwenModel,
|
isSupportedThinkingTokenQwenModel,
|
||||||
isSupportedThinkingTokenZhipuModel,
|
isSupportedThinkingTokenZhipuModel,
|
||||||
isVisionModel
|
isVisionModel,
|
||||||
|
MODEL_SUPPORTED_REASONING_EFFORT
|
||||||
} from '@renderer/config/models'
|
} from '@renderer/config/models'
|
||||||
import {
|
import {
|
||||||
isSupportArrayContentProvider,
|
isSupportArrayContentProvider,
|
||||||
@ -220,8 +222,18 @@ export class OpenAIAPIClient extends OpenAIBaseClient<
|
|||||||
|
|
||||||
// Grok models/Perplexity models/OpenAI models
|
// Grok models/Perplexity models/OpenAI models
|
||||||
if (isSupportedReasoningEffortModel(model)) {
|
if (isSupportedReasoningEffortModel(model)) {
|
||||||
return {
|
// 检查模型是否支持所选选项
|
||||||
reasoning_effort: reasoningEffort
|
const modelType = getThinkModelType(model)
|
||||||
|
const supportedOptions = MODEL_SUPPORTED_REASONING_EFFORT[modelType]
|
||||||
|
if (supportedOptions.includes(reasoningEffort)) {
|
||||||
|
return {
|
||||||
|
reasoning_effort: reasoningEffort
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果不支持,fallback到第一个支持的值
|
||||||
|
return {
|
||||||
|
reasoning_effort: supportedOptions[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -145,7 +145,13 @@ import YiModelLogoDark from '@renderer/assets/images/models/yi_dark.png'
|
|||||||
import YoudaoLogo from '@renderer/assets/images/providers/netease-youdao.svg'
|
import YoudaoLogo from '@renderer/assets/images/providers/netease-youdao.svg'
|
||||||
import NomicLogo from '@renderer/assets/images/providers/nomic.png'
|
import NomicLogo from '@renderer/assets/images/providers/nomic.png'
|
||||||
import { getProviderByModel } from '@renderer/services/AssistantService'
|
import { getProviderByModel } from '@renderer/services/AssistantService'
|
||||||
import { Model, SystemProviderId } from '@renderer/types'
|
import {
|
||||||
|
Model,
|
||||||
|
ReasoningEffortConfig,
|
||||||
|
SystemProviderId,
|
||||||
|
ThinkingModelType,
|
||||||
|
ThinkingOptionConfig
|
||||||
|
} from '@renderer/types'
|
||||||
import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils'
|
import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils'
|
||||||
import OpenAI from 'openai'
|
import OpenAI from 'openai'
|
||||||
|
|
||||||
@ -274,6 +280,56 @@ export const CLAUDE_SUPPORTED_WEBSEARCH_REGEX = new RegExp(
|
|||||||
'i'
|
'i'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 模型类型到支持的reasoning_effort的映射表
|
||||||
|
export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = {
|
||||||
|
default: ['low', 'medium', 'high'] as const,
|
||||||
|
grok: ['low', 'high'] as const,
|
||||||
|
gemini: ['low', 'medium', 'high', 'auto'] as const,
|
||||||
|
gemini_pro: ['low', 'medium', 'high', 'auto'] as const,
|
||||||
|
qwen: ['low', 'medium', 'high'] as const,
|
||||||
|
qwen_3235ba22b_thinking: ['low', 'medium', 'high'] as const,
|
||||||
|
doubao: ['auto', 'high'] as const,
|
||||||
|
hunyuan: ['auto'] as const,
|
||||||
|
zhipu: ['auto'] as const,
|
||||||
|
perplexity: ['low', 'medium', 'high'] as const
|
||||||
|
} as const
|
||||||
|
|
||||||
|
// 模型类型到支持选项的映射表
|
||||||
|
export const MODEL_SUPPORTED_OPTIONS: ThinkingOptionConfig = {
|
||||||
|
default: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.default] as const,
|
||||||
|
grok: [...MODEL_SUPPORTED_REASONING_EFFORT.grok] as const,
|
||||||
|
gemini: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.gemini] as const,
|
||||||
|
gemini_pro: [...MODEL_SUPPORTED_REASONING_EFFORT.gemini_pro] as const,
|
||||||
|
qwen: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.qwen] as const,
|
||||||
|
qwen_3235ba22b_thinking: [...MODEL_SUPPORTED_REASONING_EFFORT.qwen_3235ba22b_thinking] as const,
|
||||||
|
doubao: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao] 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] as const
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const getThinkModelType = (model: Model): ThinkingModelType => {
|
||||||
|
if (isSupportedThinkingTokenGeminiModel(model)) {
|
||||||
|
if (GEMINI_FLASH_MODEL_REGEX.test(model.id)) {
|
||||||
|
return 'gemini'
|
||||||
|
} else {
|
||||||
|
return 'gemini_pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isSupportedReasoningEffortGrokModel(model)) return 'grok'
|
||||||
|
if (isSupportedThinkingTokenQwenModel(model)) {
|
||||||
|
if (isQwen3235BA22BThinkingModel(model)) {
|
||||||
|
return 'qwen_3235ba22b_thinking'
|
||||||
|
}
|
||||||
|
return 'qwen'
|
||||||
|
}
|
||||||
|
if (isSupportedThinkingTokenDoubaoModel(model)) return 'doubao'
|
||||||
|
if (isSupportedThinkingTokenHunyuanModel(model)) return 'hunyuan'
|
||||||
|
if (isSupportedReasoningEffortPerplexityModel(model)) return 'perplexity'
|
||||||
|
if (isSupportedThinkingTokenZhipuModel(model)) return 'zhipu'
|
||||||
|
return 'default'
|
||||||
|
}
|
||||||
|
|
||||||
export function isFunctionCallingModel(model?: Model): boolean {
|
export function isFunctionCallingModel(model?: Model): boolean {
|
||||||
if (!model || isEmbeddingModel(model) || isRerankModel(model)) {
|
if (!model || isEmbeddingModel(model) || isRerankModel(model)) {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -6,27 +6,14 @@ import {
|
|||||||
MdiLightbulbOn90
|
MdiLightbulbOn90
|
||||||
} from '@renderer/components/Icons/SVGIcon'
|
} from '@renderer/components/Icons/SVGIcon'
|
||||||
import { useQuickPanel } from '@renderer/components/QuickPanel'
|
import { useQuickPanel } from '@renderer/components/QuickPanel'
|
||||||
import {
|
import { getThinkModelType, isDoubaoThinkingAutoModel, MODEL_SUPPORTED_OPTIONS } from '@renderer/config/models'
|
||||||
GEMINI_FLASH_MODEL_REGEX,
|
|
||||||
isDoubaoThinkingAutoModel,
|
|
||||||
isQwen3235BA22BThinkingModel,
|
|
||||||
isSupportedReasoningEffortGrokModel,
|
|
||||||
isSupportedReasoningEffortPerplexityModel,
|
|
||||||
isSupportedThinkingTokenDoubaoModel,
|
|
||||||
isSupportedThinkingTokenGeminiModel,
|
|
||||||
isSupportedThinkingTokenHunyuanModel,
|
|
||||||
isSupportedThinkingTokenQwenModel,
|
|
||||||
isSupportedThinkingTokenZhipuModel
|
|
||||||
} from '@renderer/config/models'
|
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { getReasoningEffortOptionsLabel } from '@renderer/i18n/label'
|
import { getReasoningEffortOptionsLabel } from '@renderer/i18n/label'
|
||||||
import { Assistant, Model, ReasoningEffortOptions } 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, useEffect, useImperativeHandle, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
type ThinkingOption = ReasoningEffortOptions | 'off'
|
|
||||||
|
|
||||||
export interface ThinkingButtonRef {
|
export interface ThinkingButtonRef {
|
||||||
openQuickPanel: () => void
|
openQuickPanel: () => void
|
||||||
}
|
}
|
||||||
@ -38,20 +25,6 @@ interface Props {
|
|||||||
ToolbarButton: any
|
ToolbarButton: any
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模型类型到支持选项的映射表
|
|
||||||
const MODEL_SUPPORTED_OPTIONS: Record<string, ThinkingOption[]> = {
|
|
||||||
default: ['off', 'low', 'medium', 'high'],
|
|
||||||
grok: ['low', 'high'],
|
|
||||||
gemini: ['off', 'low', 'medium', 'high', 'auto'],
|
|
||||||
gemini_pro: ['low', 'medium', 'high', 'auto'],
|
|
||||||
qwen: ['off', 'low', 'medium', 'high'],
|
|
||||||
qwen_3235ba22b_thinking: ['low', 'medium', 'high'],
|
|
||||||
doubao: ['off', 'auto', 'high'],
|
|
||||||
hunyuan: ['off', 'auto'],
|
|
||||||
zhipu: ['off', 'auto'],
|
|
||||||
perplexity: ['low', 'medium', 'high']
|
|
||||||
}
|
|
||||||
|
|
||||||
// 选项转换映射表:当选项不支持时使用的替代选项
|
// 选项转换映射表:当选项不支持时使用的替代选项
|
||||||
const OPTION_FALLBACK: Record<ThinkingOption, ThinkingOption> = {
|
const OPTION_FALLBACK: Record<ThinkingOption, ThinkingOption> = {
|
||||||
off: 'low', // off -> low (for Gemini Pro models)
|
off: 'low', // off -> low (for Gemini Pro models)
|
||||||
@ -66,60 +39,20 @@ const ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): Re
|
|||||||
const quickPanel = useQuickPanel()
|
const quickPanel = useQuickPanel()
|
||||||
const { updateAssistantSettings } = useAssistant(assistant.id)
|
const { updateAssistantSettings } = useAssistant(assistant.id)
|
||||||
|
|
||||||
const isGrokModel = isSupportedReasoningEffortGrokModel(model)
|
|
||||||
const isGeminiModel = isSupportedThinkingTokenGeminiModel(model)
|
|
||||||
const isGeminiFlashModel = GEMINI_FLASH_MODEL_REGEX.test(model.id)
|
|
||||||
const isQwenModel = isSupportedThinkingTokenQwenModel(model)
|
|
||||||
const isQwen3235BA22BThinking = isQwen3235BA22BThinkingModel(model)
|
|
||||||
const isDoubaoModel = isSupportedThinkingTokenDoubaoModel(model)
|
|
||||||
const isHunyuanModel = isSupportedThinkingTokenHunyuanModel(model)
|
|
||||||
const isPerplexityModel = isSupportedReasoningEffortPerplexityModel(model)
|
|
||||||
const isZhipuModel = isSupportedThinkingTokenZhipuModel(model)
|
|
||||||
|
|
||||||
const currentReasoningEffort = useMemo(() => {
|
const currentReasoningEffort = useMemo(() => {
|
||||||
return assistant.settings?.reasoning_effort || 'off'
|
return assistant.settings?.reasoning_effort || 'off'
|
||||||
}, [assistant.settings?.reasoning_effort])
|
}, [assistant.settings?.reasoning_effort])
|
||||||
|
|
||||||
// 确定当前模型支持的选项类型
|
// 确定当前模型支持的选项类型
|
||||||
const modelType = useMemo(() => {
|
const modelType = useMemo(() => getThinkModelType(model), [model])
|
||||||
if (isGeminiModel) {
|
|
||||||
if (isGeminiFlashModel) {
|
|
||||||
return 'gemini'
|
|
||||||
} else {
|
|
||||||
return 'gemini_pro'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isGrokModel) return 'grok'
|
|
||||||
if (isQwenModel) {
|
|
||||||
if (isQwen3235BA22BThinking) {
|
|
||||||
return 'qwen_3235ba22b_thinking'
|
|
||||||
}
|
|
||||||
return 'qwen'
|
|
||||||
}
|
|
||||||
if (isDoubaoModel) return 'doubao'
|
|
||||||
if (isHunyuanModel) return 'hunyuan'
|
|
||||||
if (isPerplexityModel) return 'perplexity'
|
|
||||||
if (isZhipuModel) return 'zhipu'
|
|
||||||
return 'default'
|
|
||||||
}, [
|
|
||||||
isGeminiModel,
|
|
||||||
isGrokModel,
|
|
||||||
isQwenModel,
|
|
||||||
isDoubaoModel,
|
|
||||||
isGeminiFlashModel,
|
|
||||||
isHunyuanModel,
|
|
||||||
isPerplexityModel,
|
|
||||||
isQwen3235BA22BThinking,
|
|
||||||
isZhipuModel
|
|
||||||
])
|
|
||||||
|
|
||||||
// 获取当前模型支持的选项
|
// 获取当前模型支持的选项
|
||||||
const supportedOptions = useMemo(() => {
|
const supportedOptions: ThinkingOption[] = useMemo(() => {
|
||||||
if (modelType === 'doubao') {
|
if (modelType === 'doubao') {
|
||||||
if (isDoubaoThinkingAutoModel(model)) {
|
if (isDoubaoThinkingAutoModel(model)) {
|
||||||
return ['off', 'auto', 'high'] as ThinkingOption[]
|
return ['off', 'auto', 'high']
|
||||||
}
|
}
|
||||||
return ['off', 'high'] as ThinkingOption[]
|
return ['off', 'high']
|
||||||
}
|
}
|
||||||
return MODEL_SUPPORTED_OPTIONS[modelType]
|
return MODEL_SUPPORTED_OPTIONS[modelType]
|
||||||
}, [model, modelType])
|
}, [model, modelType])
|
||||||
|
|||||||
@ -52,8 +52,39 @@ export type AssistantSettingCustomParameters = {
|
|||||||
type: 'string' | 'number' | 'boolean' | 'json'
|
type: 'string' | 'number' | 'boolean' | 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReasoningEffortOptions = 'low' | 'medium' | 'high' | 'auto'
|
export type ReasoningEffortOption = 'low' | 'medium' | 'high' | 'auto'
|
||||||
export type EffortRatio = Record<ReasoningEffortOptions, number>
|
export type ThinkingOption = ReasoningEffortOption | 'off'
|
||||||
|
export type ThinkingModelType =
|
||||||
|
| 'default'
|
||||||
|
| 'grok'
|
||||||
|
| 'gemini'
|
||||||
|
| 'gemini_pro'
|
||||||
|
| 'qwen'
|
||||||
|
| 'qwen_3235ba22b_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',
|
||||||
|
'grok',
|
||||||
|
'gemini',
|
||||||
|
'gemini_pro',
|
||||||
|
'qwen',
|
||||||
|
'qwen_3235ba22b_thinking',
|
||||||
|
'doubao',
|
||||||
|
'hunyuan',
|
||||||
|
'zhipu',
|
||||||
|
'perplexity'
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export function isThinkModelType(type: string): type is ThinkingModelType {
|
||||||
|
return ThinkModelTypes.includes(type as ThinkingModelType)
|
||||||
|
}
|
||||||
|
|
||||||
export const EFFORT_RATIO: EffortRatio = {
|
export const EFFORT_RATIO: EffortRatio = {
|
||||||
low: 0.05,
|
low: 0.05,
|
||||||
@ -73,7 +104,7 @@ export type AssistantSettings = {
|
|||||||
streamOutput: boolean
|
streamOutput: boolean
|
||||||
defaultModel?: Model
|
defaultModel?: Model
|
||||||
customParameters?: AssistantSettingCustomParameters[]
|
customParameters?: AssistantSettingCustomParameters[]
|
||||||
reasoning_effort?: ReasoningEffortOptions
|
reasoning_effort?: ReasoningEffortOption
|
||||||
qwenThinkMode?: boolean
|
qwenThinkMode?: boolean
|
||||||
toolUseMode: 'function' | 'prompt'
|
toolUseMode: 'function' | 'prompt'
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user