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
+12 -5
View File
@@ -126,12 +126,17 @@ export class AntigravityExecutor extends BaseExecutor {
let lastError = null; let lastError = null;
let lastStatus = 0; let lastStatus = 0;
const MAX_AUTO_RETRIES = 2; const MAX_AUTO_RETRIES = 2;
const retryAttemptsByUrl = {}; // Track retry attempts per URL
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) { for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
const url = this.buildUrl(model, stream, urlIndex); const url = this.buildUrl(model, stream, urlIndex);
const headers = this.buildHeaders(credentials, stream); const headers = this.buildHeaders(credentials, stream);
const transformedBody = this.transformRequest(model, body, stream, credentials); const transformedBody = this.transformRequest(model, body, stream, credentials);
let retryAttempts = 0;
// Initialize retry counter for this URL
if (!retryAttemptsByUrl[urlIndex]) {
retryAttemptsByUrl[urlIndex] = 0;
}
try { try {
const response = await fetch(url, { 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 // Auto retry only for 429 when retryMs is 0 or undefined
if (response.status === 429 && (!retryMs || retryMs === 0) && retryAttempts < MAX_AUTO_RETRIES) { if (response.status === 429 && (!retryMs || retryMs === 0) && retryAttemptsByUrl[urlIndex] < MAX_AUTO_RETRIES) {
retryAttempts++; retryAttemptsByUrl[urlIndex]++;
log?.debug?.("RETRY", `429 auto retry ${retryAttempts}/${MAX_AUTO_RETRIES} after 1s`); // Exponential backoff: 2s, 4s, 8s...
await new Promise(resolve => setTimeout(resolve, 1000)); 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--; urlIndex--;
continue; continue;
} }
+8 -2
View File
@@ -229,15 +229,21 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
// Check provider response - return error info for fallback handling // Check provider response - return error info for fallback handling
if (!providerResponse.ok) { if (!providerResponse.ok) {
trackPendingRequest(model, provider, connectionId, false); 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(() => {}); appendRequestLog({ model, provider, connectionId, status: `FAILED ${statusCode}` }).catch(() => {});
const errMsg = formatProviderError(new Error(message), provider, model, statusCode); const errMsg = formatProviderError(new Error(message), provider, model, statusCode);
console.log(`${COLORS.red}[ERROR] ${errMsg}${COLORS.reset}`); 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 // Log error with full request body for debugging
reqLogger.logError(new Error(message), finalBody || translatedBody); reqLogger.logError(new Error(message), finalBody || translatedBody);
return createErrorResult(statusCode, errMsg); return createErrorResult(statusCode, errMsg, retryAfterMs);
} }
// Non-streaming response // Non-streaming response
+14 -2
View File
@@ -1,11 +1,23 @@
// Gemini helper functions for translator // Gemini helper functions for translator
// Unsupported JSON Schema constraints that should be removed for Antigravity // Unsupported JSON Schema constraints that should be removed for Antigravity
// Reference: CLIProxyAPI/internal/util/gemini_schema.go (removeUnsupportedKeywords)
export const UNSUPPORTED_SCHEMA_CONSTRAINTS = [ export const UNSUPPORTED_SCHEMA_CONSTRAINTS = [
// Basic constraints (not supported by Gemini API)
"minLength", "maxLength", "exclusiveMinimum", "exclusiveMaximum", "minLength", "maxLength", "exclusiveMinimum", "exclusiveMaximum",
"pattern", "minItems", "maxItems", "format", "pattern", "minItems", "maxItems", "format",
"default", "examples", "$schema", "const", "title", // Claude rejects these in VALIDATED mode
"anyOf", "oneOf", "allOf", "not" "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 // Default safety settings
+60 -6
View File
@@ -78,13 +78,51 @@ export async function writeStreamError(writer, statusCode, message) {
await writer.write(encoder.encode(`data: ${JSON.stringify(errorBody)}\n\n`)); 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 * Parse upstream provider error response
* @param {Response} response - Fetch response from provider * @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 message = "";
let retryAfterMs = null;
try { try {
const text = await response.text(); const text = await response.text();
@@ -100,9 +138,17 @@ export async function parseUpstreamError(response) {
message = `Upstream error: ${response.status}`; 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 { return {
statusCode: response.status, 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 * Create error result for chatCore handler
* @param {number} statusCode - HTTP status code * @param {number} statusCode - HTTP status code
* @param {string} message - Error message * @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) { export function createErrorResult(statusCode, message, retryAfterMs = null) {
return { const result = {
success: false, success: false,
status: statusCode, status: statusCode,
error: message, error: message,
response: errorResponse(statusCode, message) response: errorResponse(statusCode, message)
}; };
// Add retryAfterMs if available (for Antigravity quota errors)
if (retryAfterMs) {
result.retryAfterMs = retryAfterMs;
}
return result;
} }
/** /**