From 673280ce2f55424fbe12707f0745e0515568f1ef Mon Sep 17 00:00:00 2001 From: muava12 Date: Tue, 24 Feb 2026 10:14:20 +0800 Subject: [PATCH] revert(providers): revert cooldown tracker per-model changes This reverts commit cd13f45dcb43f733b3239c9f184914f6a65f160a. It introduced new errors. --- pkg/providers/cooldown.go | 42 +++++++++++++++++----------------- pkg/providers/fallback.go | 10 ++++---- pkg/providers/fallback_test.go | 10 ++++---- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/pkg/providers/cooldown.go b/pkg/providers/cooldown.go index 8fedc1aea..b0d8608dc 100644 --- a/pkg/providers/cooldown.go +++ b/pkg/providers/cooldown.go @@ -37,14 +37,14 @@ func NewCooldownTracker() *CooldownTracker { } } -// MarkFailure records a failure for a specific model key and sets appropriate cooldown. +// MarkFailure records a failure for a provider and sets appropriate cooldown. // Resets error counts if last failure was more than failureWindow ago. -func (ct *CooldownTracker) MarkFailure(key string, reason FailoverReason) { +func (ct *CooldownTracker) MarkFailure(provider string, reason FailoverReason) { ct.mu.Lock() defer ct.mu.Unlock() now := ct.nowFunc() - entry := ct.getOrCreate(key) + entry := ct.getOrCreate(provider) // 24h failure window reset: if no failure in failureWindow, reset counters. if !entry.LastFailure.IsZero() && now.Sub(entry.LastFailure) > ct.failureWindow { @@ -65,12 +65,12 @@ func (ct *CooldownTracker) MarkFailure(key string, reason FailoverReason) { } } -// MarkSuccess resets all counters and cooldowns for a specific model key. -func (ct *CooldownTracker) MarkSuccess(key string) { +// MarkSuccess resets all counters and cooldowns for a provider. +func (ct *CooldownTracker) MarkSuccess(provider string) { ct.mu.Lock() defer ct.mu.Unlock() - entry := ct.entries[key] + entry := ct.entries[provider] if entry == nil { return } @@ -82,12 +82,12 @@ func (ct *CooldownTracker) MarkSuccess(key string) { entry.DisabledReason = "" } -// IsAvailable returns true if the specific model key is not in cooldown or disabled. -func (ct *CooldownTracker) IsAvailable(key string) bool { +// IsAvailable returns true if the provider is not in cooldown or disabled. +func (ct *CooldownTracker) IsAvailable(provider string) bool { ct.mu.RLock() defer ct.mu.RUnlock() - entry := ct.entries[key] + entry := ct.entries[provider] if entry == nil { return true } @@ -107,13 +107,13 @@ func (ct *CooldownTracker) IsAvailable(key string) bool { return true } -// CooldownRemaining returns how long until the specific model key becomes available. +// CooldownRemaining returns how long until the provider becomes available. // Returns 0 if already available. -func (ct *CooldownTracker) CooldownRemaining(key string) time.Duration { +func (ct *CooldownTracker) CooldownRemaining(provider string) time.Duration { ct.mu.RLock() defer ct.mu.RUnlock() - entry := ct.entries[key] + entry := ct.entries[provider] if entry == nil { return 0 } @@ -138,37 +138,37 @@ func (ct *CooldownTracker) CooldownRemaining(key string) time.Duration { return remaining } -// ErrorCount returns the current error count for a specific model key. -func (ct *CooldownTracker) ErrorCount(key string) int { +// ErrorCount returns the current error count for a provider. +func (ct *CooldownTracker) ErrorCount(provider string) int { ct.mu.RLock() defer ct.mu.RUnlock() - entry := ct.entries[key] + entry := ct.entries[provider] if entry == nil { return 0 } return entry.ErrorCount } -// FailureCount returns the failure count for a specific reason and model key. -func (ct *CooldownTracker) FailureCount(key string, reason FailoverReason) int { +// FailureCount returns the failure count for a specific reason. +func (ct *CooldownTracker) FailureCount(provider string, reason FailoverReason) int { ct.mu.RLock() defer ct.mu.RUnlock() - entry := ct.entries[key] + entry := ct.entries[provider] if entry == nil { return 0 } return entry.FailureCounts[reason] } -func (ct *CooldownTracker) getOrCreate(key string) *cooldownEntry { - entry := ct.entries[key] +func (ct *CooldownTracker) getOrCreate(provider string) *cooldownEntry { + entry := ct.entries[provider] if entry == nil { entry = &cooldownEntry{ FailureCounts: make(map[FailoverReason]int), } - ct.entries[key] = entry + ct.entries[provider] = entry } return entry } diff --git a/pkg/providers/fallback.go b/pkg/providers/fallback.go index ac556e860..0a2068005 100644 --- a/pkg/providers/fallback.go +++ b/pkg/providers/fallback.go @@ -122,11 +122,9 @@ func (fc *FallbackChain) Execute( return nil, context.Canceled } - key := ModelKey(candidate.Provider, candidate.Model) - // Check cooldown. - if !fc.cooldown.IsAvailable(key) { - remaining := fc.cooldown.CooldownRemaining(key) + if !fc.cooldown.IsAvailable(candidate.Provider) { + remaining := fc.cooldown.CooldownRemaining(candidate.Provider) result.Attempts = append(result.Attempts, FallbackAttempt{ Provider: candidate.Provider, Model: candidate.Model, @@ -147,7 +145,7 @@ func (fc *FallbackChain) Execute( if err == nil { // Success. - fc.cooldown.MarkSuccess(key) + fc.cooldown.MarkSuccess(candidate.Provider) result.Response = resp result.Provider = candidate.Provider result.Model = candidate.Model @@ -193,7 +191,7 @@ func (fc *FallbackChain) Execute( } // Retriable error: mark failure and continue to next candidate. - fc.cooldown.MarkFailure(key, failErr.Reason) + fc.cooldown.MarkFailure(candidate.Provider, failErr.Reason) result.Attempts = append(result.Attempts, FallbackAttempt{ Provider: candidate.Provider, Model: candidate.Model, diff --git a/pkg/providers/fallback_test.go b/pkg/providers/fallback_test.go index 1970ed49e..e872c672e 100644 --- a/pkg/providers/fallback_test.go +++ b/pkg/providers/fallback_test.go @@ -164,7 +164,7 @@ func TestFallback_CooldownSkip(t *testing.T) { fc := NewFallbackChain(ct) // Put openai in cooldown - ct.MarkFailure("openai/gpt-4", FailoverRateLimit) + ct.MarkFailure("openai", FailoverRateLimit) candidates := []FallbackCandidate{ makeCandidate("openai", "gpt-4"), @@ -202,8 +202,8 @@ func TestFallback_AllInCooldown(t *testing.T) { fc := NewFallbackChain(ct) // Put all providers in cooldown - ct.MarkFailure("openai/gpt-4", FailoverRateLimit) - ct.MarkFailure("anthropic/claude", FailoverBilling) + ct.MarkFailure("openai", FailoverRateLimit) + ct.MarkFailure("anthropic", FailoverBilling) candidates := []FallbackCandidate{ makeCandidate("openai", "gpt-4"), @@ -284,7 +284,7 @@ func TestFallback_SuccessResetsCooldown(t *testing.T) { run := func(ctx context.Context, provider, model string) (*LLMResponse, error) { attempt++ if attempt == 1 { - ct.MarkFailure("openai/gpt-4", FailoverRateLimit) // simulate failure tracked elsewhere + ct.MarkFailure("openai", FailoverRateLimit) // simulate failure tracked elsewhere } return &LLMResponse{Content: "ok", FinishReason: "stop"}, nil } @@ -293,7 +293,7 @@ func TestFallback_SuccessResetsCooldown(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if !ct.IsAvailable("openai/gpt-4") { + if !ct.IsAvailable("openai") { t.Error("success should reset cooldown") } }