-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix(responses): reserve the last two generic-OAuth hops from the shared budget (#4546) #4651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8615,21 +8615,36 @@ async function handleResponsesInner( | |
| && genericFailovers < GENERIC_OAUTH_MAX_FAILOVERS_PER_REQUEST | ||
| && isGenericOAuthFailoverEnabled(config, route.providerName) | ||
| ) { | ||
| // Intersection with the shared request budget. This arm re-sends through | ||
| // rebuildAndRefetch, so the roster cap alone would let one request walk the roster on | ||
| // an allowance the rest of the request cannot see. A refusal ends the ladder with the | ||
| // real 429 already in hand, which is the decided exhaustion contract. | ||
| const hop = reserveCredentialHop( | ||
| "auth-recovery", | ||
| `${route.providerName}|${route.modelId}|adapter-recovery-oauth-429`, | ||
| ); | ||
|
Comment on lines
+8622
to
+8625
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This changes shared OAuth recovery and request-budget behavior under AGENTS.md reference: src/AGENTS.md:L10-L11 Useful? React with 👍 / 👎.
Comment on lines
+8622
to
+8625
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Carry the adapter-recovery permit through base-allowance replays. At Return 🤖 Prompt for AI Agents |
||
| if (!hop.allowed) break; | ||
| const nextAccountId = rotateGenericOAuthAccountOn429( | ||
| config, | ||
| route.providerName, | ||
| genericFailoverAccountId, | ||
| upstreamResponse.headers.get("retry-after"), | ||
| ); | ||
| if (!nextAccountId) break; | ||
| if (!nextAccountId) { | ||
| hop.permit?.release(); | ||
| break; | ||
| } | ||
| try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ } | ||
| try { | ||
| // The FULL snapshot, not just the bearer: Antigravity pairs an account-matched | ||
| // projectId with its token and Kiro carries routing metadata, so a token-only swap | ||
| // would mix one account's credential with another's routing data. | ||
| const snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); | ||
| genericFailovers += 1; | ||
| if (!await applyFailoverSnapshot(snapshot)) break; | ||
| if (!await applyFailoverSnapshot(snapshot)) { | ||
| hop.permit?.release(); | ||
| break; | ||
| } | ||
| invalidateSameTargetRequest(); | ||
| activeAdapter = resolveSelectionAdapter( | ||
| resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), | ||
|
|
@@ -9085,12 +9100,22 @@ async function handleResponsesInner( | |
| && genericFailovers < GENERIC_OAUTH_MAX_FAILOVERS_PER_REQUEST | ||
| && isGenericOAuthFailoverEnabled(config, route.providerName) | ||
| ) { | ||
| const nextAccountId = rotateGenericOAuthAccountOn429( | ||
| config, | ||
| route.providerName, | ||
| genericFailoverAccountId, | ||
| response.headers.get("retry-after"), | ||
| // Intersection with the shared request budget. The continuation loop re-sends the | ||
| // turn, so without this the per-request bound could be re-armed simply by reaching a | ||
| // different loop -- which is the divergence the comment above already warns about. | ||
| const hop = reserveCredentialHop( | ||
| "auth-recovery", | ||
| `${route.providerName}|${route.modelId}|continuation-oauth-429`, | ||
| ); | ||
|
Comment on lines
+9106
to
9109
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When AGENTS.md reference: src/AGENTS.md:L24-L25 Useful? React with 👍 / 👎. |
||
| const nextAccountId = hop.allowed | ||
| ? rotateGenericOAuthAccountOn429( | ||
| config, | ||
| route.providerName, | ||
| genericFailoverAccountId, | ||
| response.headers.get("retry-after"), | ||
| ) | ||
| : null; | ||
| if (!nextAccountId) hop.permit?.release(); | ||
| if (nextAccountId) { | ||
| try { void response.body?.cancel().catch(() => {}); } catch { /* already closed */ } | ||
| try { | ||
|
|
@@ -9100,7 +9125,9 @@ async function handleResponsesInner( | |
| // routing data. | ||
| const snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); | ||
| genericFailovers += 1; | ||
| if (await applyFailoverSnapshot(snapshot, nextParsed)) { | ||
| const applied = await applyFailoverSnapshot(snapshot, nextParsed); | ||
| if (!applied) hop.permit?.release(); | ||
| if (applied) { | ||
| invalidateSameTargetRequest(); | ||
| activeAdapter = resolveSelectionAdapter( | ||
| resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a multi-account Kiro request whose accounts return quota 429s, this reservation and the retry both charge the same physical send:
rebuildAndRefetchpasses the shared budget tofetchKiroWithRetry, whosesrc/adapters/kiro-retry.ts:171reserves again before dispatch. Starting from one initial send, the first alternate therefore consumes slots 2 and 3; the second hop consumes slot 4, then Kiro's inner admission is refused and mapped to a synthetic 502 instead of trying the available account or retaining the real 429. Thread this hop permit into the adapter's first physical send, or otherwise ensure only one layer reserves it, and cover the multi-account Kiro path behaviorally rather than only counting call sites.AGENTS.md reference: src/AGENTS.md:L24-L25
Useful? React with 👍 / 👎.