Skip to content

fix(proxy): consume UsageBypass on OpenAI chat completions#776

Open
rohith500 wants to merge 3 commits into
workweave:mainfrom
rohith500:fix/openai-usage-bypass-consumer
Open

fix(proxy): consume UsageBypass on OpenAI chat completions#776
rohith500 wants to merge 3 commits into
workweave:mainfrom
rohith500:fix/openai-usage-bypass-consumer

Conversation

@rohith500

@rohith500 rohith500 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

runTurnLoop already sets UsageBypass=true for both wire formats when the per-installation gate engages, but only ProxyMessages consumed it via bypassToAnthropic (strict subscription pass-through, skip ledger, and on a retryable 429 fall through to routeFor). ProxyOpenAIChatCompletion ignored routeRes.UsageBypass and continued normal dispatch.

  • Happy path: often looked fine by coincidence (same turn-loop decision + OAuth).
  • 429 fallthrough: Messages recovered via scorer reroute; OpenAI returned a raw 429 to the client.

Confirmed unintentional incomplete port in #775 (same class as #771). No overlap with #762 (preemptive exhaustion suppress) or #773 (post-dispatch baseline / subscription-credit retry).

Expected vs actual (pre-fix → post-fix)

Happy path (gate on, util 0.20, requested claude-sonnet-4-6)

Messages OpenAI pre-fix OpenAI post-fix
Client 200 200 (coincidence) 200 via bypassAnthropicOpenAI
x-router-decision usage_bypass usage_bypass usage_bypass
Scorer 0 0 0
Path bypassToAnthropic normal OpenAI dispatch bypassAnthropicOpenAI (skip billing)
Response shape Anthropic Messages chat.completion chat.completion

Weekly-limit 429 on the bypass attempt

Messages OpenAI pre-fix (#775) OpenAI post-fix
Client 200 raw 429 200
Decision after cluster:v0.2 stuck usage_bypass cluster:v0.2
Model after scorer pick (claude-haiku-4-5) requested scorer pick
Scorer calls 1 0 1
Dispatches 2 (bypass 429 → routed OK) 3× OAuth 429 2 (bypass 429 → routed OK)

Subscription-only + bypass 429

Messages OpenAI pre-fix OpenAI post-fix
Result ErrCreditsExhaustedSubscriptionUnavailable raw 429 (no refuse) ErrCreditsExhaustedSubscriptionUnavailable
Scorer 0 0 0
Dispatches 1 (bypass only) 3× OAuth 1

Fix

  1. bypassAnthropicOpenAI (usage_bypass.go) — OpenAI-wire counterpart to bypassToAnthropic:

    • PrepareAnthropic + NewSSETranslator → OpenAI client response
    • resolve subscription credentials; skip ledger (early return)
    • errBypassRetryable on providers.IsRetryable pre-commit
    • non-retryable buffered upstream → flushBufferedIfPresent
    • emit router.usage_bypass span (same attrs as Messages)
  2. ProxyOpenAIChatCompletion after runTurnLoop — mirror Messages (service.go ~2168):

    if routeRes.UsageBypass && routeRes.Decision.Provider == providers.ProviderAnthropic {
        err := s.bypassAnthropicOpenAI(...)
        if !errors.Is(err, errBypassRetryable) { return err }
        if billing.SubscriptionOnlyFromContext(ctx) {
            return ErrCreditsExhaustedSubscriptionUnavailable
        }
        // clear UsageBypass, refresh subsidy, routeFor, continue
    }
  3. turnLoopResult.UsageBypass godoc — names both surfaces.

Design: second helper (not a shared Messages/OpenAI abstraction) because emit/flush differ (flushUpstreamErrorAsAnthropic vs flushBufferedIfPresent + SSE translator). Eligibility / fallthrough control flow matches Messages exactly. ProxyMessages untouched (diff only in OpenAI entry + new helper).

What changed

File Change
internal/proxy/usage_bypass.go Add bypassAnthropicOpenAI
internal/proxy/service.go Consume UsageBypass in ProxyOpenAIChatCompletion
internal/proxy/turnloop.go Godoc: both surfaces
internal/proxy/openai_usage_bypass_test.go Happy / 429 fallthrough / subscription-only refuse

Live verification (committed tip 7994e67)

Re-ran the same bypassFixture shapes used in #775 against the committed branch (throwaway probe; not committed):

POST-FIX 429 Messages: status=200 decision="cluster:v0.2" model="claude-haiku-4-5" scorer=1 dispatches=2
POST-FIX 429 OpenAI:   status=200 decision="cluster:v0.2" model="claude-haiku-4-5" scorer=1 dispatches=2
POST-FIX SUB-ONLY 429: err=credits exhausted… scorer=0 calls=1

Pre-fix OpenAI 429 (from #775): status=429, scorer=0, anthCalls=3, decision stuck on usage_bypass.

Billing

Successful OpenAI bypass now skips the ledger (early return), matching Messages bypassToAnthropic — not the prior coincidence path’s emitBilling delta=0 notional row.

Not in scope / not overlapping

Test plan

  • TestProxyOpenAI_UsageBypass_WeeklyLimit_FallsBackToRoutedDispatch + subscription-only refuse fail on main, pass here
  • Happy-path OpenAI UsageBypass regression kept
  • Full TestUsageBypass_* / TestBypass_* / Messages weekly-limit suite green
  • make check green
  • go test ./internal/proxy/... -race clean except known TestFireTelemetryRecoversFromPanic flake
  • ProxyMessages region unchanged in diff
  • New comments ≤2 consecutive lines (comment-length review)

Fixes #775

Made with Cursor

rohith500 and others added 2 commits July 17, 2026 14:19
Lock in bypass weekly-limit fallthrough and subscription-only refuse
on ProxyOpenAIChatCompletion before wiring the Messages consumer.

Signed-off-by: N Rohith Reddy <rohithreddy2202@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire bypassAnthropicOpenAI after runTurnLoop so Anthropic usage-bypass
turns get skip-billing pass-through and 429→routeFor fallthrough,
matching ProxyMessages.

Signed-off-by: N Rohith Reddy <rohithreddy2202@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown

PR author is not in the allowed authors list.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 7994e67. Configure here.

Comment thread internal/proxy/usage_bypass.go
Subscription-only streaming marker must not commit SSE before upstream;
Discard on retryable bypass so 402 refuse leaves no partial stream.

Signed-off-by: N Rohith Reddy <rohithreddy2202@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@rohith500

Copy link
Copy Markdown
Contributor Author

Addressed Bugbot: bypassAnthropicOpenAI now wraps the client writer in preludeBuffer, runs subscription-only OpenAIRoutingMarkerWriter.Prelude into that buffer, Seals before upstream, and Discards on errBypassRetryable so a streaming 429→402 refuse never leaves a committed SSE preamble.

Added TestSubscriptionOnly_OpenAI_BypassRetryable_Stream_NoPartialCommit.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Thanks @rohith500 — this one follows the repo conventions nicely and needs no changes. 👍 The UsageBypass consumer sits on proxy.Service (right layer), reuses the ErrCreditsExhaustedSubscriptionUnavailable sentinel and errBypassRetryable, uses providers.Provider* constants and slog throughout, and the tests are non-tautological (scorer-skip, 429 reroute, subscription-only refusal, and the buffered-prelude no-partial-commit stream case are all real behavior guards). The prelude-buffer why-comment is exactly the kind AGENTS.md wants. Nice work!

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.

ProxyOpenAIChatCompletion never consumes UsageBypass: bypassToAnthropic + 429→routeFor fallthrough absent

1 participant