From 2c95326dc1d895d604190abca7d2d3f3edf26b2f Mon Sep 17 00:00:00 2001 From: root Date: Tue, 21 Jul 2026 02:11:30 +0200 Subject: [PATCH] fix: lazy render Workbench tool output --- .../components/agent/TraceTimeline.test.ts | 52 +++++++++++++++++++ .../src/components/agent/TraceTimeline.vue | 18 +++++-- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/web/hwlab-cloud-web/src/components/agent/TraceTimeline.test.ts b/web/hwlab-cloud-web/src/components/agent/TraceTimeline.test.ts index 41c80bc3..4dd853d4 100644 --- a/web/hwlab-cloud-web/src/components/agent/TraceTimeline.test.ts +++ b/web/hwlab-cloud-web/src/components/agent/TraceTimeline.test.ts @@ -269,6 +269,13 @@ describe("TraceTimeline sequence authority", () => { expect(completedSummary.get("code").text()).toMatch(/^\d{2}:\d{2}:\d{2}$/u); expect(completedSummary.get(".trace-tool-command-preview").text()).toBe("bun test tool-state"); expect(completedSummary.text()).not.toMatch(/\b(?:ok|fail|run|running)\b|total=|duration=|exit=|工具调用成功/u); + expect(completedDetails.text()).not.toContain("1 test passed"); + + completedDetails.element.open = true; + await completedDetails.trigger("toggle"); + await nextTick(); + + expect(completedDetails.text()).toContain("1 test passed"); wrapper.unmount(); }); @@ -307,6 +314,51 @@ describe("TraceTimeline sequence authority", () => { expect(wrapper.text()).toContain("共 1000 events"); wrapper.unmount(); }); + + it("does not mount folded tool output bodies for a dense live Trace", async () => { + const events = Array.from({ length: 120 }, (_, index) => ({ + sourceEventId: `evt_tool_dense_${index + 1}`, + runId: "run_tool_dense", + sourceSeq: index + 1, + source: "agentrun.kafka", + type: "tool_call", + label: "agentrun:tool:completed", + status: "completed", + toolName: "commandExecution", + itemId: `tool_dense_${index + 1}`, + command: `rg dense-${index + 1}`, + stdoutSummary: `dense output ${index + 1}`, + exitCode: 0 + })); + const wrapper = mount(TraceTimeline, { + props: { + autoExpanded: true, + trace: { + traceId: "trc_tool_dense", + eventSource: "hwlab-kafka-sse", + status: "running", + eventCount: events.length, + events + } + } + }); + await nextTick(); + + const tools = wrapper.findAll(".trace-tool-details"); + expect(tools).toHaveLength(120); + expect(wrapper.findAll(".trace-tool-details .trace-row-body")).toHaveLength(0); + + const lastTool = tools.at(-1); + expect(lastTool).toBeDefined(); + if (!lastTool) throw new Error("last tool row was not rendered"); + lastTool.element.open = true; + await lastTool.trigger("toggle"); + await nextTick(); + + expect(wrapper.findAll(".trace-tool-details .trace-row-body")).toHaveLength(1); + expect(lastTool.text()).toContain("dense output 120"); + wrapper.unmount(); + }); }); function liveTrace(status: "running" | "completed") { diff --git a/web/hwlab-cloud-web/src/components/agent/TraceTimeline.vue b/web/hwlab-cloud-web/src/components/agent/TraceTimeline.vue index 8873aafc..62fb7c2a 100644 --- a/web/hwlab-cloud-web/src/components/agent/TraceTimeline.vue +++ b/web/hwlab-cloud-web/src/components/agent/TraceTimeline.vue @@ -22,6 +22,7 @@ const TRACE_RENDER_ROW_WINDOW = 120; const TRACE_RENDER_EVENT_WINDOW = TRACE_RENDER_ROW_WINDOW * 2; const listRef = ref(null); const expanded = ref(false); +const expandedToolRowIds = ref(new Set()); const { following, keepBottomAfterUpdate, onScroll, scrollToBottom } = useWorkbenchBottomFollowScroll(listRef, { threshold: 24 }); const events = computed(() => Array.isArray(props.trace?.events) ? props.trace.events : []); @@ -104,6 +105,13 @@ function onDisclosureToggle(event: Event): void { writeStoredExpanded(target.open); } +function onToolDisclosureToggle(row: TraceEventRow, event: Event): void { + const target = event.currentTarget; + if (!(target instanceof HTMLDetailsElement)) return; + if (target.open) expandedToolRowIds.value.add(row.rowId); + else expandedToolRowIds.value.delete(row.rowId); +} + function setExpandedProgrammatically(value: boolean): void { if (expanded.value === value) return; programmaticToggleTarget = value; @@ -175,14 +183,16 @@ function firstNonEmptyString(...values: unknown[]): string | null {

显示最近 {{ renderedRows.length }} 行,共 {{ eventCount }} events

  1. -
    +
    {{ row.header }} {{ toolCommandPreview(row) }} - -
    {{ row.body }}
    - 已记录 +