diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index a3881025..8cac43bf 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -1264,7 +1264,10 @@ function buildSampleMetrics(samples, control) { const withTotal = timeline.filter((item) => item.totalElapsedSeconds !== null).length; const withRecent = timeline.filter((item) => item.recentUpdateSeconds !== null).length; const diagnostics = timeline.filter((item) => item.diagnosticSeen).length; - const rounds = buildRoundMetricSummaries(timeline, promptCommands); + const rounds = buildRoundMetricSummaries(timeline, promptCommands, { + nonMonotonic: turnTimingNonMonotonic, + recentUpdateResets: turnTimingRecentUpdateResets + }); const recentUpdateJumpCount = turnTimingNonMonotonic.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump").length; return { summary: { @@ -1282,7 +1285,9 @@ function buildSampleMetrics(samples, control) { turnTimingTotalElapsedDecreaseCount: turnTimingNonMonotonic.filter((item) => item.metric === "totalElapsedSeconds").length, turnTimingRecentUpdateJumpCount: recentUpdateJumpCount, turnTimingRecentUpdateResetCount: turnTimingRecentUpdateResets.length, - turnTimingRecentUpdateDecreaseCount: turnTimingRecentUpdateResets.length + turnTimingRecentUpdateDecreaseCount: turnTimingRecentUpdateResets.length, + roundsWithTurnTimingNonMonotonic: rounds.filter((item) => item.turnTimingNonMonotonicCount > 0).length, + roundsWithRecentUpdateJumps: rounds.filter((item) => item.turnTimingRecentUpdateJumpCount > 0).length }, rounds, turnColumns: turnTiming.columns, @@ -1531,13 +1536,17 @@ function firstTraceId(texts) { return null; } -function buildRoundMetricSummaries(timeline, promptCommands) { +function buildRoundMetricSummaries(timeline, promptCommands, timing = {}) { const rounds = []; + const nonMonotonic = Array.isArray(timing.nonMonotonic) ? timing.nonMonotonic : []; + const recentUpdateResets = Array.isArray(timing.recentUpdateResets) ? timing.recentUpdateResets : []; for (let index = 0; index < promptCommands.length; index += 1) { const promptIndex = index + 1; const items = timeline.filter((item) => item.promptIndex === promptIndex); 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 timingResets = recentUpdateResets.filter((item) => item.promptIndex === promptIndex); rounds.push({ promptIndex, promptCommandId: promptCommands[index].commandId, @@ -1557,7 +1566,12 @@ function buildRoundMetricSummaries(timeline, promptCommands) { lastRecentUpdateSeconds: lastNonNull(items.map((item) => item.recentUpdateSeconds)), diagnosticSamples: items.filter((item) => item.diagnosticSeen).length, terminalSamples: items.filter((item) => item.terminalSeen).length, - finalTextSamples: items.filter((item) => item.finalResultTextSeen).length + finalTextSamples: items.filter((item) => item.finalResultTextSeen).length, + turnTimingNonMonotonicCount: timingAnomalies.length, + turnTimingTotalElapsedDecreaseCount: timingAnomalies.filter((item) => item.metric === "totalElapsedSeconds").length, + turnTimingRecentUpdateJumpCount: timingAnomalies.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump").length, + turnTimingRecentUpdateResetCount: timingResets.length, + turnTimingRecentUpdateDecreaseCount: timingResets.length }); } return rounds; @@ -1762,7 +1776,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 ?? "-") + " 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) + " recentJump=" + (item.turnTimingRecentUpdateJumpCount ?? 0) + " recentReset=" + (item.turnTimingRecentUpdateResetCount ?? 0) + " diagnostics=" + item.diagnosticSamples + " terminal=" + item.terminalSamples + " finalText=" + item.finalTextSamples).join("\n") : "- 无轮次指标。"; const metricLines = Array.isArray(report.sampleMetrics?.timeline) && report.sampleMetrics.timeline.length > 0 ? report.sampleMetrics.timeline.slice(0, 120).map((item) => "- #" + item.seq + " " + item.ts + " prompt=" + item.promptIndex + " totalElapsedSeconds=" + (item.totalElapsedSeconds ?? "-") + " recentUpdateSeconds=" + (item.recentUpdateSeconds ?? "-") + " terminal=" + item.terminalSeen + " finalText=" + item.finalResultTextSeen + " diagnostic=" + item.diagnosticSeen).join("\n")