fix(token-meter): dedup repeated msg_id — token accounting was ~2x inflated#165
Merged
Conversation
Claude Code writes one assistant turn as several JSONL lines (one per content/tool_use block), all sharing the same message.id and the same cumulative usage. _iter_new_usage deduped new turns only against the persisted seen_ids, so every repeated line in a read window was logged as a separate token event — roughly doubling est_cost on real sessions (observed: ai-sre token_log 2822 rows / 1409 distinct msg_id -> $993 reported vs ~$415 deduped; gecko-ristra 1260/613 -> $868 vs ~$422). Dedup new_usages by msg_id within the read window, keeping the copy with the most total tokens (the figure the API bills) when a streaming partial and the final line disagree. seen_ids stays as the cross-call safety net. Adds _usage_token_total helper + 3 regression tests (counted-once, keep-most-complete-copy). Rendered to all generated trees. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_rebuild_token_accounting summed every token_log row, so logs already written by the pre-fix runner (one turn split across rows) still rolled up to ~2x cost even after the write-time dedup. Dedup by msg_id at rollup as well (keep the most-complete copy per turn); event_count now reports distinct turns. Extracts _coerce_token_int helper (shared with _usage_token_total). Adds 2 regression tests (write-path repeat, rollup of a dup-containing log). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Problem
MAP's
token_accounting.json/est_cost_usdover-reported cost by ~2.1–2.4×.Claude Code writes a single assistant turn as several JSONL lines (one per content / tool_use block), all sharing the same
message.idand the same cumulativeusage._iter_new_usagededuped new turns only against the persistedseen_ids, so every repeated line in a read window was logged as a separate token event.Confirmed on real sessions:
.map/rca: token_log 2822 rows / 1409 distinct msg_id → reported $993.43 vs $477.80 fixed.Independent ground-truth from the raw Claude Code transcript (keep-max per msg_id) corroborates the corrected figures.
Fix
_iter_new_usage): dedupnew_usagesbymsg_idwithin the read window, keeping the copy with the most total tokens (the figure the API bills) when a streaming partial and the final line disagree.seen_idsremains the cross-call safety net._rebuild_token_accounting): dedup bymsg_idat rollup too, so logs already written by the pre-fix runner self-heal on the next rebuild.event_countnow reports distinct turns._coerce_token_inthelper (shared with_usage_token_total).Prices in
MODEL_TOKEN_PRICESwere already correct — this is purely a token-count dedup bug.Tests
5 regression tests added (write-path repeat counted once, keep-most-complete-copy, rollup of a dup-containing log). Negative-proof: NEW=1 row vs OLD=3 rows on a 3×-repeated turn.
make render-templates+make check-renderclean (propagated to all generated trees)🤖 Generated with Claude Code