fix(pricing): a missing cache rate prices as a floor, not a silent zero - #48
Merged
Conversation
For a model whose table entry omits cache_read_input_token_cost or cache_creation_input_token_cost, cache tokens were multiplied by an unwrap_or(0.0) rate and the result returned as Cost::Known - "unpriced is not free" broken silently, inside the one number the tool exists to report, on the token kind that dominates a coding-agent corpus. The two absent-rate fallbacks lean opposite ways for the same reason. Cache creation bills at the input rate: providers that publish a write rate set it above input, so this understates. Cache reads bill at zero: their real rate sits below input, so an input fallback would overstate. Both understate, so the result is a new Cost::Floor - shown as `>=` in the Cost view, "floor" in --json, and counted into every total's floor marker through Cost::not_fully_priced, which replaces is_unpriced at each aggregation site. A rate listed as 0.0 still prices as zero exactly, because the table said so: only an absent key floors, and only when the usage actually has tokens of that kind. The parse-time filter keeps cache-only entries now instead of dropping them with the embeddings, and PerToken's cache rates are Option so "absent" survives to the pricing decision. The Cost view's floor title reads "not fully priced" rather than "have no price", because a floored model has one. Closes #16. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kleyt0n
force-pushed
the
fix/cache-rate-floor
branch
from
August 3, 2026 10:27
ac6970b to
944717d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #16.
What
For a model whose LiteLLM entry omits
cache_read_input_token_costorcache_creation_input_token_cost, cache tokens were multiplied by anunwrap_or(0.0)rate and returned asCost::Known— "unpriced is not free" broken silently inside the headline number, on the token kind that dominates a coding-agent corpus. The CHANGELOG claimed input-rate fallback; code and changelog now agree, per the issue's checklist.Semantics chosen
The two fallbacks lean opposite ways for the same reason — never overstate:
Both understate, so the result is a new
Cost::Floor(f64): rendered≥$xin the Cost view,{"state":"floor","usd":x}in--json, and counted into every total's≥marker viaCost::not_fully_priced()(which replacesis_unpriced()at each aggregation site — a floor's dollars are real, unlike unpriced's, so the two stay distinct states). A rate listed as0.0still prices as zero exactly, because the table said so — only an absent key floors, and only when the usage actually has tokens of that kind.Also per the issue's last checkbox: the parse-time zero-cost filter now keeps cache-only entries instead of dropping them with the embeddings, and the Cost view's floor title reads "N model(s) not fully priced" rather than "have no price", because a floored model has one.
Verification
The issue's acceptance test verbatim (
missing_cache_rates_price_as_a_floor_never_a_silent_zero): cache reads + creations against an entry with input/output rates only →Cost::Floor(2.0), non-zero, not exact, not unpriced. Plus: a missing rate with no cache tokens staysKnown; a cache-only entry survives parsing and prices exactly; absent keys parse toNonewhile listed values parse toSome.cargo fmt,clippy -D warnings, all 246 tests pass.Note for review ordering: independent of every open PR; will need the usual one-line CHANGELOG rebase after whichever lands first.
🤖 Generated with Claude Code