Files
Alfred/src/web/consoles/workbench/components/chat/parts/ToolPart.tsx

100 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string, unknown>;
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 (
<Collapse
ghost
items={[
{
children: <Typography.Text type="danger">{toolPart.errorText}</Typography.Text>,
key: toolPart.toolCallId ?? toolName,
label: (
<Flex align="center" component="span" gap={4}>
<CloseCircleFilled className="icon-error" />
<Typography.Text type="danger">{toolName} </Typography.Text>
</Flex>
),
},
]}
size="small"
/>
);
}
return (
<Collapse
defaultActiveKey={isStreaming ? [toolPart.toolCallId ?? toolName] : undefined}
ghost
items={[
{
children: (
<Flex gap={4} vertical>
{toolPart.input != null && (
<>
<Typography.Text type="secondary"></Typography.Text>
<pre className="tool-result-pre">{FORMAT_JSON(toolPart.input)}</pre>
</>
)}
{"output" in toolPart && toolPart.output != null && (
<>
<Typography.Text type="secondary"></Typography.Text>
<pre className="tool-result-pre">{FORMAT_JSON(toolPart.output)}</pre>
</>
)}
{!toolPart.input && !("output" in toolPart) && (
<Typography.Text type="secondary">...</Typography.Text>
)}
</Flex>
),
key: toolPart.toolCallId ?? toolName,
label: isStreaming ? (
<Flex align="center" component="span" gap={4}>
<LoadingOutlined className="icon-primary" />
<Typography.Text type="secondary">
{state === "input-streaming" ? "生成参数" : `调用 ${toolName}`}
</Typography.Text>
</Flex>
) : (
<Flex align="center" component="span" gap={4}>
<CheckCircleFilled className="icon-success" />
<Typography.Text type="secondary">{toolName}</Typography.Text>
</Flex>
),
},
]}
size="small"
/>
);
}