fix: Trace Viewer 内容渲染修复

1. useAgentStream: 每轮迭代只创建一个 TraceStep, 状态转换时更新而非新建; tool_result 反向搜索包含 toolCallId 的步骤, 修复工具结果丢失

2. TraceStep: 渲染工具状态图标/结果/错误/耗时; Thought 添加 maxHeight 200+滚动; 展开状态用户操作后不被自动覆盖

3. ToolResultBlock: 移除 500 字符截断, 改为 maxHeight 300+滚动

4. ToolCallCard: 参数区域添加 overflowY auto+wordBreak
This commit is contained in:
thzxx
2026-07-07 21:13:51 +08:00
parent 6f08759f63
commit ed59305b5e
4 changed files with 168 additions and 44 deletions
+41 -31
View File
@@ -4,10 +4,10 @@
* 监听 window.metona.agent.onStreamEvent / onStateChange,将事件分发到 Zustand Store。
*
* 架构设计:
* - onStateChange:迭代号追踪 + 消息卡片创建 + Trace 步骤创建 + Agent 状态映射
* (状态变化事件总是先于同迭代的流式内容事件到达,因此在此统一管理迭代边界
* - onStateChange:迭代号追踪 + 消息卡片创建 + Trace 步骤管理 + Agent 状态映射
* 每轮迭代只创建一个 TraceStep,状态转换时更新当前步骤的 state 字段(避免碎片化
* - onStreamEvent:纯内容更新(reasoning、text、tool_call、tool_result、usage、done、error
* (不再处理迭代号比较,消除竞态条件
* tool_result 反向搜索包含对应 toolCallId 的 TraceStep(修复工具结果丢失
*/
import { useEffect, useRef } from 'react';
@@ -147,22 +147,27 @@ export function useAgentStream(): void {
getStore().updateMessage(lastMsg.id, { toolCalls: updatedToolCalls });
}
// 同步更新 Trace 步骤中的工具调用状态
// 反向搜索包含该 toolCallId 的 TraceStep(修复工具结果丢失)
// tool_call_complete 在 THINKING 阶段写入,tool_result 在 EXECUTING 阶段到达
// 两者可能在同一轮迭代但不同状态转换,需反向查找
const steps = getStore().traceSteps;
const lastTrace = steps[steps.length - 1];
if (lastTrace?.toolCalls) {
const updatedTraceToolCalls = lastTrace.toolCalls.map((tc) =>
tc.id === data.toolResult!.toolCallId
? {
...tc,
status: data.toolResult!.success ? 'success' as const : 'error' as const,
result: data.toolResult!.result,
error: data.toolResult!.error,
durationMs: data.toolResult!.durationMs,
}
: tc,
);
getStore().updateLastTraceStep({ toolCalls: updatedTraceToolCalls });
for (let i = steps.length - 1; i >= 0; i--) {
const trace = steps[i];
if (trace.toolCalls?.some((tc) => tc.id === data.toolResult!.toolCallId)) {
const updatedTraceToolCalls = trace.toolCalls.map((tc) =>
tc.id === data.toolResult!.toolCallId
? {
...tc,
status: data.toolResult!.success ? 'success' as const : 'error' as const,
result: data.toolResult!.result,
error: data.toolResult!.error,
durationMs: data.toolResult!.durationMs,
}
: tc,
);
getStore().updateTraceStep(trace.iteration, { toolCalls: updatedTraceToolCalls });
break;
}
}
}
break;
@@ -260,26 +265,31 @@ export function useAgentStream(): void {
}
}
// --- Trace 步骤管理 ---
// --- Trace 步骤管理(每轮迭代一个步骤,状态转换时更新而非新建)---
if (data.state && data.iteration != null) {
const traceSteps = store.traceSteps;
const lastStep = traceSteps[traceSteps.length - 1];
// 标记上一个 Trace 步骤为已完成
if (traceSteps.length > 0) {
const lastStep = traceSteps[traceSteps.length - 1];
if (!lastStep.completedAt) {
// 判断是否需要创建新步骤:同一迭代的首次状态(INIT/THINKING)创建新步骤
// 后续状态转换(EXECUTING/OBSERVING等)只更新当前步骤的 state 字段
const isSameIteration = lastStep && lastStep.iteration === data.iteration;
if (isSameIteration) {
// 同一迭代内的状态转换 → 更新当前步骤状态
store.updateLastTraceStep({ state: data.state });
} else {
// 新迭代 → 标记上一步完成,创建新步骤
if (lastStep && !lastStep.completedAt) {
store.updateLastTraceStep({ completedAt: Date.now() });
}
store.addTraceStep({
id: `trace_${data.iteration}_${data.state}_${Date.now()}`,
iteration: data.iteration,
state: data.state,
startedAt: Date.now(),
});
}
// 创建新的 Trace 步骤
store.addTraceStep({
id: `trace_${data.iteration}_${data.state}_${Date.now()}`,
iteration: data.iteration,
state: data.state,
startedAt: Date.now(),
});
// --- Agent 状态映射 ---
const stateToStatus: Record<string, AgentStatus> = {
THINKING: 'thinking',