mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
feat(kiro): enable multi-endpoint failover for GenerateAssistantResponse
Wire the Kiro provider into BaseExecutor baseUrls fallback so a request advances across the three CodeWhisperer surfaces (runtime kiro.dev, codewhisperer, q) on 429 and network/5xx errors. - providers.js: add baseUrls[] (newest endpoint first); keep baseUrl as default - kiro.js: replace the hand-rolled single-endpoint loop with super.execute() delegation plus EventStream to SSE transform on success Note: the three hosts are alternate DNS surfaces of one regional service. AWS throttles per authenticated identity (token plus profileArn), not per hostname, so this is edge-level failover, not extra 429 quota. Co-authored-by: thienpv <pvtcwd@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
decolua
co-authored by
Cursor
parent
dd5c575c65
commit
c24efe80f0
+17
-39
@@ -2,8 +2,6 @@ import { BaseExecutor } from "./base.js";
|
||||
import { PROVIDERS } from "../config/providers.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { refreshKiroToken } from "../services/tokenRefresh.js";
|
||||
import { proxyAwareFetch } from "../utils/proxyFetch.js";
|
||||
import { HTTP_STATUS, RETRY_CONFIG, DEFAULT_RETRY_CONFIG, resolveRetryEntry } from "../config/runtimeConfig.js";
|
||||
|
||||
/**
|
||||
* KiroExecutor - Executor for Kiro AI (AWS CodeWhisperer)
|
||||
@@ -33,45 +31,25 @@ export class KiroExecutor extends BaseExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom execute for Kiro - handles AWS EventStream binary response with retry support
|
||||
* Kiro execute — delegate to BaseExecutor for endpoint fallback + retry, then
|
||||
* transform the binary AWS EventStream into OpenAI-shaped SSE on success.
|
||||
*
|
||||
* BaseExecutor.execute() walks config.baseUrls (runtime.us-east-1.kiro.dev →
|
||||
* codewhisperer → q) advancing to the next host on 429 (shouldRetry) and on
|
||||
* network/5xx errors, while tryRetry handles in-place retries per `retry: {429: 2}`.
|
||||
* Note: the baseUrls are alternate surfaces of one regional service, so rotation
|
||||
* is edge-level failover — it does not grant fresh 429 quota. Per-account 429
|
||||
* spreading is handled upstream by account rotation in sse/handlers/chat.js.
|
||||
*
|
||||
* Errors are returned untransformed so the upstream handler can read the body,
|
||||
* classify the status, and trigger account fallback/cooldown.
|
||||
*/
|
||||
async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {
|
||||
const url = this.buildUrl(model, stream, 0);
|
||||
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||
|
||||
// Merge default retry config with provider-specific config
|
||||
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
|
||||
let retryAttempts = 0;
|
||||
|
||||
while (true) {
|
||||
const headers = this.buildHeaders(credentials, stream);
|
||||
|
||||
const response = await proxyAwareFetch(url, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(transformedBody),
|
||||
signal
|
||||
}, proxyOptions);
|
||||
|
||||
// Check if should retry based on status code
|
||||
const { attempts: maxRetries, delayMs } = resolveRetryEntry(retryConfig[response.status]);
|
||||
if (!response.ok && maxRetries > 0 && retryAttempts < maxRetries) {
|
||||
retryAttempts++;
|
||||
log?.debug?.("RETRY", `${response.status} retry ${retryAttempts}/${maxRetries} after ${delayMs / 1000}s`);
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
return { response, url, headers, transformedBody };
|
||||
}
|
||||
|
||||
// Success - transform and return
|
||||
// For Kiro, we need to transform the binary EventStream to SSE
|
||||
// Create a TransformStream to convert binary to SSE text
|
||||
const transformedResponse = this.transformEventStreamToSSE(response, model);
|
||||
return { response: transformedResponse, url, headers, transformedBody };
|
||||
async execute(args) {
|
||||
const result = await super.execute(args);
|
||||
if (result?.response?.ok) {
|
||||
result.response = this.transformEventStreamToSSE(result.response, args.model);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user