fix(web-probe): stabilize round timing attribution (#770)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-24 03:45:28 +08:00
committed by GitHub
parent 382d345a3e
commit f3075d2999
2 changed files with 55 additions and 11 deletions
@@ -4372,12 +4372,21 @@ function finalizeLoadingSegment(segment, absentEvent) {
function buildTurnTimingTable(samples, timeline) {
const columns = [];
const registry = new Map();
const promptAssignmentByKey = new Map();
const rows = [];
for (let index = 0; index < samples.length; index += 1) {
const sample = samples[index];
const timelineItem = timeline[index] || {};
const cells = {};
for (const metric of turnMetricItems(sample, timelineItem)) {
const rawMetrics = turnMetricItems(sample, timelineItem);
const domIndexes = rawMetrics.map((item) => Number(item.domIndex)).filter(Number.isFinite);
const maxDomIndex = domIndexes.length > 0 ? Math.max(...domIndexes) : null;
for (const rawMetric of rawMetrics) {
const samplePromptIndex = Number(timelineItem.promptIndex ?? 0);
const evidencePromptIndex = inferTurnMetricPromptIndex(rawMetric, samplePromptIndex, maxDomIndex);
if (evidencePromptIndex !== null && !promptAssignmentByKey.has(rawMetric.key)) promptAssignmentByKey.set(rawMetric.key, evidencePromptIndex);
const assignedPromptIndex = promptAssignmentByKey.get(rawMetric.key) ?? null;
const metric = { ...rawMetric, promptIndex: assignedPromptIndex, samplePromptIndex };
let column = registry.get(metric.key);
if (!column) {
column = {
@@ -4402,6 +4411,7 @@ function buildTurnTimingTable(samples, timeline) {
} else {
column.lastSeq = sample.seq ?? null;
column.lastTs = sample.ts ?? null;
if (!column.promptIndex && metric.promptIndex) column.promptIndex = metric.promptIndex;
column.lastPromptIndex = metric.promptIndex ?? column.lastPromptIndex ?? null;
if (!column.traceId && metric.traceId) column.traceId = metric.traceId;
if (!column.messageId && metric.messageId) column.messageId = metric.messageId;
@@ -4417,7 +4427,8 @@ function buildTurnTimingTable(samples, timeline) {
sampleGroupSeq: sample.sampleGroupSeq ?? null,
traceId: metric.traceId ?? null,
messageId: metric.messageId ?? null,
textHash: metric.textHash ?? null
textHash: metric.textHash ?? null,
samplePromptIndex: metric.samplePromptIndex ?? null
};
}
rows.push({
@@ -4445,6 +4456,28 @@ function buildTurnTimingTable(samples, timeline) {
};
}
function inferTurnMetricPromptIndex(metric, samplePromptIndex, maxDomIndex) {
const promptIndex = Number(samplePromptIndex);
if (!Number.isFinite(promptIndex) || promptIndex <= 0) return null;
if (metric?.source === "aggregate") return promptIndex;
if (isActiveTurnStatus(metric?.status)) return promptIndex;
const domIndex = Number(metric?.domIndex);
if (!isTerminalTurnStatus(metric?.status) && Number.isFinite(domIndex) && Number.isFinite(maxDomIndex) && domIndex === maxDomIndex && metric?.recentUpdateSeconds !== null && metric?.recentUpdateSeconds !== undefined) return promptIndex;
return null;
}
function isActiveTurnStatus(status) {
const value = String(status || "").toLowerCase();
if (!value) return false;
if (isTerminalTurnStatus(value)) return false;
return /running|pending|admitted|queued|dispatch|execut|progress|thinking|working|active|streaming|created|started/u.test(value);
}
function isTerminalTurnStatus(status) {
const value = String(status || "").toLowerCase();
return /completed|succeeded|success|failed|error|canceled|cancelled|blocked|done|terminal/u.test(value);
}
function detectTurnTimingNonMonotonic(columns, rows) {
const anomalies = [];
const elapsedZeroResets = [];
@@ -4480,7 +4513,8 @@ function detectTurnTimingNonMonotonic(columns, rows) {
delta: current - previous.value,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? row.promptIndex ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
@@ -4515,7 +4549,8 @@ function detectTurnTimingNonMonotonic(columns, rows) {
excessiveIncreaseSeconds: Number((delta - allowedIncreaseSeconds).toFixed(3)),
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? row.promptIndex ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
@@ -4542,7 +4577,8 @@ function detectTurnTimingNonMonotonic(columns, rows) {
delta: current - previousTerminalTotal.value,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? row.promptIndex ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
@@ -4577,7 +4613,8 @@ function detectTurnTimingNonMonotonic(columns, rows) {
excessiveIncreaseSeconds: excessiveIncrease,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? row.promptIndex ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
@@ -4599,7 +4636,8 @@ function detectTurnTimingNonMonotonic(columns, rows) {
sampleDeltaSeconds: elapsedSeconds,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? row.promptIndex ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
@@ -4625,7 +4663,8 @@ function detectTurnTimingNonMonotonic(columns, rows) {
excessiveIncreaseSeconds: excessiveIncrease,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? row.promptIndex ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
@@ -6051,7 +6090,7 @@ function detectCrossPageProjectionDiffs(samples) {
const controlMessageDigest = digestMessageTexts(control);
const observerMessageDigest = digestMessageTexts(observer);
const messageTextDigestDiff = controlMessageDigest !== observerMessageDigest;
const projectionDivergent = sameSession && (Math.abs(controlMessages - observerMessages) > 0 || controlZero !== observerZero || messageTextDigestDiff);
const projectionDivergent = sameSession && (Math.abs(controlMessages - observerMessages) > 0 || controlZero !== observerZero);
const traceVisibilityDivergent = sameSession && !projectionDivergent && (missingInObserver.length > 0 || Math.abs(controlTraceRows - observerTraceRows) > 0);
if (!projectionDivergent && !traceVisibilityDivergent) continue;
rows.push({
@@ -6184,7 +6223,7 @@ function detectAdjacentCrossPageProjectionDiffs(samples) {
const controlMessageDigest = digestMessageTexts(control);
const observerMessageDigest = digestMessageTexts(observer);
const messageTextDigestDiff = controlMessageDigest !== observerMessageDigest;
const projectionDivergent = controlMessages !== observerMessages || controlZero !== observerZero || messageTextDigestDiff;
const projectionDivergent = controlMessages !== observerMessages || controlZero !== observerZero;
const traceVisibilityDivergent = !projectionDivergent && (missingInObserver.length > 0 || controlTraceRows !== observerTraceRows);
if (!projectionDivergent && !traceVisibilityDivergent) continue;
rows.push({