refactor(Tools): replace MCPToolResponse with NormalToolResponse in m… (#10303)

* refactor(Tools): replace MCPToolResponse with NormalToolResponse in message tools and add new agent tools

- Updated MessageKnowledgeSearch, MessageMemorySearch, and MessageWebSearch components to use NormalToolResponse.
- Refactored MessageTool to handle NormalToolResponse and simplified tool rendering logic.
- Introduced new agent tools: BashTool, GlobTool, GrepTool, ReadTool, SearchTool, TaskTool, and TodoWriteTool with corresponding types and renderers.
- Enhanced type safety by updating tool response types in the codebase.

* fix(i18n): Auto update translations for PR #10303

* chunk type

* refactor(migration): renumber migration steps after removing step 155

Remove unused migration step 155 and renumber subsequent steps to maintain sequence

* fix(store): prevent mutation of assistant presets by using spread operator

* fix(store): ignore ts-2589 false positives and refactor preset updates

Refactor assistant preset updates to use forEach instead of map for consistency
Add ts-ignore comments for TypeScript false positives in store operations

* Fix tool result handling and session creation flow

- Populate toolName in tool-result chunks from contentBlockState
- Add onSessionCreated callback to SessionModal for post-creation actions
- Return created session from useSessions hook and update SWR cache optimistically

* Fix toolName reference in ClaudeCode message transformation

- Correctly reference toolName from contentBlockState using blockKey instead of block.tool_use_id
- Ensure proper tool result chunk generation when handling assistant messages
- Maintain consistent data structure for tool call processing

* Fix toolName reference and add stream event logging

- Correct toolName lookup to use tool_use_id instead of blockKey in tool-result chunks
- Add debug logging for stream event handling
- Update contentBlockState key to use event.content_block.id for tool_use events

* Add debug logging for message content blocks

- Log each content block when processing user or assistant messages
- Maintain existing switch case logic for text block handling
- Improve debugging visibility for multi-block message processing

* get toolName

* chore: bump version to 1.7.0-alpha.1

* fix(getSdkClient): add authToken to anthropic client initialization for claude code

* Update transform.ts

* Refactor logging levels in transform.ts and adjust JSON body parser configuration in app.ts

* refactor(sessions): simplify session creation by removing modal and using direct button

The SessionModal component was removed and replaced with a direct button click handler that creates a session using the agent data. Also added error handling to display an alert when session fetching fails.

* feat(agents): add api server check and warning for agent features

- Add api server enabled check in multiple components
- Show warning alert when api server is disabled
- Add dismissable warning in AgentSection
- Disable send button when api server is disabled
- Add iknow state to store dismissed warnings

* feat(i18n): add warning message for enabling API server

Add warning message in multiple languages to inform users they need to enable API server to use agent features

* feat(sessions): make session creation async and set active session

Dispatch active session id after successful creation to ensure UI reflects current state

* feat(sessions): add session waiting state and improve deletion handling

- Add sessionWaiting state to track updating/deleting sessions
- Extract updateSession logic into separate hook
- Improve session deletion with waiting state and fallback session selection
- Disable session items during deletion to prevent duplicate actions

* feat(i18n): add error message for last session deletion

Add error message to prevent deletion of the last session in all supported languages

* fix(i18n): Auto update translations for PR #10096

* fix(i18n): Auto update translations for PR #10096

* feat(tools): add WriteTool and update tool rendering logic

- Introduced WriteTool for handling file writing operations.
- Updated MessageAgentTools to include the new WriteTool in the tool renderers.
- Refactored existing tools to streamline rendering and improve code clarity.
- Enhanced BashTool and TaskTool to better display input and output information.

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: suyao <sy20010504@gmail.com>
Co-authored-by: icarus <eurfelux@gmail.com>
Co-authored-by: Vaayne <liu.vaayne@gmail.com>
This commit is contained in:
MyPrototypeWhat 2025-09-22 19:53:10 +08:00 committed by GitHub
parent c3adcf663f
commit 939782ac4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 585 additions and 47 deletions

View File

@ -0,0 +1,30 @@
import { AccordionItem, Code } from '@heroui/react'
import { Terminal } from 'lucide-react'
import { ToolTitle } from './GenericTools'
import type { BashToolInput as BashToolInputType, BashToolOutput as BashToolOutputType } from './types'
export function BashTool({ input, output }: { input: BashToolInputType; output?: BashToolOutputType }) {
// 如果有输出,计算输出行数
const outputLines = output ? output.split('\n').length : 0
return (
<AccordionItem
key="tool"
aria-label="Bash Tool"
title={
<ToolTitle
icon={<Terminal className="h-4 w-4" />}
label="Bash"
params={
<Code size="sm" className="text-sm">
{input.command}
</Code>
}
stats={output ? `${outputLines} ${outputLines === 1 ? 'line' : 'lines'}` : undefined}
/>
}>
<div>{output}</div>
</AccordionItem>
)
}

View File

@ -0,0 +1,95 @@
// 通用工具组件 - 减少重复代码
import { ReactNode } from 'react'
// 生成 AccordionItem 的标题
export function ToolTitle({
icon,
label,
params,
stats,
className = 'text-sm'
}: {
icon?: ReactNode
label: string
params?: string | ReactNode
stats?: string | ReactNode
className?: string
}) {
return (
<div className={`flex items-center gap-1 ${className}`}>
{icon}
{label && <span className="font-medium">{label}</span>}
{params && <span className="flex-shrink-0 text-muted-foreground text-sm">{params}</span>}
{stats && <span className="flex-shrink-0 text-muted-foreground">{stats}</span>}
</div>
)
}
// 纯字符串输入工具 (Task, Bash, Search)
export function StringInputTool({
input,
label,
className = ''
}: {
input: string
label: string
className?: string
}) {
return (
<div className={className}>
<div>{label}:</div>
<div>{input}</div>
</div>
)
}
// 单字段输入工具 (pattern, query, file_path 等)
export function SimpleFieldInputTool({
input,
label,
fieldName,
className = ''
}: {
input: Record<string, any>
label: string
fieldName: string
className?: string
}) {
return (
<div className={className}>
<div>{label}:</div>
<div>
<div>{input[fieldName]}</div>
{/* 显示其他字段(如 Grep 的 output_mode */}
{Object.entries(input)
.filter(([key]) => key !== fieldName)
.map(([key, value]) => (
<span key={key}>
{key}: {String(value)}
</span>
))}
</div>
</div>
)
}
// 字符串输出工具 (Read, Bash, Search, Glob, WebSearch, Grep 等)
export function StringOutputTool({
output,
label,
className = '',
textColor = ''
}: {
output: string
label: string
className?: string
textColor?: string
}) {
return (
<div className={className}>
<div className={textColor}>{label}:</div>
<div>{output}</div>
</div>
)
}

View File

@ -0,0 +1,26 @@
import { AccordionItem } from '@heroui/react'
import { FolderSearch } from 'lucide-react'
import { ToolTitle } from './GenericTools'
import type { GlobToolInput as GlobToolInputType, GlobToolOutput as GlobToolOutputType } from './types'
export function GlobTool({ input, output }: { input: GlobToolInputType; output?: GlobToolOutputType }) {
// 如果有输出,计算文件数量
const fileCount = output ? output.split('\n').filter((line) => line.trim()).length : 0
return (
<AccordionItem
key="tool"
aria-label="Glob Tool"
title={
<ToolTitle
icon={<FolderSearch className="h-4 w-4" />}
label="Glob"
params={input.pattern}
stats={output ? `${fileCount} found` : undefined}
/>
}>
<div>{output}</div>
</AccordionItem>
)
}

View File

@ -0,0 +1,31 @@
import { AccordionItem } from '@heroui/react'
import { FileSearch } from 'lucide-react'
import { ToolTitle } from './GenericTools'
import type { GrepToolInput, GrepToolOutput } from './types'
export function GrepTool({ input, output }: { input: GrepToolInput; output?: GrepToolOutput }) {
// 如果有输出,计算结果行数
const resultLines = output ? output.split('\n').filter((line) => line.trim()).length : 0
return (
<AccordionItem
key="tool"
aria-label="Grep Tool"
title={
<ToolTitle
icon={<FileSearch className="h-4 w-4" />}
label="Grep"
params={
<>
{input.pattern}
{input.output_mode && <span className="ml-1">({input.output_mode})</span>}
</>
}
stats={output ? `${resultLines} ${resultLines === 1 ? 'line' : 'lines'}` : undefined}
/>
}>
<div>{output}</div>
</AccordionItem>
)
}

View File

@ -0,0 +1,43 @@
import { AccordionItem } from '@heroui/react'
import { FileText } from 'lucide-react'
import { SimpleFieldInputTool, StringOutputTool, ToolTitle } from './GenericTools'
import type { ReadToolInput as ReadToolInputType, ReadToolOutput as ReadToolOutputType } from './types'
export function ReadTool({ input, output }: { input: ReadToolInputType; output?: ReadToolOutputType }) {
// 如果有输出,计算统计信息
const stats = output
? {
lineCount: output.split('\n').length,
fileSize: new Blob([output]).size,
formatSize: (bytes: number) => {
if (bytes < 1024) return `${bytes} B`
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
}
: null
return (
<AccordionItem
key="tool"
aria-label="Read Tool"
title={
<ToolTitle
icon={<FileText className="h-4 w-4" />}
label="Read File"
params={input.file_path.split('/').pop()}
stats={output && stats ? `${stats.lineCount} lines, ${stats.formatSize(stats.fileSize)}` : undefined}
/>
}>
<div>
<SimpleFieldInputTool input={input} label="File Path" fieldName="file_path" />
{output && (
<div>
<StringOutputTool output={output} label="File Content" />
</div>
)}
</div>
</AccordionItem>
)
}

View File

@ -0,0 +1,33 @@
import { AccordionItem } from '@heroui/react'
import { Search } from 'lucide-react'
import { StringInputTool, StringOutputTool, ToolTitle } from './GenericTools'
import type { SearchToolInput as SearchToolInputType, SearchToolOutput as SearchToolOutputType } from './types'
export function SearchTool({ input, output }: { input: SearchToolInputType; output?: SearchToolOutputType }) {
// 如果有输出,计算结果数量
const resultCount = output ? output.split('\n').filter((line) => line.trim()).length : 0
return (
<AccordionItem
key="tool"
aria-label="Search Tool"
title={
<ToolTitle
icon={<Search className="h-4 w-4" />}
label="Search"
params={`"${input}"`}
stats={output ? `${resultCount} ${resultCount === 1 ? 'result' : 'results'}` : undefined}
/>
}>
<div>
<StringInputTool input={input} label="Search Query" />
{output && (
<div>
<StringOutputTool output={output} label="Search Results" textColor="text-yellow-600 dark:text-yellow-400" />
</div>
)}
</div>
</AccordionItem>
)
}

View File

@ -0,0 +1,21 @@
import { AccordionItem } from '@heroui/react'
import { Bot } from 'lucide-react'
import { ToolTitle } from './GenericTools'
import type { TaskToolInput as TaskToolInputType, TaskToolOutput as TaskToolOutputType } from './types'
export function TaskTool({ input, output }: { input: TaskToolInputType; output?: TaskToolOutputType }) {
return (
<AccordionItem
key="tool"
aria-label="Task Tool"
title={<ToolTitle icon={<Bot className="h-4 w-4" />} label="Task" params={input.description} />}>
{output?.map((item) => (
<div key={item.type}>
<div>Type: {item.type}</div>
<div>{item.text}</div>
</div>
))}
</AccordionItem>
)
}

View File

@ -0,0 +1,36 @@
import { AccordionItem } from '@heroui/react'
import { ListTodo } from 'lucide-react'
import { ToolTitle } from './GenericTools'
import type {
TodoWriteToolInput as TodoWriteToolInputType,
TodoWriteToolOutput as TodoWriteToolOutputType
} from './types'
export function TodoWriteTool({ input, output }: { input: TodoWriteToolInputType; output?: TodoWriteToolOutputType }) {
return (
<AccordionItem
key="tool"
aria-label="Todo Write Tool"
title={
<ToolTitle
icon={<ListTodo className="h-4 w-4" />}
label="Todo Update"
stats={`${input.todos.length} ${input.todos.length === 1 ? 'item' : 'items'}`}
/>
}>
<div>
{input.todos.map((todo, index) => (
<div key={index}>
<div>
<span>{todo.status}</span>
{todo.activeForm && <span>{todo.activeForm}</span>}
</div>
<div>{todo.content}</div>
</div>
))}
</div>
{output}
</AccordionItem>
)
}

View File

@ -0,0 +1,29 @@
import { AccordionItem } from '@heroui/react'
import { Globe } from 'lucide-react'
import { SimpleFieldInputTool, ToolTitle } from './GenericTools'
import type { WebSearchToolInput, WebSearchToolOutput } from './types'
export function WebSearchTool({ input, output }: { input: WebSearchToolInput; output?: WebSearchToolOutput }) {
// 如果有输出,计算结果数量
const resultCount = output ? output.split('\n').filter((line) => line.trim()).length : 0
return (
<AccordionItem
key="tool"
aria-label="Web Search Tool"
title={
<ToolTitle
icon={<Globe className="h-4 w-4" />}
label="Web Search"
params={input.query}
stats={output ? `${resultCount} ${resultCount === 1 ? 'result' : 'results'}` : undefined}
/>
}>
<div>
<SimpleFieldInputTool input={input} label="Web Search Query" fieldName="query" />
{output}
</div>
</AccordionItem>
)
}

View File

@ -0,0 +1,16 @@
import { AccordionItem } from '@heroui/react'
import { FileText } from 'lucide-react'
import { ToolTitle } from './GenericTools'
import type { WriteToolInput, WriteToolOutput } from './types'
export function WriteTool({ input, output }: { input: WriteToolInput; output?: WriteToolOutput }) {
return (
<AccordionItem
key="tool"
aria-label="Write Tool"
title={<ToolTitle icon={<FileText className="h-4 w-4" />} label="Write" params={input.file_path} />}>
<div>{input.content}</div>
</AccordionItem>
)
}

View File

@ -0,0 +1,83 @@
import { Accordion } from '@heroui/react'
import { loggerService } from '@logger'
import { NormalToolResponse } from '@renderer/types'
// 导出所有类型
export * from './types'
// 导入所有渲染器
import { BashTool } from './BashTool'
import { GlobTool } from './GlobTool'
import { GrepTool } from './GrepTool'
import { ReadTool } from './ReadTool'
import { SearchTool } from './SearchTool'
import { TaskTool } from './TaskTool'
import { TodoWriteTool } from './TodoWriteTool'
import { AgentToolsType, ToolInput, ToolOutput } from './types'
import { WebSearchTool } from './WebSearchTool'
import { WriteTool } from './WriteTool'
const logger = loggerService.withContext('MessageAgentTools')
// 创建工具渲染器映射,这样就实现了完全的类型安全
export const toolRenderers = {
[AgentToolsType.Read]: ReadTool,
[AgentToolsType.Task]: TaskTool,
[AgentToolsType.Bash]: BashTool,
[AgentToolsType.Search]: SearchTool,
[AgentToolsType.Glob]: GlobTool,
[AgentToolsType.TodoWrite]: TodoWriteTool,
[AgentToolsType.WebSearch]: WebSearchTool,
[AgentToolsType.Grep]: GrepTool,
[AgentToolsType.Write]: WriteTool
} as const
// 类型守卫函数
export function isValidAgentToolsType(toolName: unknown): toolName is AgentToolsType {
return typeof toolName === 'string' && Object.values(AgentToolsType).includes(toolName as AgentToolsType)
}
// 统一的渲染函数
function renderToolContent(toolName: AgentToolsType, input: ToolInput, output?: ToolOutput) {
const Renderer = toolRenderers[toolName]
if (!Renderer) {
logger.error('Unknown tool type', { toolName })
return <div>Unknown tool type: {toolName}</div>
}
return (
<Accordion
className="w-max max-w-full"
itemClasses={{
trigger:
'p-0 [&>div:first-child]:!flex-none [&>div:first-child]:flex [&>div:first-child]:flex-col [&>div:first-child]:text-start'
}}>
{/* <Renderer input={input as any} output={output as any} /> */}
{Renderer({ input: input as any, output: output as any })}
</Accordion>
)
}
// 统一的组件渲染入口
export function MessageAgentTools({ toolResponse }: { toolResponse: NormalToolResponse }) {
const { arguments: args, response, tool } = toolResponse
logger.info('Rendering agent tool response', {
tool: tool,
arguments: args,
response
})
// 使用类型守卫确保类型安全
if (!isValidAgentToolsType(tool?.name)) {
logger.warn('Invalid tool name received', { toolName: tool?.name })
return (
<div className="rounded-lg bg-red-50 p-3 dark:bg-red-900/20">
<div className="text-red-600 text-sm dark:text-red-400">Invalid tool name: {tool?.name}</div>
</div>
)
}
const toolName = tool.name
return renderToolContent(toolName, args as ToolInput, response as ToolOutput)
}

View File

@ -0,0 +1,111 @@
export enum AgentToolsType {
Read = 'Read',
Task = 'Task',
Bash = 'Bash',
Search = 'Search',
Glob = 'Glob',
TodoWrite = 'TodoWrite',
WebSearch = 'WebSearch',
Grep = 'Grep',
Write = 'Write'
}
// Read 工具的类型定义
export interface ReadToolInput {
file_path: string
}
export type ReadToolOutput = string
// Task 工具的类型定义
export type TaskToolInput = {
description: string
prompt: string
subagent_type: string
}
export type TaskToolOutput = {
type: 'text'
text: string
}[]
// Bash 工具的类型定义
export type BashToolInput = {
command: string
description: string
}
export type BashToolOutput = string
// Search 工具的类型定义
export type SearchToolInput = string
export type SearchToolOutput = string
// Glob 工具的类型定义
export interface GlobToolInput {
pattern: string
}
export type GlobToolOutput = string
// TodoWrite 工具的类型定义
export interface TodoItem {
content: string
status: string
activeForm?: string
}
export type TodoWriteToolInput = {
todos: TodoItem[]
}
export type TodoWriteToolOutput = string
// WebSearch 工具的类型定义
export interface WebSearchToolInput {
query: string
}
export type WebSearchToolOutput = string
// Grep 工具的类型定义
export interface GrepToolInput {
pattern: string
output_mode: string
}
export type GrepToolOutput = string
export type WriteToolInput = {
content: string
file_path: string
}
export type WriteToolOutput = string
// 联合类型
export type ToolInput =
| ReadToolInput
| TaskToolInput
| BashToolInput
| SearchToolInput
| GlobToolInput
| TodoWriteToolInput
| WebSearchToolInput
| GrepToolInput
| WriteToolInput
export type ToolOutput =
| ReadToolOutput
| TaskToolOutput
| BashToolOutput
| SearchToolOutput
| GlobToolOutput
| TodoWriteToolOutput
| WebSearchToolOutput
| GrepToolOutput
| WriteToolOutput
// 工具渲染器接口
export interface ToolRenderer {
render: (props: { input: ToolInput; output?: ToolOutput }) => React.ReactElement
}

View File

@ -1,13 +1,13 @@
import { KnowledgeSearchToolInput, KnowledgeSearchToolOutput } from '@renderer/aiCore/tools/KnowledgeSearchTool'
import Spinner from '@renderer/components/Spinner'
import i18n from '@renderer/i18n'
import { MCPToolResponse } from '@renderer/types'
import { NormalToolResponse } from '@renderer/types'
import { Typography } from 'antd'
import { FileSearch } from 'lucide-react'
import styled from 'styled-components'
const { Text } = Typography
export function MessageKnowledgeSearchToolTitle({ toolResponse }: { toolResponse: MCPToolResponse }) {
export function MessageKnowledgeSearchToolTitle({ toolResponse }: { toolResponse: NormalToolResponse }) {
const toolInput = toolResponse.arguments as KnowledgeSearchToolInput
const toolOutput = toolResponse.response as KnowledgeSearchToolOutput
@ -28,7 +28,7 @@ export function MessageKnowledgeSearchToolTitle({ toolResponse }: { toolResponse
)
}
export function MessageKnowledgeSearchToolBody({ toolResponse }: { toolResponse: MCPToolResponse }) {
export function MessageKnowledgeSearchToolBody({ toolResponse }: { toolResponse: NormalToolResponse }) {
const toolOutput = toolResponse.response as KnowledgeSearchToolOutput
return toolResponse.status === 'done' ? (

View File

@ -4,6 +4,7 @@ import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { useSettings } from '@renderer/hooks/useSettings'
import { useTimer } from '@renderer/hooks/useTimer'
import { MCPToolResponse } from '@renderer/types'
import type { ToolMessageBlock } from '@renderer/types/newMessage'
import { isToolAutoApproved } from '@renderer/utils/mcp-tools'
import { cancelToolAction, confirmToolAction } from '@renderer/utils/userConfirmation'
@ -59,7 +60,7 @@ const MessageMcpTool: FC<Props> = ({ block }) => {
const toolResponse = block.metadata?.rawMcpToolResponse
const { id, tool, status, response } = toolResponse!
const { id, tool, status, response } = toolResponse as MCPToolResponse
const isPending = status === 'pending'
const isDone = status === 'done'
const isError = status === 'error'

View File

@ -1,6 +1,6 @@
import { MemorySearchToolInput, MemorySearchToolOutput } from '@renderer/aiCore/tools/MemorySearchTool'
import Spinner from '@renderer/components/Spinner'
import { MCPToolResponse } from '@renderer/types'
import { NormalToolResponse } from '@renderer/types'
import { Typography } from 'antd'
import { ChevronRight } from 'lucide-react'
import { useTranslation } from 'react-i18next'
@ -8,7 +8,7 @@ import styled from 'styled-components'
const { Text } = Typography
export const MessageMemorySearchToolTitle = ({ toolResponse }: { toolResponse: MCPToolResponse }) => {
export const MessageMemorySearchToolTitle = ({ toolResponse }: { toolResponse: NormalToolResponse }) => {
const { t } = useTranslation()
const toolInput = toolResponse.arguments as MemorySearchToolInput
const toolOutput = toolResponse.response as MemorySearchToolOutput

View File

@ -1,7 +1,7 @@
import { MCPToolResponse } from '@renderer/types'
import { NormalToolResponse } from '@renderer/types'
import type { ToolMessageBlock } from '@renderer/types/newMessage'
import { Collapse } from 'antd'
import { MessageAgentTools } from './MessageAgentTools'
import { MessageKnowledgeSearchToolTitle } from './MessageKnowledgeSearch'
import { MessageMemorySearchToolTitle } from './MessageMemorySearch'
import { MessageWebSearchToolTitle } from './MessageWebSearch'
@ -11,7 +11,7 @@ interface Props {
}
const prefix = 'builtin_'
const ChooseTool = (toolResponse: MCPToolResponse): { label: React.ReactNode; body: React.ReactNode } | null => {
const ChooseTool = (toolResponse: NormalToolResponse): React.ReactNode | null => {
let toolName = toolResponse.tool.name
if (toolName.startsWith(prefix)) {
toolName = toolName.slice(prefix.length)
@ -20,20 +20,21 @@ const ChooseTool = (toolResponse: MCPToolResponse): { label: React.ReactNode; bo
switch (toolName) {
case 'web_search':
case 'web_search_preview':
return {
label: <MessageWebSearchToolTitle toolResponse={toolResponse} />,
body: null
}
return <MessageWebSearchToolTitle toolResponse={toolResponse} />
case 'knowledge_search':
return {
label: <MessageKnowledgeSearchToolTitle toolResponse={toolResponse} />,
body: null
}
return <MessageKnowledgeSearchToolTitle toolResponse={toolResponse} />
case 'memory_search':
return {
label: <MessageMemorySearchToolTitle toolResponse={toolResponse} />,
body: null
}
return <MessageMemorySearchToolTitle toolResponse={toolResponse} />
case 'Read':
case 'Task':
case 'Bash':
case 'Search':
case 'Glob':
case 'TodoWrite':
case 'WebSearch':
case 'Grep':
case 'Write':
return <MessageAgentTools toolResponse={toolResponse} />
default:
return null
}
@ -45,32 +46,13 @@ export default function MessageTool({ block }: Props) {
if (!toolResponse) return null
const toolRenderer = ChooseTool(toolResponse)
const toolRenderer = ChooseTool(toolResponse as NormalToolResponse)
if (!toolRenderer) return null
return toolRenderer.body ? (
<Collapse
items={[
{
key: '1',
label: toolRenderer.label,
children: toolRenderer.body,
showArrow: false,
styles: {
header: {
paddingLeft: '0'
}
}
}
]}
size="small"
ghost
/>
) : (
toolRenderer.label
)
return toolRenderer
}
// const PrepareToolWrapper = styled.span`
// display: flex;
// align-items: center;

View File

@ -1,6 +1,6 @@
import { WebSearchToolInput, WebSearchToolOutput } from '@renderer/aiCore/tools/WebSearchTool'
import Spinner from '@renderer/components/Spinner'
import { MCPToolResponse } from '@renderer/types'
import { NormalToolResponse } from '@renderer/types'
import { Typography } from 'antd'
import { Search } from 'lucide-react'
import { useTranslation } from 'react-i18next'
@ -8,7 +8,7 @@ import styled from 'styled-components'
const { Text } = Typography
export const MessageWebSearchToolTitle = ({ toolResponse }: { toolResponse: MCPToolResponse }) => {
export const MessageWebSearchToolTitle = ({ toolResponse }: { toolResponse: NormalToolResponse }) => {
const { t } = useTranslation()
const toolInput = toolResponse.arguments as WebSearchToolInput
const toolOutput = toolResponse.response as WebSearchToolOutput

View File

@ -730,7 +730,7 @@ export type MCPToolResponseStatus = 'pending' | 'cancelled' | 'invoking' | 'done
interface BaseToolResponse {
id: string // unique id
tool: BaseTool | MCPTool
arguments: Record<string, unknown> | undefined
arguments: Record<string, unknown> | Record<string, unknown>[] | string | undefined
status: MCPToolResponseStatus
response?: any
}

View File

@ -11,6 +11,7 @@ import type {
MemoryItem,
Metrics,
Model,
NormalToolResponse,
Topic,
Usage,
WebSearchResponse,
@ -114,7 +115,7 @@ export interface ToolMessageBlock extends BaseMessageBlock {
arguments?: Record<string, any>
content?: string | object
metadata?: BaseMessageBlock['metadata'] & {
rawMcpToolResponse?: MCPToolResponse
rawMcpToolResponse?: MCPToolResponse | NormalToolResponse
}
}