import { CheckCircleFilled, CloseCircleFilled, LoadingOutlined } from "@ant-design/icons"; import { Collapse, Flex, Typography } from "antd"; import type { PartProps } from "./types"; interface ToolPartData { errorText?: string; input?: unknown; output?: unknown; toolCallId?: string; toolMetadata?: Record; toolName?: string; type?: string; } function getToolState(part: ToolPartData) { if ("errorText" in part && part.errorText) return "output-error" as const; if ("output" in part) return "output-available" as const; if ("input" in part) return "input-available" as const; return "input-streaming" as const; } const FORMAT_JSON = (v: unknown) => JSON.stringify(v, null, 2); export function ToolPart({ part }: PartProps) { const toolPart = part as unknown as ToolPartData; const state = getToolState(toolPart); const rawToolName = toolPart.toolName ?? (toolPart.type ?? "unknown").replace(/^tool-/, ""); const toolName = typeof toolPart.toolMetadata?.["displayName"] === "string" ? toolPart.toolMetadata["displayName"] : rawToolName; const isStreaming = state === "input-streaming" || state === "input-available"; if (state === "output-error") { return ( {toolPart.errorText}, key: toolPart.toolCallId ?? toolName, label: ( {toolName} 失败 ), }, ]} size="small" /> ); } return ( {toolPart.input != null && ( <> 参数:
{FORMAT_JSON(toolPart.input)}
)} {"output" in toolPart && toolPart.output != null && ( <> 结果:
{FORMAT_JSON(toolPart.output)}
)} {!toolPart.input && !("output" in toolPart) && ( 生成中... )} ), key: toolPart.toolCallId ?? toolName, label: isStreaming ? ( {state === "input-streaming" ? "生成参数" : `调用 ${toolName}`} ) : ( {toolName} ), }, ]} size="small" /> ); }