fix: lazy render Workbench tool output
This commit is contained in:
@@ -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") {
|
||||
|
||||
@@ -22,6 +22,7 @@ const TRACE_RENDER_ROW_WINDOW = 120;
|
||||
const TRACE_RENDER_EVENT_WINDOW = TRACE_RENDER_ROW_WINDOW * 2;
|
||||
const listRef = ref<HTMLOListElement | null>(null);
|
||||
const expanded = ref(false);
|
||||
const expandedToolRowIds = ref(new Set<string>());
|
||||
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 {
|
||||
<p v-if="hiddenRowCount > 0 || inputEventWindowed" class="trace-window-notice">显示最近 {{ renderedRows.length }} 行,共 {{ eventCount }} events</p>
|
||||
<ol v-if="renderedRows.length > 0" ref="listRef" @scroll="onScroll">
|
||||
<li v-for="(row, index) in renderedRows" :key="rowKey(row, index)" class="trace-render-row" data-testid="trace-render-row" :data-trace-id="trace.traceId || undefined" :data-event-seq="row.seq ?? undefined" :data-event-seq-authority="row.sequenceAuthority" :data-projected-seq="row.sequenceAuthority === 'projected' ? row.seq ?? undefined : undefined" :data-event-kind="row.rowId" :data-event-status="row.tone" :data-tool-state="row.toolState ?? undefined" :data-terminal="row.terminal ? 'true' : 'false'" :data-row-kind="rowIsTool(row) ? 'tool' : 'message'" :data-row-id="row.rowId" :aria-posinset="hiddenRowCount + index + 1">
|
||||
<details v-if="rowIsTool(row)" class="trace-tool-details" :aria-busy="row.toolState === 'running' ? 'true' : undefined">
|
||||
<details v-if="rowIsTool(row)" class="trace-tool-details" :aria-busy="row.toolState === 'running' ? 'true' : undefined" @toggle="onToolDisclosureToggle(row, $event)">
|
||||
<summary :aria-label="traceToolAccessibilityLabel(row)" :title="traceToolAccessibilityLabel(row)" aria-live="polite" aria-atomic="true">
|
||||
<code>{{ row.header }}</code>
|
||||
<span v-if="toolCommandPreview(row)" class="trace-tool-command-preview">{{ toolCommandPreview(row) }}</span>
|
||||
</summary>
|
||||
<MessageMarkdown v-if="row.body && row.bodyFormat === 'markdown'" class="trace-row-body trace-row-markdown" :source="row.body" />
|
||||
<pre v-else-if="row.body" class="trace-row-body">{{ row.body }}</pre>
|
||||
<span v-else class="trace-row-body muted">已记录</span>
|
||||
<template v-if="expandedToolRowIds.has(row.rowId)">
|
||||
<MessageMarkdown v-if="row.body && row.bodyFormat === 'markdown'" class="trace-row-body trace-row-markdown" :source="row.body" />
|
||||
<pre v-else-if="row.body" class="trace-row-body">{{ row.body }}</pre>
|
||||
<span v-else class="trace-row-body muted">已记录</span>
|
||||
</template>
|
||||
</details>
|
||||
<template v-else>
|
||||
<code>{{ row.header }}</code>
|
||||
|
||||
Reference in New Issue
Block a user