-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(responses): put combo hops and adapter inner retries on the shared send budget (#4546) #4637
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
77c068b
196ccaa
3e52f7d
4398009
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 |
|---|---|---|
|
|
@@ -45,6 +45,10 @@ import { | |
| type KiroWireClient, | ||
| } from "./wire"; | ||
|
|
||
| /** The physical-send observer an `AdapterFetchContext` may carry, and the record it receives. */ | ||
| type KiroPhysicalSendObserver = NonNullable<AdapterFetchContext["onPhysicalSend"]>; | ||
| type KiroPhysicalSend = Parameters<KiroPhysicalSendObserver>[0]; | ||
|
|
||
| // Adapter | ||
| export function createKiroAdapter(provider: OcxProviderConfig): ProviderAdapter { | ||
| // Per-request closure (resolveAdapter builds a fresh adapter per request — server.ts:440 — so this | ||
|
|
@@ -62,6 +66,25 @@ export function createKiroAdapter(provider: OcxProviderConfig): ProviderAdapter | |
| // Captured the same way as the abort signal, because the text-fallback rebuild below runs | ||
| // outside the fetchResponse frame and used to construct a context without either (#4546). | ||
| let requestSendBudget: RequestExecutionBudget | undefined; | ||
| // Captured for the same reason, and needed for the same leg to be COUNTABLE rather than merely | ||
| // bounded: the rebuild's sends were paid for out of the request budget but reported by nobody, | ||
| // so no regression could pin how many requests one Kiro turn actually makes. | ||
| let requestOnPhysicalSend: KiroPhysicalSendObserver | undefined; | ||
| // One ordinal sequence across the whole turn. `fetchKiroWithRetry` numbers from 1 inside each | ||
| // call, and the caller reads ordinal 1 as the send it already recorded itself; forwarding the | ||
| // rebuild's raw ordinals would therefore drop its first send — the very send that makes the | ||
| // fallback a second request rather than a continuation of the first. | ||
| let physicalSendsObserved = 0; | ||
| const forwardPhysicalSend = ( | ||
| send: KiroPhysicalSend, | ||
| ordinalBase: number, | ||
| defaultRecovery?: KiroPhysicalSend["recovery"], | ||
| ): void => { | ||
| const ordinal = ordinalBase + send.ordinal; | ||
| if (ordinal > physicalSendsObserved) physicalSendsObserved = ordinal; | ||
| const recovery = send.recovery ?? defaultRecovery; | ||
| requestOnPhysicalSend?.({ ordinal, ...(recovery ? { recovery } : {}) }); | ||
| }; | ||
|
|
||
| const build = async ( | ||
| parsed: OcxParsedRequest, | ||
|
|
@@ -208,13 +231,22 @@ export function createKiroAdapter(provider: OcxProviderConfig): ProviderAdapter | |
| retryBodyReservation.commitRetained(); | ||
| retryBodyRetained = true; | ||
| budget.releaseRetained(retryBodyUpperBound - retryBodyBytes, { kind: "request_copies" }); | ||
| // Fixed before the rebuild dispatches, so the leg's ordinals continue the first attempt's | ||
| // sequence even though this call's own counter restarts at 1. | ||
| const fallbackOrdinalBase = physicalSendsObserved; | ||
| const response = await fetchKiroWithRetry(retry.request, { | ||
| abortSignal: requestAbortSignal, | ||
| returnRawErrors: true, | ||
| stream: true, | ||
| // The text-fallback rebuild used to construct a fresh context and drop the budget, | ||
| // so everything after the first send escaped the per-request cap. | ||
| ...(requestSendBudget ? { sendBudget: requestSendBudget } : {}), | ||
|
Comment on lines
241
to
243
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 a Kiro request succeeds only on its third reset attempt but produces progress without a final answer, the base allowance is already exhausted. Passing the same budget into Useful? React with 👍 / 👎. |
||
| // And reported nothing, so the sends it paid for were invisible. Its own first send is | ||
| // the completion retry itself: the first attempt produced progress without a final | ||
| // answer, which is the same recovery class the generic empty-completion guard records. | ||
| ...(requestOnPhysicalSend | ||
| ? { onPhysicalSend: (send: KiroPhysicalSend) => forwardPhysicalSend(send, fallbackOrdinalBase, "empty-completion") } | ||
| : {}), | ||
| }); | ||
| return { | ||
| response, | ||
|
|
@@ -286,7 +318,16 @@ export function createKiroAdapter(provider: OcxProviderConfig): ProviderAdapter | |
| // both the first Kiro request and its one allowed completion retry. | ||
| if (ctx?.abortSignal) requestAbortSignal = ctx.abortSignal; | ||
| if (ctx?.sendBudget) requestSendBudget = ctx.sendBudget; | ||
| return fetchKiroWithRetry(request, ctx); | ||
| if (ctx?.onPhysicalSend) requestOnPhysicalSend = ctx.onPhysicalSend; | ||
| // Reset per fetch call, because `ordinal` is defined within one call and the caller records | ||
| // ordinal 1 of each new attempt itself. The text fallback that follows this attempt then | ||
| // continues THIS attempt's sequence rather than an earlier one's. | ||
| physicalSendsObserved = 0; | ||
| // Routed through the same forwarder as the fallback so both legs share one ordinal | ||
| // sequence; a context without an observer is passed through untouched. | ||
| return fetchKiroWithRetry(request, requestOnPhysicalSend | ||
| ? { ...ctx, onPhysicalSend: (send: KiroPhysicalSend) => forwardPhysicalSend(send, 0) } | ||
| : ctx); | ||
| }, | ||
|
|
||
| formatErrorBody(status: number, headers: Headers, payloadText: string): string { | ||
|
|
||
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 Cursor request whose first event is a 429, the initial transport records target
cursor, whilerotateRunTurnAdapterOnPreflight429reserves the replay under its provider/model recovery key and consumes the sole target transition. The rotated adapter then reaches this new reservation, attempts to transition back tocursor, and is refused withtarget-transition-exhaustedbefore constructing the replacement transport, so real Cursor OAuth failover returns an error instead of trying the alternate account. The replay needs to consume the outer hop permit or use a consistent target identity rather than making an independent reservation here.Useful? React with 👍 / 👎.