claudecode/frontend/src/components/ToolChain.tsx
fengmengqi 4a04faf926 feat: 添加 Web 前端及服务端 SSE 流式支持,扩展多模型兼容
后端:
  - server: 实现完整的 HTTP 会话管理(CRUD)+ SSE 事件流推送,
    支持双通道架构(POST 发消息 + GET SSE 接收流式响应)
  - runtime: ContentBlock 新增 Thinking / RedactedThinking 变体,
    支持思考过程和已编辑思考的序列化/反序列化
  - api: 注册 GLM 系列模型(glm-4/5 等)到模型注册表,
    扩展 XAI/OpenAI 兼容提供商的请求构建逻辑

  前端:
  - 基于 Ant Design X 构建完整聊天界面:Bubble.List 消息列表、
    Sender 输入框、Conversations 会话管理、Think 思考过程折叠、
    ThoughtChain 工具调用链展示
  - XMarkdown 集成:代码高亮、Mermaid 图表、LaTeX 公式、
    自定义脚注、流式渲染(incomplete 占位符)
  - SSE Hook 对接服务端事件流,手动管理 AssistantBuffer 累积 delta
  - 深色/浅色主题切换,会话侧边栏(新建/切换/删除)
2026-04-10 16:29:27 +08:00

86 lines
2.2 KiB
TypeScript

import React from 'react';
import { ThoughtChain } from '@ant-design/x';
interface ToolCall {
id: string;
name: string;
input: string;
output?: string;
isError?: boolean;
}
interface ToolChainProps {
tools: ToolCall[];
}
const ToolChain: React.FC<ToolChainProps> = ({ tools }) => {
const items = tools.map((tool) => {
const hasResult = tool.output !== undefined;
let status: 'loading' | 'success' | 'error' = 'loading';
if (hasResult) {
status = tool.isError ? 'error' : 'success';
}
return {
key: tool.id,
status,
title: tool.name,
description: hasResult ? (tool.isError ? '执行出错' : '执行完成') : '执行中...',
collapsible: true,
content: (
<div style={{ fontSize: 13 }}>
{tool.input && (
<div style={{ marginBottom: 8 }}>
<div style={{ fontWeight: 500, marginBottom: 4 }}></div>
<pre style={{
margin: 0,
padding: 8,
borderRadius: 6,
background: 'rgba(0,0,0,0.04)',
overflow: 'auto',
maxHeight: 200,
fontSize: 12,
}}>
{tryFormatJSON(tool.input)}
</pre>
</div>
)}
{tool.output !== undefined && (
<div>
<div style={{ fontWeight: 500, marginBottom: 4 }}>
{tool.isError ? '错误' : '输出'}
</div>
<pre style={{
margin: 0,
padding: 8,
borderRadius: 6,
background: tool.isError ? 'rgba(255,0,0,0.04)' : 'rgba(0,0,0,0.04)',
overflow: 'auto',
maxHeight: 300,
fontSize: 12,
}}>
{tool.output}
</pre>
</div>
)}
</div>
),
};
});
if (items.length === 0) return null;
return <ThoughtChain items={items} />;
};
function tryFormatJSON(str: string): string {
try {
return JSON.stringify(JSON.parse(str), null, 2);
} catch {
return str;
}
}
export default ToolChain;
export type { ToolCall };