From e95219f2ec1d454cb4c45a4298fb8d16ea914c5a Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat Date: Sun, 28 Sep 2025 19:00:35 +0800 Subject: [PATCH] refactor(tools): streamline tool selection and enhance unknown tool handling - Introduced a new utility function to determine if a tool is an agent tool, simplifying the tool selection logic in MessageTool. - Refactored MessageAgentTools to improve rendering logic and added an UnknownToolRenderer for better handling of unrecognized tools. - Updated BashOutputTool to remove unnecessary Card components, enhancing layout consistency. - Improved overall code clarity and maintainability by reducing redundancy and adhering to existing patterns. --- .../MessageAgentTools/BashOutputTool.tsx | 64 ++++++-------- .../MessageAgentTools/UnknownToolRenderer.tsx | 88 +++++++++++++++++++ .../Tools/MessageAgentTools/index.tsx | 48 +++++----- .../pages/home/Messages/Tools/MessageTool.tsx | 65 ++++++++------ 4 files changed, 170 insertions(+), 95 deletions(-) create mode 100644 src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/UnknownToolRenderer.tsx diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/BashOutputTool.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/BashOutputTool.tsx index f2dc8f143a..8c4402436c 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/BashOutputTool.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/BashOutputTool.tsx @@ -1,4 +1,4 @@ -import { AccordionItem, Card, CardBody, Chip, Code } from '@heroui/react' +import { AccordionItem, Chip, Code } from '@heroui/react' import { CheckCircle, Terminal, XCircle } from 'lucide-react' import { useMemo } from 'react' @@ -132,57 +132,43 @@ export function BashOutputTool({ input, output }: { input: BashOutputToolInput; {/* Standard Output */} {parsedOutput.stdout && ( - - -
stdout:
-
-                  {parsedOutput.stdout}
-                
-
-
+
+
stdout:
+
+                {parsedOutput.stdout}
+              
+
)} {/* Standard Error */} {parsedOutput.stderr && ( - - -
stderr:
-
-                  {parsedOutput.stderr}
-                
-
-
+
+
stderr:
+
+                {parsedOutput.stderr}
+              
+
)} {/* Tool Use Error */} {parsedOutput.tool_use_error && ( - - -
- - Error: -
-
-                  {parsedOutput.tool_use_error}
-                
-
-
+
+
+ + Error: +
+
+                {parsedOutput.tool_use_error}
+              
+
)} ) : ( // 原始输出(如果解析失败或非 XML 格式) output && ( - - -
-                {output}
-              
-
-
+
+
{output}
+
) )} diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/UnknownToolRenderer.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/UnknownToolRenderer.tsx new file mode 100644 index 0000000000..ad26caa549 --- /dev/null +++ b/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/UnknownToolRenderer.tsx @@ -0,0 +1,88 @@ +import { AccordionItem } from '@heroui/react' +import { useCodeStyle } from '@renderer/context/CodeStyleProvider' +import { Wrench } from 'lucide-react' +import { useEffect, useState } from 'react' + +import { ToolTitle } from './GenericTools' + +interface UnknownToolProps { + toolName: string + input?: unknown + output?: unknown +} + +export function UnknownToolRenderer({ toolName = '', input, output }: UnknownToolProps) { + const { highlightCode } = useCodeStyle() + const [inputHtml, setInputHtml] = useState('') + const [outputHtml, setOutputHtml] = useState('') + + useEffect(() => { + if (input !== undefined) { + const inputStr = JSON.stringify(input, null, 2) + highlightCode(inputStr, 'json').then(setInputHtml) + } + }, [input, highlightCode]) + + useEffect(() => { + if (output !== undefined) { + const outputStr = JSON.stringify(output, null, 2) + highlightCode(outputStr, 'json').then(setOutputHtml) + } + }, [output, highlightCode]) + + const getToolDisplayName = (name: string) => { + if (name.startsWith('mcp__')) { + const parts = name.substring(5).split('__') + if (parts.length >= 2) { + return `${parts[0]}:${parts.slice(1).join(':')}` + } + } + return name + } + + const getToolDescription = () => { + if (toolName.startsWith('mcp__')) { + return 'MCP Server Tool' + } + return 'Tool' + } + + return ( + } + label={getToolDisplayName(toolName)} + params={getToolDescription()} + /> + }> +
+ {input !== undefined && ( +
+
Input:
+
+
+ )} + + {output !== undefined && ( +
+
Output:
+
+
+ )} + + {input === undefined && output === undefined && ( +
No data available for this tool
+ )} +
+ + ) +} diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/index.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/index.tsx index 7d33a756d5..e32e4d6fd6 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/index.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageAgentTools/index.tsx @@ -19,10 +19,10 @@ import { SearchTool } from './SearchTool' import { TaskTool } from './TaskTool' import { TodoWriteTool } from './TodoWriteTool' import { AgentToolsType, ToolInput, ToolOutput } from './types' +import { UnknownToolRenderer } from './UnknownToolRenderer' import { WebFetchTool } from './WebFetchTool' import { WebSearchTool } from './WebSearchTool' import { WriteTool } from './WriteTool' - const logger = loggerService.withContext('MessageAgentTools') // 创建工具渲染器映射,这样就实现了完全的类型安全 @@ -54,20 +54,24 @@ function renderToolContent(toolName: AgentToolsType, input: ToolInput, output?: const Renderer = toolRenderers[toolName] return ( - div:first-child]:!flex-none [&>div:first-child]:flex [&>div:first-child]:flex-col [&>div:first-child]:text-start [&>div:first-child]:max-w-full', - indicator: 'flex-shrink-0', - subtitle: 'text-xs', - content: - 'rounded-md bg-foreground-50 p-2 text-foreground-900 dark:bg-foreground-100 max-h-96 p-2 overflow-scroll' - }} - defaultExpandedKeys={toolName === AgentToolsType.TodoWrite ? [AgentToolsType.TodoWrite] : []}> - {/* */} - {Renderer({ input: input as any, output: output as any })} - +
+ div:first-child]:!flex-none [&>div:first-child]:flex [&>div:first-child]:flex-col [&>div:first-child]:text-start [&>div:first-child]:max-w-full', + indicator: 'flex-shrink-0', + subtitle: 'text-xs', + content: + 'rounded-md bg-foreground-50 p-2 text-foreground-900 dark:bg-foreground-100 max-h-96 p-2 overflow-scroll', + base: 'space-y-1' + }} + defaultExpandedKeys={toolName === AgentToolsType.TodoWrite ? [AgentToolsType.TodoWrite] : []}> + {Renderer + ? Renderer({ input: input as any, output: output as any }) + : UnknownToolRenderer({ input: input as any, output: output as any, toolName })} + +
) } @@ -80,17 +84,5 @@ export function MessageAgentTools({ toolResponse }: { toolResponse: NormalToolRe response }) - // 使用类型守卫确保类型安全 - if (!isValidAgentToolsType(tool?.name)) { - logger.warn('Invalid tool name received', { toolName: tool?.name }) - return ( -
-
Invalid tool name: {tool?.name}
-
- ) - } - - const toolName = tool.name - - return renderToolContent(toolName, args as ToolInput, response as ToolOutput) + return renderToolContent(tool.name as AgentToolsType, args as ToolInput, response as ToolOutput) } diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx index 29b25e9a55..1b68bf7066 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx @@ -10,39 +10,48 @@ interface Props { block: ToolMessageBlock } const prefix = 'builtin_' +const agentPrefix = 'mcp__' +const agentTools = [ + 'Read', + 'Task', + 'Bash', + 'Search', + 'Glob', + 'TodoWrite', + 'WebSearch', + 'Grep', + 'Write', + 'WebFetch', + 'Edit', + 'MultiEdit', + 'BashOutput', + 'NotebookEdit', + 'ExitPlanMode' +] +const isAgentTool = (toolName: string) => { + if (agentTools.includes(toolName) || toolName.startsWith(agentPrefix)) { + return true + } + return false +} const ChooseTool = (toolResponse: NormalToolResponse): React.ReactNode | null => { let toolName = toolResponse.tool.name if (toolName.startsWith(prefix)) { toolName = toolName.slice(prefix.length) - } - - switch (toolName) { - case 'web_search': - case 'web_search_preview': - return - case 'knowledge_search': - return - case 'memory_search': - return - case 'Read': - case 'Task': - case 'Bash': - case 'Search': - case 'Glob': - case 'TodoWrite': - case 'WebSearch': - case 'Grep': - case 'Write': - case 'WebFetch': - case 'Edit': - case 'MultiEdit': - case 'BashOutput': - case 'NotebookEdit': - case 'ExitPlanMode': - return - default: - return null + switch (toolName) { + case 'web_search': + case 'web_search_preview': + return + case 'knowledge_search': + return + case 'memory_search': + return + default: + return null + } + } else if (isAgentTool(toolName)) { + return } }