Repository navigation
fix(inference): route Codex OAuth chat to the subscription backend (#5353) - #5371
Conversation
…inyhumansai#5353) After importing Codex CLI / OpenAI OAuth, chat still routed through the regular OpenAI API (api.openai.com, gpt-5.5) and failed with HTTP 400 "Stream must be set to true". resolve_openai_codex_routing decided Codex-vs-standard by string-comparing two independently resolved OAuth tokens: bearer_key (from lookup_key_for_slug) and credentials.access_token (a second lookup_openai_oauth_credentials read). Each read can refresh the token, and OpenAI rotates the access token on every refresh, so near expiry the two reads diverged, the equality check failed, and routing silently fell back to standard OpenAI — which rejects a non-streaming gpt-5.x request with that 400. Decide on which credential is stored, not on token equality. OAuth and API-key credentials share the same provider:openai profile store and differ only by AuthProfileKind, so a new openai_bearer_is_oauth() helper mirrors lookup_key_for_slug's precedence (provider:openai, then bare openai) and reports the *kind* of the winning profile — rotation-immune. resolve_openai_codex_routing takes that as bearer_is_oauth and routes to the Codex backend when it is true and the OAuth metadata resolved (for the account id); an API-key profile keeps the standard path. Both call sites (resolve_cloud_slug, list-models) pass it. openai_codex is widened to pub(crate) so the flow test can drive routing directly. Regression test: with only OAuth stored, routing reaches the Codex backend even when the bearer passed in differs from the stored access token (the rotation the old check tripped on); adding an active API key restores the standard path. Once routing is correct the request uses the Responses API, so the streaming 400 no longer arises.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 33 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
Comment |
|
| Filename | Overview |
|---|---|
| src/openhuman/inference/provider/factory.rs | Adds openai_bearer_is_oauth helper and wires it into resolve_cloud_slug; the helper's fallback path has a redundant double lookup_openai_oauth_credentials call and its credential-emptiness check diverges slightly from lookup_key_for_slug's is_empty() gate. |
| src/openhuman/inference/provider/openai_codex.rs | Adds bearer_is_oauth: bool parameter to resolve_openai_codex_routing and replaces the token-equality check with bearer_is_oauth && credentials.is_some(); logic is correct and the fix is well-targeted. |
| src/openhuman/inference/openai_oauth/flow_tests.rs | Adds a regression test covering the rotation scenario (stale bearer string still routes to Codex) and the API-key-wins case; test structure and assertions are correct. |
| src/openhuman/inference/provider/mod.rs | Widens openai_codex module visibility from private to pub(crate) so the new test in flow_tests.rs can reach resolve_openai_codex_routing; minimal and appropriate change. |
| src/openhuman/inference/provider/ops/models.rs | Mirrors the bearer_is_oauth precomputation pattern from factory.rs at the list-models call site; consistent with the rest of the fix. |
Sequence Diagram
sequenceDiagram
participant Caller
participant factory as resolve_cloud_slug / list_models
participant helper as openai_bearer_is_oauth
participant store as AuthProfilesStore
participant routing as resolve_openai_codex_routing
participant oauth as lookup_openai_oauth_credentials
Caller->>factory: "request with slug=openai"
factory->>helper: openai_bearer_is_oauth(config)
helper->>store: get_profile(provider:openai)
alt profile with credential found
store-->>helper: Ok(Some(profile))
helper-->>factory: "profile.kind == OAuth"
else no stored profile with credential
helper->>oauth: lookup_openai_oauth_credentials(config)
oauth-->>helper: Some / None
helper-->>factory: credentials present?
end
factory->>routing: resolve_openai_codex_routing(..., bearer_is_oauth)
routing->>oauth: lookup_openai_oauth_credentials(config)
oauth-->>routing: credentials with account_id
alt bearer_is_oauth and credentials present
routing-->>factory: "Codex backend, using_oauth=true"
else
routing-->>factory: "original endpoint, using_oauth=false"
end
factory-->>Caller: routed request
Reviews (1): Last reviewed commit: "fix(inference): route Codex OAuth chat t..." | Re-trigger Greptile
M3gA-Mind
left a comment
There was a problem hiding this comment.
Reviewed against upstream/main: CI green on every lane that applies to the changed areas, no unresolved review threads, and the diff does what the title and body claim. Checked correctness, blast radius on shared seams, that the behaviour change is pinned by a test rather than only asserted, and i18n/em-dash compliance on any new user-facing string. Nothing blocking found.
Summary
api.openai.com,gpt-5.5) and failing with HTTP 400 "Stream must be set to true".Problem
resolve_openai_codex_routingdecided Codex-vs-standard by string-comparing two independently resolved OAuth tokens:bearer_key(fromlookup_key_for_slug) andcredentials.access_token(a secondlookup_openai_oauth_credentialsread). Each read can refresh the token, and OpenAI rotates the access token on every refresh, so near expiry the two reads diverged, the equality check failed, and routing silently fell back to standard OpenAI — which rejects a non-streaminggpt-5.xrequest with that 400.Compounding subtlety: OAuth and API-key credentials share the same
provider:openaiprofile store and differ only byAuthProfileKind, so a bearer string can't reveal its source.Solution
Decide on which credential is stored, not on token equality. A new
openai_bearer_is_oauth(config)helper mirrorslookup_key_for_slug's precedence (provider:openai, then bareopenai) and reports the kind of the winning profile — immune to token rotation.resolve_openai_codex_routingtakes that asbearer_is_oauthand routes to the Codex backend when it is true and the OAuth metadata resolved (for the account id); an API-key profile keeps the standard path. Both call sites (chatresolve_cloud_slug, list-models) pass it.openai_codexis widened topub(crate)so the routing can be driven directly in a test. Once routing is correct the request uses the Responses API, so the streaming 400 no longer arises.Submission Checklist
diff-cover(cargo-llvm-cov) enforces the gate.N/A: bug fix, no added/removed/renamed feature row.## Related—N/A: bug fix.N/A: fixes existing Codex-import routing; no new release-cut surface.Closes #NNN.Impact
cargo fmt+cargo clippy -p openhuman -- -D warningsclean; unit tests green.Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
fix/codex-oauth-routing-5353