fix: include cache-write tokens in quota audit (#577) - #739
Conversation
|
| Filename | Overview |
|---|---|
| tokenjam/core/optimize/analyzers/model_downgrade.py | Adds four-bucket per-model counterfactual aggregation, but unpriceable groups still cause incomplete dollar totals. |
| tests/unit/test_opus_quota_audit.py | Adds focused regression coverage for cache-write alternative pricing and fully priceable mixed-model sessions. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Flagged premium turns] --> B[Group by provider and original model]
B --> C[Accumulate input, output, cache-read, cache-write, and observed cost]
C --> D[Resolve each model's downgrade]
D --> E[Price alternative token mix]
E --> F[Aggregate actual and alternative audit totals]
Reviews (4): Last reviewed commit: "Merge branch 'main' into fix/577-cache-w..." | Re-trigger Greptile
042a674 to
a092926
Compare
|
Addressed the mixed-model pricing finding. Quota-audit counterfactuals now aggregate token classes by original model and price each group at its own downgrade; cache-write tokens are passed explicitly. Added mixed-model regression coverage. Validation: 25 quota-audit tests and 53 related optimize tests passed; Ruff and mypy passed. |
| alt = lookup_downgrade(provider, model) if provider else None | ||
| if not alt: | ||
| continue |
There was a problem hiding this comment.
When a flagged mixed-model session contains one priceable group and another premium-model group with a missing provider or no matching downgrade, this branch omits the unresolved group's observed and alternative costs while its tokens remain in the audit population. The CLI and API consequently present incomplete dollar totals as the actual billed amount and counterfactual cost without indicating partial pricing coverage.
Knowledge Base Used:
Prompt To Fix With AI
This is a comment left during a code review.
Path: tokenjam/core/optimize/analyzers/model_downgrade.py
Line: 1170-1172
Comment:
**Partial costs appear complete**
When a flagged mixed-model session contains one priceable group and another premium-model group with a missing provider or no matching downgrade, this branch omits the unresolved group's observed and alternative costs while its tokens remain in the audit population. The CLI and API consequently present incomplete dollar totals as the actual billed amount and counterfactual cost without indicating partial pricing coverage.
**Knowledge Base Used:**
- [Usage data lifecycle](https://app.greptile.com/metabldr/-/custom-context/knowledge-base/metabuilder-labs/tokenjam/-/docs/usage-data-lifecycle.md)
- [Optimization analysis and recommendations](https://app.greptile.com/metabldr/-/custom-context/knowledge-base/metabuilder-labs/tokenjam/-/docs/optimization-analysis.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.|
@DhruvGarg111 thanks for this — the arithmetic is right and I want to land it. Two small asks on the PR body first, plus context on the CI you've been fighting. The red CI was never yours. I traced the cost path before approving, since "add a token type to a cost figure" is the kind of change that goes wrong quietly:
Greptile's P1 about mixed-model cache pricing was real, and worth saying clearly: it described a defect that already existed on Two asks, both on the body:
One observation, no action needed: Once the body has those two items and CI is green, this is ready. |
anilmurty
left a comment
There was a problem hiding this comment.
Thanks @DhruvGarg111 — merging. The arithmetic is right where it matters: cache-write is priced as its own term rather than folded into input, the four token buckets stay disjoint so nothing double-counts, and at=pricing_agg.first_turn_at keeps the counterfactual on the original traffic's instant. The correction moves the headline figure down, which is the honest direction — the alt side was understated, so the implied saving was overstated.
I expanded the PR body at merge time to record the before/after figure #577 asks for (a cache-heavy example window: $3.85 → $2.85 recoverable, 26% of overstatement removed) and to describe the per-model split from your second commit, which the body did not mention. The code is entirely yours.
Triaging Greptile's two P1s rather than merging past them:
-
"Mixed-model cache pricing is wrong" — valid against your first commit, and it described a defect that already existed on main. Your
pricing_by_modelsplit fixes it, andtest_api_counterfactual_prices_mixed_models_per_modelfails against main, so it is pinned. Resolved. -
"Partial costs appear complete" — a real observation, not a blocker here, and not introduced by this PR. Both totals exclude the same groups and move together, so the delta is a fair comparison and "Same work" scopes the sentence. The missing piece is a coverage indication, and that predates you: before the per-model split, a session whose dominant model had no downgrade was dropped whole from both sides, so
actual_cost_usdwas never the user's full spend. Your change covers strictly more real work than before. Filed as #742 rather than dropped.
The red CI you were fighting was main's expired claude-sonnet-5 introductory rate, fixed in #741 — nothing to do with your work.
|
Thanks @anilmurty , by the way i am looking forward to contributing more to your repo. |
Fixes the quota-audit alternative-cost calculation by carrying cache-write tokens through the session aggregate and including them in alternative pricing. Adds regression coverage for cache-read and cache-write pricing.
Closes #577
Summary
cache_write_tokensthrough the per-session aggregate and price them on the alternative-model side, closing the last_alt_unit_costcall site that omitted them._ModelCostAgg), so a mixed-model session no longer prices every model's tokens at the dominant model's downgrade.Root cause
The actual side of the counterfactual reads the stored
cost_usd, whichcalculate_costcomputes across all four token classes. The alternative side hand-rolled its arithmetic over three, silently dropping cache-write. The two sides were therefore measuring different things, and the difference between them — the headline recoverable figure — was inflated by the full cache-write cost of the candidate.Before / after headline figure
One example window: 10 cache-heavy Opus sessions, each 20k input / 2k output / 150k cache-read / 40k cache-write,
claude-opus-4-8→claude-sonnet-5, priced at 2026-08-15.Actual window cost is $4.75 in both. The fix removes $1.00 of overstatement, 26% of the old headline, on a window shaped to make cache-write matter. The correction always moves the number down: the alt side was understated, so the implied saving was overstated. A less cache-heavy window moves less.
What's also in this PR
The per-model split goes beyond what #577 asked for (an accumulator on
_SessionAgg). It is a genuine correctness fix in the same function, surfaced by Greptile's P1 on the first commit, and it describes a defect that already existed onmainrather than one this PR introduced:agg.new_input/output/rereadwere summed across every premium model in a session and priced atdominant_model()'s alternative.It carries one behaviour change worth naming: a session whose dominant model had no downgrade used to be dropped whole from both
actual_costandalternative_cost. Each model is now included or excluded independently. Both sides move together, so the pair stays coherent for the downstream consumer incli/cmd_quota_audit.py.Tests / Verification
test_api_counterfactual_prices_mixed_models_per_model— fails againstorigin/main, pinning the per-model split.model_downgrade.pytoorigin/main.lint,test-ts,version-lockstep.main's expiredclaude-sonnet-5introductory rate, unrelated to this PR and fixed in fix(tests): pin the pricing instant so a dated rate can't break main #741.What's NOT in this PR
TurnComposition.quota_weighted_tokensweights cache-write at 1.0, and this PR does not touch it — only the implied-dollar counterfactual. Anthropic's docs state cache hits are not deducted against rate limits but say nothing explicit about cache creation, so the 1.0 weight is plausible and unverified. Out of scope here.models.tomldoes not model at all. Pre-existing._SessionAgg'snew_input/output/reread/costare now duplicated bypricing_by_modeland survive only to feed_example_for. Left as is; two aggregations of the same turns can drift, so worth revisiting if that function is touched again.Body expanded by the maintainer at merge time to record the before/after figure required by #577's acceptance criteria and to describe the second commit. The code is entirely @DhruvGarg111's.