Skip to content

fix: Align token usage metrics for both Claude and Codex#1943

Merged
juliusmarminge merged 2 commits intopingdotgg:mainfrom
ashvinnihalani:t3code/port-token-clamp
Apr 12, 2026
Merged

fix: Align token usage metrics for both Claude and Codex#1943
juliusmarminge merged 2 commits intopingdotgg:mainfrom
ashvinnihalani:t3code/port-token-clamp

Conversation

@ashvinnihalani
Copy link
Copy Markdown
Contributor

@ashvinnihalani ashvinnihalani commented Apr 12, 2026

What Changed

Aligned the Claude token usage normalization with the same shared semantics used by the Codex adapter.

Both adapters now expose the context window fields with the same meaning:

usedTokens - the current in-window token usage
totalProcessedTokens - the accumulated total processed across requests when it is larger than usedTokens
maxTokens - the reported model context window
On the Claude side, this change makes sure accumulated result totals are not surfaced as current context usage. Instead, the current usage is bounded to the reported context window and the larger accumulated total is preserved in totalProcessedTokens.

Why

The context window UI depends on consistent normalized token usage across providers.

Codex already separates current usage from accumulated processed totals. Claude could previously report accumulated totals as current usage, which made the shared context window UI inconsistent and misleading.

This change aligns the Claude adapter with Codex semantics so the same UI can be used for both providers.

This does not solve the remaining gaps around compaction details or compact_boundary behavior. That still needs follow-up investigation in a separate PR.

UI Changes

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Fix token usage metrics to clamp usedTokens to the Claude context window

  • normalizeClaudeTokenUsage in ClaudeAdapter.ts now clamps usedTokens to the model's context window, while preserving totalProcessedTokens when it exceeds the clamped value.
  • After a task_progress snapshot, the final turn completion keeps usedTokens from the earlier observed value and only updates totalProcessedTokens if the accumulated total is larger.
  • maxClaudeContextWindowFromModelUsage is refactored to use typed ModelUsage records from @anthropic-ai/claude-agent-sdk, removing redundant finite/positive guards.
  • Behavioral Change: thread.token-usage.updated events will now report usedTokens capped at maxTokens (context window) rather than raw totals that could exceed it.
📊 Macroscope summarized f0ad5d5. 1 file reviewed, 1 issue evaluated, 0 issues filtered, 1 comment posted

🗂️ Filtered Issues


Note

Medium Risk
Changes how Claude token usage is normalized and emitted, which can affect context-window UI and any downstream logic relying on thread.token-usage.updated semantics. Scoped to usage accounting/typing with added tests to reduce regression risk.

Overview
Aligns Claude token-usage semantics with the shared provider contract. normalizeClaudeTokenUsage now treats usage.total_tokens as an accumulated processed total, clamps usedTokens to the reported model contextWindow, and surfaces oversize totals via totalProcessedTokens.

On turn completion, Claude now preserves the last in-window snapshot (from task_progress) as the current usedTokens while still updating totalProcessedTokens from the final result totals when larger. Adds regression tests covering oversize clamping and ensuring accumulated totals remain correct after progress snapshots.

Reviewed by Cursor Bugbot for commit f0ad5d5. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 12, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2e149606-94ea-4fb6-9bd9-83b55e5d0287

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions bot added size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 12, 2026
@ashvinnihalani ashvinnihalani changed the title Clamp Claude token usage to reported context window fix: Align token usuage metrics for both Claude and Codex Apr 12, 2026
@ashvinnihalani ashvinnihalani force-pushed the t3code/port-token-clamp branch from 88a4d08 to 75b5cea Compare April 12, 2026 03:47
@github-actions github-actions bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels Apr 12, 2026
@ashvinnihalani ashvinnihalani force-pushed the t3code/port-token-clamp branch from 75b5cea to e9cfd8e Compare April 12, 2026 03:58
@ashvinnihalani ashvinnihalani marked this pull request as ready for review April 12, 2026 03:59
@ashvinnihalani ashvinnihalani changed the title fix: Align token usuage metrics for both Claude and Codex fix: Align token usage metrics for both Claude and Codex Apr 12, 2026
macroscopeapp[bot]
macroscopeapp bot previously approved these changes Apr 12, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Apr 12, 2026

Approvability

Verdict: Needs human review

This PR changes runtime behavior for token usage metrics tracking, introducing clamping logic and modifying how accumulated tokens are calculated. An open review comment identifies a potential bug where removed type guards could allow NaN values to corrupt token tracking state.

You can customize Macroscope's approvability policy. Learn more.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@macroscopeapp macroscopeapp bot dismissed their stale review April 12, 2026 07:52

Dismissing prior approval to re-evaluate f0ad5d5

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Low

function maxClaudeContextWindowFromModelUsage(

In maxClaudeContextWindowFromModelUsage, the new implementation removes defensive runtime type checks for value.contextWindow. If the SDK returns malformed data where contextWindow is undefined or NaN, the function returns NaN instead of undefined. This corrupts context.lastKnownContextWindow (line 1330) since NaN !== undefined passes, causing silent degradation of token usage tracking. Consider restoring the type and finiteness checks to ensure malformed values are treated as undefined.

🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/ClaudeAdapter.ts around line 277:

In `maxClaudeContextWindowFromModelUsage`, the new implementation removes defensive runtime type checks for `value.contextWindow`. If the SDK returns malformed data where `contextWindow` is `undefined` or `NaN`, the function returns `NaN` instead of `undefined`. This corrupts `context.lastKnownContextWindow` (line 1330) since `NaN !== undefined` passes, causing silent degradation of token usage tracking. Consider restoring the type and finiteness checks to ensure malformed values are treated as undefined.

Evidence trail:
apps/server/src/provider/Layers/ClaudeAdapter.ts lines 277-290 (REVIEWED_COMMIT) - new implementation without defensive checks

git_diff MERGE_BASE..REVIEWED_COMMIT showing old code had: `typeof contextWindow !== "number" || !Number.isFinite(contextWindow) || contextWindow <= 0` checks that are now removed

apps/server/src/provider/Layers/ClaudeAdapter.ts lines 1328-1330 (REVIEWED_COMMIT) - shows `if (resultContextWindow !== undefined)` check that would pass NaN

apps/server/src/provider/Layers/ClaudeAdapter.ts line 1340, 1345, 2025, 2057 - shows lastKnownContextWindow is used in token tracking

JavaScript Math.max() behavior: Math.max(0, undefined) returns NaN

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f0ad5d5. Configure here.

@juliusmarminge juliusmarminge merged commit 7a00846 into pingdotgg:main Apr 12, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants