feat(Models, MCP, Localization): Add Qwen to tool calling models and enhance MCP server management

- Add 'qwen' to tool calling models list
- Refactor MCP server hooks to use window.api methods
- Add 'more' translation key across localization files
- Improve MCP settings modal with window.api and window.modal methods
This commit is contained in:
kangfenmao 2025-03-11 10:32:10 +08:00
parent cf92752e79
commit 56dd2d17e7
8 changed files with 32 additions and 30 deletions

View File

@ -178,7 +178,7 @@ export const EMBEDDING_REGEX = /(?:^text-|embed|bge-|e5-|LLM2Vec|retrieval|uae-|
export const NOT_SUPPORTED_REGEX = /(?:^tts|rerank|whisper|speech)/i export const NOT_SUPPORTED_REGEX = /(?:^tts|rerank|whisper|speech)/i
// Tool calling models // Tool calling models
export const TOOL_CALLING_MODELS = ['gpt-4o', 'gpt-4o-mini', 'gpt-4', 'gpt-4.5', 'claude'] export const TOOL_CALLING_MODELS = ['gpt-4o', 'gpt-4o-mini', 'gpt-4', 'gpt-4.5', 'claude', 'qwen']
export const TOOL_CALLING_REGEX = new RegExp(`\\b(?:${TOOL_CALLING_MODELS.join('|')})\\b`, 'i') export const TOOL_CALLING_REGEX = new RegExp(`\\b(?:${TOOL_CALLING_MODELS.join('|')})\\b`, 'i')
export function isToolCallingModel(model: Model): boolean { export function isToolCallingModel(model: Model): boolean {
if (['gemini', 'deepseek', 'anthropic'].includes(model.provider)) { if (['gemini', 'deepseek', 'anthropic'].includes(model.provider)) {

View File

@ -1,5 +1,5 @@
import { useAppDispatch, useAppSelector } from '@renderer/store' import store, { useAppDispatch, useAppSelector } from '@renderer/store'
import { setMCPServers as _setMCPServers } from '@renderer/store/mcp' import { setMCPServers } from '@renderer/store/mcp'
import { MCPServer } from '@renderer/types' import { MCPServer } from '@renderer/types'
import { useEffect } from 'react' import { useEffect } from 'react'
@ -7,17 +7,13 @@ const ipcRenderer = window.electron.ipcRenderer
// Set up IPC listener for main process requests // Set up IPC listener for main process requests
ipcRenderer.on('mcp:request-servers', () => { ipcRenderer.on('mcp:request-servers', () => {
// This needs to access Redux outside of a hook, so we use the store directly
const { store } = require('@renderer/store')
const servers = store.getState().mcp.servers const servers = store.getState().mcp.servers
ipcRenderer.send('mcp:servers-from-renderer', servers) ipcRenderer.send('mcp:servers-from-renderer', servers)
}) })
// Listen for server changes from main process // Listen for server changes from main process
ipcRenderer.on('mcp:servers-changed', (_event, servers) => { ipcRenderer.on('mcp:servers-changed', (_event, servers) => {
// This needs to dispatch outside of a hook, so we use the store directly store.dispatch(setMCPServers(servers))
const { store } = require('@renderer/store')
store.dispatch(_setMCPServers(servers))
}) })
export const useMCPServers = () => { export const useMCPServers = () => {
@ -33,8 +29,8 @@ export const useMCPServers = () => {
useEffect(() => { useEffect(() => {
const loadServers = async () => { const loadServers = async () => {
try { try {
const servers = await ipcRenderer.invoke('mcp:list-servers') const servers = await window.api.mcp.listServers()
dispatch(_setMCPServers(servers)) dispatch(setMCPServers(servers))
} catch (error) { } catch (error) {
console.error('Failed to load MCP servers:', error) console.error('Failed to load MCP servers:', error)
} }
@ -45,7 +41,7 @@ export const useMCPServers = () => {
const addMCPServer = async (server: MCPServer) => { const addMCPServer = async (server: MCPServer) => {
try { try {
await ipcRenderer.invoke('mcp:add-server', server) await window.api.mcp.addServer(server)
// Main process will send back updated servers via mcp:servers-changed // Main process will send back updated servers via mcp:servers-changed
} catch (error) { } catch (error) {
console.error('Failed to add MCP server:', error) console.error('Failed to add MCP server:', error)
@ -55,7 +51,7 @@ export const useMCPServers = () => {
const updateMCPServer = async (server: MCPServer) => { const updateMCPServer = async (server: MCPServer) => {
try { try {
await ipcRenderer.invoke('mcp:update-server', server) await window.api.mcp.updateServer(server)
// Main process will send back updated servers via mcp:servers-changed // Main process will send back updated servers via mcp:servers-changed
} catch (error) { } catch (error) {
console.error('Failed to update MCP server:', error) console.error('Failed to update MCP server:', error)
@ -65,7 +61,7 @@ export const useMCPServers = () => {
const deleteMCPServer = async (name: string) => { const deleteMCPServer = async (name: string) => {
try { try {
await ipcRenderer.invoke('mcp:delete-server', name) await window.api.mcp.deleteServer(name)
// Main process will send back updated servers via mcp:servers-changed // Main process will send back updated servers via mcp:servers-changed
} catch (error) { } catch (error) {
console.error('Failed to delete MCP server:', error) console.error('Failed to delete MCP server:', error)
@ -75,7 +71,7 @@ export const useMCPServers = () => {
const setMCPServerActive = async (name: string, isActive: boolean) => { const setMCPServerActive = async (name: string, isActive: boolean) => {
try { try {
await ipcRenderer.invoke('mcp:set-server-active', { name, isActive }) await window.api.mcp.setServerActive(name, isActive)
// Main process will send back updated servers via mcp:servers-changed // Main process will send back updated servers via mcp:servers-changed
} catch (error) { } catch (error) {
console.error('Failed to set MCP server active status:', error) console.error('Failed to set MCP server active status:', error)

View File

@ -222,7 +222,8 @@
"select": "Select", "select": "Select",
"topics": "Topics", "topics": "Topics",
"warning": "Warning", "warning": "Warning",
"you": "You" "you": "You",
"more": "More"
}, },
"docs": { "docs": {
"title": "Docs" "title": "Docs"

View File

@ -222,7 +222,8 @@
"select": "選択", "select": "選択",
"topics": "トピック", "topics": "トピック",
"warning": "警告", "warning": "警告",
"you": "あなた" "you": "あなた",
"more": "もっと"
}, },
"docs": { "docs": {
"title": "ドキュメント" "title": "ドキュメント"

View File

@ -222,7 +222,8 @@
"select": "Выбрать", "select": "Выбрать",
"topics": "Топики", "topics": "Топики",
"warning": "Предупреждение", "warning": "Предупреждение",
"you": "Вы" "you": "Вы",
"more": "Ещё"
}, },
"docs": { "docs": {
"title": "Документация" "title": "Документация"

View File

@ -222,7 +222,8 @@
"select": "选择", "select": "选择",
"topics": "话题", "topics": "话题",
"warning": "警告", "warning": "警告",
"you": "用户" "you": "用户",
"more": "更多"
}, },
"docs": { "docs": {
"title": "帮助文档" "title": "帮助文档"

View File

@ -222,7 +222,8 @@
"select": "選擇", "select": "選擇",
"topics": "話題", "topics": "話題",
"warning": "警告", "warning": "警告",
"you": "您" "you": "您",
"more": "更多"
}, },
"docs": { "docs": {
"title": "說明文件" "title": "說明文件"

View File

@ -3,7 +3,7 @@ import { useTheme } from '@renderer/context/ThemeProvider'
import { useAppDispatch, useAppSelector } from '@renderer/store' import { useAppDispatch, useAppSelector } from '@renderer/store'
import { addMCPServer, deleteMCPServer, setMCPServerActive, updateMCPServer } from '@renderer/store/mcp' import { addMCPServer, deleteMCPServer, setMCPServerActive, updateMCPServer } from '@renderer/store/mcp'
import { MCPServer } from '@renderer/types' import { MCPServer } from '@renderer/types'
import { Button, Card, Form, Input, message, Modal, Radio, Space, Switch, Table, Tag, Tooltip, Typography } from 'antd' import { Button, Card, Form, Input, Modal, Radio, Space, Switch, Table, Tag, Tooltip, Typography } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import { FC, useEffect, useState } from 'react' import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -113,20 +113,20 @@ const MCPSettings: FC = () => {
window.api.mcp window.api.mcp
.updateServer(mcpServer) .updateServer(mcpServer)
.then(() => { .then(() => {
message.success(t('settings.mcp.updateSuccess')) window.message.success(t('settings.mcp.updateSuccess'))
setLoading(false) setLoading(false)
setIsModalVisible(false) setIsModalVisible(false)
form.resetFields() form.resetFields()
}) })
.catch((error) => { .catch((error) => {
message.error(`${t('settings.mcp.updateError')}: ${error.message}`) window.message.error(`${t('settings.mcp.updateError')}: ${error.message}`)
setLoading(false) setLoading(false)
}) })
dispatch(updateMCPServer(mcpServer)) dispatch(updateMCPServer(mcpServer))
} else { } else {
// Check for duplicate name // Check for duplicate name
if (mcpServers.some((server: MCPServer) => server.name === mcpServer.name)) { if (mcpServers.some((server: MCPServer) => server.name === mcpServer.name)) {
message.error(t('settings.mcp.duplicateName')) window.message.error(t('settings.mcp.duplicateName'))
setLoading(false) setLoading(false)
return return
} }
@ -134,13 +134,13 @@ const MCPSettings: FC = () => {
window.api.mcp window.api.mcp
.addServer(mcpServer) .addServer(mcpServer)
.then(() => { .then(() => {
message.success(t('settings.mcp.addSuccess')) window.message.success(t('settings.mcp.addSuccess'))
setLoading(false) setLoading(false)
setIsModalVisible(false) setIsModalVisible(false)
form.resetFields() form.resetFields()
}) })
.catch((error) => { .catch((error) => {
message.error(`${t('settings.mcp.addError')}: ${error.message}`) window.message.error(`${t('settings.mcp.addError')}: ${error.message}`)
setLoading(false) setLoading(false)
}) })
dispatch(addMCPServer(mcpServer)) dispatch(addMCPServer(mcpServer))
@ -152,20 +152,21 @@ const MCPSettings: FC = () => {
} }
const handleDelete = (serverName: string) => { const handleDelete = (serverName: string) => {
Modal.confirm({ window.modal.confirm({
title: t('settings.mcp.confirmDelete'), title: t('settings.mcp.confirmDelete'),
content: t('settings.mcp.confirmDeleteMessage'), content: t('settings.mcp.confirmDeleteMessage'),
okText: t('common.delete'), okText: t('common.delete'),
okButtonProps: { danger: true }, okButtonProps: { danger: true },
cancelText: t('common.cancel'), cancelText: t('common.cancel'),
centered: true,
onOk: () => { onOk: () => {
window.api.mcp window.api.mcp
.deleteServer(serverName) .deleteServer(serverName)
.then(() => { .then(() => {
message.success(t('settings.mcp.deleteSuccess')) window.message.success(t('settings.mcp.deleteSuccess'))
}) })
.catch((error) => { .catch((error) => {
message.error(`${t('settings.mcp.deleteError')}: ${error.message}`) window.message.error(`${t('settings.mcp.deleteError')}: ${error.message}`)
}) })
dispatch(deleteMCPServer(serverName)) dispatch(deleteMCPServer(serverName))
} }
@ -179,7 +180,7 @@ const MCPSettings: FC = () => {
// Optional: Show success message or update UI // Optional: Show success message or update UI
}) })
.catch((error) => { .catch((error) => {
message.error(`${t('settings.mcp.toggleError')}: ${error.message}`) window.message.error(`${t('settings.mcp.toggleError')}: ${error.message}`)
}) })
dispatch(setMCPServerActive({ name, isActive })) dispatch(setMCPServerActive({ name, isActive }))
} }
@ -217,7 +218,7 @@ const MCPSettings: FC = () => {
ellipsis={{ ellipsis={{
rows: 1, rows: 1,
expandable: 'collapsible', expandable: 'collapsible',
symbol: 'more', symbol: t('common.more'),
onExpand: () => {}, // Empty callback required for proper functionality onExpand: () => {}, // Empty callback required for proper functionality
tooltip: true tooltip: true
}} }}