mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
Fix Antigravity
This commit is contained in:
@@ -126,12 +126,17 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
let lastError = null;
|
||||
let lastStatus = 0;
|
||||
const MAX_AUTO_RETRIES = 2;
|
||||
const retryAttemptsByUrl = {}; // Track retry attempts per URL
|
||||
|
||||
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
|
||||
const url = this.buildUrl(model, stream, urlIndex);
|
||||
const headers = this.buildHeaders(credentials, stream);
|
||||
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||
let retryAttempts = 0;
|
||||
|
||||
// Initialize retry counter for this URL
|
||||
if (!retryAttemptsByUrl[urlIndex]) {
|
||||
retryAttemptsByUrl[urlIndex] = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
@@ -152,10 +157,12 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
}
|
||||
|
||||
// Auto retry only for 429 when retryMs is 0 or undefined
|
||||
if (response.status === 429 && (!retryMs || retryMs === 0) && retryAttempts < MAX_AUTO_RETRIES) {
|
||||
retryAttempts++;
|
||||
log?.debug?.("RETRY", `429 auto retry ${retryAttempts}/${MAX_AUTO_RETRIES} after 1s`);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
if (response.status === 429 && (!retryMs || retryMs === 0) && retryAttemptsByUrl[urlIndex] < MAX_AUTO_RETRIES) {
|
||||
retryAttemptsByUrl[urlIndex]++;
|
||||
// Exponential backoff: 2s, 4s, 8s...
|
||||
const backoffMs = Math.min(1000 * Math.pow(2, retryAttemptsByUrl[urlIndex]), MAX_RETRY_AFTER_MS);
|
||||
log?.debug?.("RETRY", `429 auto retry ${retryAttemptsByUrl[urlIndex]}/${MAX_AUTO_RETRIES} after ${backoffMs/1000}s`);
|
||||
await new Promise(resolve => setTimeout(resolve, backoffMs));
|
||||
urlIndex--;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -229,15 +229,21 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
|
||||
// Check provider response - return error info for fallback handling
|
||||
if (!providerResponse.ok) {
|
||||
trackPendingRequest(model, provider, connectionId, false);
|
||||
const { statusCode, message } = await parseUpstreamError(providerResponse);
|
||||
const { statusCode, message, retryAfterMs } = await parseUpstreamError(providerResponse, provider);
|
||||
appendRequestLog({ model, provider, connectionId, status: `FAILED ${statusCode}` }).catch(() => {});
|
||||
const errMsg = formatProviderError(new Error(message), provider, model, statusCode);
|
||||
console.log(`${COLORS.red}[ERROR] ${errMsg}${COLORS.reset}`);
|
||||
|
||||
// Log Antigravity retry time if available
|
||||
if (retryAfterMs && provider === "antigravity") {
|
||||
const retrySeconds = Math.ceil(retryAfterMs / 1000);
|
||||
log?.debug?.("RETRY", `Antigravity quota reset in ${retrySeconds}s (${retryAfterMs}ms)`);
|
||||
}
|
||||
|
||||
// Log error with full request body for debugging
|
||||
reqLogger.logError(new Error(message), finalBody || translatedBody);
|
||||
|
||||
return createErrorResult(statusCode, errMsg);
|
||||
return createErrorResult(statusCode, errMsg, retryAfterMs);
|
||||
}
|
||||
|
||||
// Non-streaming response
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
// Gemini helper functions for translator
|
||||
|
||||
// Unsupported JSON Schema constraints that should be removed for Antigravity
|
||||
// Reference: CLIProxyAPI/internal/util/gemini_schema.go (removeUnsupportedKeywords)
|
||||
export const UNSUPPORTED_SCHEMA_CONSTRAINTS = [
|
||||
// Basic constraints (not supported by Gemini API)
|
||||
"minLength", "maxLength", "exclusiveMinimum", "exclusiveMaximum",
|
||||
"pattern", "minItems", "maxItems", "format",
|
||||
"default", "examples", "$schema", "const", "title",
|
||||
"anyOf", "oneOf", "allOf", "not"
|
||||
// Claude rejects these in VALIDATED mode
|
||||
"default", "examples",
|
||||
// JSON Schema meta keywords
|
||||
"$schema", "$defs", "definitions", "const", "$ref",
|
||||
// Object validation keywords (not supported)
|
||||
"additionalProperties", "propertyNames", "patternProperties",
|
||||
// Complex schema keywords (handled by flattenAnyOfOneOf/mergeAllOf)
|
||||
"anyOf", "oneOf", "allOf", "not",
|
||||
// Dependency keywords (not supported)
|
||||
"dependencies", "dependentSchemas", "dependentRequired",
|
||||
// Other unsupported keywords
|
||||
"title", "if", "then", "else", "contentMediaType", "contentEncoding"
|
||||
];
|
||||
|
||||
// Default safety settings
|
||||
|
||||
@@ -78,13 +78,51 @@ export async function writeStreamError(writer, statusCode, message) {
|
||||
await writer.write(encoder.encode(`data: ${JSON.stringify(errorBody)}\n\n`));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Antigravity error message to extract retry time
|
||||
* Example: "You have exhausted your capacity on this model. Your quota will reset after 2h7m23s."
|
||||
* @param {string} message - Error message
|
||||
* @returns {number|null} Retry time in milliseconds, or null if not found
|
||||
*/
|
||||
export function parseAntigravityRetryTime(message) {
|
||||
if (typeof message !== "string") return null;
|
||||
|
||||
// Match patterns like: 2h7m23s, 5m30s, 45s, 1h20m, etc.
|
||||
const match = message.match(/reset after (\d+h)?(\d+m)?(\d+s)?/i);
|
||||
if (!match) return null;
|
||||
|
||||
let totalMs = 0;
|
||||
|
||||
// Extract hours
|
||||
if (match[1]) {
|
||||
const hours = parseInt(match[1]);
|
||||
totalMs += hours * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
// Extract minutes
|
||||
if (match[2]) {
|
||||
const minutes = parseInt(match[2]);
|
||||
totalMs += minutes * 60 * 1000;
|
||||
}
|
||||
|
||||
// Extract seconds
|
||||
if (match[3]) {
|
||||
const seconds = parseInt(match[3]);
|
||||
totalMs += seconds * 1000;
|
||||
}
|
||||
|
||||
return totalMs > 0 ? totalMs : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse upstream provider error response
|
||||
* @param {Response} response - Fetch response from provider
|
||||
* @returns {Promise<{statusCode: number, message: string}>}
|
||||
* @param {string} provider - Provider name (for Antigravity-specific parsing)
|
||||
* @returns {Promise<{statusCode: number, message: string, retryAfterMs: number|null}>}
|
||||
*/
|
||||
export async function parseUpstreamError(response) {
|
||||
export async function parseUpstreamError(response, provider = null) {
|
||||
let message = "";
|
||||
let retryAfterMs = null;
|
||||
|
||||
try {
|
||||
const text = await response.text();
|
||||
@@ -100,9 +138,17 @@ export async function parseUpstreamError(response) {
|
||||
message = `Upstream error: ${response.status}`;
|
||||
}
|
||||
|
||||
const messageStr = typeof message === "string" ? message : JSON.stringify(message);
|
||||
|
||||
// Parse Antigravity-specific retry time from error message
|
||||
if (provider === "antigravity" && response.status === 429) {
|
||||
retryAfterMs = parseAntigravityRetryTime(messageStr);
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: response.status,
|
||||
message: typeof message === "string" ? message : JSON.stringify(message)
|
||||
message: messageStr,
|
||||
retryAfterMs
|
||||
};
|
||||
}
|
||||
|
||||
@@ -110,15 +156,23 @@ export async function parseUpstreamError(response) {
|
||||
* Create error result for chatCore handler
|
||||
* @param {number} statusCode - HTTP status code
|
||||
* @param {string} message - Error message
|
||||
* @returns {{ success: false, status: number, error: string, response: Response }}
|
||||
* @param {number|null} retryAfterMs - Optional retry-after time in milliseconds
|
||||
* @returns {{ success: false, status: number, error: string, response: Response, retryAfterMs?: number }}
|
||||
*/
|
||||
export function createErrorResult(statusCode, message) {
|
||||
return {
|
||||
export function createErrorResult(statusCode, message, retryAfterMs = null) {
|
||||
const result = {
|
||||
success: false,
|
||||
status: statusCode,
|
||||
error: message,
|
||||
response: errorResponse(statusCode, message)
|
||||
};
|
||||
|
||||
// Add retryAfterMs if available (for Antigravity quota errors)
|
||||
if (retryAfterMs) {
|
||||
result.retryAfterMs = retryAfterMs;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user