< Summary

Information
Class: Intentum.AI.Http.EmbeddingHttpRetryHandler
Assembly: Intentum.AI
File(s): /home/runner/work/Intentum/Intentum/src/Intentum.AI/Http/EmbeddingHttpRetryHandler.cs
Line coverage
88%
Covered lines: 22
Uncovered lines: 3
Coverable lines: 25
Total lines: 59
Line coverage: 88%
Branch coverage
86%
Covered branches: 19
Total branches: 22
Branch coverage: 86.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
SendWithRetryAsync()86.36%222288%

File(s)

/home/runner/work/Intentum/Intentum/src/Intentum.AI/Http/EmbeddingHttpRetryHandler.cs

#LineLine coverage
 1using System.Net;
 2
 3namespace Intentum.AI.Http;
 4
 5/// <summary>
 6/// Shared HTTP retry handler for embedding providers. Retries on 429 (Too Many Requests)
 7/// with exponential backoff and Retry-After header support.
 8/// </summary>
 9public static class EmbeddingHttpRetryHandler
 10{
 11    /// <summary>
 12    /// Sends an HTTP request with retry logic for rate limiting (429).
 13    /// </summary>
 14    /// <param name="sendRequest">Factory that produces the HTTP request for each attempt.</param>
 15    /// <param name="onRateLimitExhausted">Called when all retries are exhausted on 429. Should throw a provider-specifi
 16    /// <param name="maxAttempts">Maximum number of retry attempts.</param>
 17    /// <param name="maxWaitSeconds">Maximum wait time in seconds per retry.</param>
 18    /// <param name="cancellationToken">Cancellation token.</param>
 19    /// <returns>A successful HttpResponseMessage.</returns>
 20    [System.Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "S3966:Objects should not be disposed more than 
 21    public static async Task<HttpResponseMessage> SendWithRetryAsync(
 22        Func<CancellationToken, Task<HttpResponseMessage>> sendRequest,
 23        Action<double?, string?> onRateLimitExhausted,
 24        int maxAttempts = 5,
 25        int maxWaitSeconds = 90,
 26        CancellationToken cancellationToken = default)
 1327    {
 1328        HttpResponseMessage? response = null;
 29
 3030        for (var attempt = 1; attempt <= maxAttempts; attempt++)
 1531        {
 1532            response?.Dispose();
 1533            response = await sendRequest(cancellationToken);
 34
 1535            if (response.IsSuccessStatusCode)
 1036                return response;
 37
 538            if (response.StatusCode == HttpStatusCode.TooManyRequests && attempt == maxAttempts)
 139            {
 140                var retryAfterSec = response.Headers.RetryAfter?.Delta?.TotalSeconds;
 141                var body = await response.Content.ReadAsStringAsync(cancellationToken);
 142                response.Dispose();
 143                onRateLimitExhausted(retryAfterSec, body);
 044            }
 45
 446            if (response.StatusCode != HttpStatusCode.TooManyRequests)
 247                response.EnsureSuccessStatusCode();
 48
 249            var delay = TimeSpan.FromSeconds(5 * attempt);
 250            if (response.Headers.RetryAfter?.Delta is { } retryAfter)
 151                delay = TimeSpan.FromSeconds(Math.Min(retryAfter.TotalSeconds, maxWaitSeconds));
 52
 253            await Task.Delay(delay, cancellationToken);
 254        }
 55
 056        response!.EnsureSuccessStatusCode();
 057        return response;
 1058    }
 59}

Methods/Properties

SendWithRetryAsync()