( ctx context.Context, systemPrompt string, messages []map[string]any, opts CompleteOptions, )
| 262 | if !sleepBackoff(ctx, attempt, cfg) { |
| 263 | return nil, ctx.Err() |
| 264 | } |
| 265 | continue |
| 266 | } |
| 267 | break |
| 268 | } |
| 269 | |
| 270 | bodyBytes, readErr := io.ReadAll(resp.Body) |
| 271 | _ = resp.Body.Close() |
| 272 | cancel() |
| 273 | if readErr != nil { |
| 274 | lastErr = readErr |
| 275 | if attempt < cfg.MaxRetries { |
| 276 | if !sleepBackoff(ctx, attempt, cfg) { |
| 277 | return nil, ctx.Err() |
| 278 | } |
| 279 | continue |
| 280 | } |
| 281 | break |
| 282 | } |
| 283 | |
| 284 | if resp.StatusCode == http.StatusOK { |
| 285 | var data map[string]any |
| 286 | if err := json.Unmarshal(bodyBytes, &data); err != nil { |
| 287 | return nil, fmt.Errorf("invalid JSON in 200 response: %.200s", string(bodyBytes)) |
| 288 | } |
| 289 | noteProviderSuccess(cooldownKey) |
| 290 | return data, nil |
| 291 | } |
| 292 | |
| 293 | statusErr := NewAPIStatusError(c.ProviderName, resp.StatusCode, resp.Status, bodyBytes) |
| 294 | if resp.StatusCode == http.StatusTooManyRequests { |
| 295 | noteProviderRateLimit(cooldownKey) |
| 296 | } |
| 297 | if isRetryableHTTPError(resp.StatusCode, statusErr.Body, cfg) && attempt < cfg.MaxRetries { |
| 298 | if !sleepBackoff(ctx, attempt, cfg) { |
| 299 | return nil, ctx.Err() |
| 300 | } |
| 301 | continue |
| 302 | } |
| 303 | |
| 304 | return nil, statusErr |
| 305 | } |
| 306 | |
| 307 | return nil, fmt.Errorf("request failed after %d attempts: %w", cfg.MaxRetries+1, lastErr) |
| 308 | } |
| 309 | |
| 310 | func isRetryableHTTPError(statusCode int, errorText string, cfg RetryConfig) bool { |
| 311 | if _, ok := cfg.RetryableStatuses[statusCode]; ok { |
| 312 | return true |
| 313 | } |
| 314 | |
| 315 | if _, ok := cfg.ConditionalRetryStatuses[statusCode]; ok { |
| 316 | for keyword := range cfg.NonRetryableKeywords { |
| 317 | if strings.Contains(errorText, keyword) { |
| 318 | return false |
| 319 | } |
| 320 | } |
| 321 | return true |
nothing calls this directly
no test coverage detected