fix(web-probe): anchor prompt timing at command start (#773)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-24 03:54:44 +08:00
committed by GitHub
parent 87d6130636
commit ee565d3f42
@@ -3903,11 +3903,7 @@ function isFinalResultText(text) {
}
function buildSampleMetrics(samples, control) {
const promptCommands = control
.filter((item) => item.type === "sendPrompt" && item.phase === "completed")
.map((item) => ({ ts: item.ts, tsMs: Date.parse(item.ts), commandId: item.commandId ?? null, textHash: item.input?.textHash ?? null, textBytes: item.input?.textBytes ?? null }))
.filter((item) => Number.isFinite(item.tsMs))
.sort((a, b) => a.tsMs - b.tsMs);
const promptCommands = buildSendPromptCommandTimeline(control);
const promptTimes = promptCommands.map((item) => item.tsMs);
const timeline = samples.map((sample) => {
const texts = sampleTexts(sample);
@@ -4070,6 +4066,49 @@ function buildSampleMetrics(samples, control) {
};
}
function buildSendPromptCommandTimeline(control) {
const byCommand = new Map();
let ordinal = 0;
for (const item of Array.isArray(control) ? control : []) {
if (item?.type !== "sendPrompt") continue;
const commandId = item.commandId ? String(item.commandId) : "sendPrompt-" + String(ordinal++);
const existing = byCommand.get(commandId) || {
commandId,
startedAt: null,
startedTsMs: null,
completedAt: null,
completedTsMs: null,
failedAt: null,
failedTsMs: null,
textHash: null,
textBytes: null,
};
if (!existing.textHash && item.input?.textHash) existing.textHash = item.input.textHash;
if (!existing.textBytes && item.input?.textBytes) existing.textBytes = item.input.textBytes;
const tsMs = Date.parse(item.ts);
if (item.phase === "started" && Number.isFinite(tsMs)) {
existing.startedAt = item.ts ?? existing.startedAt;
existing.startedTsMs = tsMs;
} else if (item.phase === "completed" && Number.isFinite(tsMs)) {
existing.completedAt = item.ts ?? existing.completedAt;
existing.completedTsMs = tsMs;
} else if (item.phase === "failed" && Number.isFinite(tsMs)) {
existing.failedAt = item.ts ?? existing.failedAt;
existing.failedTsMs = tsMs;
}
byCommand.set(commandId, existing);
}
return Array.from(byCommand.values())
.map((item) => {
const tsMs = Number.isFinite(item.startedTsMs) ? item.startedTsMs : Number.isFinite(item.completedTsMs) ? item.completedTsMs : item.failedTsMs;
const ts = Number.isFinite(item.startedTsMs) ? item.startedAt : Number.isFinite(item.completedTsMs) ? item.completedAt : item.failedAt;
return { ...item, ts, tsMs };
})
.filter((item) => Number.isFinite(item.tsMs))
.sort((a, b) => a.tsMs - b.tsMs)
.map((item, index) => ({ ...item, promptIndex: index + 1 }));
}
function buildSessionRailTitleMetrics(samples, timeline) {
const rows = [];
const examplesByHash = new Map();
@@ -4384,7 +4423,10 @@ function buildTurnTimingTable(samples, timeline) {
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);
if (evidencePromptIndex !== null) {
const existingPromptIndex = promptAssignmentByKey.get(rawMetric.key);
if (existingPromptIndex === undefined || evidencePromptIndex > existingPromptIndex) promptAssignmentByKey.set(rawMetric.key, evidencePromptIndex);
}
const assignedPromptIndex = promptAssignmentByKey.get(rawMetric.key) ?? null;
const metric = { ...rawMetric, promptIndex: assignedPromptIndex, samplePromptIndex };
let column = registry.get(metric.key);
@@ -4411,7 +4453,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;
if (metric.promptIndex && 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;