fix(pi review): report cumulative token usage · Entire
fix(pi review): report cumulative token usage
14a45e8→main··
dipree·2w ago·2 files·+78 added/-8 removed
Sessions
3f7ff1353636View transcript
[?
Merge Main and Resolve Token Usage ConflictsPi·GPT-5.5·2 steps](/content/gh/entireio/cli/session/019f125d-fea3-7997-8e34-5fe72b63e5ef#timeline-3f7ff1353636/index.html)
Changes
2
cmd/entire/cli/agent/pi
Mreviewer.go+27/-8
Mreviewer_test.go+51
49 unmodified lines
50
51
52
53
54
55
56
57
22 unmodified lines
80
81
82
81
83
84
85
86
12 unmodified lines
99
100
101
100
102
103
104
105
66 unmodified lines
172
173
174
173
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
175
191
192
177
178
179
180
193
194
195
196
197
198
199
200
201
202
49 unmodified lines
scanner := bufio.NewScanner(r)
scanner.Buffer(make([]byte, min(1024*1024, piReviewMaxScannerBuf)), piReviewMaxScannerBuf)
messageIDsWithTextDelta := map[string]struct{}{}
messageIDsWithUsage := map[string]struct{}{}
tokens := reviewtypes.Tokens{}
finished := false
success := true
22 unmodified lines
success = false
}
if env.Message.Usage != nil {
out <- piReviewTokens(env.Message.Usage)
emitPiReviewTokens(out, env, &tokens, messageIDsWithUsage)
}
if _, sawDelta := messageIDsWithTextDelta[env.MessageID()]; !sawDelta {
if text := piReviewMessageText(env.Message.Content); text != "" {
12 unmodified lines
success = false
}
if env.Message.Usage != nil {
out <- piReviewTokens(env.Message.Usage)
emitPiReviewTokens(out, env, &tokens, messageIDsWithUsage)
}
case "agent_end":
finished = true
66 unmodified lines
CacheWrite int `json:"cacheWrite"`
}
func piReviewTokens(usage *piReviewUsage) reviewtypes.Tokens {
func emitPiReviewTokens(out chan<- reviewtypes.Event, env piReviewEnvelope, total *reviewtypes.Tokens, seen map[string]struct{}) {
if env.Message.Usage == nil || total == nil {
return
}
if key := env.MessageID(); key != "" {
if _, ok := seen[key]; ok {
return
}
seen[key] = struct{}{}
}
*total = addPiReviewTokens(*total, env.Message.Usage)
out <- *total
}
func addPiReviewTokens(total reviewtypes.Tokens, usage *piReviewUsage) reviewtypes.Tokens {
if usage == nil {
return reviewtypes.Tokens{}
return total
}
return reviewtypes.Tokens{
In: usage.Input + usage.CacheRead + usage.CacheWrite,
Out: usage.Output,
}
total.In += usage.Input + usage.CacheRead + usage.CacheWrite
total.Out += usage.Output
return total
}
func piReviewTokens(usage *piReviewUsage) reviewtypes.Tokens {
return addPiReviewTokens(reviewtypes.Tokens{}, usage)
}
func piReviewJSONArg(raw json.RawMessage) string {
Mcmd/entire/cli/agent/pi/reviewer.go+27/-8
117 unmodified lines
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
117 unmodified lines
}
}
func TestPiReviewer_ParseTokensAreCumulative(t *testing.T) {
t.Parallel()
input := strings.Join([]string{
`{"type":"agent_start"}`,
`{"type":"message_end","id":"m1","message":{"id":"m1","role":"assistant","usage":{"input":100,"output":50,"cacheRead":10,"cacheWrite":5},"stopReason":"toolUse"}}`,
`{"type":"message_end","id":"m2","message":{"id":"m2","role":"assistant","usage":{"input":200,"output":30,"cacheRead":0,"cacheWrite":0},"stopReason":"stop"}}`,
`{"type":"agent_end"}`,
}, "\n")
events := collectPiReviewEvents(input)
var tokens []reviewtypes.Tokens
for _, ev := range events {
if tok, ok := ev.(reviewtypes.Tokens); ok {
tokens = append(tokens, tok)
}
}
if len(tokens) != 2 {
t.Fatalf("token events = %d, want 2: %#v", len(tokens), events)
}
if got := tokens[0]; got.In != 115 || got.Out != 50 {
t.Fatalf("first Tokens = %#v, want In=115 Out=50", got)
}
if got := tokens[1]; got.In != 315 || got.Out != 80 {
t.Fatalf("final Tokens = %#v, want In=315 Out=80", got)
}
}
func TestPiReviewer_ParseTokensDedupesTurnEndForSameMessage(t *testing.T) {
t.Parallel()
input := strings.Join([]string{
`{"type":"agent_start"}`,
`{"type":"message_end","id":"m1","message":{"id":"m1","role":"assistant","usage":{"input":10,"output":5},"stopReason":"stop"}}`,
`{"type":"turn_end","id":"m1","message":{"id":"m1","role":"assistant","usage":{"input":10,"output":5},"stopReason":"stop"}}`,
`{"type":"agent_end"}`,
}, "\n")
events := collectPiReviewEvents(input)
var tokens []reviewtypes.Tokens
for _, ev := range events {
if tok, ok := ev.(reviewtypes.Tokens); ok {
tokens = append(tokens, tok)
}
}
if len(tokens) != 1 {
t.Fatalf("token events = %d, want 1: %#v", len(tokens), events)
}
if got := tokens[0]; got.In != 10 || got.Out != 5 {
t.Fatalf("Tokens = %#v, want In=10 Out=5", got)
}
}
func collectPiReviewEvents(input string) []reviewtypes.Event {
ch := parsePiReviewOutput(strings.NewReader(input))
var events []reviewtypes.Event