Commit 68ce046
committed
fix(desktop): count the paused leg's tokens and web searches
A streaming turn that Anthropic pauses (`stop_reason: pause_turn`) is finished
by a non-streaming continuation whose result is spliced into the open SSE body.
That splice reported usage as `merge_stream_usage(message_start, continuation)`,
which takes output tokens from the continuation alone — so the streamed leg was
dropped from both the client's usage chunk and the Firestore `record_llm_usage`
row.
The dropped leg is not a rounding error: it is the leg that ran the web search.
A turn pauses *because* a server tool ran, so its `usage.server_tool_use`
carries the `web_search_requests` Anthropic bills per request (see
`compute_cost`, `WEB_SEARCH_COST_PER_REQUEST`), plus every output token spent
before the pause (thinking and the search query). Every paused turn was
therefore billed as if its first half never happened.
The non-streaming lane already sums every leg (`accumulate_anthropic_usage` in
transport.rs). Extract `paused_turn_total_usage()` so the streaming splice
totals the same way: merge `message_start` with the pausing `message_delta` to
make the streamed leg whole, then accumulate the continuation on top. A pause
that reported no usage falls back to `message_start`, never to less than
before.
Verified: cargo test 382 passed; both new tests fail on the pre-fix expression
(input 130 vs 230, 110 vs 200) and pass on the fix; cargo fmt --check and cargo
clippy --all-targets -D warnings clean. The live Anthropic pause_turn round
trip is not driveable on demand and the upstream URL is a constant with no test
seam, so the fix is proven through the production usage-totalling function
rather than against the provider.
Failure-Class: none1 parent 188dd54 commit 68ce046
3 files changed
Lines changed: 87 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
167 | 168 | | |
168 | 169 | | |
169 | 170 | | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
170 | 193 | | |
171 | 194 | | |
172 | 195 | | |
| |||
547 | 570 | | |
548 | 571 | | |
549 | 572 | | |
550 | | - | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
551 | 578 | | |
552 | 579 | | |
553 | 580 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1329 | 1329 | | |
1330 | 1330 | | |
1331 | 1331 | | |
| 1332 | + | |
| 1333 | + | |
| 1334 | + | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
| 1342 | + | |
| 1343 | + | |
| 1344 | + | |
| 1345 | + | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + | |
| 1349 | + | |
| 1350 | + | |
| 1351 | + | |
| 1352 | + | |
| 1353 | + | |
| 1354 | + | |
| 1355 | + | |
| 1356 | + | |
| 1357 | + | |
| 1358 | + | |
| 1359 | + | |
| 1360 | + | |
| 1361 | + | |
| 1362 | + | |
| 1363 | + | |
| 1364 | + | |
| 1365 | + | |
| 1366 | + | |
| 1367 | + | |
| 1368 | + | |
| 1369 | + | |
| 1370 | + | |
| 1371 | + | |
| 1372 | + | |
| 1373 | + | |
| 1374 | + | |
| 1375 | + | |
| 1376 | + | |
| 1377 | + | |
| 1378 | + | |
| 1379 | + | |
| 1380 | + | |
| 1381 | + | |
| 1382 | + | |
| 1383 | + | |
| 1384 | + | |
| 1385 | + | |
| 1386 | + | |
| 1387 | + | |
| 1388 | + | |
1332 | 1389 | | |
1333 | 1390 | | |
1334 | 1391 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
153 | 153 | | |
154 | 154 | | |
155 | 155 | | |
156 | | - | |
| 156 | + | |
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
| |||
0 commit comments