diff --git a/scripts/src/hwlab-node-web-observe-analyzer-source.ts b/scripts/src/hwlab-node-web-observe-analyzer-source.ts index 4238c47e..0cd79651 100644 --- a/scripts/src/hwlab-node-web-observe-analyzer-source.ts +++ b/scripts/src/hwlab-node-web-observe-analyzer-source.ts @@ -2467,7 +2467,7 @@ function detectWorkbenchTerminalTraceMissing(samples, canarySessionIds = new Set const key = [samplePageKey(sample), sample?.routeSessionId ?? "", sample?.activeSessionId ?? "", traceId].join("|"); presentKeys.add(key); const traceRows = workbenchTraceRowsForTrace(sample, traceId); - if (traceRows.length > 0) { + if (traceRows.length > 0 || workbenchSampleHasTerminalProjection(sample, { traceIds: [traceId] })) { closeSegment(key, sample); continue; } @@ -2647,7 +2647,7 @@ function workbenchSampleMatchesTerminalEvent(sample, event) { function workbenchSampleHasTerminalProjection(sample, event) { const traceIds = event.traceIds.length > 0 ? event.traceIds : Array.from(sampleTraceIds(sample)); if (traceIds.length === 0) return false; - return traceIds.some((traceId) => workbenchTraceRowsForTrace(sample, traceId).length > 0 && (workbenchFinalMessageVisible(sample, traceId) || workbenchTerminalTurnVisible(sample, traceId))); + return traceIds.some((traceId) => workbenchFinalMessageVisible(sample, traceId) || workbenchTerminalTurnVisible(sample, traceId)); } function compactWorkbenchProjectionSample(sample, event = null) { diff --git a/scripts/src/hwlab-node-web-observe-collect.ts b/scripts/src/hwlab-node-web-observe-collect.ts index 1432ee8b..0211a64c 100644 --- a/scripts/src/hwlab-node-web-observe-collect.ts +++ b/scripts/src/hwlab-node-web-observe-collect.ts @@ -362,6 +362,8 @@ function renderTraceFrame(sample,rows){ const visibleTraceRows=traceRows.slice(-24); const rowLines=visibleTraceRows.map((row,index)=>{const text=textOf(row); return short((row.status?row.status+' ':'')+text,180)||('row#'+index+' '+(row.textHash||'-'));}); if(traceRows.length>visibleTraceRows.length) rowLines.unshift('(已省略 '+(traceRows.length-visibleTraceRows.length)+' 条较早 trace rows;需要原始数据请看 samples.jsonl)'); + const terminalProjectionVisible=status==='completed'&&finalResponse&&!finalResponse.empty&&turns.some((turn)=>String(turn?.status||'').toLowerCase()==='completed'||/completed|轮次完成|final response/iu.test(textOf(turn))); + if(rowLines.length===0&&terminalProjectionVisible) rowLines.push('(trace rows collapsed; terminal turn and Final Response are visible)'); const missingRows=rowLines.length===0; const bodyRows=missingRows?['(无 trace rows;这是 blocker,不能当业务通过证据)',...traceFrameDiagnosticLines(sample,traceId,traceRows)]:rowLines; const rendered=['Code Agent 耗时 '+(elapsed>=0?fmtDuration(elapsed):'-')+' 最近 '+(recent>=0?String(recent)+' 秒前':'-')+' ('+status+')','=======================================================','sample seq='+(sample.seq??'-')+' ts='+(sample.ts||'-')+' traceId='+(traceId||'-')+' routeSession='+(sample.routeSessionId||'-')+' activeSession='+(sample.activeSessionId||'-'),...bodyRows,'==========================','Final Response',finalResponse.preview||'(空内容)'].join('\\n');