fix(hwlab): extract web-probe diagnostic summaries (#545)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-21 09:29:11 +08:00
committed by GitHub
parent ac4de2c1d5
commit 0758f1ecb2
@@ -568,6 +568,38 @@ async function samplePage(reason) {
return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none";
};
const textHashInput = (element) => trim(element.textContent || "", 800);
const diagnosticSummaryText = (element) => {
const summarySelectors = [
".api-error-diagnostic-summary-text p",
".api-error-diagnostic-summary-text",
"[class*='diagnostic-summary-text' i] p",
"[class*='diagnostic-summary-text' i]",
"[data-testid*='diagnostic-summary' i]",
"[data-testid*='error-summary' i]",
"[role='alert'] p",
"[role='alert']"
];
const parts = [];
for (const selector of summarySelectors) {
for (const candidate of Array.from(element.querySelectorAll(selector))) {
if (!visible(candidate)) continue;
const text = trim(candidate.textContent || "", 800);
if (text && !parts.includes(text)) parts.push(text);
}
}
const ownText = textHashInput(element);
const text = parts.length > 0 ? parts.join(" ") : ownText;
return text.replace(/\s+(?:!|i诊断|诊断详情)$/u, "").trim();
};
const diagnosticToggleOnly = (element, text) => {
const compact = String(text || "").trim();
if (!/^(?:!|i诊断|诊断|诊断详情)$/u.test(compact)) return false;
const tag = element.tagName.toLowerCase();
const role = element.getAttribute("role");
const aria = element.getAttribute("aria-label") || "";
const title = element.getAttribute("title") || "";
return tag === "button" || role === "button" || /诊断/u.test(aria) || /诊断/u.test(title);
};
const summarize = (selector, limit) => Array.from(document.querySelectorAll(selector)).filter(visible).slice(-limit).map((element, index) => {
const rect = element.getBoundingClientRect();
return {
@@ -591,10 +623,13 @@ async function samplePage(reason) {
const traceRows = summarize(traceSelector, 30);
const diagnostics = Array.from(document.querySelectorAll(diagnosticSelector)).filter(visible).slice(-40).map((element, index) => {
const rect = element.getBoundingClientRect();
const text = textHashInput(element);
const text = diagnosticSummaryText(element);
if (!text || diagnosticToggleOnly(element, text)) return null;
const traceMatch = text.match(/\b(?:trace_id=)?(trc_[A-Za-z0-9_-]+|[a-f0-9]{16,64})\b/iu);
const httpStatusMatch = text.match(/\bHTTP\s+([1-5][0-9]{2})\b/iu);
const idleMatch = text.match(/\bidle\s+(\d+)s\b/iu);
const waitingForMatch = text.match(/\bwaitingFor=([^\s;,)]+)/iu);
const lastEventLabelMatch = text.match(/\blastEventLabel=([^\s;,)]+)/iu);
const diagnosticCode = httpStatusMatch ? "http-" + httpStatusMatch[1] : /turn\s*超过|无新活动/iu.test(text) ? "turn-idle-no-activity" : /Failed to fetch/iu.test(text) ? "failed-to-fetch" : "diagnostic";
return {
index,
@@ -609,10 +644,12 @@ async function samplePage(reason) {
traceId: traceMatch?.[1] || null,
httpStatus: httpStatusMatch ? Number(httpStatusMatch[1]) : null,
idleSeconds: idleMatch ? Number(idleMatch[1]) : null,
waitingFor: waitingForMatch?.[1] || null,
lastEventLabel: lastEventLabelMatch?.[1] || null,
text,
rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
};
});
}).filter(Boolean);
const turns = Array.from(document.querySelectorAll('article.message-card[data-role="agent"], .message-card[data-role="agent"], article[data-role="agent"]')).filter(visible).map((element, index) => {
const rect = element.getBoundingClientRect();
const text = textHashInput(element);
@@ -1007,6 +1044,30 @@ function buildPromptNetworkReport(control, network) {
};
}
function parseDomDiagnosticSummary(text) {
const value = String(text || "");
const traceMatch = value.match(/\b(?:trace_id=)?(trc_[A-Za-z0-9_-]+|[a-f0-9]{16,64})\b/iu);
const httpStatusMatch = value.match(/\bHTTP\s+([1-5][0-9]{2})\b/iu);
const idleMatch = value.match(/\bidle\s+(\d+)s\b/iu);
const waitingForMatch = value.match(/\bwaitingFor=([^\s;,)]+)/iu);
const lastEventLabelMatch = value.match(/\blastEventLabel=([^\s;,)]+)/iu);
const diagnosticCode = httpStatusMatch
? "http-" + httpStatusMatch[1]
: /turn\s*超过|无新活动/iu.test(value)
? "turn-idle-no-activity"
: /Failed to fetch/iu.test(value)
? "failed-to-fetch"
: "diagnostic";
return {
diagnosticCode,
traceId: traceMatch?.[1] || null,
httpStatus: httpStatusMatch ? Number(httpStatusMatch[1]) : null,
idleSeconds: idleMatch ? Number(idleMatch[1]) : null,
waitingFor: waitingForMatch?.[1] || null,
lastEventLabel: lastEventLabelMatch?.[1] || null
};
}
function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) {
const promptTimes = control
.filter((item) => item.type === "sendPrompt" && item.phase === "completed")
@@ -1032,16 +1093,19 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) {
for (const diagnostic of sample.diagnostics.slice(0, 12)) {
const text = diagnostic?.textPreview || diagnostic?.text || "";
if (!String(text).trim()) continue;
const parsedDiagnostic = parseDomDiagnosticSummary(text);
domDiagnostics.push({
seq: sample.seq ?? null,
ts: sample.ts ?? null,
promptIndex,
source: "diagnostic-node",
className: diagnostic.className ?? null,
diagnosticCode: diagnostic.diagnosticCode ?? null,
traceId: diagnostic.traceId ?? null,
httpStatus: diagnostic.httpStatus ?? null,
idleSeconds: diagnostic.idleSeconds ?? null,
diagnosticCode: diagnostic.diagnosticCode ?? parsedDiagnostic.diagnosticCode,
traceId: diagnostic.traceId ?? parsedDiagnostic.traceId,
httpStatus: diagnostic.httpStatus ?? parsedDiagnostic.httpStatus,
idleSeconds: diagnostic.idleSeconds ?? parsedDiagnostic.idleSeconds,
waitingFor: diagnostic.waitingFor ?? parsedDiagnostic.waitingFor,
lastEventLabel: diagnostic.lastEventLabel ?? parsedDiagnostic.lastEventLabel,
compact: diagnostic.compact ?? null,
expanded: diagnostic.expanded ?? null,
routeSessionId: sample.routeSessionId ?? null,
@@ -1053,11 +1117,18 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) {
}
const texts = sampleTexts(sample).filter(isDiagnosticText);
for (const text of texts.slice(0, 4)) {
const parsedDiagnostic = parseDomDiagnosticSummary(text);
domDiagnostics.push({
seq: sample.seq ?? null,
ts: sample.ts ?? null,
promptIndex,
source: "sample-text",
diagnosticCode: parsedDiagnostic.diagnosticCode,
traceId: parsedDiagnostic.traceId,
httpStatus: parsedDiagnostic.httpStatus,
idleSeconds: parsedDiagnostic.idleSeconds,
waitingFor: parsedDiagnostic.waitingFor,
lastEventLabel: parsedDiagnostic.lastEventLabel,
routeSessionId: sample.routeSessionId ?? null,
activeSessionId: sample.activeSessionId ?? null,
textHash: sha256(text),
@@ -1941,7 +2012,7 @@ function renderMarkdown(report) {
? report.runtimeAlerts.networkRequestFailedByPath.slice(0, 40).map((item) => "- requestfailed " + item.method + " " + item.urlPath + " count=" + item.count + " failure=" + (item.failureKinds?.slice(0, 4).join(",") || "-") + " prompts=" + (item.promptIndexes?.join(",") || "-") + " first=" + (item.firstAt || "-") + " last=" + (item.lastAt || "-")).join("\n")
: "- 无 requestfailed。";
const domDiagnosticLines = Array.isArray(report.runtimeAlerts?.domDiagnostics) && report.runtimeAlerts.domDiagnostics.length > 0
? report.runtimeAlerts.domDiagnostics.slice(0, 40).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " source=" + (item.source || "-") + " code=" + (item.diagnosticCode || "-") + " traceId=" + (item.traceId || "-") + " http=" + (item.httpStatus ?? "-") + " idle=" + (item.idleSeconds ?? "-") + " textHash=" + (item.textHash || "-") + " preview=" + escapeMarkdownCell(item.preview || "")).join("\n")
? report.runtimeAlerts.domDiagnostics.slice(0, 40).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " source=" + (item.source || "-") + " code=" + (item.diagnosticCode || "-") + " traceId=" + (item.traceId || "-") + " http=" + (item.httpStatus ?? "-") + " idle=" + (item.idleSeconds ?? "-") + " waitingFor=" + (item.waitingFor || "-") + " lastEventLabel=" + (item.lastEventLabel || "-") + " textHash=" + (item.textHash || "-") + " preview=" + escapeMarkdownCell(item.preview || "")).join("\n")
: "- 无 DOM 诊断文本。";
const consoleAlertLines = Array.isArray(report.runtimeAlerts?.consoleAlerts) && report.runtimeAlerts.consoleAlerts.length > 0
? report.runtimeAlerts.consoleAlerts.slice(0, 40).map((item) => "- " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " type=" + (item.type || "-") + " status=" + (item.status ?? "-") + " path=" + (item.urlPath || "-") + " traceId=" + (item.traceId || "-") + " textHash=" + (item.textHash || "-") + " preview=" + escapeMarkdownCell(item.preview || "")).join("\n")