code quality improvements and refactoring

This commit is contained in:
Badri Narayanan S
2025-12-21 22:11:44 +05:30
parent f282b36d1e
commit 712da8f7f2
5 changed files with 81 additions and 56 deletions

View File

@@ -4,7 +4,8 @@
* automatic failover, and smart cooldown for rate-limited accounts. * automatic failover, and smart cooldown for rate-limited accounts.
*/ */
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { readFile, writeFile, mkdir, access } from 'fs/promises';
import { constants as fsConstants } from 'fs';
import { dirname } from 'path'; import { dirname } from 'path';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import { homedir } from 'os'; import { homedir } from 'os';
@@ -71,8 +72,9 @@ export class AccountManager {
if (this.#initialized) return; if (this.#initialized) return;
try { try {
if (existsSync(this.#configPath)) { // Check if config file exists using async access
const configData = readFileSync(this.#configPath, 'utf-8'); await access(this.#configPath, fsConstants.F_OK);
const configData = await readFile(this.#configPath, 'utf-8');
const config = JSON.parse(configData); const config = JSON.parse(configData);
this.#accounts = (config.accounts || []).map(acc => ({ this.#accounts = (config.accounts || []).map(acc => ({
@@ -91,13 +93,13 @@ export class AccountManager {
} }
console.log(`[AccountManager] Loaded ${this.#accounts.length} account(s) from config`); console.log(`[AccountManager] Loaded ${this.#accounts.length} account(s) from config`);
} else { } catch (error) {
if (error.code === 'ENOENT') {
// No config file - use single account from Antigravity database // No config file - use single account from Antigravity database
console.log('[AccountManager] No config file found. Using Antigravity database (single account mode)'); console.log('[AccountManager] No config file found. Using Antigravity database (single account mode)');
await this.#loadDefaultAccount(); } else {
}
} catch (error) {
console.error('[AccountManager] Failed to load config:', error.message); console.error('[AccountManager] Failed to load config:', error.message);
}
// Fall back to default account // Fall back to default account
await this.#loadDefaultAccount(); await this.#loadDefaultAccount();
} }
@@ -341,7 +343,7 @@ export class AccountManager {
if (account.isInvalid) { if (account.isInvalid) {
account.isInvalid = false; account.isInvalid = false;
account.invalidReason = null; account.invalidReason = null;
this.saveToDisk(); await this.saveToDisk();
} }
console.log(`[AccountManager] Refreshed OAuth token for: ${account.email}`); console.log(`[AccountManager] Refreshed OAuth token for: ${account.email}`);
} catch (error) { } catch (error) {
@@ -454,15 +456,13 @@ export class AccountManager {
} }
/** /**
* Save current state to disk * Save current state to disk (async)
*/ */
saveToDisk() { async saveToDisk() {
try { try {
// Ensure directory exists // Ensure directory exists
const dir = dirname(this.#configPath); const dir = dirname(this.#configPath);
if (!existsSync(dir)) { await mkdir(dir, { recursive: true });
mkdirSync(dir, { recursive: true });
}
const config = { const config = {
accounts: this.#accounts.map(acc => ({ accounts: this.#accounts.map(acc => ({
@@ -483,7 +483,7 @@ export class AccountManager {
activeIndex: this.#currentIndex activeIndex: this.#currentIndex
}; };
writeFileSync(this.#configPath, JSON.stringify(config, null, 2)); await writeFile(this.#configPath, JSON.stringify(config, null, 2));
} catch (error) { } catch (error) {
console.error('[AccountManager] Failed to save config:', error.message); console.error('[AccountManager] Failed to save config:', error.message);
} }

View File

@@ -14,7 +14,9 @@ import {
ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_HEADERS, ANTIGRAVITY_HEADERS,
AVAILABLE_MODELS, AVAILABLE_MODELS,
MAX_RETRIES MAX_RETRIES,
MAX_WAIT_BEFORE_ERROR_MS,
MIN_SIGNATURE_LENGTH
} from './constants.js'; } from './constants.js';
import { import {
mapModelName, mapModelName,
@@ -250,7 +252,7 @@ export async function sendMessage(anthropicRequest, accountManager) {
const resetTime = new Date(Date.now() + waitMs).toISOString(); const resetTime = new Date(Date.now() + waitMs).toISOString();
// If wait time is too long (> 2 minutes), throw error immediately // If wait time is too long (> 2 minutes), throw error immediately
if (waitMs > 120000) { if (waitMs > MAX_WAIT_BEFORE_ERROR_MS) {
throw new Error( throw new Error(
`RESOURCE_EXHAUSTED: Rate limited. Quota will reset after ${formatDuration(waitMs)}. Next available: ${resetTime}` `RESOURCE_EXHAUSTED: Rate limited. Quota will reset after ${formatDuration(waitMs)}. Next available: ${resetTime}`
); );
@@ -448,7 +450,9 @@ async function parseThinkingSSEResponse(response, originalModel) {
accumulatedText += part.text; accumulatedText += part.text;
} }
} }
} catch (e) { /* skip parse errors */ } } catch (e) {
console.log('[CloudCode] SSE parse warning:', e.message, 'Raw:', jsonText.slice(0, 100));
}
} }
} }
@@ -496,7 +500,7 @@ export async function* sendMessageStream(anthropicRequest, accountManager) {
const resetTime = new Date(Date.now() + waitMs).toISOString(); const resetTime = new Date(Date.now() + waitMs).toISOString();
// If wait time is too long (> 2 minutes), throw error immediately // If wait time is too long (> 2 minutes), throw error immediately
if (waitMs > 120000) { if (waitMs > MAX_WAIT_BEFORE_ERROR_MS) {
throw new Error( throw new Error(
`RESOURCE_EXHAUSTED: Rate limited. Quota will reset after ${formatDuration(waitMs)}. Next available: ${resetTime}` `RESOURCE_EXHAUSTED: Rate limited. Quota will reset after ${formatDuration(waitMs)}. Next available: ${resetTime}`
); );
@@ -692,7 +696,7 @@ async function* streamSSEResponse(response, originalModel) {
}; };
} }
if (signature && signature.length >= 50) { if (signature && signature.length >= MIN_SIGNATURE_LENGTH) {
currentThinkingSignature = signature; currentThinkingSignature = signature;
} }

View File

@@ -81,6 +81,14 @@ export const ACCOUNT_CONFIG_PATH = join(
export const DEFAULT_COOLDOWN_MS = 60 * 1000; // 1 minute default cooldown export const DEFAULT_COOLDOWN_MS = 60 * 1000; // 1 minute default cooldown
export const MAX_RETRIES = 5; // Max retry attempts across accounts export const MAX_RETRIES = 5; // Max retry attempts across accounts
// Rate limit wait thresholds
export const MAX_WAIT_BEFORE_ERROR_MS = 120000; // 2 minutes - throw error if wait exceeds this
// Thinking model constants
export const DEFAULT_THINKING_BUDGET = 16000; // Default thinking budget tokens
export const CLAUDE_THINKING_MAX_OUTPUT_TOKENS = 64000; // Max output tokens for thinking models
export const MIN_SIGNATURE_LENGTH = 50; // Minimum valid thinking signature length
export default { export default {
ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_HEADERS, ANTIGRAVITY_HEADERS,
@@ -93,5 +101,9 @@ export default {
DEFAULT_PORT, DEFAULT_PORT,
ACCOUNT_CONFIG_PATH, ACCOUNT_CONFIG_PATH,
DEFAULT_COOLDOWN_MS, DEFAULT_COOLDOWN_MS,
MAX_RETRIES MAX_RETRIES,
MAX_WAIT_BEFORE_ERROR_MS,
DEFAULT_THINKING_BUDGET,
CLAUDE_THINKING_MAX_OUTPUT_TOKENS,
MIN_SIGNATURE_LENGTH
}; };

View File

@@ -8,12 +8,12 @@
*/ */
import crypto from 'crypto'; import crypto from 'crypto';
import { MODEL_MAPPINGS } from './constants.js'; import {
MODEL_MAPPINGS,
// Default thinking budget (16K tokens) DEFAULT_THINKING_BUDGET,
const DEFAULT_THINKING_BUDGET = 16000; CLAUDE_THINKING_MAX_OUTPUT_TOKENS,
// Claude thinking models need larger max output tokens MIN_SIGNATURE_LENGTH
const CLAUDE_THINKING_MAX_OUTPUT_TOKENS = 64000; } from './constants.js';
/** /**
* Map Anthropic model name to Antigravity model name * Map Anthropic model name to Antigravity model name
@@ -33,11 +33,11 @@ function isThinkingPart(part) {
} }
/** /**
* Check if a thinking part has a valid signature (>= 50 chars) * Check if a thinking part has a valid signature (>= MIN_SIGNATURE_LENGTH chars)
*/ */
function hasValidSignature(part) { function hasValidSignature(part) {
const signature = part.thought === true ? part.thoughtSignature : part.signature; const signature = part.thought === true ? part.thoughtSignature : part.signature;
return typeof signature === 'string' && signature.length >= 50; return typeof signature === 'string' && signature.length >= MIN_SIGNATURE_LENGTH;
} }
/** /**
@@ -187,8 +187,8 @@ export function restoreThinkingSignatures(content) {
continue; continue;
} }
// Keep blocks with valid signatures (>= 50 chars), sanitized // Keep blocks with valid signatures (>= MIN_SIGNATURE_LENGTH chars), sanitized
if (block.signature && block.signature.length >= 50) { if (block.signature && block.signature.length >= MIN_SIGNATURE_LENGTH) {
filtered.push(sanitizeAnthropicThinkingBlock(block)); filtered.push(sanitizeAnthropicThinkingBlock(block));
} }
// Unsigned thinking blocks are dropped // Unsigned thinking blocks are dropped
@@ -360,7 +360,7 @@ function convertContentToParts(content, isClaudeModel = false) {
parts.push({ functionResponse }); parts.push({ functionResponse });
} else if (block.type === 'thinking') { } else if (block.type === 'thinking') {
// Handle thinking blocks - only those with valid signatures // Handle thinking blocks - only those with valid signatures
if (block.signature && block.signature.length >= 50) { if (block.signature && block.signature.length >= MIN_SIGNATURE_LENGTH) {
// Convert to Gemini format with signature // Convert to Gemini format with signature
parts.push({ parts.push({
text: block.thinking, text: block.thinking,

View File

@@ -19,13 +19,18 @@ const accountManager = new AccountManager();
// Track initialization status // Track initialization status
let isInitialized = false; let isInitialized = false;
let initError = null; let initError = null;
let initPromise = null;
/** /**
* Ensure account manager is initialized * Ensure account manager is initialized (with race condition protection)
*/ */
async function ensureInitialized() { async function ensureInitialized() {
if (isInitialized) return; if (isInitialized) return;
// If initialization is already in progress, wait for it
if (initPromise) return initPromise;
initPromise = (async () => {
try { try {
await accountManager.initialize(); await accountManager.initialize();
isInitialized = true; isInitialized = true;
@@ -33,9 +38,13 @@ async function ensureInitialized() {
console.log(`[Server] Account pool initialized: ${status.summary}`); console.log(`[Server] Account pool initialized: ${status.summary}`);
} catch (error) { } catch (error) {
initError = error; initError = error;
initPromise = null; // Allow retry on failure
console.error('[Server] Failed to initialize account manager:', error.message); console.error('[Server] Failed to initialize account manager:', error.message);
throw error; throw error;
} }
})();
return initPromise;
} }
// Middleware // Middleware