fix: address code review feedback
- Move MAX_EMPTY_RESPONSE_RETRIES to constants.js for consistency - Handle 429/401/5xx errors properly during retry fetch - Use proper message ID format (crypto.randomBytes) instead of Date.now() - Add crypto import for UUID generation Code review by: Gemini 3 Pro Preview + Claude Opus 4.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
import {
|
import {
|
||||||
ANTIGRAVITY_ENDPOINT_FALLBACKS,
|
ANTIGRAVITY_ENDPOINT_FALLBACKS,
|
||||||
MAX_RETRIES,
|
MAX_RETRIES,
|
||||||
|
MAX_EMPTY_RESPONSE_RETRIES,
|
||||||
MAX_WAIT_BEFORE_ERROR_MS
|
MAX_WAIT_BEFORE_ERROR_MS
|
||||||
} from '../constants.js';
|
} from '../constants.js';
|
||||||
import { isRateLimitError, isAuthError, isEmptyResponseError } from '../errors.js';
|
import { isRateLimitError, isAuthError, isEmptyResponseError } from '../errors.js';
|
||||||
@@ -17,9 +18,7 @@ import { parseResetTime } from './rate-limit-parser.js';
|
|||||||
import { buildCloudCodeRequest, buildHeaders } from './request-builder.js';
|
import { buildCloudCodeRequest, buildHeaders } from './request-builder.js';
|
||||||
import { streamSSEResponse } from './sse-streamer.js';
|
import { streamSSEResponse } from './sse-streamer.js';
|
||||||
import { getFallbackModel } from '../fallback-config.js';
|
import { getFallbackModel } from '../fallback-config.js';
|
||||||
|
import crypto from 'crypto';
|
||||||
// Maximum retries for empty responses before giving up
|
|
||||||
const MAX_EMPTY_RETRIES = 2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a streaming request to Cloud Code with multi-account support
|
* Send a streaming request to Cloud Code with multi-account support
|
||||||
@@ -149,15 +148,15 @@ export async function* sendMessageStream(anthropicRequest, accountManager, fallb
|
|||||||
let emptyRetries = 0;
|
let emptyRetries = 0;
|
||||||
let currentResponse = response;
|
let currentResponse = response;
|
||||||
|
|
||||||
while (emptyRetries <= MAX_EMPTY_RETRIES) {
|
while (emptyRetries <= MAX_EMPTY_RESPONSE_RETRIES) {
|
||||||
try {
|
try {
|
||||||
yield* streamSSEResponse(currentResponse, anthropicRequest.model);
|
yield* streamSSEResponse(currentResponse, anthropicRequest.model);
|
||||||
logger.debug('[CloudCode] Stream completed');
|
logger.debug('[CloudCode] Stream completed');
|
||||||
return;
|
return;
|
||||||
} catch (streamError) {
|
} catch (streamError) {
|
||||||
if (isEmptyResponseError(streamError) && emptyRetries < MAX_EMPTY_RETRIES) {
|
if (isEmptyResponseError(streamError) && emptyRetries < MAX_EMPTY_RESPONSE_RETRIES) {
|
||||||
emptyRetries++;
|
emptyRetries++;
|
||||||
logger.warn(`[CloudCode] Empty response, retry ${emptyRetries}/${MAX_EMPTY_RETRIES}...`);
|
logger.warn(`[CloudCode] Empty response, retry ${emptyRetries}/${MAX_EMPTY_RESPONSE_RETRIES}...`);
|
||||||
|
|
||||||
// Refetch the response
|
// Refetch the response
|
||||||
currentResponse = await fetch(url, {
|
currentResponse = await fetch(url, {
|
||||||
@@ -166,15 +165,38 @@ export async function* sendMessageStream(anthropicRequest, accountManager, fallb
|
|||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle specific error codes on retry
|
||||||
if (!currentResponse.ok) {
|
if (!currentResponse.ok) {
|
||||||
throw new Error(`Empty response retry failed: ${currentResponse.status}`);
|
const retryErrorText = await currentResponse.text();
|
||||||
|
|
||||||
|
// Re-throw rate limit errors to trigger account switch
|
||||||
|
if (currentResponse.status === 429) {
|
||||||
|
const resetMs = parseResetTime(currentResponse, retryErrorText);
|
||||||
|
throw new Error(`Rate limited during retry: ${retryErrorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-throw auth errors for proper handling
|
||||||
|
if (currentResponse.status === 401) {
|
||||||
|
accountManager.clearTokenCache(account.email);
|
||||||
|
accountManager.clearProjectCache(account.email);
|
||||||
|
throw new Error(`Auth error during retry: ${retryErrorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For 5xx errors, continue to next retry attempt
|
||||||
|
if (currentResponse.status >= 500) {
|
||||||
|
logger.warn(`[CloudCode] Retry got ${currentResponse.status}, continuing...`);
|
||||||
|
await sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Empty response retry failed: ${currentResponse.status} - ${retryErrorText}`);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// After max retries, emit fallback message
|
// After max retries, emit fallback message
|
||||||
if (isEmptyResponseError(streamError)) {
|
if (isEmptyResponseError(streamError)) {
|
||||||
logger.error(`[CloudCode] Empty response after ${MAX_EMPTY_RETRIES} retries`);
|
logger.error(`[CloudCode] Empty response after ${MAX_EMPTY_RESPONSE_RETRIES} retries`);
|
||||||
yield* emitEmptyResponseFallback(anthropicRequest.model);
|
yield* emitEmptyResponseFallback(anthropicRequest.model);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -245,7 +267,8 @@ export async function* sendMessageStream(anthropicRequest, accountManager, fallb
|
|||||||
* @yields {Object} Anthropic-format SSE events for empty response fallback
|
* @yields {Object} Anthropic-format SSE events for empty response fallback
|
||||||
*/
|
*/
|
||||||
function* emitEmptyResponseFallback(model) {
|
function* emitEmptyResponseFallback(model) {
|
||||||
const messageId = `msg_${Date.now()}_empty`;
|
// Use proper message ID format consistent with Anthropic API
|
||||||
|
const messageId = `msg_${crypto.randomBytes(16).toString('hex')}`;
|
||||||
|
|
||||||
yield {
|
yield {
|
||||||
type: 'message_start',
|
type: 'message_start',
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export const ANTIGRAVITY_DB_PATH = getAntigravityDbPath();
|
|||||||
|
|
||||||
export const DEFAULT_COOLDOWN_MS = 10 * 1000; // 10 second default cooldown
|
export const DEFAULT_COOLDOWN_MS = 10 * 1000; // 10 second default cooldown
|
||||||
export const MAX_RETRIES = 5; // Max retry attempts across accounts
|
export const MAX_RETRIES = 5; // Max retry attempts across accounts
|
||||||
|
export const MAX_EMPTY_RESPONSE_RETRIES = 2; // Max retries for empty API responses
|
||||||
export const MAX_ACCOUNTS = 10; // Maximum number of accounts allowed
|
export const MAX_ACCOUNTS = 10; // Maximum number of accounts allowed
|
||||||
|
|
||||||
// Rate limit wait thresholds
|
// Rate limit wait thresholds
|
||||||
@@ -166,6 +167,7 @@ export default {
|
|||||||
ANTIGRAVITY_DB_PATH,
|
ANTIGRAVITY_DB_PATH,
|
||||||
DEFAULT_COOLDOWN_MS,
|
DEFAULT_COOLDOWN_MS,
|
||||||
MAX_RETRIES,
|
MAX_RETRIES,
|
||||||
|
MAX_EMPTY_RESPONSE_RETRIES,
|
||||||
MAX_ACCOUNTS,
|
MAX_ACCOUNTS,
|
||||||
MAX_WAIT_BEFORE_ERROR_MS,
|
MAX_WAIT_BEFORE_ERROR_MS,
|
||||||
MIN_SIGNATURE_LENGTH,
|
MIN_SIGNATURE_LENGTH,
|
||||||
|
|||||||
Reference in New Issue
Block a user