From 39fa0802632e307428c7ea3221345aa18f714dfd Mon Sep 17 00:00:00 2001 From: Phantom Date: Wed, 22 Oct 2025 04:39:38 +0800 Subject: [PATCH] fix: check model capability with model name (#10860) * fix(ModelListItem): fallback to model name when logo not found by id Use model name as fallback when fetching model logo if lookup by id fails * refactor(model-logo): simplify model logo handling with unified function Replace direct calls to getModelLogo with model.id with new getModelLogo function that handles both id and name fallback Rename original getModelLogo to getModelLogoById for clarity Update all components to use the new unified function * refactor(model-utils): improve model type detection with fallback logic Add helper function to check both model ID and name as ID for type detection Refactor getThinkModelType and isSupportedThinkingTokenModel to use new fallback logic * refactor(agent-popups): make avatar optional in BaseOption interface update getModelLogo functions to return undefined instead of null for consistency * refactor(models): remove outdated comment in reasoning.ts --- src/renderer/src/components/ApiModelLabel.tsx | 7 +++-- .../src/components/Avatar/ModelAvatar.tsx | 2 +- .../SelectModelPopup/api-model-popup.tsx | 4 +-- .../Popups/SelectModelPopup/popup.tsx | 2 +- .../components/Popups/agent/AgentModal.tsx | 4 +-- .../src/components/Popups/agent/shared.tsx | 2 +- src/renderer/src/config/models/logo.ts | 9 ++++-- src/renderer/src/config/models/reasoning.ts | 29 +++++++++++++++---- .../home/Inputbar/MentionModelsButton.tsx | 4 +-- .../pages/home/Messages/ChatFlowHistory.tsx | 8 +++-- .../pages/home/Messages/MessageAnchorLine.tsx | 4 +-- .../src/pages/home/Messages/MessageHeader.tsx | 4 +-- .../ModelList/ManageModelsList.tsx | 4 +-- .../ModelList/ModelListItem.tsx | 2 +- 14 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/renderer/src/components/ApiModelLabel.tsx b/src/renderer/src/components/ApiModelLabel.tsx index 68cc3dbbaf..c101689ff9 100644 --- a/src/renderer/src/components/ApiModelLabel.tsx +++ b/src/renderer/src/components/ApiModelLabel.tsx @@ -1,5 +1,5 @@ import { Avatar, cn } from '@heroui/react' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogoById } from '@renderer/config/models' import { ApiModel } from '@renderer/types' import React from 'react' @@ -19,7 +19,10 @@ export interface ModelLabelProps extends Omit export const ApiModelLabel: React.FC = ({ model, className, classNames, ...props }) => { return (
- + {model?.name} | {model?.provider_name} diff --git a/src/renderer/src/components/Avatar/ModelAvatar.tsx b/src/renderer/src/components/Avatar/ModelAvatar.tsx index 22fafcd98f..04e8615fbb 100644 --- a/src/renderer/src/components/Avatar/ModelAvatar.tsx +++ b/src/renderer/src/components/Avatar/ModelAvatar.tsx @@ -14,7 +14,7 @@ interface Props { const ModelAvatar: FC = ({ model, size, props, className }) => { return ( = ({ model, apiFilter, modelFilter, showTa ), icon: ( - + {first(model.name) || 'M'} ), diff --git a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx index 0e4ca65036..60ebc3fe77 100644 --- a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx +++ b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx @@ -123,7 +123,7 @@ const PopupContainer: React.FC = ({ model, filter: baseFilter, showTagFil ), icon: ( - + {first(model.name) || 'M'} ), diff --git a/src/renderer/src/components/Popups/agent/AgentModal.tsx b/src/renderer/src/components/Popups/agent/AgentModal.tsx index d190c33179..248bfd2823 100644 --- a/src/renderer/src/components/Popups/agent/AgentModal.tsx +++ b/src/renderer/src/components/Popups/agent/AgentModal.tsx @@ -16,7 +16,7 @@ import { import { loggerService } from '@logger' import type { Selection } from '@react-types/shared' import ClaudeIcon from '@renderer/assets/images/models/claude.png' -import { agentModelFilter, getModelLogo } from '@renderer/config/models' +import { agentModelFilter, getModelLogoById } from '@renderer/config/models' import { permissionModeCards } from '@renderer/constants/permissionModes' import { useAgents } from '@renderer/hooks/agents/useAgents' import { useApiModels } from '@renderer/hooks/agents/useModels' @@ -244,7 +244,7 @@ export const AgentModal: React.FC = ({ agent, isOpen: _isOpen, onClose: _ type: 'model', key: model.id, label: model.name, - avatar: getModelLogo(model.id), + avatar: getModelLogoById(model.id), providerId: model.provider, providerName: model.provider_name })) satisfies ModelOption[] diff --git a/src/renderer/src/components/Popups/agent/shared.tsx b/src/renderer/src/components/Popups/agent/shared.tsx index cc67a5a33b..5de33d0bc8 100644 --- a/src/renderer/src/components/Popups/agent/shared.tsx +++ b/src/renderer/src/components/Popups/agent/shared.tsx @@ -7,7 +7,7 @@ export interface BaseOption { key: string label: string // img src - avatar: string + avatar?: string } export interface ModelOption extends BaseOption { diff --git a/src/renderer/src/config/models/logo.ts b/src/renderer/src/config/models/logo.ts index 40df0af30e..ef8cc87125 100644 --- a/src/renderer/src/config/models/logo.ts +++ b/src/renderer/src/config/models/logo.ts @@ -155,8 +155,9 @@ import ZhipuModelLogoDark from '@renderer/assets/images/models/zhipu_dark.png' import YoudaoLogo from '@renderer/assets/images/providers/netease-youdao.svg' import NomicLogo from '@renderer/assets/images/providers/nomic.png' import ZhipuProviderLogo from '@renderer/assets/images/providers/zhipu.png' +import { Model } from '@renderer/types' -export function getModelLogo(modelId: string) { +export function getModelLogoById(modelId: string): string | undefined { const isLight = true if (!modelId) { @@ -289,7 +290,7 @@ export function getModelLogo(modelId: string) { longcat: LongCatAppLogo, bytedance: BytedanceModelLogo, '(V_1|V_1_TURBO|V_2|V_2A|V_2_TURBO|DESCRIBE|UPSCALE)': IdeogramModelLogo - } as const + } as const satisfies Record for (const key in logoMap) { const regex = new RegExp(key, 'i') @@ -300,3 +301,7 @@ export function getModelLogo(modelId: string) { return undefined } + +export function getModelLogo(model: Model | undefined | null): string | undefined { + return model ? (getModelLogoById(model.id) ?? getModelLogoById(model.name)) : undefined +} diff --git a/src/renderer/src/config/models/reasoning.ts b/src/renderer/src/config/models/reasoning.ts index a7e825ef4f..c3134b1f7d 100644 --- a/src/renderer/src/config/models/reasoning.ts +++ b/src/renderer/src/config/models/reasoning.ts @@ -59,7 +59,15 @@ export const MODEL_SUPPORTED_OPTIONS: ThinkingOptionConfig = { deepseek_hybrid: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.deepseek_hybrid] as const } as const -export const getThinkModelType = (model: Model): ThinkingModelType => { +const withModelIdAndNameAsId = (model: Model, fn: (model: Model) => T): { idResult: T; nameResult: T } => { + const modelWithNameAsId = { ...model, id: model.name } + return { + idResult: fn(model), + nameResult: fn(modelWithNameAsId) + } +} + +const _getThinkModelType = (model: Model): ThinkingModelType => { let thinkingModelType: ThinkingModelType = 'default' const modelId = getLowerBaseModelName(model.id) if (isGPT5SeriesModel(model)) { @@ -99,12 +107,16 @@ export const getThinkModelType = (model: Model): ThinkingModelType => { return thinkingModelType } -/** 用于判断是否支持控制思考,但不一定以reasoning_effort的方式 */ -export function isSupportedThinkingTokenModel(model?: Model): boolean { - if (!model) { - return false +export const getThinkModelType = (model: Model): ThinkingModelType => { + const { idResult, nameResult } = withModelIdAndNameAsId(model, _getThinkModelType) + if (idResult !== 'default') { + return idResult + } else { + return nameResult } +} +function _isSupportedThinkingTokenModel(model: Model): boolean { // Specifically for DeepSeek V3.1. White list for now if (isDeepSeekHybridInferenceModel(model)) { return ( @@ -132,6 +144,13 @@ export function isSupportedThinkingTokenModel(model?: Model): boolean { ) } +/** 用于判断是否支持控制思考,但不一定以reasoning_effort的方式 */ +export function isSupportedThinkingTokenModel(model?: Model): boolean { + if (!model) return false + const { idResult, nameResult } = withModelIdAndNameAsId(model, _isSupportedThinkingTokenModel) + return idResult || nameResult +} + export function isSupportedReasoningEffortModel(model?: Model): boolean { if (!model) { return false diff --git a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx index 27de57c564..1122fef0d8 100644 --- a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx @@ -134,7 +134,7 @@ const MentionModelsButton: FC = ({ ), description: , icon: ( - + {first(m.name)} ), @@ -170,7 +170,7 @@ const MentionModelsButton: FC = ({ ), description: , icon: ( - + {first(m.name)} ), diff --git a/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx b/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx index 9cd1db2163..4559387437 100644 --- a/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx +++ b/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx @@ -3,7 +3,7 @@ import '@xyflow/react/dist/style.css' import { RobotOutlined, UserOutlined } from '@ant-design/icons' import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogo, getModelLogoById } from '@renderer/config/models' import { useTheme } from '@renderer/context/ThemeProvider' import useAvatar from '@renderer/hooks/useAvatar' import { useSettings } from '@renderer/hooks/useSettings' @@ -49,6 +49,7 @@ const TooltipFooter = styled.div` ` // 自定义节点组件 +// FIXME: no any plz... const CustomNode: FC<{ data: any }> = ({ data }) => { const { t } = useTranslation() const { setTimeoutTimer } = useTimer() @@ -87,7 +88,7 @@ const CustomNode: FC<{ data: any }> = ({ data }) => { if (data.modelInfo) { avatar = } else if (data.modelId) { - const modelLogo = getModelLogo(data.modelId) + const modelLogo = getModelLogo(data.modelInfo) ?? getModelLogoById(data.modelId) avatar = ( type FlowEdge = Edge @@ -202,6 +204,7 @@ const defaultEdgeOptions = { const ChatFlowHistory: FC = ({ conversationId }) => { const { t } = useTranslation() + // FIXME: no any plz const [nodes, setNodes, onNodesChange] = useNodesState([]) const [edges, setEdges, onEdgesChange] = useEdgesState([]) const [loading, setLoading] = useState(true) @@ -408,6 +411,7 @@ const ChatFlowHistory: FC = ({ conversationId }) => { const assistantNodeId = `orphan-assistant-${aMsg.id}` // 获取模型数据 + // FIXME: No any plz const aMsgAny = aMsg as any // 获取模型名称 diff --git a/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx b/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx index f4ac909b91..d36448913f 100644 --- a/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx +++ b/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx @@ -1,6 +1,6 @@ import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar' import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogoById } from '@renderer/config/models' import { useTheme } from '@renderer/context/ThemeProvider' import useAvatar from '@renderer/hooks/useAvatar' import { useSettings } from '@renderer/hooks/useSettings' @@ -25,7 +25,7 @@ interface MessageLineProps { const getAvatarSource = (isLocalAi: boolean, modelId: string | undefined) => { if (isLocalAi) return AppLogo - return modelId ? getModelLogo(modelId) : undefined + return modelId ? getModelLogoById(modelId) : undefined } const MessageAnchorLine: FC = ({ messages }) => { diff --git a/src/renderer/src/pages/home/Messages/MessageHeader.tsx b/src/renderer/src/pages/home/Messages/MessageHeader.tsx index 95a6d219cb..b43e12ce18 100644 --- a/src/renderer/src/pages/home/Messages/MessageHeader.tsx +++ b/src/renderer/src/pages/home/Messages/MessageHeader.tsx @@ -2,7 +2,7 @@ import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar' import { HStack } from '@renderer/components/Layout' import UserPopup from '@renderer/components/Popups/UserPopup' import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogoById } from '@renderer/config/models' import { useTheme } from '@renderer/context/ThemeProvider' import { useAgent } from '@renderer/hooks/agents/useAgent' import useAvatar from '@renderer/hooks/useAvatar' @@ -32,7 +32,7 @@ interface Props { const getAvatarSource = (isLocalAi: boolean, modelId: string | undefined) => { if (isLocalAi) return AppLogo - return modelId ? getModelLogo(modelId) : undefined + return modelId ? getModelLogoById(modelId) : undefined } const MessageHeader: FC = memo(({ assistant, model, message, topic, isGroupContextMessage }) => { diff --git a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsList.tsx b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsList.tsx index 2210c8bd30..064055129b 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsList.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsList.tsx @@ -2,7 +2,7 @@ import ExpandableText from '@renderer/components/ExpandableText' import ModelIdWithTags from '@renderer/components/ModelIdWithTags' import CustomTag from '@renderer/components/Tags/CustomTag' import { DynamicVirtualList } from '@renderer/components/VirtualList' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogoById } from '@renderer/config/models' import { isNewApiProvider } from '@renderer/config/providers' import FileItem from '@renderer/pages/files/FileItem' import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup' @@ -200,7 +200,7 @@ const ModelListItem: React.FC = memo(({ model, provider, onA boxShadow: 'none' }} fileInfo={{ - icon: {model?.name?.[0]?.toUpperCase()}, + icon: {model?.name?.[0]?.toUpperCase()}, name: , extra: model.description && , ext: '.model', diff --git a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx index 186f11d220..24d64fdfbe 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx @@ -36,7 +36,7 @@ const ModelListItem: React.FC = ({ ref, model, modelStatus, return ( - + {model?.name?.[0]?.toUpperCase()}