feat(cliproxy): add provider error detection for composite fallback

- Add PROVIDER_ERROR_PATTERNS for HTTP 4xx/5xx, overloaded, quota, rate limit
- Add detectFailedTier() to identify which composite tier failed from stderr
- Add isProviderError() to distinguish provider failures from normal exits
This commit is contained in:
Tam Nhu Tran
2026-02-12 04:24:29 +07:00
parent 43e73f335f
commit 478e9e8f73
+28
View File
@@ -11,6 +11,7 @@
import { fail, warn, info } from '../../utils/ui';
import { CLIProxyProvider } from '../types';
import { handleBanDetection } from '../account-safety';
import { CompositeTierConfig } from '../../config/unified-config-types';
/**
* Check if error is network-related
@@ -98,3 +99,30 @@ export async function handleQuotaCheck(provider: CLIProxyProvider): Promise<void
}
}
}
/** Error patterns indicating provider failure */
export const PROVIDER_ERROR_PATTERNS = [
/Error:\s*4[012][0-9]/i,
/Error:\s*5[0-9]{2}/i,
/overloaded/i,
/quota.*exceeded/i,
/ECONNREFUSED/i,
/rate.?limit/i,
];
/** Detect which composite tier failed from stderr output */
export function detectFailedTier(
stderr: string,
tiers: { opus: CompositeTierConfig; sonnet: CompositeTierConfig; haiku: CompositeTierConfig }
): 'opus' | 'sonnet' | 'haiku' | null {
for (const tier of ['opus', 'sonnet', 'haiku'] as const) {
if (stderr.includes(tiers[tier].model)) return tier;
}
return null;
}
/** Check if Claude exit indicates provider error (vs normal user exit) */
export function isProviderError(exitCode: number, stderr: string): boolean {
if (exitCode === 0) return false;
return PROVIDER_ERROR_PATTERNS.some((p) => p.test(stderr));
}