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 68cc3dbba..c101689ff 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 22fafcd98..04e8615fb 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 0e4ca6503..60ebc3fe7 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 d190c3317..248bfd282 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 cc67a5a33..5de33d0bc 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 40df0af30..ef8cc8712 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 a7e825ef4..c3134b1f7 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 27de57c56..1122fef0d 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 9cd1db216..455938743 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 f4ac909b9..d36448913 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 95a6d219c..b43e12ce1 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 2210c8bd3..064055129 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 186f11d22..24d64fdfb 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()}