-
Notifications
You must be signed in to change notification settings - Fork 2
Accuracy and Pricing
Two questions decide whether this tool is worth trusting: are the token counts right? and is the cost right? They are answered separately — counts come from careful parsing reconciled against ccusage; cost comes from a transparent, read-time pricing grid.
Claude Code writes a single assistant message as several JSONL lines carrying
progressive snapshots of the same response — output_tokens grows line by
line, and the model can even change mid-message. Naively summing every line
double-counts and inflates totals by roughly 2.5×.
The parser counts each response exactly once:
-
Progressive snapshots — for a given
message.id + requestIdit keeps the largest usage snapshot; ties break to the first line, so a message straddling midnight belongs to its start day. -
Global dedup — deduplication is across all files, not per session.
That is what corrects resumed /
--resumesessions, where the same records are replayed into a new file. -
Fake prompts filtered —
isMetaentries,<command-name>/<local-command-stdout>blocks,[Request interrupted by user], and the synthetic post-compaction "continuation" message are kept out ofprompts.csvand out of categorization, while their token usage is still counted against the session. -
Sidechain / sub-agent policy — inline
isSidechainevents and separatesubagents/*.jsonlfiles are parsed and their cost rolled into the parent prompt (so nothing is silently under-counted), but excluded from the parent'sassistant_turns/tool_calls. -
Cache writes split by TTL —
cache_write_5m(1.25×) vscache_write_1h(2×);server_tool_userequests counted separately (billed per request).
These totals reconcile bucket-for-bucket with bunx ccusage --json (day ×
model) on real history. The reconciliation is scripted in
scripts/reconcile_ccusage.py — it is not a one-time claim but a re-runnable
check.
The parser is also pinned against fixtures per Claude Code version
(tests/fixtures/claude-code-<version>/, captured with
scripts/capture_fixture.py), so a format change in a new Claude Code release
surfaces as a failing test rather than silent drift.
On every run, each command prints its data source, and extract ends with a
loud report — files read / skipped, unknown event types, unpriced models, Claude
Code versions seen — so a silent format break shows up immediately.
tokens.csv carries two dimensions that keep usage attributable and priceable:
-
session_id/modelstay denormalized, so pseudo-prompt usage (<session>:_continuation, with noprompts.csvrow) is still attributable. -
is_sidechain(0/1) splits subagent usage as a dimension.
Summing over is_sidechain reproduces per-prompt totals exactly. Any schema
change must keep per-prompt sums identical and re-reconcile with ccusage —
that is the V7 invariant, and it is tested (requests.csv sums also equal
tokens.csv per prompt, exactly).
Costs are computed at read time from raw token counts, never stored. That is why changing a price never requires a re-extract.
The rates live in a generic multi-provider grid,
prompt_analytics/data/pricing.yml, loaded and validated by pricing.py. Grid
sections:
-
providers:— each a rate card of per-token prices, optionallyper_request(forserver_tool_use). Two ship by default:-
anthropic— published API rates. -
copilot— the GitHub Copilot equivalent, socompareanswers "what would this usage cost billed through Copilot instead of the API?"
-
-
plans:— Claude flat-rate subscriptions, used bybreak-even. -
copilot_plans:— GitHub Copilot subscription tiers plus their bundled AI-credit allowance, used by the Copilot cost view (overage beyond the allowance is per-token on thecopilotgrid). -
updated_at:— the grid's date stamp.
Model lookup: get_model_pricing() does an exact match first, then the
longest-prefix match (version suffixes stripped), so a newly released point
version is priced by its family until the grid is updated. Long-context
surcharges are handled by is_long_context().
Add your own rate card under providers: (an internal plan, a Bedrock tier,
a negotiated rate) or pass --pricing ./my.yml. See CONTRIBUTING.md for the
schema and lookup rules.
Drift control: the bundled grid is kept honest by a weekly CI drift job that checks it against LiteLLM and the live GitHub Copilot pricing page, so the defaults don't silently rot.
See Architecture for where pricing sits in the pipeline and Codebase Guide for the files to touch when you add a provider.