Commit 1a5e8b5
authored
🤖 fix: accurate cost estimation for multi-step tool usage (#831)
## Problem
The application was severely underestimating costs for conversations
involving tool calls.
### Root Cause
The Vercel AI SDK provides two usage metrics:
- `streamResult.usage` — Token usage from **the last step only**
- `streamResult.totalUsage` — Sum of token usage across **all steps**
The application was using `usage` instead of `totalUsage`. For a
conversation with 10 tool calls, only ~1/10th of actual consumption was
reported. A $5 conversation would display as $0.50.
### The Complicating Factor
Two UI elements use token data with different semantic requirements:
| Display | Needs | Why |
|---------|-------|-----|
| **Cost** | Sum of all steps | If model read context 10 times, you paid
for 10 reads |
| **Context window** | Last step only | Shows "how full is the
conversation now" for the next request |
Simply switching to `totalUsage` would fix costs but break context
display (showing 500% utilization after many tool calls).
### Cache Creation Tokens
Anthropic's cache creation tokens (`cacheCreationInputTokens`) are:
- Only in provider-specific metadata, not normalized usage
- Need to be summed across all steps
- Not automatically aggregated by the AI SDK
Even with `totalUsage`, cache creation costs were lost unless manually
aggregated from each step's provider metadata.
## Solution
Track both values with different semantic purposes:
**For cost calculation:**
- `usage` / `cumulativeUsage` — total across all steps
- `providerMetadata` / `cumulativeProviderMetadata` — aggregated cache
creation tokens
**For context window display:**
- `contextUsage` / `lastContextUsage` — last step only
- `contextProviderMetadata` — last step only
### Key Changes
1. **Backend** (`streamManager.ts`): Use `totalUsage` for cost, track
`lastStepUsage` for context, aggregate provider metadata across steps
2. **Types**: Extended `StreamEndEvent`, `MuxMetadata`,
`UsageDeltaEvent` with dual fields
3. **Frontend**: `StreamingMessageAggregator` tracks both cumulative and
per-step usage
4. **Store**: `WorkspaceUsageState` provides `usageHistory` (cost) and
`lastContextUsage` (context window)
5. **UI**: Components use appropriate field for their purpose
### Also Fixed
- **OpenAI cached token double-counting**: Gateway models
(`mux-gateway:openai/gpt-5.1`) weren't recognized as OpenAI, causing
cached tokens to be counted in both "Cache Read" and "Input". Now
normalizes gateway model strings before provider detection.
- **Google/Gemini cached token double-counting**: Google, like OpenAI,
reports `inputTokens` inclusive of `cachedInputTokens`. Extended the
subtraction logic to handle Google models.
---
_Generated with `mux`_1 parent 1e3dce5 commit 1a5e8b5
File tree
15 files changed
+847
-61
lines changed- src
- browser
- components
- RightSidebar
- stores
- utils
- compaction
- messages
- common
- types
- utils/tokens
- node/services
15 files changed
+847
-61
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
135 | 135 | | |
136 | 136 | | |
137 | 137 | | |
138 | | - | |
| 138 | + | |
| 139 | + | |
139 | 140 | | |
140 | 141 | | |
141 | 142 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
68 | | - | |
69 | | - | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
70 | 71 | | |
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
74 | 75 | | |
75 | | - | |
| 76 | + | |
| 77 | + | |
76 | 78 | | |
77 | 79 | | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
84 | 90 | | |
85 | 91 | | |
86 | 92 | | |
| |||
109 | 115 | | |
110 | 116 | | |
111 | 117 | | |
112 | | - | |
113 | | - | |
| 118 | + | |
| 119 | + | |
114 | 120 | | |
115 | 121 | | |
116 | 122 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
62 | 66 | | |
63 | 67 | | |
| 68 | + | |
64 | 69 | | |
| 70 | + | |
| 71 | + | |
65 | 72 | | |
66 | | - | |
| 73 | + | |
67 | 74 | | |
| 75 | + | |
| 76 | + | |
68 | 77 | | |
69 | 78 | | |
70 | 79 | | |
| |||
441 | 450 | | |
442 | 451 | | |
443 | 452 | | |
| 453 | + | |
| 454 | + | |
444 | 455 | | |
445 | 456 | | |
446 | 457 | | |
| |||
455 | 466 | | |
456 | 467 | | |
457 | 468 | | |
458 | | - | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
459 | 488 | | |
460 | | - | |
461 | | - | |
462 | 489 | | |
463 | | - | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
464 | 510 | | |
465 | 511 | | |
466 | 512 | | |
| |||
Lines changed: 29 additions & 23 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
| 43 | + | |
| 44 | + | |
44 | 45 | | |
45 | | - | |
| 46 | + | |
| 47 | + | |
46 | 48 | | |
47 | 49 | | |
48 | 50 | | |
| |||
136 | 138 | | |
137 | 139 | | |
138 | 140 | | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
139 | 149 | | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
| 150 | + | |
| 151 | + | |
150 | 152 | | |
151 | 153 | | |
152 | 154 | | |
| |||
232 | 234 | | |
233 | 235 | | |
234 | 236 | | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
235 | 245 | | |
236 | | - | |
237 | | - | |
238 | | - | |
239 | | - | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
| 246 | + | |
| 247 | + | |
246 | 248 | | |
247 | 249 | | |
248 | 250 | | |
| |||
357 | 359 | | |
358 | 360 | | |
359 | 361 | | |
360 | | - | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
361 | 367 | | |
362 | 368 | | |
363 | 369 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
97 | | - | |
98 | | - | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
99 | 100 | | |
100 | 101 | | |
101 | 102 | | |
| |||
0 commit comments