router: serve subscription-only below the overdraft floor instead of 402#747
Conversation
Co-Authored-By: Steven Tohme <steven@workweave.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
PR author is not in the allowed authors list. |
End-to-end local test — PASS (3/3 scenarios)Ran the router locally (Docker Compose, managed mode + billing enabled) with a deterministic mock Anthropic upstream, driving real
Scenario A — the fix in action (subscription-only serve + warning)Response headers: Warning injected as content-block index 0, real content re-indexed to 1 (SSE framing preserved): Server log: Mock hit once with the requested model (no scorer substitution); ledger count 0 (no debit), balance unchanged. Scenario B — retryable upstream → refuse, don't reroute to paidMock: exactly one attempt → 429. Ledger count still 0. Scenario C — regression: non-subscription still 402s at the normal thresholdNo mock hit — request never reached the proxy. Harness notes: required a temporary Devin session: https://app.devin.ai/sessions/3d96bf143dbb41d3b11dca19995f2ba7 |
Below the overdraft floor the balance middleware also flags Codex-covered /v1/chat/completions and /v1/responses requests subscription-only, but only ProxyMessages honored the flag. A Codex turn that routed to a paid model would dispatch and debit with no bound. ProxyOpenAIChatCompletion now restricts routing to the caller's own subscription providers, pins dispatch to that binding (no paid failover), refuses with ErrCreditsExhaustedSubscriptionUnavailable when the turn can't serve on the subscription (route resolved to a paid model, or a pre-commit sub failure), and surfaces the depleted-credits warning where the stream allows it. Co-Authored-By: Steven Tohme <steven@workweave.ai>
… drop ineffective OpenAI 402 rewrite
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7de3608. Configure here.
…ing on non-bypass path

Summary
A usage-bypass org whose prepaid balance falls past the
-$5subscription overdraft floor was getting a hard 402 on every request — even though those turns serve for free on the caller's own Anthropic subscription. The balance middleware aborted before the proxy's usage-bypass path could run, so the org was permanently locked out of its own-account traffic (only a top-up above the floor could recover it). Prod confirmed this fororg_ytbWhf0XxuuKLR3XAjtviSl0:subscription_exempt=true,balance_usd_micros=-6988315,threshold=-5000000→ 402, 167/167 requests over 14 days.This changes the below-floor behavior for subscription-covered requests from "402" to "serve subscription-only": the turn flows on the caller's own subscription with paid failover disabled and no debit, so the customer's traffic never stops while our unbilled spend stays bounded at the floor. If the turn can't stay on the subscription (sub exhausted, or requested model isn't subscription-covered), it's refused with a clear 402 rather than billed. The customer also gets a visible warning prepended to the reply explaining credits are depleted and how to top up.
Non-subscription requests are unaffected — they still 402 at the normal balance gate.
How it works
New
billing.SubscriptionOnlyContextKey(mirrors the existingHasOverrideContextKeymarker), set by the middleware and consumed by the proxy:In subscription-only mode the proxy:
util < thresholdgate exists only to conserve quota by routing to a cheaper paid model — pointless when paid failover is off):ProxyMessages, both when the turn never resolved to a bypass passthrough and when a bypass attempt hits a retryable error (which normally reroutes to a paid model):translate.NewAnthropicRoutingMarkerWriter(streaming responses only), always emitted (not gated by the routing-marker opt-out):New sentinel
proxy.ErrCreditsExhaustedSubscriptionUnavailableclassifies to HTTP 402 viaClassifyDispatchError, so the API handlers surface a clear top-up message.Tests
make precommit(fmt/vet/build/test) green.Caveats / follow-up
org_ytbWhf0XxuuKLR3XAjtviSl0until someone with write access tops up its credits above the floor (or adds a billing override). I have read-only prod access.Link to Devin session: https://app.devin.ai/sessions/3d96bf143dbb41d3b11dca19995f2ba7
Requested by: @steventohme