import "server-only"; export type ProviderHealthEventType = "success" | "timeout" | "quota" | "error"; export interface ProviderHealthSnapshot { providerKey: string; providerType?: string; lastEventType: ProviderHealthEventType; lastError?: string; consecutiveFailures: number; lastSuccessAt?: number; updatedAt: number; } const providerHealthMap = new Map(); function normalizeErrorMessage(error: unknown) { return error instanceof Error ? String(error.message || "").trim() : String(error || "").trim(); } export function classifyProviderHealthEvent(error: unknown): ProviderHealthEventType { const normalized = normalizeErrorMessage(error).toLowerCase(); if ( normalized.includes("token quota is not enough") || normalized.includes("pre_consume_token_quota_failed") || normalized.includes("quota is not enough") ) { return "quota"; } if ( normalized.includes("timeout") || normalized.includes("timed out") || normalized.includes("body timeout") || normalized.includes("headers timeout") ) { return "timeout"; } return "error"; } export function recordProviderHealthSuccess(params: { providerKey?: string; providerType?: string; }) { const providerKey = String(params.providerKey || "").trim(); if (!providerKey) return; const existing = providerHealthMap.get(providerKey); providerHealthMap.set(providerKey, { providerKey, providerType: params.providerType || existing?.providerType, lastEventType: "success", consecutiveFailures: 0, lastError: undefined, lastSuccessAt: Date.now(), updatedAt: Date.now(), }); } export function recordProviderHealthFailure(params: { providerKey?: string; providerType?: string; error: unknown; }) { const providerKey = String(params.providerKey || "").trim(); if (!providerKey) return; const existing = providerHealthMap.get(providerKey); providerHealthMap.set(providerKey, { providerKey, providerType: params.providerType || existing?.providerType, lastEventType: classifyProviderHealthEvent(params.error), lastError: normalizeErrorMessage(params.error).slice(0, 400), consecutiveFailures: existing?.lastEventType === "success" ? 1 : Math.max(1, (existing?.consecutiveFailures || 0) + 1), lastSuccessAt: existing?.lastSuccessAt, updatedAt: Date.now(), }); } export function getProviderHealthSnapshot(providerKey?: string) { const normalized = String(providerKey || "").trim(); if (!normalized) return null; return providerHealthMap.get(normalized) || null; } export function shouldAvoidProvider(snapshot?: ProviderHealthSnapshot | null) { if (!snapshot) return false; const ageMs = Date.now() - snapshot.updatedAt; if (snapshot.lastEventType === "quota") { return ageMs <= 15 * 60 * 1000; } if (snapshot.lastEventType === "timeout") { return snapshot.consecutiveFailures >= 2 && ageMs <= 5 * 60 * 1000; } if (snapshot.lastEventType === "error") { return snapshot.consecutiveFailures >= 3 && ageMs <= 5 * 60 * 1000; } return false; }