Skip to content

fix(core): context tokens from exact totals, not the rounded percentage - #37

Merged
leeguooooo merged 1 commit into
leeguooooo:mainfrom
CelChe:fix/exact-ctx-tokens
Jul 26, 2026
Merged

fix(core): context tokens from exact totals, not the rounded percentage#37
leeguooooo merged 1 commit into
leeguooooo:mainfrom
CelChe:fix/exact-ctx-tokens

Conversation

@CelChe

@CelChe CelChe commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Problem

_context_window_usage derives the displayed token count from
context_window.used_percentage, which Claude Code sends as a whole
number
— while total_input_tokens / total_output_tokens in the same
payload are exact.

That makes the readout quantise to 1% of the window. On a 200k model that is
a 2k error and nobody notices. On a 1M-context model it is 10k: the bar steps
60.0k → 70.0k → 80.0k and can sit ~10k off the truth.

Real payloads captured from v3.32.0 session buckets:

total_input_tokens bar showed error
354,031 350.0k 4.0k
75,354 70.0k 5.4k
63,824 60.0k 3.8k

Fix

Prefer the exact totals; keep the percentage-derived value as the fallback
for payloads that omit them (older Claude Code, some relays) or send them
malformed.

ctx_pct is deliberately left as reported — the percentage and its
severity colour stay byte-identical to what Claude Code itself shows. Only
the token count changes.

Before / after

before:  ◆ Opus 5(70.0k/1.0M)
after:   ◆ Opus 5(75.4k/1.0M)

Tests

Three cases added to tests/test_core_ctx_pct.py: exact totals win, absent
totals fall back to the percentage, malformed totals fall back to the
percentage. Full suite run before and after — identical pass/fail sets (the
18 pre-existing failures on this Windows checkout are untouched by the diff).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved context-window usage reporting by prioritizing exact input and output token totals.
    • Added a reliable fallback to percentage-based estimates when token totals are unavailable or invalid.
    • Reduced inaccuracies caused by rounding context usage percentages.
  • Tests

    • Added coverage for exact totals, missing totals, and malformed token data.

Claude Code reports `context_window.used_percentage` as a whole number and
`total_input_tokens` / `total_output_tokens` exactly. `_context_window_usage`
derived the token count from the percentage, so the readout quantised to 1%
of the window.

On a 200k model that is a 2k error — invisible. On a 1M-context model it is
10k: the bar steps 60.0k → 70.0k → 80.0k and can sit ~10k off the truth.

Observed on 1M sessions (real payloads, v3.32.0):

  reported tokens | bar showed
  354,031         | 350.0k
   75,354         |  70.0k
   63,824         |  60.0k

Use the reported totals when present, keep the percentage-derived value as
the fallback for payloads that omit them (older Claude Code, some relays).
`ctx_pct` is deliberately left as reported, so the percentage and its
severity colour stay identical to what Claude Code itself shows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Context window usage now derives used tokens from valid exact input and output totals, falling back to percentage-based calculation when totals are missing or malformed. Tests cover exact totals, absent totals, malformed totals, and preservation of the reported percentage.

Changes

Context token usage

Layer / File(s) Summary
Exact token calculation and fallback
src/claude_statusbar/core.py, tests/test_core_ctx_pct.py
Adds validation and summation of exact token totals, prioritizes them in context usage, and tests percentage fallback for absent or malformed totals.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: leeguooooo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: using exact token totals instead of the rounded percentage for context usage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/claude_statusbar/core.py`:
- Around line 730-743: Update _exact_used_tokens to require both
total_input_tokens and total_output_tokens to be present, valid non-negative
integers before summing; reject missing, empty, malformed, negative, and
fractional values by returning None so callers use the percentage fallback. Add
a regression test covering a payload with one missing total.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ac998013-40f0-4196-b63a-b2fecfc0d1ce

📥 Commits

Reviewing files that changed from the base of the PR and between b33bb9e and d95d0f1.

📒 Files selected for processing (2)
  • src/claude_statusbar/core.py
  • tests/test_core_ctx_pct.py

Comment on lines +730 to +743
def _exact_used_tokens(stdin_data: Dict[str, Any]) -> Optional[int]:
"""Context tokens as Claude Code reported them, or None when unavailable.

None means "no usable signal" — an older Claude Code that omits the
totals, a relay payload that zeroes them, or a malformed value. Callers
fall back to deriving the count from the percentage.
"""
total = 0
for key in ('total_input_tokens', 'total_output_tokens'):
try:
total += int(stdin_data.get(key, 0) or 0)
except (TypeError, ValueError):
return None
return total or None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Require both complete, non-negative token totals.

.get(key, 0) or 0 silently treats a missing or empty field as zero, so a payload with only one total is used as an exact count instead of falling back to the percentage. Negative values and fractional floats are also accepted by int(). Require both fields and reject non-integer or negative values before summing; add a regression test for a single missing total.

As per path instructions, malformed Claude JSONL must not produce incorrect token calculations.

Proposed fix
-    total = 0
+    totals = []
     for key in ('total_input_tokens', 'total_output_tokens'):
+        if key not in stdin_data:
+            return None
+        raw = stdin_data[key]
+        if raw is None or isinstance(raw, bool):
+            return None
         try:
-            total += int(stdin_data.get(key, 0) or 0)
+            value = int(raw)
         except (TypeError, ValueError):
             return None
-    return total or None
+        if value < 0 or (isinstance(raw, float) and not raw.is_integer()):
+            return None
+        totals.append(value)
+    total = sum(totals)
+    return total or None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _exact_used_tokens(stdin_data: Dict[str, Any]) -> Optional[int]:
"""Context tokens as Claude Code reported them, or None when unavailable.
None means "no usable signal"an older Claude Code that omits the
totals, a relay payload that zeroes them, or a malformed value. Callers
fall back to deriving the count from the percentage.
"""
total = 0
for key in ('total_input_tokens', 'total_output_tokens'):
try:
total += int(stdin_data.get(key, 0) or 0)
except (TypeError, ValueError):
return None
return total or None
def _exact_used_tokens(stdin_data: Dict[str, Any]) -> Optional[int]:
"""Context tokens as Claude Code reported them, or None when unavailable.
None means "no usable signal"an older Claude Code that omits the
totals, a relay payload that zeroes them, or a malformed value. Callers
fall back to deriving the count from the percentage.
"""
totals = []
for key in ('total_input_tokens', 'total_output_tokens'):
if key not in stdin_data:
return None
raw = stdin_data[key]
if raw is None or isinstance(raw, bool):
return None
try:
value = int(raw)
except (TypeError, ValueError):
return None
if value < 0 or (isinstance(raw, float) and not raw.is_integer()):
return None
totals.append(value)
total = sum(totals)
return total or None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/claude_statusbar/core.py` around lines 730 - 743, Update
_exact_used_tokens to require both total_input_tokens and total_output_tokens to
be present, valid non-negative integers before summing; reject missing, empty,
malformed, negative, and fractional values by returning None so callers use the
percentage fallback. Add a regression test covering a payload with one missing
total.

Source: Path instructions

CelChe added a commit to CelChe/claude-code-usage-bar that referenced this pull request Jul 25, 2026
Fork-local. This machine is Windows and installs with `pip install -e`, so
none of this is reachable here:

  install.sh, web-install.sh, uninstall.sh   POSIX shell installers
  publish.sh                                 upstream's PyPI release script
  packaging/                                 PyInstaller spec for the
                                             standalone macOS/Linux binary
  .github/workflows/release-binaries.yml     builds that binary; would break
                                             once packaging/ is gone

Nothing in `src/` imports any of them. `updater.py`'s INSTALL_SH_URL points at
upstream's raw copy on GitHub, not the local file, so the frozen-binary hint
still resolves.

ci.yml is kept on purpose — it is the only signal that changes here still work
on the platform upstream targets, which matters while PRs leeguooooo#37-leeguooooo#40 are open.

Everything else non-Windows (service.py, the macOS HUD, the /proc + ps
branches in daemon.py, darwin paths, POSIX docs) is recorded in TODO.md with
the reason each was deferred: they all sit inside files upstream edits often,
so removing them makes every future merge a conflict inside live code.

923 passed, 3 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@leeguooooo
leeguooooo merged commit 5987973 into leeguooooo:main Jul 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants