fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(server): stop overpricing cached Claude tokens - #8806

Merged
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing
Aug 30, 2026
Merged

fix(server): stop overpricing cached Claude tokens#8806
maria-rcks merged 1 commit into
pingdotgg:mainfrom
SunkenInTime:t3code/fix-claude-usage-pricing

Conversation

@SunkenInTime

@SunkenInTimeSunkenInTime commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The Usage page can overprice cached Claude tokens when LiteLLM publishes canonical and provider-qualified entries with the same bare model name. The parser stripped provider prefixes before inserting rates, so a later entry without the Anthropic cache discount could overwrite the canonical rate and price cache reads as ordinary input.

This builds on the exact-key approach in #8529 by @none23. @mackinleysmith identified one important edge case during that review: multiple qualified entries are safe to alias when their effective rates are identical. Treating those entries as ambiguous leaves models such as Claude Opus 4.6 and Claude Haiku 4.5 unpriced.

What changed

  • Preserve each LiteLLM rate under its full normalized key.
  • Keep canonical bare entries authoritative.
  • Add a bare alias when every qualified entry has the same effective rate.
  • Leave genuinely conflicting bare names unpriced instead of guessing.

Verification

  • vp test run apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts apps/server/src/usage/usageScanCache.test.ts
  • vp lint apps/server/src/usage/usagePricing.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageScanCache.ts apps/server/src/usage/usageScanCache.test.ts
  • vp run --filter t3 typecheck
  • Replayed the parser against the current LiteLLM table and confirmed Claude Opus 5, Claude Fable 5, Claude Opus 4.6, Claude Haiku 4.5, and GPT-5.2 Chat all retain prices.

Closes#5797.

Built with GPT-5.6 Sol in the Codex harness via T3 Code.

Note

Fix cached Claude token overpricing by preserving provider-qualified keys in lookupRate

  • Changes normalizeRateKey to trim and lowercase keys without stripping provider prefixes, so parseRateTable stores full qualified keys like anthropic/claude-3.5-sonnet
  • lookupRate now matches the exact normalized key; it no longer falls back to the bare model name automatically, which previously caused a cheaper cache entry to match a more expensive non-cache rate
  • Adds bare-name aliases (e.g. claude-3.5-sonnet) only when no canonical bare entry exists and all provider-qualified entries for that name share the same rate; conflicting rates leave the bare name unpriced
  • Adds sameRate and bareModelName helpers; refactors normalizeModelName to delegate to normalizeRateKey then bareModelName
  • Risk: any rate table relying on bare-name fallback for a model with provider-specific rates will now return unpriced instead of guessing; check rate table configs in apps/server/src/usage/usagePricing.ts and any callers of lookupRate that expect bare-name matching

Macroscope summarized 63bf1f8.


Note

Medium Risk
Changes how usage costs are computed for models with multiple LiteLLM keys; incorrect aliasing could misprice or leave models unpriced, but behavior is covered by new tests and avoids silent overwrites.

Overview
Fixes cached Claude token overpricing when LiteLLM publishes both a canonical bare model entry (with cache-read discounts) and provider-qualified entries that share the same trailing name. Rates are no longer collapsed to bare names at insert time, so a later DeepInfra-style row cannot overwrite the canonical cache-read rate.

parseRateTable now stores each entry under its full normalized key (trim + lowercase). It may add a bare-name alias only when no canonical bare key exists and every qualified variant agrees on input, output, and cache rates; conflicting bare names stay unpriced (lookupRate returns null).

lookupRate matches the normalized transcript model string against the table directly (still blocking ambiguous family names like opus/sonnet). normalizeModelName keeps the same bare-name contract for callers that strip provider prefixes.

Adds usagePricing.test.ts for normalization, Fable vs DeepInfra ordering, unanimous aliasing, and ambiguous bare names. Scan-cache comments were updated to reference lookupRate instead of normalizeModelName.

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

@coderabbitai

coderabbitaiBot commented Aug 30, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 1fbff086-af1d-4c82-a012-eecf26e7cb3a

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 30, 2026
@SunkenInTime
SunkenInTimeforce-pushed the t3code/fix-claude-usage-pricing branch from a8024c0 to 63bf1f8CompareAugust 30, 2026 21:19
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This focused production fix changes token-cost and cache-savings calculations in existing usage summaries. Because those calculations have direct billing and metering implications, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@maria-rcks
maria-rcks merged commit 9072aa1 into pingdotgg:mainAug 30, 2026
26 checks passed
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
**Full Changelog**: pingdotgg/t3code@v0.0.37-nightly.20260830.1232...v0.0.37-nightly.20260831.1233
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37-nightly.20260831.1233
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 31, 2026
## What's Changed
* Remove Messages Glass Lab experiment by @juliusmarminge in pingdotgg/t3code#8599
* Require human review for pull requests changing product defaults by @juliusmarminge in pingdotgg/t3code#8603
* fix(codex): avoid quadratic app-server input buffering by @juliusmarminge in pingdotgg/t3code#8605
* fix(mobile): stabilize iOS header item transitions by @juliusmarminge in pingdotgg/t3code#8607
* chore(mobile): upgrade to Expo SDK 57 by @juliusmarminge in pingdotgg/t3code#8609
* fix(mobile): harden native header toolbar items by @juliusmarminge in pingdotgg/t3code#8611
* fix(server): stop querying Claude context usage after turns by @t3dotgg in pingdotgg/t3code#8610
* chore: vouch ryanrhughes by @t3dotgg in pingdotgg/t3code#8613
* feat(web): attach PDFs, ZIPs, and other files to a turn by @t3dotgg in pingdotgg/t3code#8236
* feat(web): keybinding settings as settings rows by @StiensWout in pingdotgg/t3code#8532
* feat: let an environment publish themes as a file by @ryanrhughes in pingdotgg/t3code#8569
* fix(web): clean up provider settings list and editor by @StiensWout in pingdotgg/t3code#8504
* fix(web): keep project picker popup inside the sidebar by @SunkenInTime in pingdotgg/t3code#8627
* fix(mobile): prevent header overflow and back-button artifacts by @juliusmarminge in pingdotgg/t3code#8624
* fix(server): retry automatic thread title generation by @Bil0000 in pingdotgg/t3code#8087
* fix(client-runtime): refresh edited pull request comments by @Bil0000 in pingdotgg/t3code#8094
* fix(web): four composer spacing defects by @Bil0000 in pingdotgg/t3code#8090
* perf(desktop): skip duplicate browser updates by @Bil0000 in pingdotgg/t3code#8018
* fix(web): render nested markdown images correctly by @flamboh in pingdotgg/t3code#8501
* fix(web): unify activity logs and composer banners by @juliusmarminge in pingdotgg/t3code#8693
* fix(mobile): reduce dev-client reload and Metro startup cost by @juliusmarminge in pingdotgg/t3code#8694
* revert(web): restore previous composer banners by @t3dotgg in pingdotgg/t3code#8733
* test(web): remove tests for unreachable helpers by @t3-code[bot] in pingdotgg/t3code#8738
* feat(mobile): update tool summaries and chat transitions by @juliusmarminge in pingdotgg/t3code#8793
* feat(web): play video attachments in chat by @Bil0000 in pingdotgg/t3code#8688
* fix(web,mobile): snooze menu no longer offers the same wake time twice by @vitalyiegorov in pingdotgg/t3code#8741
* fix(grok): allow model changes in existing threads by @ahmed-besic in pingdotgg/t3code#8392
* feat(mobile): pick, share, and receive files in threads by @t3dotgg in pingdotgg/t3code#8237
* fix(web): reduce title bar scroll fade height by @maria-rcks in pingdotgg/t3code#8799
* fix(windows): strip quotes from repaired PATH by @UtkarshUsername in pingdotgg/t3code#8746
* fix(web): open agent images in expanded preview by @maria-rcks in pingdotgg/t3code#8807
* fix(git): follow repository instructions in generated source control text by @maria-rcks in pingdotgg/t3code#8804
* fix(server): stop overpricing cached Claude tokens by @SunkenInTime in pingdotgg/t3code#8806
* fix(web): keep image preview above sidebar control by @maria-rcks in pingdotgg/t3code#8811
* fix(web): keep right panel synced with agent edits by @maria-rcks in pingdotgg/t3code#8803
* fix(web,mobile): render Codex citations and artifact templates by @Yash-Singh1 in pingdotgg/t3code#8584
* chore: add Windows setup script to t3.json by @UtkarshUsername in pingdotgg/t3code#8814
* fix(web): fold interim turn responses by @maria-rcks in pingdotgg/t3code#8828
* fix(web): use circle alert for failed tool calls by @maria-rcks in pingdotgg/t3code#8840
* feat(mobile): add offline iPhone voice input by @t3dotgg in pingdotgg/t3code#8614
* fix(web): prevent pull request metadata overlap by @MatthewFeroz in pingdotgg/t3code#8790
## New Contributors
* @ryanrhughes made their first contribution in pingdotgg/t3code#8569
* @ahmed-besic made their first contribution in pingdotgg/t3code#8392
* @MatthewFeroz made their first contribution in pingdotgg/t3code#8790
**Full Changelog**: pingdotgg/t3code@v0.0.36...v0.0.37
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Usage pricing is order-dependent across provider-qualified models

2 participants

@SunkenInTime@maria-rcks