Feat/assistant level mcp (#4220)
This commit is contained in:
parent
8a7db19e73
commit
a5b0480418
@ -205,7 +205,7 @@ export const FUNCTION_CALLING_MODELS = [
|
|||||||
'claude',
|
'claude',
|
||||||
'qwen',
|
'qwen',
|
||||||
'hunyuan',
|
'hunyuan',
|
||||||
'deepseek-ai/',
|
'deepseek',
|
||||||
'glm-4(?:-[\\w-]+)?',
|
'glm-4(?:-[\\w-]+)?',
|
||||||
'learnlm(?:-[\\w-]+)?',
|
'learnlm(?:-[\\w-]+)?',
|
||||||
'gemini(?:-[\\w-]+)?' // 提前排除了gemini的嵌入模型
|
'gemini(?:-[\\w-]+)?' // 提前排除了gemini的嵌入模型
|
||||||
|
|||||||
@ -46,6 +46,11 @@
|
|||||||
"search": "Search assistants...",
|
"search": "Search assistants...",
|
||||||
"settings.default_model": "Default Model",
|
"settings.default_model": "Default Model",
|
||||||
"settings.knowledge_base": "Knowledge Base Settings",
|
"settings.knowledge_base": "Knowledge Base Settings",
|
||||||
|
"settings.mcp": "MCP Servers",
|
||||||
|
"settings.mcp.enableFirst": "Enable this server in MCP settings first",
|
||||||
|
"settings.mcp.title": "MCP Settings",
|
||||||
|
"settings.mcp.noServersAvailable": "No MCP servers available. Add servers in settings",
|
||||||
|
"settings.mcp.description": "Default enabled MCP servers",
|
||||||
"settings.model": "Model Settings",
|
"settings.model": "Model Settings",
|
||||||
"settings.preset_messages": "Preset Messages",
|
"settings.preset_messages": "Preset Messages",
|
||||||
"settings.prompt": "Prompt Settings",
|
"settings.prompt": "Prompt Settings",
|
||||||
|
|||||||
@ -44,6 +44,11 @@
|
|||||||
"save.success": "保存に成功しました",
|
"save.success": "保存に成功しました",
|
||||||
"save.title": "エージェントに保存",
|
"save.title": "エージェントに保存",
|
||||||
"search": "アシスタントを検索...",
|
"search": "アシスタントを検索...",
|
||||||
|
"settings.mcp": "MCP サーバー",
|
||||||
|
"settings.mcp.enableFirst": "まず MCP 設定でこのサーバーを有効にしてください",
|
||||||
|
"settings.mcp.title": "MCP 設定",
|
||||||
|
"settings.mcp.noServersAvailable": "利用可能な MCP サーバーがありません。設定でサーバーを追加してください",
|
||||||
|
"settings.mcp.description": "デフォルトで有効な MCP サーバー",
|
||||||
"settings.default_model": "デフォルトモデル",
|
"settings.default_model": "デフォルトモデル",
|
||||||
"settings.knowledge_base": "ナレッジベース設定",
|
"settings.knowledge_base": "ナレッジベース設定",
|
||||||
"settings.model": "モデル設定",
|
"settings.model": "モデル設定",
|
||||||
|
|||||||
@ -44,6 +44,11 @@
|
|||||||
"save.success": "Успешно сохранено",
|
"save.success": "Успешно сохранено",
|
||||||
"save.title": "Сохранить в агента",
|
"save.title": "Сохранить в агента",
|
||||||
"search": "Поиск ассистентов...",
|
"search": "Поиск ассистентов...",
|
||||||
|
"settings.mcp": "Серверы MCP",
|
||||||
|
"settings.mcp.enableFirst": "Сначала включите этот сервер в настройках MCP",
|
||||||
|
"settings.mcp.title": "Настройки MCP",
|
||||||
|
"settings.mcp.noServersAvailable": "Нет доступных серверов MCP. Добавьте серверы в настройках",
|
||||||
|
"settings.mcp.description": "Серверы MCP, включенные по умолчанию",
|
||||||
"settings.default_model": "Модель по умолчанию",
|
"settings.default_model": "Модель по умолчанию",
|
||||||
"settings.knowledge_base": "Настройки базы знаний",
|
"settings.knowledge_base": "Настройки базы знаний",
|
||||||
"settings.model": "Настройки модели",
|
"settings.model": "Настройки модели",
|
||||||
|
|||||||
@ -44,6 +44,11 @@
|
|||||||
"save.success": "保存成功",
|
"save.success": "保存成功",
|
||||||
"save.title": "保存到智能体",
|
"save.title": "保存到智能体",
|
||||||
"search": "搜索助手",
|
"search": "搜索助手",
|
||||||
|
"settings.mcp": "MCP 服务器",
|
||||||
|
"settings.mcp.enableFirst": "请先在 MCP 设置中启用此服务器",
|
||||||
|
"settings.mcp.title": "MCP 设置",
|
||||||
|
"settings.mcp.noServersAvailable": "无可用 MCP 服务器。请在设置中添加服务器",
|
||||||
|
"settings.mcp.description": "默认启用的 MCP 服务器",
|
||||||
"settings.default_model": "默认模型",
|
"settings.default_model": "默认模型",
|
||||||
"settings.knowledge_base": "知识库设置",
|
"settings.knowledge_base": "知识库设置",
|
||||||
"settings.model": "模型设置",
|
"settings.model": "模型设置",
|
||||||
|
|||||||
@ -44,6 +44,11 @@
|
|||||||
"save.success": "儲存成功",
|
"save.success": "儲存成功",
|
||||||
"save.title": "儲存到智慧代理人",
|
"save.title": "儲存到智慧代理人",
|
||||||
"search": "搜尋助手...",
|
"search": "搜尋助手...",
|
||||||
|
"settings.mcp": "MCP 伺服器",
|
||||||
|
"settings.mcp.enableFirst": "請先在 MCP 設定中啟用此伺服器",
|
||||||
|
"settings.mcp.title": "MCP 設定",
|
||||||
|
"settings.mcp.noServersAvailable": "無可用 MCP 伺服器。請在設定中新增伺服器",
|
||||||
|
"settings.mcp.description": "預設啟用的 MCP 伺服器",
|
||||||
"settings.default_model": "預設模型",
|
"settings.default_model": "預設模型",
|
||||||
"settings.knowledge_base": "知識庫設定",
|
"settings.knowledge_base": "知識庫設定",
|
||||||
"settings.model": "模型設定",
|
"settings.model": "模型設定",
|
||||||
|
|||||||
@ -91,7 +91,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
const [isTranslating, setIsTranslating] = useState(false)
|
const [isTranslating, setIsTranslating] = useState(false)
|
||||||
const [selectedKnowledgeBases, setSelectedKnowledgeBases] = useState<KnowledgeBase[]>([])
|
const [selectedKnowledgeBases, setSelectedKnowledgeBases] = useState<KnowledgeBase[]>([])
|
||||||
const [mentionModels, setMentionModels] = useState<Model[]>([])
|
const [mentionModels, setMentionModels] = useState<Model[]>([])
|
||||||
const [enabledMCPs, setEnabledMCPs] = useState<MCPServer[]>([])
|
const [enabledMCPs, setEnabledMCPs] = useState<MCPServer[]>(assistant.mcpServers || [])
|
||||||
const [isMentionPopupOpen, setIsMentionPopupOpen] = useState(false)
|
const [isMentionPopupOpen, setIsMentionPopupOpen] = useState(false)
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
const [textareaHeight, setTextareaHeight] = useState<number>()
|
const [textareaHeight, setTextareaHeight] = useState<number>()
|
||||||
@ -145,6 +145,15 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
}
|
}
|
||||||
}, [textareaHeight])
|
}, [textareaHeight])
|
||||||
|
|
||||||
|
// reset state when assistant changes
|
||||||
|
useEffect(() => {
|
||||||
|
// Reset to assistant default model
|
||||||
|
assistant.defaultModel && setModel(assistant.defaultModel)
|
||||||
|
|
||||||
|
// Reset to assistant knowledge mcp servers
|
||||||
|
setEnabledMCPs(assistant.mcpServers || [])
|
||||||
|
}, [assistant, setModel])
|
||||||
|
|
||||||
const sendMessage = useCallback(async () => {
|
const sendMessage = useCallback(async () => {
|
||||||
if (inputEmpty || loading) {
|
if (inputEmpty || loading) {
|
||||||
return
|
return
|
||||||
@ -323,8 +332,11 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
await db.topics.add({ id: topic.id, messages: [] })
|
await db.topics.add({ id: topic.id, messages: [] })
|
||||||
await addAssistantMessagesToTopic({ assistant, topic })
|
await addAssistantMessagesToTopic({ assistant, topic })
|
||||||
|
|
||||||
|
// Clear previous state
|
||||||
// Reset to assistant default model
|
// Reset to assistant default model
|
||||||
assistant.defaultModel && setModel(assistant.defaultModel)
|
assistant.defaultModel && setModel(assistant.defaultModel)
|
||||||
|
// Reset to assistant knowledge mcp servers
|
||||||
|
setEnabledMCPs(assistant.mcpServers || [])
|
||||||
|
|
||||||
addTopic(topic)
|
addTopic(topic)
|
||||||
setActiveTopic(topic)
|
setActiveTopic(topic)
|
||||||
|
|||||||
@ -0,0 +1,198 @@
|
|||||||
|
import { InfoCircleOutlined } from '@ant-design/icons'
|
||||||
|
import { Box } from '@renderer/components/Layout'
|
||||||
|
import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||||
|
import { Assistant, AssistantSettings } from '@renderer/types'
|
||||||
|
import { Empty, Switch, Tooltip } from 'antd'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
export interface MCPServer {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
baseUrl?: string
|
||||||
|
command?: string
|
||||||
|
args?: string[]
|
||||||
|
env?: Record<string, string>
|
||||||
|
isActive: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
assistant: Assistant
|
||||||
|
updateAssistant: (assistant: Assistant) => void
|
||||||
|
updateAssistantSettings: (settings: AssistantSettings) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssistantMCPSettings: React.FC<Props> = ({ assistant, updateAssistant }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const { mcpServers: allMcpServers } = useMCPServers()
|
||||||
|
const onUpdate = (ids: string[]) => {
|
||||||
|
const mcpServers = ids
|
||||||
|
.map((id) => allMcpServers.find((server) => server.id === id))
|
||||||
|
.filter((server): server is MCPServer => server !== undefined && server.isActive)
|
||||||
|
const _assistant = { ...assistant, mcpServers }
|
||||||
|
updateAssistant(_assistant)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleServerToggle = (serverId: string) => {
|
||||||
|
const currentServerIds = assistant.mcpServers?.map((server) => server.id) || []
|
||||||
|
|
||||||
|
if (currentServerIds.includes(serverId)) {
|
||||||
|
// Remove server if it's already enabled
|
||||||
|
onUpdate(currentServerIds.filter((id) => id !== serverId))
|
||||||
|
} else {
|
||||||
|
// Add server if it's not enabled
|
||||||
|
onUpdate([...currentServerIds, serverId])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabledCount = assistant.mcpServers?.length || 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<HeaderContainer>
|
||||||
|
<Box style={{ fontWeight: 'bold', fontSize: '16px' }}>
|
||||||
|
{t('assistants.settings.mcp.title')}
|
||||||
|
<Tooltip title={t('assistants.settings.mcp.description', 'Select MCP servers to use with this assistant')}>
|
||||||
|
<InfoIcon />
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
{allMcpServers.length > 0 && (
|
||||||
|
<EnabledCount>
|
||||||
|
{enabledCount} / {allMcpServers.length} {t('settings.mcp.active')}
|
||||||
|
</EnabledCount>
|
||||||
|
)}
|
||||||
|
</HeaderContainer>
|
||||||
|
|
||||||
|
{allMcpServers.length > 0 ? (
|
||||||
|
<ServerList>
|
||||||
|
{allMcpServers.map((server) => {
|
||||||
|
const isEnabled = assistant.mcpServers?.some((s) => s.id === server.id) || false
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ServerItem key={server.id} isEnabled={isEnabled}>
|
||||||
|
<ServerInfo>
|
||||||
|
<ServerName>{server.name}</ServerName>
|
||||||
|
{server.description && <ServerDescription>{server.description}</ServerDescription>}
|
||||||
|
{server.baseUrl && <ServerUrl>{server.baseUrl}</ServerUrl>}
|
||||||
|
</ServerInfo>
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
!server.isActive
|
||||||
|
? t('assistants.settings.mcp.enableFirst', 'Enable this server in MCP settings first')
|
||||||
|
: undefined
|
||||||
|
}>
|
||||||
|
<Switch
|
||||||
|
checked={isEnabled}
|
||||||
|
disabled={!server.isActive}
|
||||||
|
onChange={() => handleServerToggle(server.id)}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</ServerItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ServerList>
|
||||||
|
) : (
|
||||||
|
<EmptyContainer>
|
||||||
|
<Empty
|
||||||
|
description={t('assistants.settings.mcp.noAvaliable', 'No MCP servers available')}
|
||||||
|
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||||
|
/>
|
||||||
|
</EmptyContainer>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: ${(props) => props.theme.colors?.background || '#f9f9f9'};
|
||||||
|
border-radius: 8px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const HeaderContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const InfoIcon = styled(InfoCircleOutlined)`
|
||||||
|
margin-left: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: ${(props) => props.theme.colors?.textSecondary || '#8c8c8c'};
|
||||||
|
cursor: help;
|
||||||
|
`
|
||||||
|
|
||||||
|
const EnabledCount = styled.span`
|
||||||
|
font-size: 12px;
|
||||||
|
color: ${(props) => props.theme.colors?.textSecondary || '#8c8c8c'};
|
||||||
|
`
|
||||||
|
|
||||||
|
const EmptyContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ServerList = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 4px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ServerItem = styled.div<{ isEnabled: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: ${(props) => props.theme.colors?.cardBackground || '#fff'};
|
||||||
|
border: 1px solid ${(props) => props.theme.colors?.border || '#e6e6e6'};
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: ${(props) => (props.isEnabled ? 1 : 0.7)};
|
||||||
|
`
|
||||||
|
|
||||||
|
const ServerInfo = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ServerName = styled.div`
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ServerDescription = styled.div`
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: ${(props) => props.theme.colors?.textSecondary || '#8c8c8c'};
|
||||||
|
margin-bottom: 3px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ServerUrl = styled.div`
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: ${(props) => props.theme.colors?.textTertiary || '#bfbfbf'};
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default AssistantMCPSettings
|
||||||
@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import AssistantKnowledgeBaseSettings from './AssistantKnowledgeBaseSettings'
|
import AssistantKnowledgeBaseSettings from './AssistantKnowledgeBaseSettings'
|
||||||
|
import AssistantMCPSettings from './AssistantMCPSettings'
|
||||||
import AssistantMessagesSettings from './AssistantMessagesSettings'
|
import AssistantMessagesSettings from './AssistantMessagesSettings'
|
||||||
import AssistantModelSettings from './AssistantModelSettings'
|
import AssistantModelSettings from './AssistantModelSettings'
|
||||||
import AssistantPromptSettings from './AssistantPromptSettings'
|
import AssistantPromptSettings from './AssistantPromptSettings'
|
||||||
@ -19,7 +20,7 @@ interface AssistantSettingPopupShowParams {
|
|||||||
tab?: AssistantSettingPopupTab
|
tab?: AssistantSettingPopupTab
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssistantSettingPopupTab = 'prompt' | 'model' | 'messages' | 'knowledge_base'
|
type AssistantSettingPopupTab = 'prompt' | 'model' | 'messages' | 'knowledge_base' | 'mcp'
|
||||||
|
|
||||||
interface Props extends AssistantSettingPopupShowParams {
|
interface Props extends AssistantSettingPopupShowParams {
|
||||||
resolve: (assistant: Assistant) => void
|
resolve: (assistant: Assistant) => void
|
||||||
@ -68,6 +69,10 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
|||||||
showKnowledgeIcon && {
|
showKnowledgeIcon && {
|
||||||
key: 'knowledge_base',
|
key: 'knowledge_base',
|
||||||
label: t('assistants.settings.knowledge_base')
|
label: t('assistants.settings.knowledge_base')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'mcp',
|
||||||
|
label: t('assistants.settings.mcp')
|
||||||
}
|
}
|
||||||
].filter(Boolean) as { key: string; label: string }[]
|
].filter(Boolean) as { key: string; label: string }[]
|
||||||
|
|
||||||
@ -133,6 +138,13 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
|||||||
updateAssistantSettings={updateAssistantSettings}
|
updateAssistantSettings={updateAssistantSettings}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{menu === 'mcp' && (
|
||||||
|
<AssistantMCPSettings
|
||||||
|
assistant={assistant}
|
||||||
|
updateAssistant={updateAssistant}
|
||||||
|
updateAssistantSettings={updateAssistantSettings}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Settings>
|
</Settings>
|
||||||
</HStack>
|
</HStack>
|
||||||
</StyledModal>
|
</StyledModal>
|
||||||
|
|||||||
@ -17,6 +17,7 @@ export type Assistant = {
|
|||||||
messages?: AssistantMessage[]
|
messages?: AssistantMessage[]
|
||||||
enableWebSearch?: boolean
|
enableWebSearch?: boolean
|
||||||
enableGenerateImage?: boolean
|
enableGenerateImage?: boolean
|
||||||
|
mcpServers?: MCPServer[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AssistantMessage = {
|
export type AssistantMessage = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user