@@ -161,6 +161,39 @@ export interface CompressionGraphData {
161161 olderCompressedTokens : number
162162 remainingTokens : number
163163 totalSessionTokens : number
164+ segments : CompressionGraphSegment [ ]
165+ }
166+
167+ type CompressionGraphSegmentType = "system" | "recentCompressed" | "olderCompressed" | "inContext"
168+
169+ export interface CompressionGraphSegment {
170+ type : CompressionGraphSegmentType
171+ tokens : number
172+ }
173+
174+ function appendGraphSegment (
175+ segments : CompressionGraphSegment [ ] ,
176+ type : CompressionGraphSegmentType ,
177+ tokens : number ,
178+ ) : void {
179+ if ( tokens <= 0 ) {
180+ return
181+ }
182+
183+ const last = segments [ segments . length - 1 ]
184+ if ( last && last . type === type ) {
185+ last . tokens += tokens
186+ return
187+ }
188+
189+ segments . push ( { type, tokens } )
190+ }
191+
192+ function incrementMapValue ( map : Map < string , number > , key : string , value : number ) : void {
193+ if ( value <= 0 ) {
194+ return
195+ }
196+ map . set ( key , ( map . get ( key ) || 0 ) + value )
164197}
165198
166199function countMessageTokensExcludingPrunedTools ( state : SessionState , msg : WithParts ) : number {
@@ -256,19 +289,44 @@ export function buildCompressionGraphData(
256289) : CompressionGraphData {
257290 const toolParentMap = buildToolParentMap ( messages )
258291 const prunedMessageIds = new Set ( state . prune . messages . keys ( ) )
292+ const messageIds = new Set ( messages . map ( ( m ) => m . info . id ) )
259293
260294 let compressedMessageTokens = 0
261295 for ( const tokens of state . prune . messages . values ( ) ) {
262296 compressedMessageTokens += tokens
263297 }
264298
299+ const recentStandaloneByMessage = new Map < string , number > ( )
300+ const olderStandaloneByMessage = new Map < string , number > ( )
301+
302+ let unparentedRecentStandaloneTokens = 0
303+ let unparentedOlderStandaloneTokens = 0
265304 let compressedStandaloneToolTokens = 0
305+ let recentStandaloneToolTokens = 0
266306 for ( const [ toolId , toolTokens ] of state . prune . tools . entries ( ) ) {
267307 const parentMessageId = toolParentMap . get ( toolId )
268308 if ( parentMessageId && prunedMessageIds . has ( parentMessageId ) ) {
269309 continue
270310 }
311+
271312 compressedStandaloneToolTokens += toolTokens
313+
314+ const isRecent = newToolIds . has ( toolId )
315+ if ( isRecent ) {
316+ recentStandaloneToolTokens += toolTokens
317+ }
318+
319+ if ( parentMessageId ) {
320+ incrementMapValue (
321+ isRecent ? recentStandaloneByMessage : olderStandaloneByMessage ,
322+ parentMessageId ,
323+ toolTokens ,
324+ )
325+ } else if ( isRecent ) {
326+ unparentedRecentStandaloneTokens += toolTokens
327+ } else {
328+ unparentedOlderStandaloneTokens += toolTokens
329+ }
272330 }
273331
274332 const compressedTotalTokens = compressedMessageTokens + compressedStandaloneToolTokens
@@ -278,25 +336,21 @@ export function buildCompressionGraphData(
278336 recentMessageTokens += state . prune . messages . get ( messageId ) || 0
279337 }
280338
281- let recentStandaloneToolTokens = 0
282- for ( const toolId of newToolIds ) {
283- const parentMessageId = toolParentMap . get ( toolId )
284-
285- if ( parentMessageId && newMessageIds . has ( parentMessageId ) ) {
286- continue
287- }
339+ const recentCompressedTokens = recentMessageTokens + recentStandaloneToolTokens
340+ const olderCompressedTokens = Math . max ( 0 , compressedTotalTokens - recentCompressedTokens )
288341
289- if ( parentMessageId && prunedMessageIds . has ( parentMessageId ) ) {
342+ const summaryTokensByAnchor = new Map < string , number > ( )
343+ let summaryTokensTotal = 0
344+ for ( const summary of state . compressSummaries ) {
345+ if ( ! messageIds . has ( summary . anchorMessageId ) ) {
290346 continue
291347 }
292348
293- recentStandaloneToolTokens += state . prune . tools . get ( toolId ) || 0
349+ const tokens = countTokens ( summary . summary )
350+ summaryTokensTotal += tokens
351+ incrementMapValue ( summaryTokensByAnchor , summary . anchorMessageId , tokens )
294352 }
295353
296- const recentCompressedTokens = recentMessageTokens + recentStandaloneToolTokens
297- const olderCompressedTokens = Math . max ( 0 , compressedTotalTokens - recentCompressedTokens )
298-
299- const messageIds = new Set ( messages . map ( ( m ) => m . info . id ) )
300354 let remainingTokens = 0
301355
302356 for ( const msg of messages ) {
@@ -309,23 +363,54 @@ export function buildCompressionGraphData(
309363 remainingTokens += countMessageTokensExcludingPrunedTools ( state , msg )
310364 }
311365
312- for ( const summary of state . compressSummaries ) {
313- if ( ! messageIds . has ( summary . anchorMessageId ) ) {
314- continue
315- }
316- remainingTokens += countTokens ( summary . summary )
317- }
366+ remainingTokens += summaryTokensTotal
318367
319368 const systemTokens = state . systemPromptTokens ?? 0
320369 const totalSessionTokens =
321370 systemTokens + recentCompressedTokens + olderCompressedTokens + remainingTokens
322371
372+ const segments : CompressionGraphSegment [ ] = [ ]
373+ appendGraphSegment ( segments , "system" , systemTokens )
374+
375+ for ( const msg of messages ) {
376+ const messageId = msg . info . id
377+ const summaryTokens = summaryTokensByAnchor . get ( messageId ) || 0
378+ appendGraphSegment ( segments , "inContext" , summaryTokens )
379+
380+ if ( prunedMessageIds . has ( messageId ) ) {
381+ const messageTokens = state . prune . messages . get ( messageId ) || 0
382+ appendGraphSegment (
383+ segments ,
384+ newMessageIds . has ( messageId ) ? "recentCompressed" : "olderCompressed" ,
385+ messageTokens ,
386+ )
387+ } else if ( ! ( msg . info . role === "user" && isIgnoredUserMessage ( msg ) ) ) {
388+ const messageTokens = countMessageTokensExcludingPrunedTools ( state , msg )
389+ appendGraphSegment ( segments , "inContext" , messageTokens )
390+ }
391+
392+ appendGraphSegment (
393+ segments ,
394+ "recentCompressed" ,
395+ recentStandaloneByMessage . get ( messageId ) || 0 ,
396+ )
397+ appendGraphSegment (
398+ segments ,
399+ "olderCompressed" ,
400+ olderStandaloneByMessage . get ( messageId ) || 0 ,
401+ )
402+ }
403+
404+ appendGraphSegment ( segments , "recentCompressed" , unparentedRecentStandaloneTokens )
405+ appendGraphSegment ( segments , "olderCompressed" , unparentedOlderStandaloneTokens )
406+
323407 clog . info ( C . COMPRESS , "Compression graph token accounting" , {
324408 systemTokens,
325409 recentCompressedTokens,
326410 olderCompressedTokens,
327411 remainingTokens,
328412 totalSessionTokens,
413+ segments : segments . length ,
329414 } )
330415
331416 return {
@@ -334,6 +419,7 @@ export function buildCompressionGraphData(
334419 olderCompressedTokens,
335420 remainingTokens,
336421 totalSessionTokens,
422+ segments,
337423 }
338424}
339425
@@ -359,18 +445,31 @@ function allocateSegmentWidths(values: number[], total: number, width: number):
359445}
360446
361447export function formatCompressionGraph ( data : CompressionGraphData , width : number = 50 ) : string {
362- const values = [
363- data . systemTokens ,
364- data . recentCompressedTokens ,
365- data . olderCompressedTokens ,
366- data . remainingTokens ,
367- ]
368- const chars = [ "▌" , "⣿" , "░" , "█" ]
369- const segmentWidths = allocateSegmentWidths ( values , data . totalSessionTokens , width )
448+ const segments : CompressionGraphSegment [ ] =
449+ data . segments . length > 0
450+ ? data . segments
451+ : [
452+ { type : "system" , tokens : data . systemTokens } ,
453+ { type : "recentCompressed" , tokens : data . recentCompressedTokens } ,
454+ { type : "olderCompressed" , tokens : data . olderCompressedTokens } ,
455+ { type : "inContext" , tokens : data . remainingTokens } ,
456+ ]
457+
458+ const chars : Record < CompressionGraphSegmentType , string > = {
459+ system : "▌" ,
460+ recentCompressed : "⣿" ,
461+ olderCompressed : "░" ,
462+ inContext : "█" ,
463+ }
464+ const segmentWidths = allocateSegmentWidths (
465+ segments . map ( ( segment ) => segment . tokens ) ,
466+ data . totalSessionTokens ,
467+ width ,
468+ )
370469
371470 let bar = ""
372- for ( let i = 0 ; i < segmentWidths . length ; i ++ ) {
373- bar += chars [ i ] . repeat ( Math . max ( 0 , segmentWidths [ i ] ) )
471+ for ( let i = 0 ; i < segments . length ; i ++ ) {
472+ bar += chars [ segments [ i ] . type ] . repeat ( Math . max ( 0 , segmentWidths [ i ] ) )
374473 }
375474
376475 if ( bar . length < width ) {
0 commit comments