| | | 1 | | namespace NexusLabs.Needlr.AgentFramework.Providers; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Configuration for <see cref="TieredProviderSelector{TQuery, TResult}"/> controlling |
| | | 5 | | /// how exceptions thrown by providers are handled. |
| | | 6 | | /// </summary> |
| | | 7 | | /// <remarks> |
| | | 8 | | /// <para> |
| | | 9 | | /// The selector evaluates <see cref="FailurePolicies"/> in order against each thrown |
| | | 10 | | /// exception (first match wins). When a policy matches, the selector falls through to |
| | | 11 | | /// the next provider, optionally caches a per-provider skip-until timestamp so future |
| | | 12 | | /// calls bypass the failing provider for a configurable duration, and optionally |
| | | 13 | | /// invokes the policy's <see cref="ProviderFailurePolicy.OnHit"/> callback. When no |
| | | 14 | | /// policy matches, the exception is re-thrown unchanged. |
| | | 15 | | /// </para> |
| | | 16 | | /// <para> |
| | | 17 | | /// Use <see cref="Default"/> to preserve the framework's historical behaviour |
| | | 18 | | /// (<see cref="ProviderUnavailableException"/> falls through, no skip, no callback) and |
| | | 19 | | /// extend it with <c>with</c>-clones to add custom policies: |
| | | 20 | | /// </para> |
| | | 21 | | /// <code> |
| | | 22 | | /// var options = TieredProviderSelectorOptions.Default with |
| | | 23 | | /// { |
| | | 24 | | /// FailurePolicies = |
| | | 25 | | /// [ |
| | | 26 | | /// .. TieredProviderSelectorOptions.Default.FailurePolicies, |
| | | 27 | | /// new ProviderFailurePolicy( |
| | | 28 | | /// Match: ex => ex is ApiAuthException, |
| | | 29 | | /// SkipDuration: TimeSpan.FromMinutes(5)), |
| | | 30 | | /// ], |
| | | 31 | | /// }; |
| | | 32 | | /// </code> |
| | | 33 | | /// </remarks> |
| | | 34 | | [DoNotAutoRegister] |
| | | 35 | | public sealed record TieredProviderSelectorOptions |
| | | 36 | | { |
| | | 37 | | /// <summary> |
| | | 38 | | /// Ordered list of failure policies. The first policy whose |
| | | 39 | | /// <see cref="ProviderFailurePolicy.Match"/> predicate returns <see langword="true"/> |
| | | 40 | | /// for a thrown exception is applied; subsequent policies are not evaluated. |
| | | 41 | | /// Defaults to an empty list (every exception propagates raw). |
| | | 42 | | /// </summary> |
| | 76 | 43 | | public IReadOnlyList<ProviderFailurePolicy> FailurePolicies { get; init; } = []; |
| | | 44 | | |
| | | 45 | | /// <summary> |
| | | 46 | | /// Default options preserving the framework's historical fall-through behaviour: |
| | | 47 | | /// <see cref="ProviderUnavailableException"/> matches with no skip and no callback, |
| | | 48 | | /// so the selector falls through to the next provider on every PUE without any |
| | | 49 | | /// cross-call memory of the failure. Other exception types propagate raw. |
| | | 50 | | /// </summary> |
| | | 51 | | /// <remarks> |
| | | 52 | | /// Use this as the starting point for custom configurations: |
| | | 53 | | /// <c>TieredProviderSelectorOptions.Default with { FailurePolicies = [...] }</c>. |
| | | 54 | | /// </remarks> |
| | 63 | 55 | | public static TieredProviderSelectorOptions Default { get; } = new() |
| | 1 | 56 | | { |
| | 1 | 57 | | FailurePolicies = |
| | 1 | 58 | | [ |
| | 1 | 59 | | new ProviderFailurePolicy( |
| | 20 | 60 | | Match: static ex => ex is ProviderUnavailableException, |
| | 1 | 61 | | SkipDuration: null, |
| | 1 | 62 | | OnHit: null), |
| | 1 | 63 | | ], |
| | 1 | 64 | | }; |
| | | 65 | | } |