< Summary

Information
Class: Intentum.AI.Resilience.FallbackEmbeddingProvider
Assembly: Intentum.AI
File(s): /home/runner/work/Intentum/Intentum/src/Intentum.AI/Resilience/FallbackEmbeddingProvider.cs
Line coverage
95%
Covered lines: 42
Uncovered lines: 2
Coverable lines: 44
Total lines: 77
Line coverage: 95.4%
Branch coverage
92%
Covered branches: 13
Total branches: 14
Branch coverage: 92.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%66100%
Embed(...)100%11100%
EmbedAsync()100%4495%
IsCircuitOpen(...)75%4490%

File(s)

/home/runner/work/Intentum/Intentum/src/Intentum.AI/Resilience/FallbackEmbeddingProvider.cs

#LineLine coverage
 1using Intentum.AI.Embeddings;
 2
 3namespace Intentum.AI.Resilience;
 4
 5/// <summary>
 6/// Embedding provider that tries multiple providers in order, falling back to the next
 7/// when one fails. Implements a simple circuit breaker: after consecutive failures,
 8/// a provider is skipped for a cooldown period before being retried.
 9/// </summary>
 10public sealed class FallbackEmbeddingProvider : IIntentEmbeddingProvider
 11{
 12    private readonly List<IIntentEmbeddingProvider> _providers;
 13    private readonly int _maxConsecutiveFailures;
 14    private readonly TimeSpan _cooldownPeriod;
 15    private readonly int[] _consecutiveFailures;
 16    private readonly DateTimeOffset[] _lastFailureTimes;
 17
 718    public FallbackEmbeddingProvider(
 719        IEnumerable<IIntentEmbeddingProvider> providers,
 720        int maxConsecutiveFailures = 3,
 721        TimeSpan? cooldownPeriod = null)
 722    {
 723        _providers = (providers ?? throw new ArgumentNullException(nameof(providers))).ToList();
 624        if (_providers.Count == 0)
 125            throw new ArgumentException("At least one provider is required.", nameof(providers));
 26
 527        _maxConsecutiveFailures = maxConsecutiveFailures;
 528        _cooldownPeriod = cooldownPeriod ?? TimeSpan.FromMinutes(1);
 529        _consecutiveFailures = new int[_providers.Count];
 530        _lastFailureTimes = new DateTimeOffset[_providers.Count];
 531    }
 32
 33    public IntentEmbedding Embed(string behaviorKey)
 134        => EmbedAsync(behaviorKey, CancellationToken.None).GetAwaiter().GetResult();
 35
 36    public async Task<IntentEmbedding> EmbedAsync(string behaviorKey, CancellationToken cancellationToken = default)
 737    {
 738        Exception? lastException = null;
 39
 2440        for (var i = 0; i < _providers.Count; i++)
 941        {
 942            if (IsCircuitOpen(i))
 043                continue;
 44
 45            try
 946            {
 947                var result = await _providers[i].EmbedAsync(behaviorKey, cancellationToken);
 448                _consecutiveFailures[i] = 0;
 449                return result;
 50            }
 551            catch (Exception ex)
 552            {
 553                _consecutiveFailures[i]++;
 554                _lastFailureTimes[i] = DateTimeOffset.UtcNow;
 555                lastException = ex;
 556            }
 557        }
 58
 359        throw new InvalidOperationException(
 360            "All embedding providers failed.", lastException);
 461    }
 62
 63    private bool IsCircuitOpen(int index)
 964    {
 965        if (_consecutiveFailures[index] < _maxConsecutiveFailures)
 866            return false;
 67
 168        var elapsed = DateTimeOffset.UtcNow - _lastFailureTimes[index];
 169        if (elapsed >= _cooldownPeriod)
 170        {
 171            _consecutiveFailures[index] = 0;
 172            return false;
 73        }
 74
 075        return true;
 976    }
 77}