fix: add rate-limit retry with exponential backoff

The retry loop only handled context/token errors, letting 429s and
rate-limit responses fail immediately. Add detection for 429, rate_limit,
resource_exhausted, overloaded, quota, and too_many_requests errors
with exponential backoff (5s, 10s, 20s). Works regardless of whether
fallback candidates are configured.
This commit is contained in:
Leandro Barbosa 2026-02-18 16:10:10 -03:00
parent 09061d8175
commit 751c7f4839

View file

@ -617,8 +617,8 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, agent *AgentInstance,
}) })
} }
// Retry loop for context/token errors // Retry loop for recoverable errors (context window + rate limits).
maxRetries := 2 maxRetries := 3
for retry := 0; retry <= maxRetries; retry++ { for retry := 0; retry <= maxRetries; retry++ {
response, err = callLLM() response, err = callLLM()
if err == nil { if err == nil {
@ -626,6 +626,34 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, agent *AgentInstance,
} }
errMsg := strings.ToLower(err.Error()) errMsg := strings.ToLower(err.Error())
// Rate-limit / transient errors: wait with exponential backoff.
isRateLimitError := strings.Contains(errMsg, "429") ||
strings.Contains(errMsg, "rate limit") ||
strings.Contains(errMsg, "rate_limit") ||
strings.Contains(errMsg, "resource_exhausted") ||
strings.Contains(errMsg, "resource exhausted") ||
strings.Contains(errMsg, "too many requests") ||
strings.Contains(errMsg, "overloaded") ||
strings.Contains(errMsg, "quota")
if isRateLimitError && retry < maxRetries {
backoff := time.Duration(1<<uint(retry)) * 5 * time.Second // 5s, 10s, 20s
logger.WarnCF("agent", "Rate limit detected, backing off", map[string]interface{}{
"error": err.Error(),
"retry": retry + 1,
"backoff": backoff.String(),
})
select {
case <-ctx.Done():
return "", iteration, ctx.Err()
case <-time.After(backoff):
}
continue
}
// Context window errors: compress history and retry.
isContextError := strings.Contains(errMsg, "token") || isContextError := strings.Contains(errMsg, "token") ||
strings.Contains(errMsg, "context") || strings.Contains(errMsg, "context") ||
strings.Contains(errMsg, "invalidparameter") || strings.Contains(errMsg, "invalidparameter") ||