@@ -10,8 +10,13 @@ export interface TraceEventRow {
}
type TraceEvent = Record < string , unknown > ;
type TraceClockFormatter = ( value : string ) = > string ;
export function traceDisplayRows ( trace : Record < string , unknown > = { } , events : TraceEvent [ ] = [ ] ) : TraceEventRow [ ] {
export interface TraceDisplayRowsOptions {
formatClock? : TraceClockFormatter ;
}
export function traceDisplayRows ( trace : Record < string , unknown > = { } , events : TraceEvent [ ] = [ ] , options : TraceDisplayRowsOptions = { } ) : TraceEventRow [ ] {
const effectiveTrace = traceWithInferredStart ( trace , events ) ;
const rows : TraceEventRow [ ] = [ ] ;
const renderedSourceEvents = new Set < string > ( ) ;
@@ -31,7 +36,7 @@ export function traceDisplayRows(trace: Record<string, unknown> = {}, events: Tr
if ( renderedToolIdentities . has ( identity ) ) continue ;
renderedToolIdentities . add ( identity ) ;
}
rows . push ( traceToolCallRow ( effectiveTrace , event ) ) ;
rows . push ( traceToolCallRow ( effectiveTrace , event , options ) ) ;
continue ;
}
if ( isRequestTraceEvent ( event ) ) {
@@ -41,25 +46,25 @@ export function traceDisplayRows(trace: Record<string, unknown> = {}, events: Tr
continue ;
}
if ( isTerminalAssistantTraceEvent ( event ) ) {
rows . push ( traceAssistantMessageRow ( effectiveTrace , event , { terminal : true } ) ) ;
rows . push ( traceAssistantMessageRow ( effectiveTrace , event , { terminal : true } , options ) ) ;
continue ;
}
if ( isAssistantTraceEvent ( event ) ) {
rows . push ( traceAssistantMessageRow ( effectiveTrace , event , { terminal : false } ) ) ;
rows . push ( traceAssistantMessageRow ( effectiveTrace , event , { terminal : false } , options ) ) ;
continue ;
}
if ( isCompletionTraceEvent ( event ) ) {
const finalResponseText = traceFinalResponseText ( effectiveTrace ) ;
rows . push ( finalResponseText ? traceFinalResponseRow ( event , finalResponseText ) : traceCompletionSummaryRow ( effectiveTrace , event ) ) ;
rows . push ( finalResponseText ? traceFinalResponseRow ( event , finalResponseText , options ) : traceCompletionSummaryRow ( effectiveTrace , event , options ) ) ;
continue ;
}
rows . push ( traceDisplayRow ( effectiveTrace , event ) ) ;
rows . push ( traceDisplayRow ( effectiveTrace , event , options ) ) ;
}
const progressRow = traceBackendProgressSummaryRow ( effectiveTrace , rows , events ) ;
const progressRow = traceBackendProgressSummaryRow ( effectiveTrace , rows , events , options ) ;
if ( progressRow ) rows . push ( progressRow ) ;
if ( rows . length > 0 ) return rows ;
if ( events . length === 0 ) return [ ] ;
return events . filter ( ( event ) = > ! isSuppressedTraceEvent ( event ) ) . map ( ( event ) = > traceDisplayRow ( effectiveTrace , event ) ) ;
return events . filter ( ( event ) = > ! isSuppressedTraceEvent ( event ) ) . map ( ( event ) = > traceDisplayRow ( effectiveTrace , event , options ) ) ;
}
export function renderTraceRowsMarkdown ( rows : TraceEventRow [ ] = [ ] ) : string {
@@ -100,7 +105,7 @@ function stripTraceToolOutputFromSummary(value: string): string {
return value . replace ( / \ s + ( s t d o u t : | s t d e r r : | e x i t C o d e = ) . * $ / i s u , " " ) . t r i m ( ) ;
}
function traceToolCallRow ( trace : Record < string , unknown > , event : TraceEvent ) : TraceEventRow {
function traceToolCallRow ( trace : Record < string , unknown > , event : TraceEvent , options : TraceDisplayRowsOptions ) : TraceEventRow {
const command = cleanShellCommand ( event . command ) ;
const status = traceStatusToken ( event ) ;
const toolName = traceToolName ( event , command ) ;
@@ -109,7 +114,7 @@ function traceToolCallRow(trace: Record<string, unknown>, event: TraceEvent): Tr
rowId : ` tool: ${ event . itemId ? ? event . seq ? ? ` ${ event . label ? ? event . type ? ? "tool" } : ${ event . createdAt ? ? "unknown" } ` } ` ,
seq : numberOrNull ( event . seq ) ,
tone : traceEventTone ( event ) ,
header : ` ${ traceEventClock ( event ) } total= ${ formatTraceDuration ( traceRelativeMs ( trace , event ) ) } ${ status } ${ toolName } ` . trim ( ) ,
header : ` ${ traceEventClock ( event , options ) } total= ${ formatTraceDuration ( traceRelativeMs ( trace , event ) ) } ${ status } ${ toolName } ` . trim ( ) ,
body ,
preview : command ? compactTraceOneLine ( command , 2000 ) : null ,
bodyFormat : "text"
@@ -171,7 +176,7 @@ export function traceNoiseEventCount(events: TraceEvent[] = []): number {
return Array . isArray ( events ) ? events . filter ( ( event ) = > isNoisyTraceEvent ( event ) ) . length : 0 ;
}
function traceAssistantMessageRow ( trace : Record < string , unknown > , event : TraceEvent , { terminal } : { terminal : boolean } ) : TraceEventRow {
function traceAssistantMessageRow ( trace : Record < string , unknown > , event : TraceEvent , { terminal } : { terminal : boolean } , options : TraceDisplayRowsOptions ) : TraceEventRow {
const text = cleanTraceDetailText ( event . message ? ? event . outputSummary ? ? assistantStreamText ( trace , event ) ? ? "" ) ;
const index = Number . isInteger ( event . messageIndex ) ? Number ( event . messageIndex ) : null ;
const count = Number . isInteger ( event . messageCount ) ? Number ( event . messageCount ) : null ;
@@ -179,19 +184,19 @@ function traceAssistantMessageRow(trace: Record<string, unknown>, event: TraceEv
rowId : ` event: ${ event . seq ? ? ` ${ event . label ? ? event . type ? ? "assistant" } : ${ event . createdAt ? ? "unknown" } ` } ` ,
seq : numberOrNull ( event . seq ) ,
tone : "ok" ,
header : ` ${ traceEventClock ( event ) } ${ terminal ? "助手最终消息" : "助手消息" } ${ index ? ` ${ index } ${ count ? ` / ${ count } ` : "" } ` : "" } ` ,
header : ` ${ traceEventClock ( event , options ) } ${ terminal ? "助手最终消息" : "助手消息" } ${ index ? ` ${ index } ${ count ? ` / ${ count } ` : "" } ` : "" } ` ,
terminal : terminal ? true : undefined ,
body : text || null ,
bodyFormat : "markdown"
} ;
}
function traceFinalResponseRow ( event : TraceEvent , finalText : string ) : TraceEventRow {
function traceFinalResponseRow ( event : TraceEvent , finalText : string , options : TraceDisplayRowsOptions ) : TraceEventRow {
return {
rowId : ` trace-final-response: ${ event . seq ? ? "completed" } ` ,
seq : numberOrNull ( event . seq ) ,
tone : "ok" ,
header : ` ${ traceEventClock ( event ) } 助手最终消息 ` ,
header : ` ${ traceEventClock ( event , options ) } 助手最终消息 ` ,
terminal : true ,
body : finalText ,
bodyFormat : "markdown"
@@ -203,17 +208,17 @@ function traceFinalResponseText(trace: Record<string, unknown>): string | null {
return nonEmptyString ( response ? . text ? ? response ? . content ? ? response ? . message ) ;
}
function traceCompletionSummaryRow ( trace : Record < string , unknown > , event : TraceEvent ) : TraceEventRow {
function traceCompletionSummaryRow ( trace : Record < string , unknown > , event : TraceEvent , options : TraceDisplayRowsOptions ) : TraceEventRow {
return {
rowId : ` trace-completion: ${ event . seq ? ? "turn" } ` ,
seq : numberOrNull ( event . seq ) ,
tone : traceEventTone ( event ) ,
header : ` ${ traceEventClock ( event ) } 轮次完成(总耗时 ${ formatTraceDuration ( traceRelativeMs ( trace , event ) ) } ) ` ,
header : ` ${ traceEventClock ( event , options ) } 轮次完成(总耗时 ${ formatTraceDuration ( traceRelativeMs ( trace , event ) ) } ) ` ,
body : null
} ;
}
function traceNoiseSummaryRow ( trace : Record < string , unknown > , events : TraceEvent [ ] ) : TraceEventRow {
function traceNoiseSummaryRow ( trace : Record < string , unknown > , events : TraceEvent [ ] , options : TraceDisplayRowsOptions ) : TraceEventRow {
const lastEvent = events . at ( - 1 ) ;
const status = nonEmptyString ( trace . status ? ? trace . traceStatus ? ? lastEvent ? . status ) ? ? "running" ;
const lastLabel = lastEvent ? readableTraceLabel ( lastEvent ) : "未观测" ;
@@ -221,13 +226,13 @@ function traceNoiseSummaryRow(trace: Record<string, unknown>, events: TraceEvent
rowId : "trace-noise-summary" ,
seq : numberOrNull ( lastEvent ? . seq ) ,
tone : lastEvent ? traceEventTone ( lastEvent ) : "source" ,
header : ` ${ traceEventClock ( lastEvent ) } Trace ${ status } ,等待可读事件 ` ,
header : ` ${ traceEventClock ( lastEvent , options ) } Trace ${ status } ,等待可读事件 ` ,
body : ` 已隐藏 ${ events . length } 条 AgentRun backend 状态事件。最新原始事件: ${ lastLabel } 。 ` ,
bodyFormat : "text"
} ;
}
function traceBackendProgressSummaryRow ( trace : Record < string , unknown > , rows : TraceEventRow [ ] , events : TraceEvent [ ] ) : TraceEventRow | null {
function traceBackendProgressSummaryRow ( trace : Record < string , unknown > , rows : TraceEventRow [ ] , events : TraceEvent [ ] , options : TraceDisplayRowsOptions ) : TraceEventRow | null {
if ( ! Array . isArray ( events ) || events . length === 0 ) return null ;
if ( rows . some ( ( row ) = > row . rowId . startsWith ( "tool:" ) || / 助 手 | 轮 次 完 成 / u . t e s t ( r o w . h e a d e r ) ) ) r e t u r n n u l l ;
const lastRenderedSeq = Math . max ( 0 , . . . rows . map ( ( row ) = > row . seq ? ? 0 ) . filter ( Number . isFinite ) ) ;
@@ -235,16 +240,16 @@ function traceBackendProgressSummaryRow(trace: Record<string, unknown>, rows: Tr
const seq = numberOrNull ( event . seq ) ? ? 0 ;
return seq > lastRenderedSeq && ( isRequestTraceEvent ( event ) || isSetupTraceEvent ( event ) || isNoisyTraceEvent ( event ) ) ;
} ) ;
return hiddenProgress . length > 0 ? traceNoiseSummaryRow ( trace , hiddenProgress ) : null ;
return hiddenProgress . length > 0 ? traceNoiseSummaryRow ( trace , hiddenProgress , options ) : null ;
}
function traceDisplayRow ( trace : Record < string , unknown > , event : TraceEvent ) : TraceEventRow {
function traceDisplayRow ( trace : Record < string , unknown > , event : TraceEvent , options : TraceDisplayRowsOptions ) : TraceEventRow {
const label = readableTraceLabel ( event ) ;
return {
rowId : ` event: ${ event . seq ? ? ` ${ event . label ? ? event . type ? ? "event" } : ${ event . createdAt ? ? "unknown" } ` } ` ,
seq : numberOrNull ( event . seq ) ,
tone : traceEventTone ( event ) ,
header : ` ${ traceEventClock ( event ) } total= ${ formatTraceDuration ( traceRelativeMs ( trace , event ) ) } ${ traceStatusToken ( event ) } ${ label } ` . trim ( ) ,
header : ` ${ traceEventClock ( event , options ) } total= ${ formatTraceDuration ( traceRelativeMs ( trace , event ) ) } ${ traceStatusToken ( event ) } ${ label } ` . trim ( ) ,
body : traceDisplayBody ( event )
} ;
}
@@ -437,13 +442,15 @@ function traceEventTone(event: TraceEvent): TraceEventRow["tone"] {
return "source" ;
}
function traceClock ( value : unknown ) : string {
function traceClock ( value : unknown , options : TraceDisplayRowsOptions ) : string {
const timestamp = nonEmptyString ( value ) ;
if ( timestamp && options . formatClock ) return options . formatClock ( timestamp ) ;
const date = new Date ( String ( value ? ? "" ) ) ;
return Number . isNaN ( date . getTime ( ) ) ? "--:--:--" : date . toISOString ( ) . slice ( 11 , 19 ) ;
}
function traceEventClock ( event : TraceEvent | null | undefined ) : string {
return traceClock ( traceEventTimestamp ( event ) ) ;
function traceEventClock ( event : TraceEvent | null | undefined , options : TraceDisplayRowsOptions ) : string {
return traceClock ( traceEventTimestamp ( event ) , options ) ;
}
function traceWithInferredStart ( trace : Record < string , unknown > , events : TraceEvent [ ] ) : Record < string , unknown > {