fix: bind web observe turn summary to prompt trace (#857)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-25 08:01:39 +08:00
committed by GitHub
parent 3ddedd4f61
commit 94d985d628
+17 -4
View File
@@ -43,15 +43,23 @@ function unique(values){return Array.from(new Set(values.filter(Boolean)));}
function numOrNull(value){const n=Number(value); return Number.isFinite(n)?n:null}
function promptIndexOf(value){const n=Number(value); return Number.isInteger(n)&&n>0?n:null}
function tsMs(value){const ms=Date.parse(String(value||'')); return Number.isFinite(ms)?ms:null}
function sessionIdFromUrl(value){const match=String(value||'').match(/\\/workbench\\/sessions\\/(ses_[A-Za-z0-9_-]+)/u); return match?match[1]:null}
function commandSessionId(item){return item?.sessionId||item?.detail?.sessionId||item?.input?.sessionId||sessionIdFromUrl(item?.afterUrl)||sessionIdFromUrl(item?.detail?.afterUrl)||null}
function commandTraceId(item){return item?.traceId||item?.detail?.chatSubmit?.traceId||item?.detail?.traceId||item?.input?.traceId||null}
function promptCommands(){
const map=new Map();
for(const item of control){
if(item.type!=='sendPrompt'||(item.phase!=='started'&&item.phase!=='completed'&&item.phase!=='failed')) continue;
const id=item.commandId||item.seq||String(map.size+1);
const existing=map.get(id)||{};
map.set(id,{...existing,...item,input:{...(existing.input||{}),...(item.input||{})},firstTs:existing.firstTs||item.ts,lastTs:item.ts});
map.set(id,{...existing,...item,input:{...(existing.input||{}),...(item.input||{})},sessionId:existing.sessionId||commandSessionId(item),traceId:existing.traceId||commandTraceId(item),firstTs:existing.firstTs||item.ts,lastTs:item.ts});
}
return Array.from(map.values()).filter((item)=>tsMs(item.firstTs)!==null).sort((a,b)=>tsMs(a.firstTs)-tsMs(b.firstTs));
const prompts=Array.from(map.values()).filter((item)=>tsMs(item.firstTs)!==null).sort((a,b)=>tsMs(a.firstTs)-tsMs(b.firstTs));
const latestSession=samples.slice().reverse().find((sample)=>sample.routeSessionId||sample.activeSessionId);
const sessionId=latestSession?.routeSessionId||latestSession?.activeSessionId||null;
if(!sessionId) return prompts;
const scoped=prompts.filter((item)=>item.sessionId===sessionId);
return scoped.length>0?scoped:prompts;
}
function segmentFor(index,prompts){
const start=tsMs(prompts[index]?.firstTs); const end=index+1<prompts.length?tsMs(prompts[index+1].firstTs):Infinity;
@@ -118,7 +126,12 @@ function reportColumnsForPrompt(index,segment){
.filter((column)=>segmentIds.size===0||segmentIds.has(column.traceId)||column.source==='turn')
.sort((a,b)=>(a.firstSeq??0)-(b.firstSeq??0)||(a.lastSeq??0)-(b.lastSeq??0)||String(a.label||'').localeCompare(String(b.label||'')));
}
function choosePrimaryTraceId(index,segment){
function choosePrimaryTraceId(index,segment,prompt=null){
const promptTraceId=commandTraceId(prompt)||prompt?.traceId||null;
if(promptTraceId){
const segmentIds=new Set(traceIdsFromSamples(segment));
if(segmentIds.size===0||segmentIds.has(promptTraceId)) return {traceId:promptTraceId,columns:reportColumnsForPrompt(index,segment)};
}
const columns=reportColumnsForPrompt(index,segment);
const promptIndex=index+1;
const promptColumns=columns.filter((column)=>column.promptIndex===promptIndex);
@@ -245,7 +258,7 @@ function turnSummaryRows(){
return [{round:0,commandId:null,commandType:null,userPreview:'(无 sendPrompt control 记录)',userHash:null,userBytes:null,traceId:allTraceIds[0]||null,status:statusFor(samples,allTraceIds[0]||null),elapsedSeconds:null,recentUpdateSeconds:null,marks:'-',firstSeq:samples[0]?.seq??null,lastSeq:samples[samples.length-1]?.seq??null,lastTs:samples[samples.length-1]?.ts??null,finalResponse:finalResponseFor(samples,allTraceIds[0]||null),valuesRedacted:true}];
}
return prompts.map((prompt,index)=>{
const segment=segmentFor(index,prompts); const segmentControls=controlsFor(index,prompts); const selected=choosePrimaryTraceId(index,segment); const traceId=selected.traceId; const user=userMessageFor(segment,prompt);
const segment=segmentFor(index,prompts); const segmentControls=controlsFor(index,prompts); const selected=choosePrimaryTraceId(index,segment,prompt); const traceId=selected.traceId; const user=userMessageFor(segment,prompt);
const primaryColumn=selected.columns.find((column)=>column.traceId===traceId)||null;
const traceSample=latestSampleForTrace(segment,traceId,primaryColumn?.lastSeq??null);
const texts=textsFor(segment,traceId);