Merge pull request #625 from pikasTech/fix-v02-device-pod-ui
fix: 修复 v0.2 Device Pod 右栏与助手消息
This commit is contained in:
@@ -332,7 +332,7 @@ function normalizeTraceEvent(event, { traceId, seq, now, fallbackRunnerKind } =
|
|||||||
stdoutSummary: safeText(event.stdoutSummary, 600),
|
stdoutSummary: safeText(event.stdoutSummary, 600),
|
||||||
stderrSummary: safeText(event.stderrSummary, 600),
|
stderrSummary: safeText(event.stderrSummary, 600),
|
||||||
chunk: safeText(event.chunk, 400),
|
chunk: safeText(event.chunk, 400),
|
||||||
message: safeText(event.message, 500),
|
message: safeText(event.message, 1200),
|
||||||
errorCode: safeText(event.errorCode, 120),
|
errorCode: safeText(event.errorCode, 120),
|
||||||
waitingFor: safeText(event.waitingFor, 220),
|
waitingFor: safeText(event.waitingFor, 220),
|
||||||
timeoutMs: typeof event.timeoutMs === "number" ? Math.trunc(event.timeoutMs) : undefined,
|
timeoutMs: typeof event.timeoutMs === "number" ? Math.trunc(event.timeoutMs) : undefined,
|
||||||
|
|||||||
@@ -161,11 +161,14 @@ export function createAppServerTurnState({ traceRecorder, session } = {}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (method === "item/completed" && item?.type === "agentMessage") {
|
if (method === "item/completed" && item?.type === "agentMessage") {
|
||||||
if (typeof item.text === "string") finalResponse = item.text;
|
const completedText = typeof item.text === "string" ? item.text : "";
|
||||||
|
if (completedText) finalResponse = completedText;
|
||||||
appendTrace({
|
appendTrace({
|
||||||
type: "assistant_message",
|
type: "assistant_message",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
label: "assistant:item_completed",
|
label: "assistant:item_completed",
|
||||||
|
itemId: optionalId(item.id),
|
||||||
|
message: redactText(completedText),
|
||||||
sessionId: session?.sessionId,
|
sessionId: session?.sessionId,
|
||||||
sessionStatus: session?.status,
|
sessionStatus: session?.status,
|
||||||
turn: session?.turn,
|
turn: session?.turn,
|
||||||
|
|||||||
@@ -32,6 +32,41 @@ test("trace display rows render completion when no terminal assistant event exis
|
|||||||
assert.equal(rows.some((row) => /轮次完成/u.test(row.header)), true);
|
assert.equal(rows.some((row) => /轮次完成/u.test(row.header)), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("trace display rows render assistant item text when stream snapshot is absent", () => {
|
||||||
|
const events = [
|
||||||
|
event(1, "request:accepted", { promptSummary: "请列出 device-pod" }),
|
||||||
|
event(2, "assistant:item_completed", {
|
||||||
|
type: "assistant_message",
|
||||||
|
status: "completed",
|
||||||
|
itemId: "msg_item_0",
|
||||||
|
message: "当前有以下 **5 个 device-pod** 可用。"
|
||||||
|
}),
|
||||||
|
commandEvent(3, "item/commandExecution:completed", {
|
||||||
|
itemId: "cmd-trace-2",
|
||||||
|
command: "grep -rn device-pod /workspace/hwlab",
|
||||||
|
status: "completed",
|
||||||
|
exitCode: 0,
|
||||||
|
stdoutSummary: "success=true"
|
||||||
|
}),
|
||||||
|
event(4, "assistant:completed", {
|
||||||
|
type: "assistant_message",
|
||||||
|
terminal: true,
|
||||||
|
status: "completed",
|
||||||
|
itemId: "assistant-final",
|
||||||
|
message: "可以继续指定要操作的 device-pod。"
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
const rows = traceDisplayRows({ traceId: "trc_assistant_item_text", events }, events);
|
||||||
|
const text = rows.map((row) => `${row.header}\n${row.body ?? ""}`).join("\n---\n");
|
||||||
|
|
||||||
|
assert.match(text, /🤖 助手消息(第一条)/u);
|
||||||
|
assert.match(text, /当前有以下 \*\*5 个 device-pod\*\* 可用/u);
|
||||||
|
assert.match(text, /工具调用/u);
|
||||||
|
assert.match(text, /助手最后一条消息/u);
|
||||||
|
assert.match(text, /可以继续指定要操作的 device-pod/u);
|
||||||
|
});
|
||||||
|
|
||||||
function traceEvents() {
|
function traceEvents() {
|
||||||
return [
|
return [
|
||||||
event(1, "request:accepted", { promptSummary: "请检查 trace" }),
|
event(1, "request:accepted", { promptSummary: "请检查 trace" }),
|
||||||
|
|||||||
@@ -314,10 +314,6 @@
|
|||||||
<pre id="device-event-text">等待 /v1/device-pods 事件流。</pre>
|
<pre id="device-event-text">等待 /v1/device-pods 事件流。</pre>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div class="readonly-rpc sidebar-note">
|
|
||||||
<h3>复核入口</h3>
|
|
||||||
<p class="panel-copy">技术复核保留在二级页面;默认右栏只显示 Device Pod 摘要和纯文本事件流。</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
<dialog class="device-detail-dialog" id="device-detail-dialog" aria-labelledby="device-detail-title">
|
<dialog class="device-detail-dialog" id="device-detail-dialog" aria-labelledby="device-detail-title">
|
||||||
<form method="dialog" class="device-detail-shell">
|
<form method="dialog" class="device-detail-shell">
|
||||||
|
|||||||
@@ -82,9 +82,12 @@ for (const term of [
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.doesNotMatch(rightSidebar, /<details\b/iu, "right sidebar must not use internal expand/collapse details");
|
assert.doesNotMatch(rightSidebar, /<details\b/iu, "right sidebar must not use internal expand/collapse details");
|
||||||
|
assert.doesNotMatch(rightSidebar, /readonly-rpc|复核入口/iu, "right sidebar must not render the readonly RPC review note");
|
||||||
assert.doesNotMatch(rightSidebar, /data-hardware-tab|m3-control|hardware-list|wiring-body|records-list/iu, "right sidebar must not keep legacy hardware panels");
|
assert.doesNotMatch(rightSidebar, /data-hardware-tab|m3-control|hardware-list|wiring-body|records-list/iu, "right sidebar must not keep legacy hardware panels");
|
||||||
assert.match(rightSidebar, /class="summary-tile"[^>]*role="button"[^>]*tabindex="0"/u, "Pod summary is a click/keyboard summary tile");
|
assert.match(rightSidebar, /class="summary-tile"[^>]*role="button"[^>]*tabindex="0"/u, "Pod summary is a click/keyboard summary tile");
|
||||||
assert.match(rightSidebar, /<dialog\b[\s\S]*id="device-detail-dialog"/u, "summary details must open in a dialog");
|
assert.match(rightSidebar, /<dialog\b[\s\S]*id="device-detail-dialog"/u, "summary details must open in a dialog");
|
||||||
|
assert.match(cssRule(styles, ".right-sidebar"), /grid-auto-rows:\s*max-content;/u, "right sidebar rows must keep Device Pod workspace from being squeezed by the status area");
|
||||||
|
assert.doesNotMatch(cssRule(styles, ".device-pod-workspace"), /minmax\(180px,\s*1fr\)/u, "Device Pod workspace must not use a 1fr event row inside the sidebar scroll column");
|
||||||
|
|
||||||
for (const selector of [
|
for (const selector of [
|
||||||
".device-pod-status",
|
".device-pod-status",
|
||||||
@@ -265,6 +268,14 @@ function sectionById(source, tagName, id) {
|
|||||||
return source.slice(start);
|
return source.slice(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cssRule(source, selector) {
|
||||||
|
const match = new RegExp(`${escapeRegExp(selector)}\\s*\\{`, "u").exec(source);
|
||||||
|
if (!match) return "";
|
||||||
|
const bodyStart = match.index + match[0].length;
|
||||||
|
const bodyEnd = source.indexOf("}", bodyStart);
|
||||||
|
return bodyEnd >= 0 ? source.slice(match.index, bodyEnd + 1) : source.slice(match.index);
|
||||||
|
}
|
||||||
|
|
||||||
function functionBody(source, name) {
|
function functionBody(source, name) {
|
||||||
const match = new RegExp(`function\\s+${escapeRegExp(name)}\\s*\\([^)]*\\)\\s*\\{`, "u").exec(source);
|
const match = new RegExp(`function\\s+${escapeRegExp(name)}\\s*\\([^)]*\\)\\s*\\{`, "u").exec(source);
|
||||||
if (!match) return "";
|
if (!match) return "";
|
||||||
|
|||||||
@@ -1797,7 +1797,7 @@ h3 {
|
|||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto auto auto minmax(0, 1fr);
|
grid-auto-rows: max-content;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 10px 10px 10px 16px;
|
padding: 10px 10px 10px 16px;
|
||||||
background: rgba(18, 20, 18, 0.98);
|
background: rgba(18, 20, 18, 0.98);
|
||||||
@@ -2465,7 +2465,7 @@ tbody tr:last-child td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.device-pod-workspace {
|
.device-pod-workspace {
|
||||||
grid-template-rows: auto minmax(180px, 1fr) auto;
|
grid-template-rows: auto auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-pod-selector-row {
|
.device-pod-selector-row {
|
||||||
@@ -2567,9 +2567,9 @@ tbody tr:last-child td {
|
|||||||
|
|
||||||
.device-event-panel {
|
.device-event-panel {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 180px;
|
min-height: 240px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto minmax(0, 1fr);
|
grid-template-rows: auto minmax(180px, 1fr);
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
background: #101310;
|
background: #101310;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user