From 1b83b58d487f04c5eb77faf440f1205b4e83cafe Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Mon, 9 Feb 2026 15:07:51 +0800 Subject: [PATCH] Tool config using db --- web/pages/Assistants.tsx | 192 +++++++++++---------------------------- 1 file changed, 52 insertions(+), 140 deletions(-) diff --git a/web/pages/Assistants.tsx b/web/pages/Assistants.tsx index 9759930..0311057 100644 --- a/web/pages/Assistants.tsx +++ b/web/pages/Assistants.tsx @@ -2,17 +2,8 @@ import React, { useState, useEffect, useRef } from 'react'; import { Plus, Search, Play, Copy, Trash2, Mic, MessageSquare, Save, Video, PhoneOff, Camera, ArrowLeftRight, Send, Phone, Rocket, AlertTriangle, PhoneCall, CameraOff, Image, Images, CloudSun, Calendar, TrendingUp, Coins, Wrench, Globe, Terminal, X, ClipboardCheck, Sparkles, Volume2, Timer, ChevronDown, Database, Server, Zap, ExternalLink, Key, BrainCircuit, Ear, Book, Filter } from 'lucide-react'; import { Button, Input, Badge, Drawer, Dialog } from '../components/UI'; -import { ASRModel, Assistant, KnowledgeBase, LLMModel, TabValue, Voice } from '../types'; -import { createAssistant, deleteAssistant, fetchASRModels, fetchAssistants, fetchKnowledgeBases, fetchLLMModels, fetchVoices, updateAssistant as updateAssistantApi } from '../services/backendApi'; - -interface ToolItem { - id: string; - name: string; - icon: React.ReactNode; - desc: string; - category: 'system' | 'query'; - isCustom?: boolean; -} +import { ASRModel, Assistant, KnowledgeBase, LLMModel, TabValue, Tool, Voice } from '../types'; +import { createAssistant, deleteAssistant, fetchASRModels, fetchAssistants, fetchKnowledgeBases, fetchLLMModels, fetchTools, fetchVoices, updateAssistant as updateAssistantApi } from '../services/backendApi'; const isSiliconflowVendor = (vendor?: string) => { const normalized = String(vendor || '').trim().toLowerCase(); @@ -42,12 +33,31 @@ const resolveRuntimeTtsVoice = (selectedVoiceId: string, voice: Voice) => { return explicitKey || buildSiliconflowVoiceKey(selectedVoiceId, voice.model); }; +const renderToolIcon = (icon: string) => { + const className = 'w-4 h-4'; + const map: Record = { + Camera: , + CameraOff: , + Image: , + Images: , + CloudSun: , + Calendar: , + TrendingUp: , + Coins: , + Terminal: , + Globe: , + Wrench: , + }; + return map[icon] || ; +}; + export const AssistantsPage: React.FC = () => { const [assistants, setAssistants] = useState([]); const [voices, setVoices] = useState([]); const [knowledgeBases, setKnowledgeBases] = useState([]); const [llmModels, setLlmModels] = useState([]); const [asrModels, setAsrModels] = useState([]); + const [tools, setTools] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [selectedId, setSelectedId] = useState(null); const [activeTab, setActiveTab] = useState(TabValue.GLOBAL); @@ -58,16 +68,6 @@ export const AssistantsPage: React.FC = () => { const [isPublishModalOpen, setIsPublishModalOpen] = useState(false); const [publishTab, setPublishTab] = useState<'web' | 'api'>('web'); - // Custom Tools State - const [customTools, setCustomTools] = useState([]); - const [hiddenToolIds, setHiddenToolIds] = useState([]); - const [isAddToolModalOpen, setIsAddToolModalOpen] = useState(false); - const [addingToCategory, setAddingToCategory] = useState<'system' | 'query'>('system'); - - // New Tool Form State - const [newToolName, setNewToolName] = useState(''); - const [newToolDesc, setNewToolDesc] = useState(''); - const [deleteId, setDeleteId] = useState(null); const [copySuccess, setCopySuccess] = useState(false); const [saveLoading, setSaveLoading] = useState(false); @@ -83,18 +83,20 @@ export const AssistantsPage: React.FC = () => { const loadInitialData = async () => { setIsLoading(true); try { - const [assistantList, voiceList, kbList, llmList, asrList] = await Promise.all([ + const [assistantList, voiceList, kbList, llmList, asrList, toolList] = await Promise.all([ fetchAssistants(), fetchVoices(), fetchKnowledgeBases(), fetchLLMModels(), fetchASRModels(), + fetchTools(), ]); setAssistants(assistantList); setVoices(voiceList); setKnowledgeBases(kbList); setLlmModels(llmList); setAsrModels(asrList); + setTools(toolList); if (assistantList.length > 0) { setSelectedId(assistantList[0].id); } @@ -209,18 +211,10 @@ export const AssistantsPage: React.FC = () => { updateAssistant('tools', newTools); }; - const deleteTool = (e: React.MouseEvent, toolId: string) => { + const removeImportedTool = (e: React.MouseEvent, tool: Tool) => { e.stopPropagation(); - setAssistants(prev => prev.map(a => ({ - ...a, - tools: a.tools?.filter(id => id !== toolId) || [] - }))); - - if (customTools.some(t => t.id === toolId)) { - setCustomTools(prev => prev.filter(t => t.id !== toolId)); - } else { - setHiddenToolIds(prev => [...prev, toolId]); - } + if (!selectedAssistant) return; + updateAssistant('tools', (selectedAssistant.tools || []).filter((id) => id !== tool.id)); }; const addHotword = () => { @@ -236,44 +230,8 @@ export const AssistantsPage: React.FC = () => { } }; - const handleAddCustomTool = () => { - if (!newToolName.trim()) return; - const newTool: ToolItem = { - id: `custom_${Date.now()}`, - name: newToolName, - desc: newToolDesc, - category: addingToCategory, - icon: addingToCategory === 'system' ? : , - isCustom: true - }; - setCustomTools([...customTools, newTool]); - setIsAddToolModalOpen(false); - setNewToolName(''); - setNewToolDesc(''); - }; - - const openAddToolModal = (e: React.MouseEvent, cat: 'system' | 'query') => { - e.stopPropagation(); - setAddingToCategory(cat); - setIsAddToolModalOpen(true); - }; - - const baseSystemTools: ToolItem[] = [ - { id: 'cam_open', name: '打开相机', icon: , desc: '允许 AI 开启摄像头流', category: 'system' }, - { id: 'cam_close', name: '关闭相机', icon: , desc: '允许 AI 停止摄像头流', category: 'system' }, - { id: 'take_photo', name: '拍照', icon: , desc: 'AI 触发单张拍摄', category: 'system' }, - { id: 'burst_3', name: '连拍三张', icon: , desc: 'AI 触发快速连拍', category: 'system' }, - ]; - - const baseQueryTools: ToolItem[] = [ - { id: 'q_weather', name: '天气查询', icon: , desc: '查询实时及未来天气', category: 'query' }, - { id: 'q_calendar', name: '日历查询', icon: , desc: '查询日程及节假日信息', category: 'query' }, - { id: 'q_stock', name: '股价查询', icon: , desc: '查询股票实时行情', category: 'query' }, - { id: 'q_exchange', name: '汇率查询', icon: , desc: '查询多国货币汇率', category: 'query' }, - ]; - - const systemTools = [...baseSystemTools, ...customTools.filter(t => t.category === 'system')].filter(t => !hiddenToolIds.includes(t.id)); - const queryTools = [...baseQueryTools, ...customTools.filter(t => t.category === 'query')].filter(t => !hiddenToolIds.includes(t.id)); + const systemTools = tools.filter((t) => t.enabled !== false && t.category === 'system'); + const queryTools = tools.filter((t) => t.enabled !== false && t.category === 'query'); const isExternalConfig = selectedAssistant?.configMode === 'dify' || selectedAssistant?.configMode === 'fastgpt'; const isNoneConfig = selectedAssistant?.configMode === 'none' || !selectedAssistant?.configMode; @@ -766,12 +724,6 @@ export const AssistantsPage: React.FC = () => {

系统指令

-
{systemTools.map(tool => ( @@ -781,7 +733,7 @@ export const AssistantsPage: React.FC = () => { className={`p-4 rounded-xl border transition-all cursor-pointer group relative flex items-start space-x-3 ${selectedAssistant.tools?.includes(tool.id) ? 'bg-primary/10 border-primary/40 shadow-[0_0_15px_rgba(6,182,212,0.1)]' : 'bg-card/30 border-white/5 hover:bg-white/5 hover:border-white/10'}`} >
- {tool.icon} + {renderToolIcon(tool.icon)}
@@ -790,15 +742,17 @@ export const AssistantsPage: React.FC = () => { {selectedAssistant.tools?.includes(tool.id) &&
}
-

{tool.desc}

+

{tool.description}

- + {selectedAssistant.tools?.includes(tool.id) && ( + + )} ))} @@ -809,12 +763,6 @@ export const AssistantsPage: React.FC = () => {

信息查询

-
{queryTools.map(tool => ( @@ -824,7 +772,7 @@ export const AssistantsPage: React.FC = () => { className={`p-4 rounded-xl border transition-all cursor-pointer group relative flex items-start space-x-3 ${selectedAssistant.tools?.includes(tool.id) ? 'bg-blue-500/10 border-blue-500/40 shadow-[0_0_15px_rgba(59,130,246,0.1)]' : 'bg-card/30 border-white/5 hover:bg-white/5 hover:border-white/10'}`} >
- {tool.icon} + {renderToolIcon(tool.icon)}
@@ -833,22 +781,24 @@ export const AssistantsPage: React.FC = () => { {selectedAssistant.tools?.includes(tool.id) &&
}
-

{tool.desc}

+

{tool.description}

- + {selectedAssistant.tools?.includes(tool.id) && ( + + )} ))}
- 提示:启用工具后,AI 将能在对话中自动识别并调用相关功能以协助用户。 + 提示:此处仅导入工具库中的已有工具。移除仅对当前小助手生效,不会删除工具库。
)} @@ -968,44 +918,6 @@ export const AssistantsPage: React.FC = () => { /> )} - {/* Add Custom Tool Modal */} - setIsAddToolModalOpen(false)} - title={addingToCategory === 'system' ? '添加自定义系统指令' : '添加自定义信息查询'} - footer={ - <> - - - - } - > -
-
- - setNewToolName(e.target.value)} - placeholder="例如: 智能家居控制" - autoFocus - /> -
-
- -