Skip to content

Commit 1d5ad81

Browse files
committed
minor
1 parent 7e2a530 commit 1d5ad81

File tree

3 files changed

+28
-86
lines changed

3 files changed

+28
-86
lines changed

pkg/runtime/runtime.go

Lines changed: 22 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -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

114111
type 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.
709696
func 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

pkg/session/session.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,6 @@ type Session struct {
6868
InputTokens int `json:"input_tokens"`
6969
OutputTokens int `json:"output_tokens"`
7070
Cost float64 `json:"cost"`
71-
// Lifetime/cumulative token counters across the entire session (self + children).
72-
TotalInputTokens int `json:"total_input_tokens,omitempty"`
73-
TotalOutputTokens int `json:"total_output_tokens,omitempty"`
74-
// Self* fields track the most recent provider-reported usage for this session only (no children).
75-
SelfInputTokens int `json:"self_input_tokens"`
76-
SelfOutputTokens int `json:"self_output_tokens"`
77-
SelfCost float64 `json:"self_cost"`
7871
}
7972

8073
// Message is a message from an agent

pkg/tui/components/sidebar/sidebar.go

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,12 @@ func (m *model) SetTokenUsage(event *runtime.TokenUsageEvent) { // updates usage
105105
}
106106

107107
if event.SessionID != "" {
108-
if selfUsage != nil {
109-
m.accumulateSessionUsage(event.SessionID, selfUsage)
108+
snapshot := cloneUsage(selfUsage)
109+
if snapshot == nil {
110+
snapshot = cloneUsage(inclusiveUsage)
111+
}
112+
if snapshot != nil {
113+
m.usageState.sessions[event.SessionID] = snapshot
110114
}
111115

112116
if inclusiveUsage != nil { // store inclusive (lifetime) snapshot per session for legacy fallbacks
@@ -317,24 +321,6 @@ func cloneUsage(u *runtime.Usage) *runtime.Usage { // helper to copy runtime usa
317321
return &clone // return pointer to independent copy
318322
}
319323

320-
func (m *model) accumulateSessionUsage(sessionID string, delta *runtime.Usage) {
321-
if sessionID == "" || delta == nil {
322-
return
323-
}
324-
current := m.usageState.sessions[sessionID]
325-
if current == nil {
326-
current = &runtime.Usage{}
327-
m.usageState.sessions[sessionID] = current
328-
}
329-
current.InputTokens += delta.InputTokens
330-
current.OutputTokens += delta.OutputTokens
331-
current.ContextLength += delta.ContextLength
332-
if delta.ContextLimit > current.ContextLimit {
333-
current.ContextLimit = delta.ContextLimit
334-
}
335-
current.Cost += delta.Cost
336-
}
337-
338324
func (m *model) renderTotals() (string, *runtime.Usage) { // resolves label + totals for display
339325
totals := m.computeTeamTotals() // compute aggregate usage first
340326
if totals == nil { // ensure downstream code always receives a struct

0 commit comments

Comments
 (0)