diff --git a/backend/internal/service/grok_quota_service_test.go b/backend/internal/service/grok_quota_service_test.go index 2ee2196429..0bfe577af3 100644 --- a/backend/internal/service/grok_quota_service_test.go +++ b/backend/internal/service/grok_quota_service_test.go @@ -155,6 +155,14 @@ func TestShouldAutoPauseGrokAccountByQuota(t *testing.T) { }, want: true, }, + { + name: "retry after expired", + snapshot: xai.QuotaSnapshot{ + RetryAfterSeconds: &retryAfter, + UpdatedAt: time.Now().Add(-time.Duration(retryAfter+1) * time.Second).UTC().Format(time.RFC3339), + }, + want: false, + }, { name: "stale snapshot ignored", snapshot: xai.QuotaSnapshot{ diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index 3e80cb7a2a..aaa99d1292 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -1418,7 +1418,7 @@ func shouldAutoPauseGrokAccountByQuota(account *Account) (bool, openAIQuotaAutoP if grokQuotaSnapshotStaleForPause(snapshot, now) { return false, openAIQuotaAutoPauseDecision{} } - if snapshot.RetryAfterSeconds != nil && *snapshot.RetryAfterSeconds > 0 { + if grokQuotaRetryAfterActive(snapshot, now) { return true, openAIQuotaAutoPauseDecision{window: "retry_after", threshold: 1, utilization: 1} } if paused, decision := shouldAutoPauseGrokQuotaWindow("requests", snapshot.Requests, now); paused { @@ -1430,6 +1430,21 @@ func shouldAutoPauseGrokAccountByQuota(account *Account) (bool, openAIQuotaAutoP return false, openAIQuotaAutoPauseDecision{} } +func grokQuotaRetryAfterActive(snapshot *xai.QuotaSnapshot, now time.Time) bool { + if snapshot == nil || snapshot.RetryAfterSeconds == nil || *snapshot.RetryAfterSeconds <= 0 { + return false + } + if strings.TrimSpace(snapshot.UpdatedAt) == "" { + return true + } + updatedAt, err := parseTime(snapshot.UpdatedAt) + if err != nil { + return true + } + retryAfterUntil := updatedAt.Add(time.Duration(*snapshot.RetryAfterSeconds) * time.Second) + return now.Before(retryAfterUntil) +} + func shouldAutoPauseGrokQuotaWindow(name string, window *xai.QuotaWindow, now time.Time) (bool, openAIQuotaAutoPauseDecision) { if window == nil || window.Limit == nil || window.Remaining == nil || *window.Limit <= 0 { return false, openAIQuotaAutoPauseDecision{} diff --git a/frontend/src/components/account/AccountUsageCell.vue b/frontend/src/components/account/AccountUsageCell.vue index c1c7bdd395..3d565e1c60 100644 --- a/frontend/src/components/account/AccountUsageCell.vue +++ b/frontend/src/components/account/AccountUsageCell.vue @@ -102,7 +102,11 @@ -
-
+
+
-
+ + +