diff --git a/src/renderer/src/components/Icons/ReasoningIcon.tsx b/src/renderer/src/components/Icons/ReasoningIcon.tsx index 92b86588..300b45ec 100644 --- a/src/renderer/src/components/Icons/ReasoningIcon.tsx +++ b/src/renderer/src/components/Icons/ReasoningIcon.tsx @@ -1,10 +1,16 @@ +import { Tooltip } from 'antd' import React, { FC } from 'react' +import { useTranslation } from 'react-i18next' import styled from 'styled-components' const ReasoningIcon: FC, HTMLElement>> = (props) => { + const { t } = useTranslation() + return ( - + + + ) } diff --git a/src/renderer/src/components/Icons/ToolsCallingIcon.tsx b/src/renderer/src/components/Icons/ToolsCallingIcon.tsx new file mode 100644 index 00000000..7009fb8a --- /dev/null +++ b/src/renderer/src/components/Icons/ToolsCallingIcon.tsx @@ -0,0 +1,31 @@ +import { ToolOutlined } from '@ant-design/icons' +import { Tooltip } from 'antd' +import React, { FC } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +const ToolsCallingIcon: FC, HTMLElement>> = (props) => { + const { t } = useTranslation() + + return ( + + + + + + ) +} + +const Container = styled.div` + display: flex; + justify-content: center; + align-items: center; +` + +const Icon = styled(ToolOutlined)` + color: #d97757; + font-size: 15px; + margin-right: 6px; +` + +export default ToolsCallingIcon diff --git a/src/renderer/src/components/Icons/VisionIcon.tsx b/src/renderer/src/components/Icons/VisionIcon.tsx index 515cd336..21904336 100644 --- a/src/renderer/src/components/Icons/VisionIcon.tsx +++ b/src/renderer/src/components/Icons/VisionIcon.tsx @@ -1,11 +1,17 @@ import { EyeOutlined } from '@ant-design/icons' +import { Tooltip } from 'antd' import React, { FC } from 'react' +import { useTranslation } from 'react-i18next' import styled from 'styled-components' const VisionIcon: FC, HTMLElement>> = (props) => { + const { t } = useTranslation() + return ( - + + + ) } diff --git a/src/renderer/src/components/Icons/WebSearchIcon.tsx b/src/renderer/src/components/Icons/WebSearchIcon.tsx index 41bc4334..507135e0 100644 --- a/src/renderer/src/components/Icons/WebSearchIcon.tsx +++ b/src/renderer/src/components/Icons/WebSearchIcon.tsx @@ -1,11 +1,17 @@ import { GlobalOutlined } from '@ant-design/icons' +import { Tooltip } from 'antd' import React, { FC } from 'react' +import { useTranslation } from 'react-i18next' import styled from 'styled-components' const WebSearchIcon: FC, HTMLElement>> = (props) => { + const { t } = useTranslation() + return ( - + + + ) } diff --git a/src/renderer/src/components/ModelTags.tsx b/src/renderer/src/components/ModelTags.tsx index 463e8697..6415d725 100644 --- a/src/renderer/src/components/ModelTags.tsx +++ b/src/renderer/src/components/ModelTags.tsx @@ -1,4 +1,10 @@ -import { isEmbeddingModel, isReasoningModel, isVisionModel, isWebSearchModel } from '@renderer/config/models' +import { + isEmbeddingModel, + isReasoningModel, + isToolCallingModel, + isVisionModel, + isWebSearchModel +} from '@renderer/config/models' import { Model } from '@renderer/types' import { isFreeModel } from '@renderer/utils' import { Tag } from 'antd' @@ -7,6 +13,7 @@ import { useTranslation } from 'react-i18next' import styled from 'styled-components' import ReasoningIcon from './Icons/ReasoningIcon' +import ToolsCallingIcon from './Icons/ToolsCallingIcon' import VisionIcon from './Icons/VisionIcon' import WebSearchIcon from './Icons/WebSearchIcon' @@ -14,15 +21,17 @@ interface ModelTagsProps { model: Model showFree?: boolean showReasoning?: boolean + showToolsCalling?: boolean } -const ModelTags: FC = ({ model, showFree = true, showReasoning = true }) => { +const ModelTags: FC = ({ model, showFree = true, showReasoning = true, showToolsCalling = true }) => { const { t } = useTranslation() return ( {isVisionModel(model) && } {isWebSearchModel(model) && } {showReasoning && isReasoningModel(model) && } + {showToolsCalling && isToolCallingModel(model) && } {isEmbeddingModel(model) && {t('models.embedding')}} {showFree && isFreeModel(model) && {t('models.free')}} diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index 31d84634..8243a82e 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -134,6 +134,7 @@ import OpenAI from 'openai' import { getWebSearchTools } from './tools' +// Vision models const visionAllowedModels = [ 'llava', 'moondream', @@ -159,19 +160,33 @@ const visionAllowedModels = [ ] const visionExcludedModels = ['gpt-4-\\d+-preview', 'gpt-4-turbo-preview', 'gpt-4-32k', 'gpt-4-\\d+'] - export const VISION_REGEX = new RegExp( `\\b(?!(?:${visionExcludedModels.join('|')})\\b)(${visionAllowedModels.join('|')})\\b`, 'i' ) +// Text to image models export const TEXT_TO_IMAGE_REGEX = /flux|diffusion|stabilityai|sd-|dall|cogview|janus/i + +// Reasoning models export const REASONING_REGEX = /^(o\d+(?:-[\w-]+)?|.*\b(?:reasoner|thinking)\b.*|.*-[rR]\d+.*|.*\bqwq(?:-[\w-]+)?\b.*)$/i +// Embedding models export const EMBEDDING_REGEX = /(?:^text-|embed|bge-|e5-|LLM2Vec|retrieval|uae-|gte-|jina-clip|jina-embeddings)/i export const NOT_SUPPORTED_REGEX = /(?:^tts|rerank|whisper|speech)/i +// Tool calling models +export const TOOL_CALLING_MODELS = ['gpt-4o', 'gpt-4o-mini', 'gpt-4', 'gpt-4.5', 'claude'] +export const TOOL_CALLING_REGEX = new RegExp(`\\b(?:${TOOL_CALLING_MODELS.join('|')})\\b`, 'i') +export function isToolCallingModel(model: Model): boolean { + if (['gemini', 'deepseek', 'anthropic'].includes(model.provider)) { + return true + } + + return TOOL_CALLING_REGEX.test(model.id) +} + export function getModelLogo(modelId: string) { const isLight = true @@ -990,10 +1005,6 @@ export const SYSTEM_MODELS: Record = { ], yi: [ { id: 'yi-lightning', name: 'Yi Lightning', provider: 'yi', group: 'yi-lightning', owned_by: '01.ai' }, - // yi-medium, yi-large, yi-vision 已被 yi-lightning 替代 (详见 https://archive.ph/0Idg3) - // { id: 'yi-medium', name: 'yi-medium', provider: 'yi', group: 'yi-medium', owned_by: '01.ai' }, - // { id: 'yi-large', name: 'yi-large', provider: 'yi', group: 'yi-large', owned_by: '01.ai' }, - // { id: 'yi-vision', name: 'yi-vision', provider: 'yi', group: 'yi-vision', owned_by: '01.ai' } { id: 'yi-vision-v2', name: 'Yi Vision v2', provider: 'yi', group: 'yi-vision', owned_by: '01.ai' } ], zhipu: [ diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 8799a1a5..596f9d44 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -479,8 +479,9 @@ }, "vision": "视觉", "websearch": "联网", - "edit": "编辑模型", - "no_matches": "无可用模型" + "edit": "编辑模型", + "no_matches": "无可用模型", + "tool_calling": "工具调用" }, "navbar": { "expand": "伸缩对话框", diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 6148b908..c374a11a 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -685,7 +685,6 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic }) => { onMentionModel={(model) => onMentionModel(model, mentionFromKeyboard)} ToolbarButton={ToolbarButton} /> - = ({ assistant: _assistant, setActiveTopic }) => { /> + {showKnowledgeIcon && ( + 0} + /> + )} + + = ({ assistant: _assistant, setActiveTopic }) => { - {showKnowledgeIcon && ( - 0} - /> - )} - diff --git a/src/renderer/src/pages/home/Inputbar/MCPToolsButton.tsx b/src/renderer/src/pages/home/Inputbar/MCPToolsButton.tsx index a0d63d40..c93605c2 100644 --- a/src/renderer/src/pages/home/Inputbar/MCPToolsButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/MCPToolsButton.tsx @@ -1,3 +1,4 @@ +import { ToolOutlined } from '@ant-design/icons' import { useMCPServers } from '@renderer/hooks/useMCPServers' import { MCPServer } from '@renderer/types' import { Dropdown, Switch, Tooltip } from 'antd' @@ -39,7 +40,7 @@ const MCPToolsButton: FC = ({ enabledMCPs, onEnableMCP, ToolbarButton }) } }) } - }, [enableAll]) + }, [activeServers, enableAll, enabledMCPs, onEnableMCP]) const menu = (
@@ -84,9 +85,9 @@ const MCPToolsButton: FC = ({ enabledMCPs, onEnableMCP, ToolbarButton }) open={isOpen} onOpenChange={setIsOpen} overlayClassName="mention-models-dropdown"> - + - + 0 ? '#d97757' : 'var(--color-icon)' }} /> diff --git a/src/renderer/src/pages/home/components/SelectModelButton.tsx b/src/renderer/src/pages/home/components/SelectModelButton.tsx index bac2fee8..8c629a26 100644 --- a/src/renderer/src/pages/home/components/SelectModelButton.tsx +++ b/src/renderer/src/pages/home/components/SelectModelButton.tsx @@ -39,7 +39,7 @@ const SelectModelButton: FC = ({ assistant }) => { {model ? model.name : t('button.select_model')} {providerName ? '| ' + providerName : ''} - + )