fix(retry): handle Retry-After date and align review nits

This commit is contained in:
pikaxinge 2026-02-27 10:45:18 +00:00
parent e171d77353
commit 07e3f16ca8
3 changed files with 17 additions and 14 deletions

View file

@ -139,8 +139,10 @@ func TestClassifyError_TimeoutPatterns(t *testing.T) {
"connection timed out",
"deadline exceeded",
"context deadline exceeded",
"connection reset",
"connection reset by peer",
"tls handshake timeout",
"EOF",
}
for _, msg := range patterns {

View file

@ -43,7 +43,7 @@ type RetryPolicy struct {
Jitter RetryJitterFunc
}
var retryAfterPattern = regexp.MustCompile(`(?i)retry[- ]after[:=]?\s*([^\s\r\n]+)`)
var retryAfterPattern = regexp.MustCompile(`(?i)retry[- ]after[:=]?\s*([^\r\n]+)`)
// DefaultLLMRetryPolicy returns the default retry behavior for LLM calls.
func DefaultLLMRetryPolicy() RetryPolicy {
@ -101,7 +101,7 @@ func DoWithRetry[T any](ctx context.Context, policy RetryPolicy, fn func(context
sleepFn := policy.Sleep
if sleepFn == nil {
sleepFn = sleepWithContext
sleepFn = sleepWithCtx
}
jitterFn := policy.Jitter
if jitterFn == nil {
@ -232,18 +232,6 @@ func boundedAttemptTimeout(ctx context.Context, configured time.Duration) (time.
return configured, true
}
func sleepWithContext(ctx context.Context, d time.Duration) error {
timer := time.NewTimer(d)
defer timer.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
return nil
}
}
func defaultJitter(max time.Duration) time.Duration {
if max <= 0 {
return 0

View file

@ -150,3 +150,16 @@ func TestLLMRetry_DoWithRetry_UsesRetryAfterFor429(t *testing.T) {
t.Fatalf("slept = %v, want 3s", slept)
}
}
func TestLLMRetry_ExtractRetryAfter_HTTPDate(t *testing.T) {
now := time.Date(2015, 10, 21, 7, 27, 0, 0, time.UTC)
err := errors.New("API request failed:\n Status: 429\n Retry-After: Wed, 21 Oct 2015 07:28:00 GMT")
delay, ok := extractRetryAfter(err, now)
if !ok {
t.Fatal("expected retry-after date to parse")
}
if delay != time.Minute {
t.Fatalf("delay = %v, want 1m", delay)
}
}