From c63dc7fe2f7bdb6e6fb980555342fc75a753e2b1 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:51:50 +0800 Subject: [PATCH] fix(auth): Scope unavailability checks to specific models --- sdk/cliproxy/auth/selector.go | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/sdk/cliproxy/auth/selector.go b/sdk/cliproxy/auth/selector.go index f356cce9..71c33eb7 100644 --- a/sdk/cliproxy/auth/selector.go +++ b/sdk/cliproxy/auth/selector.go @@ -57,21 +57,32 @@ func isAuthBlockedForModel(auth *Auth, model string, now time.Time) bool { if auth.Disabled || auth.Status == StatusDisabled { return true } - if model != "" && len(auth.ModelStates) > 0 { - if state, ok := auth.ModelStates[model]; ok && state != nil { - if state.Status == StatusDisabled { - return true - } - if state.Unavailable { - if state.NextRetryAfter.IsZero() { - return false - } - if state.NextRetryAfter.After(now) { + // If a specific model is requested, prefer its per-model state over any aggregated + // auth-level unavailable flag. This prevents a failure on one model (e.g., 429 quota) + // from blocking other models of the same provider that have no errors. + if model != "" { + if len(auth.ModelStates) > 0 { + if state, ok := auth.ModelStates[model]; ok && state != nil { + if state.Status == StatusDisabled { return true } + if state.Unavailable { + if state.NextRetryAfter.IsZero() { + return false + } + if state.NextRetryAfter.After(now) { + return true + } + } + // Explicit state exists and is not blocking. + return false } } + // No explicit state for this model; do not block based on aggregated + // auth-level unavailable status. Allow trying this model. + return false } + // No specific model context: fall back to auth-level unavailable window. if auth.Unavailable && auth.NextRetryAfter.After(now) { return true }