fix: 精简工具调用摘要状态展示

This commit is contained in:
root
2026-07-11 19:05:38 +02:00
parent cc35ae61e4
commit 51ce93b9be
7 changed files with 323 additions and 53 deletions
+6 -6
View File
@@ -779,8 +779,8 @@ test("case aggregate writes one registry markdown entry without grading or broad
renderedRowCount: 4,
rows: [
{ rowId: "row-1", seq: 1, header: "请求接受", body: "CaseRun started" },
{ rowId: "row-2", seq: 2, header: "ok commandExecution", body: "hwpod build --spec .hwlab/hwpod-spec.yaml\nexitCode=0" },
{ rowId: "row-3", seq: 3, header: "ok commandExecution", body: `${longTraceCommand} stdout: first line\nsecond line exitCode=0` },
{ rowId: "tool:hwpod-build", seq: 2, tone: "ok", header: "00:00:02 hwpod duration=00:00:01 exit=0", body: "hwpod build --spec .hwlab/hwpod-spec.yaml\nexitCode=0" },
{ rowId: "tool:hwpod-cat", seq: 3, tone: "ok", header: "00:00:03 hwpod duration=00:00:01 exit=0", body: `${longTraceCommand} stdout: first line\nsecond line exitCode=0` },
{ rowId: "row-4", seq: 4, header: "助手最终消息", terminal: true, bodyFormat: "markdown", body: "Build completed" }
]
});
@@ -834,12 +834,12 @@ test("case aggregate writes one registry markdown entry without grading or broad
assert.match(aggregate, /## Trace/u);
assert.doesNotMatch(aggregate, / 2\. ok commandExecution/u);
assert.match(aggregate, /\*\*CaseRun started\*\*/u);
assert.match(aggregate, /- <details>\n <summary> hwpod build --spec \.hwlab\/hwpod-spec\.yaml<\/summary>/u);
assert.match(aggregate, /- <details>\n <summary aria-label="[^"]+" title="[^"]+"><code>00:00:02 hwpod duration=00:00:01 exit=0<\/code> hwpod build --spec \.hwlab\/hwpod-spec\.yaml<\/summary>/u);
assert.match(aggregate, / ```text\n hwpod build --spec \.hwlab\/hwpod-spec\.yaml\n exitCode=0[\s\S]* <\/details>/u);
assert.match(aggregate, /<summary> hwpod workspace cat projects\/01_baseline\/Middlewares\/Arm-2D\/Library\/include\/arm_2d_type\.\.\.<\/summary>/u);
assert.match(aggregate, /<summary aria-label="[^"]+" title="[^"]+"><code>00:00:03 hwpod duration=00:00:01 exit=0<\/code> hwpod workspace cat projects\/01_baseline\/Middlewares\/Arm-2D\/Library\/include\/arm_2d_type/u);
assert.match(aggregate, / ```text\n hwpod workspace cat [^\n]+\n stdout:\n first line\n second line\n exitCode=0[\s\S]* <\/details>\n\n\*\*Build completed\*\*\n\n## Final Response/u);
for (const [, summary] of aggregate.matchAll(/<summary>([\s\S]*?)<\/summary>/gu)) assert.doesNotMatch(summary, /\n|stdout:|stderr:|exitCode=|grep -A 20/u);
assert.doesNotMatch(aggregate, /<summary>.*/u);
for (const [, summary] of aggregate.matchAll(/<summary[^>]*>([\s\S]*?)<\/summary>/gu)) assert.doesNotMatch(summary, /\n|stdout:|stderr:|exitCode=|grep -A 20/u);
assert.doesNotMatch(aggregate, /<summary[^>]*>.*/u);
assert.match(aggregate, /## Final Response/u);
assert.match(aggregate, /## Diff/u);
assert.match(aggregate, /Implement ARM-2D integration/u);
+79 -2
View File
@@ -7,7 +7,7 @@ import { test } from "bun:test";
import { queryKafkaEventStream } from "../../internal/cloud/kafka-event-bridge.ts";
import { mapAgentRunRecordsToHwlabDebugEvents, renderKafkaCliText, runKafkaCli } from "../src/hwlab-cli/kafka-regenerate.ts";
import { traceDisplayRows } from "../src/hwlab-cli/trace-renderer.ts";
import { renderTraceRowsMarkdown, traceDisplayRows, traceToolSummary } from "../src/hwlab-cli/trace-renderer.ts";
const SESSION_ID = "ses_agentrun_5ec4e141_6abc_466d_9afe_049f7c0ac105";
const HWLAB_SESSION_ID = "ses_5ec4e141-6abc-466d-9afe-049f7c0ac105";
@@ -86,7 +86,10 @@ test("offline HWLAB JSONL uses the shared Web pipeline and renders final respons
assert.equal(result.payload.validation.allMatchedEventsApplied, true);
assert.equal(result.payload.validation.finalResponseOutsideTrace, true);
const toolRow = result.payload.render.rows.find((row: any) => row.rowId.startsWith("tool:"));
assert.match(toolRow.header, /ok rg duration=00:00:01 exit=0/u);
assert.match(toolRow.header, /^\d{2}:\d{2}:\d{2} rg duration=00:00:01 exit=0$/u);
assert.doesNotMatch(toolRow.header, /\b(?:ok|fail|run|running)\b|total=/u);
assert.equal(toolRow.toolState, "success");
assert.equal(toolRow.statusLabel, "工具调用成功");
assert.match(toolRow.preview, /rg -n target src/u);
assert.match(toolRow.preview, /src\/main\.ts:42:target/u);
const markdown = await readFile(outputFile, "utf8");
@@ -96,6 +99,8 @@ test("offline HWLAB JSONL uses the shared Web pipeline and renders final respons
assert.match(markdown, /target trace: trc_render_trace_target/u);
assert.match(markdown, /## /u);
assert.match(markdown, /## /u);
assert.match(markdown, /<summary aria-label="[^"]+" title="[^"]+"><code>\d{2}:\d{2}:\d{2} rg duration=00:00:01 exit=0<\/code> [\s\S]*rg -n target src[\s\S]*src\/main\.ts:42:target<\/summary>/u);
assert.doesNotMatch(markdown, /total=|/u);
assert.equal(result.markdownOutput?.trim(), markdown.trim());
});
@@ -162,6 +167,78 @@ test("shared row model removes late assistant progress after the same item compl
assert.equal((result.markdownOutput?.match(new RegExp(duplicateText.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"), "gu")) ?? []).length, 1);
});
test("shared tool summary model keeps status semantic and visible content status-free", () => {
const rows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, [
{
sourceSeq: 1,
sourceEventId: "evt_tool_running",
type: "tool_call",
status: "running",
label: "agentrun:tool:commandExecution:started",
toolName: "commandExecution",
itemId: "tool_running",
command: "bun test active-fixture",
createdAt: "2026-07-10T11:00:01.000Z"
},
{
sourceSeq: 2,
sourceEventId: "evt_tool_success",
type: "tool_call",
status: "completed",
label: "agentrun:tool:commandExecution:completed",
toolName: "commandExecution",
itemId: "tool_success",
command: "rg -n success src",
durationMs: 1234,
exitCode: 0,
stdoutSummary: "src/main.ts:1:success",
createdAt: "2026-07-10T11:00:02.000Z"
},
{
sourceSeq: 3,
sourceEventId: "evt_tool_failure",
type: "tool_call",
status: "completed",
label: "agentrun:tool:commandExecution:completed",
toolName: "commandExecution",
itemId: "tool_failure",
command: "bun test failure-fixture",
durationMs: 2345,
exitCode: 1,
stderrSummary: "failure output",
createdAt: "2026-07-10T11:00:03.000Z"
},
{
sourceSeq: 4,
sourceEventId: "evt_tool_output_chunk",
type: "tool_call",
status: "output_chunk",
label: "item/commandExecution/outputDelta",
outputSummary: "streamed output",
createdAt: "2026-07-10T11:00:04.000Z"
}
]).filter((row) => row.rowId.startsWith("tool:"));
assert.deepEqual(rows.map((row) => row.toolState), ["running", "success", "failure", "running"]);
assert.deepEqual(rows.map((row) => row.tone), ["warn", "ok", "blocked", "warn"]);
assert.deepEqual(rows.map((row) => row.statusLabel), ["工具调用中", "工具调用成功", "工具调用失败", "工具调用中"]);
assert.ok(rows.every((row) => !/\b(?:ok|fail|run|running)\b|total=/u.test(row.header)));
assert.match(rows[1]!.header, /rg duration=00:00:01 exit=0$/u);
assert.match(rows[2]!.header, /bun duration=00:00:02 exit=1$/u);
assert.doesNotMatch(rows[3]!.header, /commandExecution|outputDelta/u);
const markdown = renderTraceRowsMarkdown(rows);
const visibleSummaries = [...markdown.matchAll(/<summary[^>]*>([\s\S]*?)<\/summary>/gu)].map((match) => match[1]);
assert.equal(visibleSummaries.length, 4);
for (const [index, summary] of visibleSummaries.entries()) {
assert.equal(summary, `<code>${rows[index]!.header}</code> ${traceToolSummary(rows[index]!)}`);
assert.doesNotMatch(summary, /\b(?:ok|fail|run|running)\b|total=/u);
}
assert.match(markdown, /aria-label="/u);
assert.match(markdown, /aria-label="/u);
assert.match(markdown, /aria-label="/u);
});
test("outer final response stays out of the Trace fallback when no completion event exists", () => {
const finalText = "Hi. What do you want to work on?";
const rows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, [