@@ -106,9 +106,6 @@ type streamResult struct {
106106 ReasoningContent string
107107 ThinkingSignature string // Used with Anthropic's extended thinking feature
108108 Stopped bool
109- SelfInputTokens int
110- SelfOutputTokens int
111- SelfCost float64
112109}
113110
114111type Opt func (* LocalRuntime )
@@ -386,9 +383,9 @@ func (r *LocalRuntime) RunStream(ctx context.Context, sess *session.Session) <-c
386383 if m != nil {
387384 contextLimit = m .Limit .Context
388385 }
389- // Emit a snapshot that downstream components can use for both self delta and inclusive totals.
386+ // Emit a snapshot that downstream components can use for both per-session and team totals.
390387 inclusiveUsage := buildInclusiveUsageSnapshot (sess , contextLimit )
391- selfUsage := buildSelfUsageDelta ( res . SelfInputTokens , res . SelfOutputTokens , res . SelfCost , contextLimit )
388+ selfUsage := buildSelfUsageSnapshot ( sess , contextLimit )
392389 events <- TokenUsage (sess .ID , a .Name (), selfUsage , inclusiveUsage )
393390
394391 if m != nil && r .sessionCompaction {
@@ -400,7 +397,7 @@ func (r *LocalRuntime) RunStream(ctx context.Context, sess *session.Session) <-c
400397 r .Summarize (ctx , sess , events )
401398 // Refresh usage after compaction since token counts may have changed.
402399 inclusiveUsage := buildInclusiveUsageSnapshot (sess , contextLimit )
403- selfUsage := buildSelfUsageDelta ( 0 , 0 , 0 , contextLimit )
400+ selfUsage := buildSelfUsageSnapshot ( sess , contextLimit )
404401 events <- TokenUsage (sess .ID , a .Name (), selfUsage , inclusiveUsage )
405402 events <- SessionCompaction (sess .ID , "completed" , r .currentAgent )
406403 }
@@ -417,7 +414,7 @@ func (r *LocalRuntime) RunStream(ctx context.Context, sess *session.Session) <-c
417414 r .Summarize (ctx , sess , events )
418415 // Emit the post-compaction snapshot as well for consistency.
419416 inclusiveUsage := buildInclusiveUsageSnapshot (sess , contextLimit )
420- selfUsage := buildSelfUsageDelta ( 0 , 0 , 0 , contextLimit )
417+ selfUsage := buildSelfUsageSnapshot ( sess , contextLimit )
421418 events <- TokenUsage (sess .ID , a .Name (), selfUsage , inclusiveUsage )
422419 events <- SessionCompaction (sess .ID , "completed" , r .currentAgent )
423420 }
@@ -562,36 +559,28 @@ func (r *LocalRuntime) handleStream(ctx context.Context, stream chat.MessageStre
562559 }
563560
564561 if response .Usage != nil {
565- childInputTotal , childOutputTotal := childTokenTotals (sess )
566-
567562 selfInput := response .Usage .InputTokens + response .Usage .CachedInputTokens
568563 selfOutput := response .Usage .OutputTokens + response .Usage .CachedOutputTokens + response .Usage .ReasoningTokens
569564
570- var callCost float64
571565 if m != nil {
572- callCost = (float64 (response .Usage .InputTokens )* m .Cost .Input +
566+ lastCallCost = (float64 (response .Usage .InputTokens )* m .Cost .Input +
573567 float64 (response .Usage .OutputTokens + response .Usage .ReasoningTokens )* m .Cost .Output +
574568 float64 (response .Usage .CachedInputTokens )* m .Cost .CacheRead +
575569 float64 (response .Usage .CachedOutputTokens )* m .Cost .CacheWrite ) / 1e6
576- sess .Cost += callCost
570+ } else {
571+ lastCallCost = 0
577572 }
578573
579- sess .SelfCost = callCost
580- // Update per-call snapshot fields used for UI live view and compaction heuristics
581- sess .SelfInputTokens = selfInput
582- sess .SelfOutputTokens = selfOutput
583- sess .InputTokens = childInputTotal + selfInput
584- sess .OutputTokens = childOutputTotal + selfOutput
585574 // Remember latest self snapshot for adding to lifetime totals when the call finishes
586575 lastSelfInput = selfInput
587576 lastSelfOutput = selfOutput
588- lastCallCost = callCost
589577
590578 modelName := "unknown"
591579 if m != nil {
592580 modelName = m .Name
593581 }
594- telemetry .RecordTokenUsage (ctx , modelName , int64 (response .Usage .InputTokens ), int64 (response .Usage .OutputTokens + response .Usage .ReasoningTokens ), sess .Cost )
582+ liveCost := sess .Cost + lastCallCost
583+ telemetry .RecordTokenUsage (ctx , modelName , int64 (response .Usage .InputTokens ), int64 (response .Usage .OutputTokens + response .Usage .ReasoningTokens ), liveCost )
595584 }
596585
597586 if len (response .Choices ) == 0 {
@@ -600,19 +589,17 @@ func (r *LocalRuntime) handleStream(ctx context.Context, stream chat.MessageStre
600589 choice := response .Choices [0 ]
601590 if choice .FinishReason == chat .FinishReasonStop || choice .FinishReason == chat .FinishReasonLength {
602591 // On finish, fold this call's final self usage into lifetime totals.
603- if lastSelfInput > 0 || lastSelfOutput > 0 {
604- sess .TotalInputTokens += lastSelfInput
605- sess .TotalOutputTokens += lastSelfOutput
592+ if lastSelfInput > 0 || lastSelfOutput > 0 || lastCallCost > 0 {
593+ sess .InputTokens += lastSelfInput
594+ sess .OutputTokens += lastSelfOutput
595+ sess .Cost += lastCallCost
606596 }
607597 return streamResult {
608598 Calls : toolCalls ,
609599 Content : fullContent .String (),
610600 ReasoningContent : fullReasoningContent .String (),
611601 ThinkingSignature : thinkingSignature ,
612602 Stopped : true ,
613- SelfInputTokens : lastSelfInput ,
614- SelfOutputTokens : lastSelfOutput ,
615- SelfCost : lastCallCost ,
616603 }, nil
617604 }
618605
@@ -707,9 +694,9 @@ func (r *LocalRuntime) handleStream(ctx context.Context, stream chat.MessageStre
707694
708695// buildInclusiveUsageSnapshot captures the session's current inclusive usage in the shared event format.
709696func buildInclusiveUsageSnapshot (sess * session.Session , contextLimit int ) * Usage {
710- // Build a live lifetime-inclusive snapshot: lifetime totals + current in-flight self snapshot
711- liveInput := sess .TotalInputTokens + sess . SelfInputTokens
712- liveOutput := sess .TotalOutputTokens + sess . SelfOutputTokens
697+ // Build a lifetime snapshot for this session only.
698+ liveInput := sess .InputTokens
699+ liveOutput := sess .OutputTokens
713700 return & Usage {
714701 ContextLength : liveInput + liveOutput ,
715702 ContextLimit : contextLimit ,
@@ -719,26 +706,14 @@ func buildInclusiveUsageSnapshot(sess *session.Session, contextLimit int) *Usage
719706 }
720707}
721708
722- func buildSelfUsageDelta ( input , output int , cost float64 , contextLimit int ) * Usage {
709+ func buildSelfUsageSnapshot ( sess * session. Session , contextLimit int ) * Usage {
723710 return & Usage {
724- ContextLength : input + output ,
711+ ContextLength : sess . InputTokens + sess . OutputTokens ,
725712 ContextLimit : contextLimit ,
726- InputTokens : input ,
727- OutputTokens : output ,
728- Cost : cost ,
729- }
730- }
731-
732- func childTokenTotals (sess * session.Session ) (int , int ) {
733- childInput := sess .InputTokens - sess .SelfInputTokens
734- if childInput < 0 {
735- childInput = 0
736- }
737- childOutput := sess .OutputTokens - sess .SelfOutputTokens
738- if childOutput < 0 {
739- childOutput = 0
713+ InputTokens : sess .InputTokens ,
714+ OutputTokens : sess .OutputTokens ,
715+ Cost : sess .Cost ,
740716 }
741- return childInput , childOutput
742717}
743718
744719// processToolCalls handles the execution of tool calls for an agent
@@ -1063,23 +1038,11 @@ func (r *LocalRuntime) handleTaskTransfer(ctx context.Context, sess *session.Ses
10631038
10641039 sess .ToolsApproved = s .ToolsApproved
10651040
1066- sess .Cost += s .Cost
1067- // Mirror cost behavior: once the child finishes, fold its token usage into the parent.
1068- // Keep per-turn inclusive fields for compaction heuristics, and separately track lifetime totals.
1069- childInputTotal , childOutputTotal := childTokenTotals (sess )
1070- childInputTotal += s .InputTokens
1071- childOutputTotal += s .OutputTokens
1072- sess .InputTokens = childInputTotal + sess .SelfInputTokens
1073- sess .OutputTokens = childOutputTotal + sess .SelfOutputTokens
1074- // Lifetime cumulative totals include completed child session totals
1075- sess .TotalInputTokens += s .TotalInputTokens
1076- sess .TotalOutputTokens += s .TotalOutputTokens
1077-
10781041 sess .AddSubSession (s )
10791042
10801043 // Emit an updated token usage snapshot so the UI sees the merged totals immediately.
10811044 inclusiveUsage := buildInclusiveUsageSnapshot (sess , 0 )
1082- selfUsage := buildSelfUsageDelta ( 0 , 0 , 0 , 0 )
1045+ selfUsage := buildSelfUsageSnapshot ( sess , 0 )
10831046 parentAgentName := ca
10841047 evts <- TokenUsage (sess .ID , parentAgentName , selfUsage , inclusiveUsage )
10851048
0 commit comments