From ff42a48b54681f3b647617d4f7153e47e0fdc7b4 Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Mon, 26 Jan 2026 21:59:38 -0500 Subject: [PATCH] Skip cooldowned providers during model failover (#2143) * feat(agents): skip cooldowned providers during failover When all auth profiles for a provider are in cooldown, the failover mechanism now skips that provider immediately rather than attempting and waiting for the cooldown error. This prevents long delays when multiple OAuth providers fail in sequence. * fix(agents): correct imports and API usage for cooldown check --- src/agents/model-fallback.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/agents/model-fallback.ts b/src/agents/model-fallback.ts index ea338ae60..60827ea00 100644 --- a/src/agents/model-fallback.ts +++ b/src/agents/model-fallback.ts @@ -14,6 +14,9 @@ import { resolveModelRefFromString, } from "./model-selection.js"; import type { FailoverReason } from "./pi-embedded-helpers.js"; +import { isProfileInCooldown } from "./auth-profiles/usage.js"; +import { loadAuthProfileStore } from "./auth-profiles/store.js"; +import { resolveAuthProfileOrder } from "./auth-profiles/order.js"; type ModelCandidate = { provider: string; @@ -211,11 +214,36 @@ export async function runWithModelFallback(params: { model: params.model, fallbacksOverride: params.fallbacksOverride, }); + + const authStore = params.cfg ? loadAuthProfileStore() : null; + const attempts: FallbackAttempt[] = []; let lastError: unknown; for (let i = 0; i < candidates.length; i += 1) { const candidate = candidates[i] as ModelCandidate; + + // Skip candidates that are in cooldown + if (authStore) { + const profileIds = resolveAuthProfileOrder({ + cfg: params.cfg, + store: authStore, + provider: candidate.provider, + }); + const isAnyProfileAvailable = profileIds.some((id) => !isProfileInCooldown(authStore, id)); + + if (profileIds.length > 0 && !isAnyProfileAvailable) { + // All profiles for this provider are in cooldown; skip without attempting + attempts.push({ + provider: candidate.provider, + model: candidate.model, + error: `Provider ${candidate.provider} is in cooldown (all profiles unavailable)`, + reason: "auth", // Best effort classification + }); + continue; + } + } + try { const result = await params.run(candidate.provider, candidate.model); return {