fix: expose full-window web probe timing anomalies (#651)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -7602,17 +7602,21 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
"const takeHead = (value, limit) => Array.isArray(value) ? value.slice(0, limit) : [];",
|
||||
"const takeTail = (value, limit) => Array.isArray(value) ? value.slice(-limit) : [];",
|
||||
"const firstArray = (...values) => { for (const value of values) if (Array.isArray(value)) return value; return []; };",
|
||||
"const mergeArrays = (...values) => { const out = []; const seen = new Set(); for (const value of values) { if (!Array.isArray(value)) continue; for (const item of value) { const key = JSON.stringify([item?.id ?? item?.kind ?? item?.code ?? item?.columnLabel ?? item?.traceId ?? null, item?.summary ?? item?.message ?? item?.fromSeq ?? null, item?.toSeq ?? null]); if (seen.has(key)) continue; seen.add(key); out.push(item); } } return out; };",
|
||||
"const clip = (value, limit = 160) => value === null || value === undefined ? null : String(value).slice(0, limit);",
|
||||
"const findingRank = (item) => { const id = String(item?.id ?? item?.kind ?? item?.code ?? ''); if (id === 'page-performance-slow-same-origin-api') return 0; if (id.startsWith('turn-timing-total-elapsed')) return 1; if (id.startsWith('turn-timing-recent-update')) return 2; if (id.includes('runtime-execution') || id.includes('prompt-chat-submit-failed')) return 3; return 10; };",
|
||||
"const severityRank = (item) => { const severity = String(item?.severity ?? item?.level ?? '').toLowerCase(); if (severity === 'red') return 0; if (severity === 'amber' || severity === 'warning') return 1; if (severity === 'info') return 3; return 2; };",
|
||||
"const sortFindings = (items) => (Array.isArray(items) ? items : []).slice().sort((a, b) => findingRank(a) - findingRank(b) || severityRank(a) - severityRank(b));",
|
||||
"const stdoutJson = readJson(stdoutPath);",
|
||||
"const reportJson = readJson(reportJsonPath);",
|
||||
"const source = (stdoutJson && stdoutJson.ok !== false ? stdoutJson : null) || reportJson || stdoutJson || null;",
|
||||
"const fullSource = reportJson || source;",
|
||||
"const objectOrNull = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : null;",
|
||||
"const slimRound = (item) => { const v = objectOrNull(item) || {}; return { promptIndex: v.promptIndex ?? null, sampleCount: v.sampleCount ?? null, lastTotalElapsedSeconds: v.lastTotalElapsedSeconds ?? null, lastRecentUpdateSeconds: v.lastRecentUpdateSeconds ?? null, diagnosticSamples: v.diagnosticSamples ?? null, terminalSamples: v.terminalSamples ?? null, promptTextHash: clip(v.promptTextHash, 80) }; };",
|
||||
"const slimRound = (item) => { const v = objectOrNull(item) || {}; return { promptIndex: v.promptIndex ?? null, sampleCount: v.sampleCount ?? null, lastTotalElapsedSeconds: v.lastTotalElapsedSeconds ?? null, lastRecentUpdateSeconds: v.lastRecentUpdateSeconds ?? null, diagnosticSamples: v.diagnosticSamples ?? null, terminalSamples: v.terminalSamples ?? null, turnTimingTotalElapsedForwardJumpCount: v.turnTimingTotalElapsedForwardJumpCount ?? null, turnTimingTotalElapsedForwardJumpMaxSeconds: v.turnTimingTotalElapsedForwardJumpMaxSeconds ?? null, promptTextHash: clip(v.promptTextHash, 80) }; };",
|
||||
"const slimTurnColumn = (item) => { const v = objectOrNull(item) || {}; return { label: clip(v.label, 24), promptIndex: v.promptIndex ?? null, lastPromptIndex: v.lastPromptIndex ?? null, traceId: clip(v.traceId, 48), firstSeq: v.firstSeq ?? null, lastSeq: v.lastSeq ?? null, source: clip(v.source, 48) }; };",
|
||||
"const slimSlowSample = (item) => { const v = objectOrNull(item) || {}; return { ts: v.ts ?? null, seq: v.seq ?? null, path: clip(v.path ?? v.rawPath, 96), initiatorType: clip(v.initiatorType, 24), durationMs: v.durationMs ?? null, requestToResponseStartMs: v.requestToResponseStartMs ?? v.streamOpenMs ?? null, responseTransferMs: v.responseTransferMs ?? null, nextHopProtocol: clip(v.nextHopProtocol, 24) }; };",
|
||||
"const slimSlowApi = (item) => { const v = objectOrNull(item) || {}; return { path: clip(v.path ?? v.route, 96), route: clip(v.route ?? v.path, 96), sampleCount: v.sampleCount ?? null, p95Ms: v.p95Ms ?? v.p95 ?? null, maxMs: v.maxMs ?? v.max ?? null, overFiveSecondCount: v.overFiveSecondCount ?? null, slowSamples: Array.isArray(v.slowSamples) ? v.slowSamples.slice(0, 3).map(slimSlowSample) : [] }; };",
|
||||
"const slimFinding = (item) => { const v = objectOrNull(item) || {}; return { kind: clip(v.kind ?? v.code, 48), code: clip(v.code ?? v.kind, 48), severity: clip(v.severity ?? v.level, 24), level: clip(v.level ?? v.severity, 24), count: v.count ?? v.sampleCount ?? null, sampleCount: v.sampleCount ?? v.count ?? null, summary: clip(v.summary ?? v.message, 180), message: clip(v.message ?? v.summary, 180) }; };",
|
||||
"const slimFinding = (item) => { const v = objectOrNull(item) || {}; return { kind: clip(v.kind ?? v.id ?? v.code, 48), code: clip(v.code ?? v.id ?? v.kind, 48), severity: clip(v.severity ?? v.level, 24), level: clip(v.level ?? v.severity, 24), count: v.count ?? v.sampleCount ?? null, sampleCount: v.sampleCount ?? v.count ?? null, summary: clip(v.summary ?? v.message, 180), message: clip(v.message ?? v.summary, 180) }; };",
|
||||
"const slimDomGroup = (item) => { const v = objectOrNull(item) || {}; return { count: v.count ?? null, firstAt: v.firstAt ?? null, lastAt: v.lastAt ?? null, text: clip(v.text ?? v.preview, 180) }; };",
|
||||
"const slimNetworkGroup = (item) => { const v = objectOrNull(item) || {}; return { count: v.count ?? null, method: clip(v.method, 12), status: v.status ?? null, path: clip(v.path, 96), firstAt: v.firstAt ?? null, lastAt: v.lastAt ?? null, promptIndexes: Array.isArray(v.promptIndexes) ? v.promptIndexes.slice(0, 6) : [], failureKinds: Array.isArray(v.failureKinds) ? v.failureKinds.slice(0, 4).map((x) => clip(x, 48)) : [] }; };",
|
||||
"const slimDomSample = (item) => { const v = objectOrNull(item) || {}; return { seq: v.seq ?? null, ts: v.ts ?? null, source: clip(v.source, 32), diagnosticCode: clip(v.diagnosticCode, 48), traceId: clip(v.traceId, 64), httpStatus: v.httpStatus ?? null, idleSeconds: v.idleSeconds ?? null, waitingFor: clip(v.waitingFor, 48), lastEventLabel: clip(v.lastEventLabel, 80), text: clip(v.text ?? v.preview, 180) }; };",
|
||||
@@ -7628,6 +7632,8 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" turnTimingRows: Array.isArray(srcMetrics.turnTimingRows) ? srcMetrics.turnTimingRows.length : (srcMetrics.turnTimingRows ?? null),",
|
||||
" turnTimingNonMonotonicCount: srcMetrics.turnTimingNonMonotonicCount ?? null,",
|
||||
" turnTimingTotalElapsedDecreaseCount: srcMetrics.turnTimingTotalElapsedDecreaseCount ?? null,",
|
||||
" turnTimingTotalElapsedForwardJumpCount: srcMetrics.turnTimingTotalElapsedForwardJumpCount ?? null,",
|
||||
" turnTimingTotalElapsedForwardJumpMaxSeconds: srcMetrics.turnTimingTotalElapsedForwardJumpMaxSeconds ?? null,",
|
||||
" turnTimingTerminalElapsedGrowthCount: srcMetrics.turnTimingTerminalElapsedGrowthCount ?? null,",
|
||||
" turnTimingRecentUpdateJumpCount: srcMetrics.turnTimingRecentUpdateJumpCount ?? null,",
|
||||
" turnTimingRecentUpdateSawtoothJumpCount: srcMetrics.turnTimingRecentUpdateSawtoothJumpCount ?? null,",
|
||||
@@ -7642,6 +7648,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
"const fullRecentWindow = objectOrNull(fullSource?.windows?.recent);",
|
||||
"const fullPagePerformance = objectOrNull(fullRecentWindow?.pagePerformance) || objectOrNull(fullSource?.pagePerformance);",
|
||||
"const sourceSlowApi = Array.isArray(source?.pagePerformanceSlowApi) && source.pagePerformanceSlowApi.some((item) => Array.isArray(item?.slowSamples) && item.slowSamples.length > 0) ? source.pagePerformanceSlowApi : (Array.isArray(fullPagePerformance?.sameOriginApiByPath) ? fullPagePerformance.sameOriginApiByPath.filter((item) => Number(item?.overFiveSecondCount ?? 0) > 0) : []);",
|
||||
"const combinedFindings = sortFindings(mergeArrays(source?.findings, fullSource?.findings));",
|
||||
"const srcPromptNetwork = objectOrNull(source?.promptNetwork);",
|
||||
"const promptNetwork = srcPromptNetwork ? { promptSegments: srcPromptNetwork.promptSegments ?? null } : null;",
|
||||
"const compact = source ? {",
|
||||
@@ -7654,7 +7661,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" pagePerformance,",
|
||||
" promptNetwork,",
|
||||
" pagePerformanceSlowApi: takeHead(sourceSlowApi, 4).map(slimSlowApi),",
|
||||
" findings: takeHead(firstArray(source.findings, fullSource?.findings), 6).map(slimFinding),",
|
||||
" findings: takeHead(combinedFindings, 8).map(slimFinding),",
|
||||
" httpErrorGroups: takeHead(firstArray(source.httpErrorGroups, fullSource?.httpErrorGroups), 4).map(slimNetworkGroup),",
|
||||
" requestFailedGroups: takeHead(firstArray(source.requestFailedGroups, fullSource?.requestFailedGroups), 5).map(slimNetworkGroup),",
|
||||
" domDiagnosticGroups: takeHead(firstArray(source.domDiagnosticGroups, fullSource?.domDiagnosticGroups), 3).map(slimDomGroup),",
|
||||
@@ -7662,8 +7669,9 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" consoleAlertGroups: takeHead(firstArray(source.consoleAlertGroups, fullSource?.consoleAlertGroups), 5).map(slimConsoleGroup),",
|
||||
" consoleAlertSamples: takeHead(firstArray(source.consoleAlertSamples, fullSource?.consoleAlertSamples), 5).map(slimConsoleSample),",
|
||||
" runnerErrors: takeTail(firstArray(source.runnerErrors, fullSource?.runnerErrors), 8).map(slimRunnerError),",
|
||||
" turnTimingRecentUpdateJumps: takeHead(firstArray(source.turnTimingRecentUpdateJumps, srcMetrics?.turnTimingRecentUpdateJumps, fullSource?.turnTimingRecentUpdateJumps), 5).map(slimJump),",
|
||||
" turnTimingElapsedZeroResets: takeHead(firstArray(source.turnTimingElapsedZeroResets, srcMetrics?.turnTimingElapsedZeroResets, fullSource?.turnTimingElapsedZeroResets), 5).map(slimJump),",
|
||||
" turnTimingRecentUpdateJumps: takeHead(mergeArrays(source.turnTimingRecentUpdateJumps, srcMetrics?.turnTimingRecentUpdateJumps, fullSource?.sampleMetrics?.turnTimingRecentUpdateSawtoothJumps, fullSource?.turnTimingRecentUpdateJumps), 5).map(slimJump),",
|
||||
" turnTimingElapsedZeroResets: takeHead(mergeArrays(source.turnTimingElapsedZeroResets, srcMetrics?.turnTimingElapsedZeroResets, fullSource?.sampleMetrics?.turnTimingElapsedZeroResets, fullSource?.turnTimingElapsedZeroResets), 5).map(slimJump),",
|
||||
" turnTimingTotalElapsedForwardJumps: takeHead(mergeArrays(source.turnTimingTotalElapsedForwardJumps, srcMetrics?.turnTimingTotalElapsedForwardJumps, fullSource?.sampleMetrics?.turnTimingTotalElapsedForwardJumps, fullSource?.turnTimingTotalElapsedForwardJumps), 5).map(slimJump),",
|
||||
" reportJsonPath: source.reportJsonPath || reportJsonPath,",
|
||||
" reportJsonSha256: source.reportJsonSha256 || sha256(reportJsonPath),",
|
||||
" reportMdPath: source.reportMdPath || reportMdPath,",
|
||||
@@ -7688,7 +7696,47 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" stderrTail: tail(readText(stderrPath))",
|
||||
" }",
|
||||
"};",
|
||||
"console.log(JSON.stringify(compact));",
|
||||
"const compactStdoutLimitBytes = 8000;",
|
||||
"const compactOutput = (value) => JSON.stringify(value);",
|
||||
"let output = compactOutput(compact);",
|
||||
"if (Buffer.byteLength(output, 'utf8') > compactStdoutLimitBytes) {",
|
||||
" const minimal = {",
|
||||
" ok: compact.ok,",
|
||||
" counts: compact.counts ?? null,",
|
||||
" analysisWindow: compact.analysisWindow ?? null,",
|
||||
" archiveSummary: compact.archiveSummary ?? null,",
|
||||
" sampleMetrics: compact.sampleMetrics ? {",
|
||||
" rounds: Array.isArray(compact.sampleMetrics.rounds) ? compact.sampleMetrics.rounds : compact.sampleMetrics.rounds ?? null,",
|
||||
" turnTimingRows: compact.sampleMetrics.turnTimingRows ?? null,",
|
||||
" turnTimingNonMonotonicCount: compact.sampleMetrics.turnTimingNonMonotonicCount ?? null,",
|
||||
" turnTimingTotalElapsedDecreaseCount: compact.sampleMetrics.turnTimingTotalElapsedDecreaseCount ?? null,",
|
||||
" turnTimingTotalElapsedForwardJumpCount: compact.sampleMetrics.turnTimingTotalElapsedForwardJumpCount ?? null,",
|
||||
" turnTimingTotalElapsedForwardJumpMaxSeconds: compact.sampleMetrics.turnTimingTotalElapsedForwardJumpMaxSeconds ?? null,",
|
||||
" turnTimingTerminalElapsedGrowthCount: compact.sampleMetrics.turnTimingTerminalElapsedGrowthCount ?? null,",
|
||||
" turnTimingRecentUpdateJumpCount: compact.sampleMetrics.turnTimingRecentUpdateJumpCount ?? null,",
|
||||
" turnTimingRecentUpdateSawtoothJumpCount: compact.sampleMetrics.turnTimingRecentUpdateSawtoothJumpCount ?? null,",
|
||||
" turnTimingRecentUpdateMaxIncreaseSeconds: compact.sampleMetrics.turnTimingRecentUpdateMaxIncreaseSeconds ?? null,",
|
||||
" turnColumns: Array.isArray(compact.sampleMetrics.turnColumns) ? compact.sampleMetrics.turnColumns.slice(-6) : []",
|
||||
" } : null,",
|
||||
" runtimeAlerts: compact.runtimeAlerts ?? null,",
|
||||
" pagePerformance: compact.pagePerformance ?? null,",
|
||||
" promptNetwork: compact.promptNetwork ?? null,",
|
||||
" findings: Array.isArray(compact.findings) ? compact.findings.slice(0, 4) : [],",
|
||||
" pagePerformanceSlowApi: Array.isArray(compact.pagePerformanceSlowApi) ? compact.pagePerformanceSlowApi.slice(0, 3) : [],",
|
||||
" runnerErrors: Array.isArray(compact.runnerErrors) ? compact.runnerErrors.slice(-4) : [],",
|
||||
" turnTimingRecentUpdateJumps: Array.isArray(compact.turnTimingRecentUpdateJumps) ? compact.turnTimingRecentUpdateJumps.slice(0, 4) : [],",
|
||||
" turnTimingElapsedZeroResets: Array.isArray(compact.turnTimingElapsedZeroResets) ? compact.turnTimingElapsedZeroResets.slice(0, 4) : [],",
|
||||
" turnTimingTotalElapsedForwardJumps: Array.isArray(compact.turnTimingTotalElapsedForwardJumps) ? compact.turnTimingTotalElapsedForwardJumps.slice(0, 4) : [],",
|
||||
" reportJsonPath: compact.reportJsonPath ?? reportJsonPath,",
|
||||
" reportJsonSha256: compact.reportJsonSha256 ?? sha256(reportJsonPath),",
|
||||
" reportMdPath: compact.reportMdPath ?? reportMdPath,",
|
||||
" reportMdSha256: compact.reportMdSha256 ?? sha256(reportMdPath),",
|
||||
" analyzer: { ...(compact.analyzer ?? {}), compactStdoutLimited: true, compactStdoutLimitBytes, originalCompactBytes: Buffer.byteLength(output, 'utf8') },",
|
||||
" valuesRedacted: true",
|
||||
" };",
|
||||
" output = compactOutput(minimal);",
|
||||
"}",
|
||||
"console.log(output);",
|
||||
"UNIDESK_WEB_OBSERVE_ANALYZE_COMPACT",
|
||||
"exit \"$analyzer_exit\"",
|
||||
].join("\n");
|
||||
@@ -7725,6 +7773,7 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
const analysisWindow = record(analysis?.analysisWindow);
|
||||
const archiveSummary = record(analysis?.archiveSummary);
|
||||
const sampleMetrics = record(analysis?.sampleMetrics);
|
||||
const archiveMetrics = record(archiveSummary?.sampleMetrics);
|
||||
const runtimeAlerts = record(analysis?.runtimeAlerts);
|
||||
const pagePerformance = record(analysis?.pagePerformance);
|
||||
const promptNetwork = record(analysis?.promptNetwork);
|
||||
@@ -7743,6 +7792,7 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
const runnerErrors = Array.isArray(analysis?.runnerErrors) ? analysis.runnerErrors.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(-8) : [];
|
||||
const recentUpdateJumps = Array.isArray(analysis?.turnTimingRecentUpdateJumps) ? analysis.turnTimingRecentUpdateJumps.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
const elapsedZeroResets = Array.isArray(analysis?.turnTimingElapsedZeroResets) ? analysis.turnTimingElapsedZeroResets.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
const elapsedForwardJumps = Array.isArray(analysis?.turnTimingTotalElapsedForwardJumps) ? analysis.turnTimingTotalElapsedForwardJumps.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
const domDiagnosticRows = domDiagnostics.length > 0
|
||||
? domDiagnostics.map((item) => [
|
||||
webObserveText(item.count),
|
||||
@@ -7801,6 +7851,17 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
webObserveText(item.delta),
|
||||
webObserveShort(webObserveText(item.traceId), 24),
|
||||
]);
|
||||
const elapsedForwardJumpRows = elapsedForwardJumps.map((item) => [
|
||||
webObserveShort(webObserveText(item.columnLabel), 14),
|
||||
webObserveText(item.promptIndex),
|
||||
webObserveText(item.fromSeq),
|
||||
webObserveText(item.toSeq),
|
||||
`${webObserveText(item.fromValue)}->${webObserveText(item.toValue)}`,
|
||||
webObserveText(item.delta),
|
||||
webObserveText(item.sampleDeltaSeconds),
|
||||
webObserveText(item.allowedIncreaseSeconds),
|
||||
webObserveShort(webObserveText(item.traceId), 24),
|
||||
]);
|
||||
const httpErrorRows = httpErrorGroups.map((item) => [
|
||||
webObserveText(item.count),
|
||||
webObserveText(item.method),
|
||||
@@ -7866,18 +7927,34 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
]),
|
||||
"",
|
||||
"Turn timing:",
|
||||
webObserveTable(["ROUNDS", "TURNS", "ROWS", "NON_MONO", "ELAPSED_DEC", "ZERO_RESET", "TERMINAL_GROWTH", "RECENT_JUMP", "MAX_RECENT_STEP"], [[
|
||||
webObserveTable(["ROUNDS", "TURNS", "ROWS", "NON_MONO", "ELAPSED_DEC", "ZERO_RESET", "ELAPSED_JUMP", "TERMINAL_GROWTH", "RECENT_JUMP", "MAX_RECENT_STEP"], [[
|
||||
webObserveText(roundCount),
|
||||
webObserveText(turnColumnCount),
|
||||
webObserveText(sampleMetrics?.turnTimingRows),
|
||||
webObserveText(sampleMetrics?.turnTimingNonMonotonicCount),
|
||||
webObserveText(sampleMetrics?.turnTimingTotalElapsedDecreaseCount),
|
||||
webObserveText(sampleMetrics?.turnTimingTotalElapsedZeroResetCount ?? elapsedZeroResets.length),
|
||||
webObserveText(sampleMetrics?.turnTimingTotalElapsedForwardJumpCount ?? elapsedForwardJumps.length),
|
||||
webObserveText(sampleMetrics?.turnTimingTerminalElapsedGrowthCount),
|
||||
webObserveText(sampleMetrics?.turnTimingRecentUpdateJumpCount ?? sampleMetrics?.turnTimingRecentUpdateSawtoothJumpCount),
|
||||
webObserveText(sampleMetrics?.turnTimingRecentUpdateMaxIncreaseSeconds),
|
||||
]]),
|
||||
"",
|
||||
"Archive turn timing:",
|
||||
webObserveTable(["SAMPLES", "ROUNDS", "TURNS", "ROWS", "NON_MONO", "ELAPSED_DEC", "ZERO_RESET", "ELAPSED_JUMP", "TERMINAL_GROWTH", "RECENT_JUMP", "MAX_RECENT_STEP"], [[
|
||||
webObserveText(archiveMetrics?.sampleCount),
|
||||
webObserveText(archiveMetrics?.rounds),
|
||||
webObserveText(archiveMetrics?.turnColumns),
|
||||
webObserveText(archiveMetrics?.turnTimingRows),
|
||||
webObserveText(archiveMetrics?.turnTimingNonMonotonicCount),
|
||||
webObserveText(archiveMetrics?.turnTimingTotalElapsedDecreaseCount),
|
||||
webObserveText(archiveMetrics?.turnTimingTotalElapsedZeroResetCount),
|
||||
webObserveText(archiveMetrics?.turnTimingTotalElapsedForwardJumpCount),
|
||||
webObserveText(archiveMetrics?.turnTimingTerminalElapsedGrowthCount),
|
||||
webObserveText(archiveMetrics?.turnTimingRecentUpdateJumpCount ?? archiveMetrics?.turnTimingRecentUpdateSawtoothJumpCount),
|
||||
webObserveText(archiveMetrics?.turnTimingRecentUpdateMaxIncreaseSeconds),
|
||||
]]),
|
||||
"",
|
||||
"Rounds:",
|
||||
webObserveTable(["ROUND", "SAMPLES", "TOTAL_LAST", "RECENT_LAST", "DIAG", "TERMINAL", "PROMPT_HASH"], rounds.length > 0 ? rounds.map((item) => [
|
||||
webObserveText(item.promptIndex),
|
||||
@@ -7905,6 +7982,9 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
"Total elapsed zero resets:",
|
||||
webObserveTable(["TURN", "PROMPT", "FROM_SEQ", "TO_SEQ", "VALUE", "DELTA", "TRACE"], elapsedZeroResetRows.length > 0 ? elapsedZeroResetRows : [["-", "-", "-", "-", "-", "-", "-"]]),
|
||||
"",
|
||||
"Total elapsed forward jumps:",
|
||||
webObserveTable(["TURN", "PROMPT", "FROM_SEQ", "TO_SEQ", "VALUE", "DELTA", "SAMPLE_S", "ALLOWED", "TRACE"], elapsedForwardJumpRows.length > 0 ? elapsedForwardJumpRows : [["-", "-", "-", "-", "-", "-", "-", "-", "-"]]),
|
||||
"",
|
||||
"Runtime alerts:",
|
||||
webObserveTable(["HTTP_ERR", "REQUEST_FAILED", "DOM_DIAG", "CONSOLE_ALERT", "PROMPT_SEGMENTS", "PROMPT_NETWORK"], [[
|
||||
webObserveText(runtimeAlerts?.httpErrorCount),
|
||||
@@ -8372,6 +8452,8 @@ function renderWebObserveAnalyzeResult(result: Record<string, unknown>): Record<
|
||||
[
|
||||
["nonMonotonic", sampleMetrics.turnTimingNonMonotonicCount],
|
||||
["elapsedDecrease", sampleMetrics.turnTimingTotalElapsedDecreaseCount],
|
||||
["elapsedForwardJump", sampleMetrics.turnTimingTotalElapsedForwardJumpCount],
|
||||
["elapsedForwardJumpMaxSec", sampleMetrics.turnTimingTotalElapsedForwardJumpMaxSeconds],
|
||||
["recentJump", sampleMetrics.turnTimingRecentUpdateJumpCount],
|
||||
["maxRecentStepSec", sampleMetrics.turnTimingRecentUpdateMaxIncreaseSeconds],
|
||||
],
|
||||
@@ -8380,7 +8462,7 @@ function renderWebObserveAnalyzeResult(result: Record<string, unknown>): Record<
|
||||
rounds.length === 0
|
||||
? "ROUNDS\n-"
|
||||
: webObserveTable(
|
||||
["ROUND", "COMMAND", "SAMPLES", "TOTAL_MAX", "TOTAL_LAST", "RECENT_MAX", "RECENT_LAST", "DIAG", "TERM", "NONMONO", "JUMP"],
|
||||
["ROUND", "COMMAND", "SAMPLES", "TOTAL_MAX", "TOTAL_LAST", "RECENT_MAX", "RECENT_LAST", "DIAG", "TERM", "NONMONO", "ELAPSED_JUMP", "JUMP"],
|
||||
rounds.map((item) => [
|
||||
item.promptIndex,
|
||||
item.promptCommandId,
|
||||
@@ -8392,6 +8474,7 @@ function renderWebObserveAnalyzeResult(result: Record<string, unknown>): Record<
|
||||
item.diagnosticSamples,
|
||||
item.terminalSamples,
|
||||
item.turnTimingNonMonotonicCount,
|
||||
item.turnTimingTotalElapsedForwardJumpCount,
|
||||
item.turnTimingRecentUpdateJumpCount,
|
||||
]),
|
||||
),
|
||||
|
||||
@@ -1308,7 +1308,7 @@ console.log(JSON.stringify({
|
||||
readIssues: jsonlReadIssues.slice(0, 3).map((item) => ({ file: item.file, line: item.line ?? null, error: String(item.error ?? "").slice(0, 160) })),
|
||||
sampleMetrics: {
|
||||
...recentWindow.sampleMetrics.summary,
|
||||
rounds: recentWindow.sampleMetrics.rounds.slice(-8).map((item) => ({ promptIndex: item.promptIndex, promptTextHash: item.promptTextHash, sampleCount: item.sampleCount, firstSeq: item.firstSeq, lastSeq: item.lastSeq, lastTotalElapsedSeconds: item.lastTotalElapsedSeconds, lastRecentUpdateSeconds: item.lastRecentUpdateSeconds, diagnosticSamples: item.diagnosticSamples, terminalSamples: item.terminalSamples, finalTextSamples: item.finalTextSamples, turnTimingTotalElapsedZeroResetCount: item.turnTimingTotalElapsedZeroResetCount, turnTimingRecentUpdateJumpCount: item.turnTimingRecentUpdateJumpCount, turnTimingRecentUpdateMaxIncreaseSeconds: item.turnTimingRecentUpdateMaxIncreaseSeconds })),
|
||||
rounds: recentWindow.sampleMetrics.rounds.slice(-8).map((item) => ({ promptIndex: item.promptIndex, promptTextHash: item.promptTextHash, sampleCount: item.sampleCount, firstSeq: item.firstSeq, lastSeq: item.lastSeq, lastTotalElapsedSeconds: item.lastTotalElapsedSeconds, lastRecentUpdateSeconds: item.lastRecentUpdateSeconds, diagnosticSamples: item.diagnosticSamples, terminalSamples: item.terminalSamples, finalTextSamples: item.finalTextSamples, turnTimingTotalElapsedZeroResetCount: item.turnTimingTotalElapsedZeroResetCount, turnTimingTotalElapsedForwardJumpCount: item.turnTimingTotalElapsedForwardJumpCount, turnTimingTotalElapsedForwardJumpMaxSeconds: item.turnTimingTotalElapsedForwardJumpMaxSeconds, turnTimingRecentUpdateJumpCount: item.turnTimingRecentUpdateJumpCount, turnTimingRecentUpdateMaxIncreaseSeconds: item.turnTimingRecentUpdateMaxIncreaseSeconds })),
|
||||
turnColumns: recentWindow.sampleMetrics.turnColumns.slice(-12).map((item) => ({ label: item.label, source: item.source, promptIndex: item.promptIndex, lastPromptIndex: item.lastPromptIndex, firstSeq: item.firstSeq, lastSeq: item.lastSeq, traceId: item.traceId, messageId: item.messageId })),
|
||||
},
|
||||
pageProvenance: recentWindow.pageProvenance.summary,
|
||||
@@ -1405,8 +1405,24 @@ console.log(JSON.stringify({
|
||||
delta: item.delta ?? null,
|
||||
anomaly: item.anomaly ?? null,
|
||||
})),
|
||||
pagePerformanceSlowApi: recentWindow.pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, p95Ms: item.p95Ms, maxMs: item.maxMs, overFiveSecondCount: item.overFiveSecondCount })),
|
||||
findings: recentWindow.findings.slice(0, 8).map((item) => ({ kind: item.id ?? item.kind ?? item.code, severity: item.severity, count: item.count ?? item.sampleCount ?? null, summary: String(item.summary ?? item.message ?? "").slice(0, 180) })),
|
||||
turnTimingTotalElapsedForwardJumps: recentWindow.sampleMetrics.turnTimingTotalElapsedForwardJumps.slice(0, 8).map((item) => ({
|
||||
columnLabel: item.columnLabel ?? item.columnId ?? null,
|
||||
promptIndex: item.promptIndex ?? null,
|
||||
traceId: item.traceId ?? null,
|
||||
fromSeq: item.fromSeq ?? null,
|
||||
toSeq: item.toSeq ?? null,
|
||||
fromTs: item.fromTs ?? null,
|
||||
toTs: item.toTs ?? null,
|
||||
fromValue: item.fromValue ?? null,
|
||||
toValue: item.toValue ?? null,
|
||||
delta: item.delta ?? null,
|
||||
sampleDeltaSeconds: item.sampleDeltaSeconds ?? null,
|
||||
allowedIncreaseSeconds: item.allowedIncreaseSeconds ?? null,
|
||||
excessiveIncreaseSeconds: item.excessiveIncreaseSeconds ?? null,
|
||||
anomaly: item.anomaly ?? null,
|
||||
})),
|
||||
pagePerformanceSlowApi: recentWindow.pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, p95Ms: item.p95Ms, maxMs: item.maxMs, overFiveSecondCount: item.overFiveSecondCount, slowSamples: item.slowSamples })),
|
||||
findings: prioritizeFindings(recentWindow.findings).slice(0, 8).map((item) => ({ kind: item.id ?? item.kind ?? item.code, severity: item.severity, count: item.count ?? item.sampleCount ?? null, summary: String(item.summary ?? item.message ?? "").slice(0, 180) })),
|
||||
valuesRedacted: true,
|
||||
}));
|
||||
|
||||
@@ -1575,6 +1591,8 @@ function buildFindings(samples, control, network, errors, sampleMetrics, promptN
|
||||
if (promptFailures.length > 0) findings.push({ id: "prompt-chat-submit-failed", severity: "red", summary: "sendPrompt command had no successful /v1/agent/chat POST response in the sampling window", count: promptFailures.length, rounds: promptFailures.slice(0, 10) });
|
||||
const elapsedZeroResets = Array.isArray(sampleMetrics?.turnTimingElapsedZeroResets) ? sampleMetrics.turnTimingElapsedZeroResets : [];
|
||||
if (elapsedZeroResets.length > 0) findings.push({ id: "turn-timing-total-elapsed-zero-reset", severity: "red", summary: "Code Agent total elapsed jumped from a non-zero value back to 0 seconds", count: elapsedZeroResets.length, samples: elapsedZeroResets.slice(0, 20) });
|
||||
const elapsedForwardJumps = Array.isArray(sampleMetrics?.turnTimingTotalElapsedForwardJumps) ? sampleMetrics.turnTimingTotalElapsedForwardJumps : [];
|
||||
if (elapsedForwardJumps.length > 0) findings.push({ id: "turn-timing-total-elapsed-forward-jump", severity: "red", summary: "Code Agent total elapsed jumped forward faster than browser sample interval", count: elapsedForwardJumps.length, samples: elapsedForwardJumps.slice(0, 20) });
|
||||
const recentUpdateSawtoothJumps = Array.isArray(sampleMetrics?.turnTimingRecentUpdateSawtoothJumps)
|
||||
? sampleMetrics.turnTimingRecentUpdateSawtoothJumps
|
||||
: Array.isArray(sampleMetrics?.turnTimingNonMonotonic)
|
||||
@@ -2496,6 +2514,30 @@ function isDiagnosticText(text) {
|
||||
return /Failed to (?:fetch|load resource)|request failed|net::ERR_[A-Z0-9_:-]+|server responded with a status of [45][0-9]{2}|HTTP\s+[45][0-9]{2}\b|trace_id=|workbench turn\s*超过|turn\s*超过|无新活动|idle\s+\d+s|waitingFor=|lastEventLabel=|无法连接上游|代理暂时无法连接上游|provider-unavailable|agentrun:error|AgentRun error|projection-resume|sync-failed|durable projection store|realtime-gap|Trace 更新超时|加载失败|请求失败|请求已失败/iu.test(value);
|
||||
}
|
||||
|
||||
function prioritizeFindings(findings) {
|
||||
const items = Array.isArray(findings) ? findings : [];
|
||||
const severityRank = (severity) => {
|
||||
const value = String(severity || "").toLowerCase();
|
||||
if (value === "red") return 0;
|
||||
if (value === "amber" || value === "warning") return 1;
|
||||
if (value === "info") return 3;
|
||||
return 2;
|
||||
};
|
||||
const kindRank = (item) => {
|
||||
const id = String(item?.id ?? item?.kind ?? item?.code ?? "");
|
||||
if (id === "page-performance-slow-same-origin-api") return 0;
|
||||
if (id.startsWith("turn-timing-total-elapsed")) return 1;
|
||||
if (id.startsWith("turn-timing-recent-update")) return 2;
|
||||
if (id.includes("runtime-execution") || id.includes("prompt-chat-submit-failed")) return 3;
|
||||
return 10;
|
||||
};
|
||||
return items.slice().sort((left, right) => {
|
||||
const kindDelta = kindRank(left) - kindRank(right);
|
||||
if (kindDelta !== 0) return kindDelta;
|
||||
return severityRank(left?.severity ?? left?.level) - severityRank(right?.severity ?? right?.level);
|
||||
});
|
||||
}
|
||||
|
||||
function isTerminalTraceText(text) {
|
||||
return /轮次完成|轮次失败|轮次取消|已记录|已完成第\d+轮|final response|sealed final response|turn completed|turn failed|turn canceled|terminal result|\bcompleted\b|\bfailed\b|\bcanceled\b|\bcancelled\b|\bterminal\b|\bdone\b/iu.test(String(text || ""));
|
||||
}
|
||||
@@ -2541,6 +2583,7 @@ function buildSampleMetrics(samples, control) {
|
||||
const turnCells = turnTiming.rows.flatMap((row) => Object.values(row.cells || {}));
|
||||
const turnTimingNonMonotonic = Array.isArray(turnTiming.nonMonotonic) ? turnTiming.nonMonotonic : [];
|
||||
const turnTimingElapsedZeroResets = Array.isArray(turnTiming.elapsedZeroResets) ? turnTiming.elapsedZeroResets : [];
|
||||
const turnTimingTotalElapsedForwardJumps = Array.isArray(turnTiming.totalElapsedForwardJumps) ? turnTiming.totalElapsedForwardJumps : [];
|
||||
const turnTimingRecentUpdateSawtoothJumps = turnTimingNonMonotonic.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump");
|
||||
const turnTimingTerminalElapsedGrowth = Array.isArray(turnTiming.terminalElapsedGrowth) ? turnTiming.terminalElapsedGrowth : [];
|
||||
const turnTimingRecentUpdateResets = Array.isArray(turnTiming.recentUpdateResets) ? turnTiming.recentUpdateResets : [];
|
||||
@@ -2565,6 +2608,7 @@ function buildSampleMetrics(samples, control) {
|
||||
const rounds = buildRoundMetricSummaries(timeline, promptCommands, {
|
||||
nonMonotonic: turnTimingNonMonotonic,
|
||||
elapsedZeroResets: turnTimingElapsedZeroResets,
|
||||
totalElapsedForwardJumps: turnTimingTotalElapsedForwardJumps,
|
||||
terminalElapsedGrowth: turnTimingTerminalElapsedGrowth,
|
||||
recentUpdateResets: turnTimingRecentUpdateResets,
|
||||
recentUpdateSteps: turnTimingRecentUpdateSteps
|
||||
@@ -2585,6 +2629,8 @@ function buildSampleMetrics(samples, control) {
|
||||
turnTimingNonMonotonicCount: turnTimingNonMonotonic.length,
|
||||
turnTimingTotalElapsedDecreaseCount: turnTimingNonMonotonic.filter((item) => item.metric === "totalElapsedSeconds").length,
|
||||
turnTimingTotalElapsedZeroResetCount: turnTimingElapsedZeroResets.length,
|
||||
turnTimingTotalElapsedForwardJumpCount: turnTimingTotalElapsedForwardJumps.length,
|
||||
turnTimingTotalElapsedForwardJumpMaxSeconds: maxPositiveDelta(turnTimingTotalElapsedForwardJumps),
|
||||
turnTimingTerminalElapsedGrowthCount: turnTimingTerminalElapsedGrowth.length,
|
||||
turnTimingTerminalElapsedGrowthMaxSeconds: turnTimingTerminalElapsedGrowthDeltas.length > 0 ? Math.max(...turnTimingTerminalElapsedGrowthDeltas) : 0,
|
||||
turnTimingRecentUpdateJumpCount: recentUpdateJumpCount,
|
||||
@@ -2595,6 +2641,7 @@ function buildSampleMetrics(samples, control) {
|
||||
turnTimingRecentUpdateResetCount: turnTimingRecentUpdateResets.length,
|
||||
turnTimingRecentUpdateDecreaseCount: turnTimingRecentUpdateResets.length,
|
||||
roundsWithTurnTimingNonMonotonic: rounds.filter((item) => item.turnTimingNonMonotonicCount > 0).length,
|
||||
roundsWithTurnTimingTotalElapsedForwardJumps: rounds.filter((item) => item.turnTimingTotalElapsedForwardJumpCount > 0).length,
|
||||
roundsWithTerminalElapsedGrowth: rounds.filter((item) => item.turnTimingTerminalElapsedGrowthCount > 0).length,
|
||||
roundsWithRecentUpdateJumps: rounds.filter((item) => item.turnTimingRecentUpdateJumpCount > 0).length
|
||||
},
|
||||
@@ -2603,6 +2650,7 @@ function buildSampleMetrics(samples, control) {
|
||||
turnTimingTable: turnTiming.rows,
|
||||
turnTimingNonMonotonic,
|
||||
turnTimingElapsedZeroResets,
|
||||
turnTimingTotalElapsedForwardJumps,
|
||||
turnTimingTerminalElapsedGrowth,
|
||||
turnTimingRecentUpdateSawtoothJumps,
|
||||
turnTimingRecentUpdateSteps,
|
||||
@@ -2673,6 +2721,7 @@ function buildTurnTimingTable(samples, timeline) {
|
||||
rows,
|
||||
nonMonotonic: timingEvents.anomalies,
|
||||
elapsedZeroResets: timingEvents.elapsedZeroResets,
|
||||
totalElapsedForwardJumps: timingEvents.totalElapsedForwardJumps,
|
||||
terminalElapsedGrowth: timingEvents.terminalElapsedGrowth,
|
||||
recentUpdateResets: timingEvents.recentUpdateResets,
|
||||
recentUpdateSteps: timingEvents.recentUpdateSteps
|
||||
@@ -2682,6 +2731,7 @@ function buildTurnTimingTable(samples, timeline) {
|
||||
function detectTurnTimingNonMonotonic(columns, rows) {
|
||||
const anomalies = [];
|
||||
const elapsedZeroResets = [];
|
||||
const totalElapsedForwardJumps = [];
|
||||
const terminalElapsedGrowth = [];
|
||||
const recentUpdateResets = [];
|
||||
const recentUpdateSteps = [];
|
||||
@@ -2720,6 +2770,35 @@ function detectTurnTimingNonMonotonic(columns, rows) {
|
||||
anomalies.push(event);
|
||||
if (anomaly === "zero-reset") elapsedZeroResets.push(event);
|
||||
}
|
||||
if (previous && metric === "totalElapsedSeconds" && current > previous.value) {
|
||||
const sampleDeltaSeconds = elapsedSecondsBetween(previous.ts, row.ts);
|
||||
const delta = current - previous.value;
|
||||
const allowedIncreaseSeconds = sampleDeltaSeconds + 3;
|
||||
if (delta > allowedIncreaseSeconds) {
|
||||
totalElapsedForwardJumps.push({
|
||||
columnId: column.id,
|
||||
columnLabel: column.label,
|
||||
metric,
|
||||
anomaly: "forward-jump",
|
||||
expectedPattern: "total-elapsed-increase-matches-browser-sample-interval",
|
||||
fromSeq: previous.seq,
|
||||
fromTs: previous.ts,
|
||||
fromValue: previous.value,
|
||||
toSeq: row.seq ?? null,
|
||||
toTs: row.ts ?? null,
|
||||
toValue: current,
|
||||
delta,
|
||||
sampleDeltaSeconds,
|
||||
allowedIncreaseSeconds,
|
||||
excessiveIncreaseSeconds: Number((delta - allowedIncreaseSeconds).toFixed(3)),
|
||||
traceId: cell.traceId ?? column.traceId ?? null,
|
||||
messageId: cell.messageId ?? column.messageId ?? null,
|
||||
promptIndex: cell.promptIndex ?? row.promptIndex ?? null,
|
||||
source: cell.source ?? column.source ?? null,
|
||||
valuesRedacted: true
|
||||
});
|
||||
}
|
||||
}
|
||||
if (metric === "totalElapsedSeconds" && isTerminalTurnStatus(cell.status)) {
|
||||
if (previousTerminalTotal && current > previousTerminalTotal.value) {
|
||||
terminalElapsedGrowth.push({
|
||||
@@ -2824,7 +2903,21 @@ function detectTurnTimingNonMonotonic(columns, rows) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return { anomalies, elapsedZeroResets, terminalElapsedGrowth, recentUpdateResets, recentUpdateSteps };
|
||||
return { anomalies, elapsedZeroResets, totalElapsedForwardJumps, terminalElapsedGrowth, recentUpdateResets, recentUpdateSteps };
|
||||
}
|
||||
|
||||
function elapsedSecondsBetween(fromTs, toTs) {
|
||||
const from = Date.parse(fromTs);
|
||||
const to = Date.parse(toTs);
|
||||
if (!Number.isFinite(from) || !Number.isFinite(to) || to < from) return 0;
|
||||
return Number(((to - from) / 1000).toFixed(3));
|
||||
}
|
||||
|
||||
function maxPositiveDelta(items) {
|
||||
const values = (Array.isArray(items) ? items : [])
|
||||
.map((item) => Number(item.delta))
|
||||
.filter((value) => Number.isFinite(value) && value > 0);
|
||||
return values.length > 0 ? Math.max(...values) : 0;
|
||||
}
|
||||
|
||||
function isTerminalTurnStatus(value) {
|
||||
@@ -2931,6 +3024,7 @@ function firstTraceId(texts) {
|
||||
function buildRoundMetricSummaries(timeline, promptCommands, timing = {}) {
|
||||
const rounds = [];
|
||||
const nonMonotonic = Array.isArray(timing.nonMonotonic) ? timing.nonMonotonic : [];
|
||||
const totalElapsedForwardJumps = Array.isArray(timing.totalElapsedForwardJumps) ? timing.totalElapsedForwardJumps : [];
|
||||
const elapsedZeroResets = Array.isArray(timing.elapsedZeroResets) ? timing.elapsedZeroResets : [];
|
||||
const terminalElapsedGrowth = Array.isArray(timing.terminalElapsedGrowth) ? timing.terminalElapsedGrowth : [];
|
||||
const recentUpdateResets = Array.isArray(timing.recentUpdateResets) ? timing.recentUpdateResets : [];
|
||||
@@ -2941,6 +3035,7 @@ function buildRoundMetricSummaries(timeline, promptCommands, timing = {}) {
|
||||
const totalElapsed = items.map((item) => item.totalElapsedSeconds).filter((value) => value !== null);
|
||||
const recentUpdate = items.map((item) => item.recentUpdateSeconds).filter((value) => value !== null);
|
||||
const timingAnomalies = nonMonotonic.filter((item) => item.promptIndex === promptIndex);
|
||||
const timingForwardJumps = totalElapsedForwardJumps.filter((item) => item.promptIndex === promptIndex);
|
||||
const timingZeroResets = elapsedZeroResets.filter((item) => item.promptIndex === promptIndex);
|
||||
const timingTerminalGrowth = terminalElapsedGrowth.filter((item) => item.promptIndex === promptIndex);
|
||||
const timingResets = recentUpdateResets.filter((item) => item.promptIndex === promptIndex);
|
||||
@@ -2971,6 +3066,8 @@ function buildRoundMetricSummaries(timeline, promptCommands, timing = {}) {
|
||||
turnTimingNonMonotonicCount: timingAnomalies.length,
|
||||
turnTimingTotalElapsedDecreaseCount: timingAnomalies.filter((item) => item.metric === "totalElapsedSeconds").length,
|
||||
turnTimingTotalElapsedZeroResetCount: timingZeroResets.length,
|
||||
turnTimingTotalElapsedForwardJumpCount: timingForwardJumps.length,
|
||||
turnTimingTotalElapsedForwardJumpMaxSeconds: maxPositiveDelta(timingForwardJumps),
|
||||
turnTimingTerminalElapsedGrowthCount: timingTerminalGrowth.length,
|
||||
turnTimingTerminalElapsedGrowthMaxSeconds: terminalGrowthDeltas.length > 0 ? Math.max(...terminalGrowthDeltas) : 0,
|
||||
turnTimingRecentUpdateJumpCount: timingAnomalies.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump").length,
|
||||
@@ -3151,6 +3248,10 @@ function renderTurnTimingTable(sampleMetrics) {
|
||||
const elapsedZeroResetLines = elapsedZeroResets.length > 0
|
||||
? elapsedZeroResets.slice(0, 80).map((item) => "- " + (item.columnLabel || item.columnId || "-") + " totalElapsed zero-reset " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
|
||||
: "- 未观察到总耗时从非零跳回 0 秒。";
|
||||
const totalElapsedForwardJumps = Array.isArray(sampleMetrics?.turnTimingTotalElapsedForwardJumps) ? sampleMetrics.turnTimingTotalElapsedForwardJumps : [];
|
||||
const totalElapsedForwardJumpLines = totalElapsedForwardJumps.length > 0
|
||||
? totalElapsedForwardJumps.slice(0, 80).map((item) => "- " + (item.columnLabel || item.columnId || "-") + " totalElapsed forward-jump " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " sampleDelta=" + (item.sampleDeltaSeconds ?? "-") + " allowed=" + (item.allowedIncreaseSeconds ?? "-") + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
|
||||
: "- 未观察到总耗时超出采样间隔的异常前跳。";
|
||||
const sawtoothJumps = Array.isArray(sampleMetrics?.turnTimingRecentUpdateSawtoothJumps)
|
||||
? sampleMetrics.turnTimingRecentUpdateSawtoothJumps
|
||||
: nonMonotonic.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump");
|
||||
@@ -3169,7 +3270,7 @@ function renderTurnTimingTable(sampleMetrics) {
|
||||
const resetLines = recentUpdateResets.length > 0
|
||||
? recentUpdateResets.slice(0, 80).map((item) => "- " + (item.columnLabel || item.columnId || "-") + " reset " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " sampleDelta=" + (item.sampleDeltaSeconds ?? "-") + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
|
||||
: "- 未观察到最近更新归零/回落。";
|
||||
return lines.join("\n") + "\n\n列说明:\n" + columnLines + "\n\n异常事件(仅报表暴露,不做下游 repair;最近更新按三角波模型检测异常跳增):\n" + nonMonotonicLines + "\n\nTerminal 后总耗时增长事件(终态 turn 的总耗时应 sealed,不应继续增长):\n" + terminalGrowthLines + "\n\n总耗时归零跳变事件(例如已显示真实耗时后又变成 0 秒):\n" + elapsedZeroResetLines + "\n\n最近更新 sawtooth jump 事件(预期每秒增长约 1,遇到新活动归零;例如 1 秒 -> 1 分 4 秒应被列入这里):\n" + sawtoothJumpLines + "\n\n最近更新相邻采样 step(按 delta 降序;用于人工识别一秒跳几十秒/一分钟的瞬态):\n" + stepLines + "\n\n最近更新 reset 事件(预期三角波归零,不计为异常):\n" + resetLines;
|
||||
return lines.join("\n") + "\n\n列说明:\n" + columnLines + "\n\n异常事件(仅报表暴露,不做下游 repair;最近更新按三角波模型检测异常跳增):\n" + nonMonotonicLines + "\n\nTerminal 后总耗时增长事件(终态 turn 的总耗时应 sealed,不应继续增长):\n" + terminalGrowthLines + "\n\n总耗时归零跳变事件(例如已显示真实耗时后又变成 0 秒):\n" + elapsedZeroResetLines + "\n\n总耗时异常前跳事件(预期按采样间隔近似递增;例如 14 秒 -> 1137 秒应被列入这里):\n" + totalElapsedForwardJumpLines + "\n\n最近更新 sawtooth jump 事件(预期每秒增长约 1,遇到新活动归零;例如 1 秒 -> 1 分 4 秒应被列入这里):\n" + sawtoothJumpLines + "\n\n最近更新相邻采样 step(按 delta 降序;用于人工识别一秒跳几十秒/一分钟的瞬态):\n" + stepLines + "\n\n最近更新 reset 事件(预期三角波归零,不计为异常):\n" + resetLines;
|
||||
}
|
||||
|
||||
function formatMetricCell(value) {
|
||||
@@ -3206,7 +3307,7 @@ function renderMarkdown(report) {
|
||||
? report.promptNetwork.rounds.map((item) => "- round " + item.promptIndex + " promptHash=" + (item.promptTextHash || "-") + " chatPostOk=" + String(item.chatPostOk) + " failure=" + (item.failureKind || "-") + " statuses=" + (Array.isArray(item.responseStatuses) && item.responseStatuses.length > 0 ? item.responseStatuses.join(",") : "-") + " firstChat=" + (item.firstChatEventAt || "-") + " lastChat=" + (item.lastChatEventAt || "-")).join("\n")
|
||||
: "- 无 prompt 网络记录。";
|
||||
const roundLines = Array.isArray(report.sampleMetrics?.rounds) && report.sampleMetrics.rounds.length > 0
|
||||
? report.sampleMetrics.rounds.map((item) => "- round " + item.promptIndex + " promptHash=" + (item.promptTextHash || "-") + " samples=" + item.sampleCount + " totalMax=" + (item.maxTotalElapsedSeconds ?? "-") + " totalLast=" + (item.lastTotalElapsedSeconds ?? "-") + " recentMax=" + (item.maxRecentUpdateSeconds ?? "-") + " recentLast=" + (item.lastRecentUpdateSeconds ?? "-") + " totalDecrease=" + (item.turnTimingTotalElapsedDecreaseCount ?? 0) + " terminalGrowth=" + (item.turnTimingTerminalElapsedGrowthCount ?? 0) + " terminalGrowthMax=" + (item.turnTimingTerminalElapsedGrowthMaxSeconds ?? 0) + " recentJump=" + (item.turnTimingRecentUpdateJumpCount ?? 0) + " recentSawtoothJump=" + (item.turnTimingRecentUpdateSawtoothJumpCount ?? item.turnTimingRecentUpdateJumpCount ?? 0) + " recentStep=" + (item.turnTimingRecentUpdateStepCount ?? 0) + " recentMaxIncrease=" + (item.turnTimingRecentUpdateMaxIncreaseSeconds ?? "-") + " recentMaxExcess=" + (item.turnTimingRecentUpdateMaxExcessSeconds ?? 0) + " recentReset=" + (item.turnTimingRecentUpdateResetCount ?? 0) + " diagnostics=" + item.diagnosticSamples + " terminal=" + item.terminalSamples + " finalText=" + item.finalTextSamples).join("\n")
|
||||
? report.sampleMetrics.rounds.map((item) => "- round " + item.promptIndex + " promptHash=" + (item.promptTextHash || "-") + " samples=" + item.sampleCount + " totalMax=" + (item.maxTotalElapsedSeconds ?? "-") + " totalLast=" + (item.lastTotalElapsedSeconds ?? "-") + " recentMax=" + (item.maxRecentUpdateSeconds ?? "-") + " recentLast=" + (item.lastRecentUpdateSeconds ?? "-") + " totalDecrease=" + (item.turnTimingTotalElapsedDecreaseCount ?? 0) + " totalForwardJump=" + (item.turnTimingTotalElapsedForwardJumpCount ?? 0) + " totalForwardJumpMax=" + (item.turnTimingTotalElapsedForwardJumpMaxSeconds ?? 0) + " terminalGrowth=" + (item.turnTimingTerminalElapsedGrowthCount ?? 0) + " terminalGrowthMax=" + (item.turnTimingTerminalElapsedGrowthMaxSeconds ?? 0) + " recentJump=" + (item.turnTimingRecentUpdateJumpCount ?? 0) + " recentSawtoothJump=" + (item.turnTimingRecentUpdateSawtoothJumpCount ?? item.turnTimingRecentUpdateJumpCount ?? 0) + " recentStep=" + (item.turnTimingRecentUpdateStepCount ?? 0) + " recentMaxIncrease=" + (item.turnTimingRecentUpdateMaxIncreaseSeconds ?? "-") + " recentMaxExcess=" + (item.turnTimingRecentUpdateMaxExcessSeconds ?? 0) + " recentReset=" + (item.turnTimingRecentUpdateResetCount ?? 0) + " diagnostics=" + item.diagnosticSamples + " terminal=" + item.terminalSamples + " finalText=" + item.finalTextSamples).join("\n")
|
||||
: "- 无轮次指标。";
|
||||
const provenanceLines = Array.isArray(report.pageProvenance?.segments) && report.pageProvenance.segments.length > 0
|
||||
? report.pageProvenance.segments.slice(0, 40).map((item) => "- fingerprint=" + (item.assetFingerprint || "-") + " samples=" + item.sampleCount + " seq=" + (item.firstSeq ?? "-") + ".." + (item.lastSeq ?? "-") + " ts=" + (item.firstAt || "-") + ".." + (item.lastAt || "-") + " scripts=" + (item.scriptCount ?? "-") + " styles=" + (item.stylesheetCount ?? "-") + " urlPaths=" + (Array.isArray(item.urlPaths) ? item.urlPaths.slice(0, 4).join(",") : "-")).join("\n")
|
||||
@@ -3237,6 +3338,8 @@ function renderMarkdown(report) {
|
||||
+ "- turnTimingRows: " + (metricSummary.turnTimingRows ?? 0) + "\n"
|
||||
+ "- turnTimingNonMonotonicCount: " + (metricSummary.turnTimingNonMonotonicCount ?? 0) + "\n"
|
||||
+ "- turnTimingTotalElapsedDecreaseCount: " + (metricSummary.turnTimingTotalElapsedDecreaseCount ?? 0) + "\n"
|
||||
+ "- turnTimingTotalElapsedForwardJumpCount: " + (metricSummary.turnTimingTotalElapsedForwardJumpCount ?? 0) + "\n"
|
||||
+ "- turnTimingTotalElapsedForwardJumpMaxSeconds: " + (metricSummary.turnTimingTotalElapsedForwardJumpMaxSeconds ?? 0) + "\n"
|
||||
+ "- turnTimingTerminalElapsedGrowthCount: " + (metricSummary.turnTimingTerminalElapsedGrowthCount ?? 0) + "\n"
|
||||
+ "- turnTimingTerminalElapsedGrowthMaxSeconds: " + (metricSummary.turnTimingTerminalElapsedGrowthMaxSeconds ?? 0) + "\n"
|
||||
+ "- turnTimingRecentUpdateJumpCount: " + (metricSummary.turnTimingRecentUpdateJumpCount ?? 0) + "\n"
|
||||
|
||||
Reference in New Issue
Block a user