Fix Antigravity

This commit is contained in:
decolua
2026-01-13 17:19:41 +07:00
parent e989796024
commit f9ef718fc6
4 changed files with 94 additions and 15 deletions

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
/**