fix(billing): charge 0% on subscription-served turns (Anthropic + Codex)#513
Merged
Conversation
A turn served on the customer's own Claude or Codex subscription OAuth token already has its tokens paid for by that plan, so Weave should not skim a 5% routing margin on top. Debit 0 for these turns (same as the override case); the full notional cost is still recorded on the ledger as a shadow trail. Removes the now-unused SubscriptionFeeRate const and subscriptionFeeMicros helper. Non-subscription (Weave-fronted) turns are unaffected — still debited at cost. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What T-Rex did
Reviews (1): Last reviewed commit: "fix(billing): charge 0% on subscription-..." | Re-trigger Greptile |
rohith500
added a commit
to rohith500/router
that referenced
this pull request
Jun 25, 2026
…ppear in dashboard metrics bypassToAnthropic returned at service.go:1564 before fireTelemetry at line 2178 was ever reached, so turns served via the subscription usage-bypass gate wrote no row to router_request_telemetry. They were invisible to /metrics/details, the model distribution widget, cost savings charts, and the substitution rate stat card. Fix: add fireTelemetry call in bypassToAnthropic after otel.Flush, gated on installationID != uuid.Nil (matching the ProxyMessages gate). Fields follow the same shape as the normal path: - DecisionReason: 'usage_bypass' - SubscriptionServed: true (billing stays 0 per workweave#513) - TurnType: 'main_loop' (bypass only engages on scorer branch) - Costs zeroed (subscription-served turns debit nothing) - UpstreamStatusCode captured before proxyErr is cleared so real 4xx/5xx statuses (e.g. 429) are recorded correctly - UpstreamLatencyMs captured immediately after p.Proxy returns, before otel.Record/Flush, so OTel export time is not included The bypassToAnthropic comment listed what it deliberately skips (scorer, planner, session pin, cache, billing) — telemetry was absent from that list, confirming the omission was unintentional. Also adds TestBypassToAnthropic_RecordsTelemetry_UpstreamError to cover the 4xx path: sets proxyErr to *providers.UpstreamErrorResponse with Status 429 and asserts UpstreamStatusCode == 429 in the telemetry row, guarding against regressions that move the status capture after the proxyErr clear. Manually verified before and after fix: Before: usage_bypass request produces no DB row After: row written with decision_reason=usage_bypass, decision_model=claude-haiku-4-5, provider=anthropic Fixes workweave#516. Signed-off-by: N Rohith Reddy <rohithreddy2202@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Stop charging the 5% routing margin on turns served by the customer's own Claude/Codex subscription OAuth token. Those turns are paid for by the customer's own plan — Weave shouldn't skim a margin on tokens it never paid for.
DebitForInferencenow debits 0 for a subscription-served turn (same as an override pass-through). The full notional cost is still written to the ledger (NotionalCostMicros) as a shadow trail, so we keep visibility without billing. Removes the now-unusedSubscriptionFeeRateconst andsubscriptionFeeMicroshelper.Non-subscription (Weave-fronted / BYOK-routed) turns are unaffected — still debited at cost.
Why
servedOnSubscriptionis true when the resolved credential is a Claude or Codex OAuth subscription token (creds.OAuth), so this covers both. Charging a % of notional on a cost the customer's own plan already covers surprises customers and is out of step with how gateways price routing on customer-owned credentials (the norm is 0% on BYOK/subscription + a platform/seat fee).Note
Revenue corollary for product: with subscription turns at 0%, subscription-heavy customers generate ~$0 of inference revenue (only non-subscription routing draws down credits, at cost). Monetizing those customers will need a per-seat/platform fee — separate decision, not in this PR.
Test
go build ./...clean;go test ./internal/billing/...passes.TestDebitForInference_SubscriptionDebitsOnlyTheFeerenamed →...DebitsNothingand updated to assert a 0 debit.🤖 Generated with Claude Code