From 2e20ea4c872a9697714941e03e77e3bafcf35c4b Mon Sep 17 00:00:00 2001 From: IanShaw027 Date: Fri, 7 Aug 2026 14:55:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(grok):=20=E4=BF=9D=E6=8C=81=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=BA=A7=E5=85=8D=E8=B4=B9=E9=A2=9D=E5=BA=A6=E9=9A=94?= =?UTF-8?q?=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/service/grok_p2_test.go | 4 ++-- backend/internal/service/grok_upstream_failure.go | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/internal/service/grok_p2_test.go b/backend/internal/service/grok_p2_test.go index d3d6e67cf6..a110e15875 100644 --- a/backend/internal/service/grok_p2_test.go +++ b/backend/internal/service/grok_p2_test.go @@ -93,9 +93,9 @@ func TestApplyGrokUpstreamFailure_ModelSpecificFreeUsage(t *testing.T) { svc.handleGrokAccountUpstreamError(context.Background(), account, 400, nil, body) - require.Equal(t, 1, repo.tempUnschedCalls) - require.Equal(t, "grok free usage exhausted", repo.lastTempUnschedReason) + require.Zero(t, repo.tempUnschedCalls, "model-scoped free usage must not cool sibling models") require.True(t, isGrokModelQuotaBlocked(account.ID, "grok-4.5", time.Now())) + require.False(t, isGrokModelQuotaBlocked(account.ID, "grok-4.3", time.Now())) } func TestApplyGrokUpstreamFailure_SpendingLimitMarksReauth(t *testing.T) { diff --git a/backend/internal/service/grok_upstream_failure.go b/backend/internal/service/grok_upstream_failure.go index 0979e5edb3..6dba876508 100644 --- a/backend/internal/service/grok_upstream_failure.go +++ b/backend/internal/service/grok_upstream_failure.go @@ -480,11 +480,10 @@ func (s *OpenAIGatewayService) applyGrokUpstreamFailureDecision( if decision.Model != "" && isGrokModelSpecificFreeUsage(low, decision.Model) { until := time.Now().Add(decision.Cooldown) markGrokModelQuotaBlock(account.ID, decision.Model, until) - // Account-wide cool is still applied as a safety net when the body - // does not isolate a model cleanly; for model-specific free we only - // model-block and skip the long account cool when BlockModel was set - // by the classifier... actually free-usage keeps BlockModel false. - // Prefer model block + shorter account cool via tempUnschedule below. + // The upstream explicitly scoped exhaustion to this model, so an + // account-wide cool would incorrectly take healthy sibling models out + // of rotation. + return true } coolUntil := time.Now().Add(decision.Cooldown) markGrokFreeUsageRecovery(account.ID, coolUntil)