Skip to content

feat(billing): alibaba cache write/read tracking - #2323

Merged
steebchen merged 3 commits into
theopenco:mainfrom
RATCHAW:RATCHAW/cache-write-tokens-tracking
May 18, 2026
Merged

steebchen merged 3 commits into
theopenco:mainfrom
RATCHAW:RATCHAW/cache-write-tokens-tracking

Conversation

@RATCHAW

@RATCHAW RATCHAW commented May 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Mirrors Anthropic's cache-write billing for Alibaba Qwen: extracts cache_creation_input_tokens from the nested prompt_tokens_details shape, forwards the cache_control: {type: "ephemeral"} marker to the upstream (stripping unsupported ttl: "1h"), and bills writes at the 1.25× rate.
  • Adds a cacheReadInputPrice schema field plus a request-side explicitCacheUsed flag so cache-read hits bill at Alibaba's 10% explicit rate when cache_control was sent vs. the 20% implicit rate otherwise — a fork Anthropic doesn't need since it only has explicit caching.
  • Pricing data for every Qwen model/tier/region now includes both cacheWriteInputPrice (125%) and cacheReadInputPrice (10%); qwen3-coder-plus cachedInputPrice corrected from a mislabelled 10% to the documented 20%.

Test plan

  • Unit tests across 6 affected suites (280 passing), incl. ratio guards for write/read/implicit prices
  • End-to-end against a live Alibaba upstream: no-marker baseline, cache write, explicit hit (10% rate), implicit hit (20% rate, exactly 2× explicit), streaming, and ttl: "1h" strip

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Improved Alibaba Qwen handling: preserves provider cache control, recognizes cache-creation tokens in usage reports, and applies explicit cache read/write pricing when explicit cache is used—affecting reported usage and billing.
  • Tests
    • Expanded test coverage for Alibaba parsing, streaming transforms, cache-creation tokens, request-body behavior, and pricing/billing scenarios.
  • Chores
    • Updated Alibaba pricing data and pricing type definitions to support cache read/write rates.

Review Change Stack

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

coderabbitai Bot commented May 18, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e1f95b9c-73a5-4082-aaf8-f52453227ca6

📥 Commits

Reviewing files that changed from the base of the PR and between d1278fd and 19cb80a.

📒 Files selected for processing (2)
  • packages/actions/src/prepare-request-body.spec.ts
  • packages/actions/src/prepare-request-body.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/actions/src/prepare-request-body.ts
  • packages/actions/src/prepare-request-body.spec.ts

Walkthrough

Adds explicit-cache detection and billing for Alibaba: pricing types and model data for cache read/write rates, updates cost calculation to accept an explicitCacheUsed flag, normalizes/prepares Alibaba cache_control in requests, extracts/mapping cache creation tokens from responses, and threads the flag through chat billing paths.

Changes

Alibaba Explicit Cache Billing Support

Layer / File(s) Summary
Pricing Type Schema for Cache Read Rate
packages/models/src/models.ts
PricingTier, ProviderRegion, and ProviderModelMapping gain optional cacheReadInputPrice?: Price to support explicit-cache-read billing.
Explicit Cache Cost Calculation Logic
apps/gateway/src/lib/costs.ts, apps/gateway/src/lib/costs.spec.ts
getPricingForTokenCount accepts/returns cacheReadInputPrice. calculateCosts options add explicitCacheUsed?: boolean; when true, cached reads use cacheReadInputPrice with fallbacks. Tests cover qwen-plus behavior and non-Alibaba fallback.
Alibaba Model Pricing with Cache Rates
packages/models/src/models/alibaba.ts
Adds cacheReadInputPrice and cacheWriteInputPrice across Alibaba Qwen models, regions, and tiers, with updated tier ranges and prices.
Alibaba Pricing Relationship Tests
apps/gateway/src/lib/alibaba-pricing.spec.ts
Parameterized tests assert cache write = 1.25× input (5m TTL only), implicit cache hit = 0.2× input, explicit cache hit = 0.1× input across provider/tier/region/region-tier.
Alibaba Token Extraction and Normalization
apps/gateway/src/chat/tools/extract-token-usage.ts, apps/gateway/src/chat/tools/extract-token-usage.spec.ts, apps/gateway/src/chat/tools/parse-provider-response.ts, apps/gateway/src/chat/tools/parse-provider-response.spec.ts, apps/gateway/src/chat/tools/transform-openai-streaming.ts, apps/gateway/src/chat/tools/transform-openai-streaming.spec.ts
Parses prompt_tokens_details.cache_creation_input_tokens for Alibaba into cacheCreationTokens / cacheCreation5mTokens; streaming normalization maps cache_creation_input_tokens to cache_write_tokens and cache_creation_tokens.
Alibaba Cache Control Request Normalization
packages/actions/src/prepare-request-body.ts, packages/actions/src/prepare-request-body.spec.ts
Treats alibaba as cache-control-aware: preserves cache_control but strips unsupported ttl on text content blocks. Tests added for passthrough and TTL stripping; some Anthropic max_tokens regression tests were removed.
Chat Endpoint Explicit Cache Detection and Wiring
apps/gateway/src/chat/chat.ts
Scans incoming messages for cache_control to set explicitCacheUsed and passes it into multiple calculateCosts call sites (streaming, cached, content-filtered, early/final streaming, non-streaming) for billing/logging.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • theopenco/llmgateway#2193: Related changes to gateway cache-related usage/cost computation; this PR focuses on explicit cache-read pricing while #2193 centers on Bedrock cache writes.
  • theopenco/llmgateway#2166: Both modify apps/gateway/src/chat/chat.ts and apps/gateway/src/lib/costs.ts by extending calculateCosts(...) options and threading new flags through cost computation paths.
  • theopenco/llmgateway#2171: Both extend cache-related token cost accounting in the gateway; this PR adds explicit cache-read selection while #2171 added cache-write accounting.

Suggested reviewers

  • steebchen
  • smakosh
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately captures the main feature: adding Alibaba cache write and read tracking for billing purposes, which is the central theme across all changes.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@packages/actions/src/prepare-request-body.ts`:
- Around line 1018-1035: When stripping ttl from the cache_control object in the
asRecord branch, ensure we don't forward an empty cache_control or a record
missing type: after computing const { ttl: _ttl, ...ccRest } = cc, check if
Object.keys(ccRest).length === 0 || !("type" in asRecord); if so, return a copy
of asRecord with cache_control omitted (not set to {}), otherwise return the
spread with cache_control: ccRest; keep mutated = true and preserve the same
return casting/type as used around part and asRecord.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f4087d09-43db-415b-9174-697176e5315b

📥 Commits

Reviewing files that changed from the base of the PR and between 78290b2 and aefc6b0.

📒 Files selected for processing (14)
  • apps/gateway/src/chat/chat.ts
  • apps/gateway/src/chat/tools/extract-token-usage.spec.ts
  • apps/gateway/src/chat/tools/extract-token-usage.ts
  • apps/gateway/src/chat/tools/parse-provider-response.spec.ts
  • apps/gateway/src/chat/tools/parse-provider-response.ts
  • apps/gateway/src/chat/tools/transform-openai-streaming.spec.ts
  • apps/gateway/src/chat/tools/transform-openai-streaming.ts
  • apps/gateway/src/lib/alibaba-pricing.spec.ts
  • apps/gateway/src/lib/costs.spec.ts
  • apps/gateway/src/lib/costs.ts
  • packages/actions/src/prepare-request-body.spec.ts
  • packages/actions/src/prepare-request-body.ts
  • packages/models/src/models.ts
  • packages/models/src/models/alibaba.ts

Comment thread packages/actions/src/prepare-request-body.ts
RATCHAW and others added 2 commits May 18, 2026 19:12
…-tokens-tracking

# Conflicts:
#	packages/actions/src/prepare-request-body.spec.ts
#	packages/actions/src/prepare-request-body.ts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/actions/src/prepare-request-body.spec.ts (1)

1445-1445: ⚡ Quick win

Replace as any with a minimal typed response shape in the new Alibaba tests.

Line 1445 and Line 1490 use as any, which weakens assertions and hides shape regressions in this new path.

Proposed typed test cast
-		)) as any;
+		)) as {
+			messages: Array<{
+				content: Array<{
+					cache_control?: { type: "ephemeral"; ttl?: string };
+				}>;
+			}>;
+		};
...
-		)) as any;
+		)) as {
+			messages: Array<{
+				content: Array<{
+					cache_control?: { type: "ephemeral"; ttl?: string };
+				}>;
+			}>;
+		};

As per coding guidelines, "Never use any type or force cast with as any unless absolutely necessary."

Also applies to: 1490-1490

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

In `@packages/actions/src/prepare-request-body.spec.ts` at line 1445, Replace the
two occurrences of "as any" in the new Alibaba tests with a minimal, explicit
test interface that matches only the properties the assertions access; locate
the casts in the Alibaba-specific tests in prepare-request-body.spec.ts (the two
casts around the new Alibaba test path) and define a small type (e.g.,
MinimalAlibabaResponse) capturing just the fields used by the assertions, then
cast the mocked response to that type instead of any so shape regressions are
caught by the compiler and tests remain strict.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/actions/src/prepare-request-body.spec.ts`:
- Line 1445: Replace the two occurrences of "as any" in the new Alibaba tests
with a minimal, explicit test interface that matches only the properties the
assertions access; locate the casts in the Alibaba-specific tests in
prepare-request-body.spec.ts (the two casts around the new Alibaba test path)
and define a small type (e.g., MinimalAlibabaResponse) capturing just the fields
used by the assertions, then cast the mocked response to that type instead of
any so shape regressions are caught by the compiler and tests remain strict.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cd39dfa9-42ea-4f5b-93f9-811cc5aee8c1

📥 Commits

Reviewing files that changed from the base of the PR and between aefc6b0 and d1278fd.

📒 Files selected for processing (5)
  • apps/gateway/src/chat/chat.ts
  • apps/gateway/src/chat/tools/parse-provider-response.ts
  • packages/actions/src/prepare-request-body.spec.ts
  • packages/actions/src/prepare-request-body.ts
  • packages/models/src/models.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/models/src/models.ts
  • packages/actions/src/prepare-request-body.ts
  • apps/gateway/src/chat/chat.ts

@steebchen
steebchen merged commit 3e7b917 into theopenco:main May 18, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants