< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap
Assembly: NexusLabs.Needlr.AgentFramework
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework/AgentFrameworkGeneratedBootstrap.cs
Line coverage
81%
Covered lines: 183
Uncovered lines: 42
Coverable lines: 225
Total lines: 413
Line coverage: 81.3%
Branch coverage
54%
Covered branches: 48
Total branches: 88
Branch coverage: 54.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_FunctionTypes()100%11100%
get_GroupTypes()100%11100%
get_AgentTypes()100%11100%
get_HandoffTopology()100%11100%
get_GroupChatGroups()100%11100%
get_SequentialTopology()100%11100%
.cctor()100%11100%
RegisterAIFunctionProvider(...)100%11100%
TryGetAIFunctionProvider(...)100%22100%
Register(...)50%22100%
TryGetFunctionTypes(...)50%5466.66%
TryGetGroupTypes(...)75%4483.33%
TryGetAgentTypes(...)75%4483.33%
TryGetHandoffTopology(...)75%4483.33%
TryGetGroupChatGroups(...)75%4483.33%
TryGetSequentialTopology(...)75%4483.33%
BeginTestScope(...)100%66100%
EnsureCombined()40.74%1315470.21%
.ctor(...)100%11100%
Dispose()100%11100%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework/AgentFrameworkGeneratedBootstrap.cs

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2
 3namespace NexusLabs.Needlr.AgentFramework;
 4
 5/// <summary>
 6/// Runtime bootstrap registry for source-generated Agent Framework components.
 7/// </summary>
 8/// <remarks>
 9/// The source generator emits a <c>[ModuleInitializer]</c> in the host assembly that calls
 10/// <see cref="Register"/> with the generated type providers.
 11/// <c>UsingAgentFramework()</c> checks this bootstrap
 12/// and auto-populates function types, groups, and agent types without requiring any explicit
 13/// <c>Add*FromGenerated()</c> calls.
 14/// </remarks>
 15public static class AgentFrameworkGeneratedBootstrap
 16{
 17    private sealed class Registration
 18    {
 1219        public Registration(
 1220            Func<IReadOnlyList<Type>> functionTypes,
 1221            Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> groupTypes,
 1222            Func<IReadOnlyList<Type>> agentTypes,
 1223            Func<IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason)>>> handoffTopology,
 1224            Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> groupChatGroups,
 1225            Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> sequentialTopology)
 26        {
 1227            FunctionTypes = functionTypes;
 1228            GroupTypes = groupTypes;
 1229            AgentTypes = agentTypes;
 1230            HandoffTopology = handoffTopology;
 1231            GroupChatGroups = groupChatGroups;
 1232            SequentialTopology = sequentialTopology;
 1233        }
 34
 135        public Func<IReadOnlyList<Type>> FunctionTypes { get; }
 336        public Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> GroupTypes { get; }
 337        public Func<IReadOnlyList<Type>> AgentTypes { get; }
 438        public Func<IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason)>>> HandoffTopology 
 439        public Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> GroupChatGroups { get; }
 440        public Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> SequentialTopology { get; }
 41    }
 42
 143    private static readonly object _gate = new();
 144    private static readonly List<Registration> _registrations = [];
 145    private static readonly AsyncLocal<Registration?> _asyncLocalOverride = new();
 46    private static IAIFunctionProvider? _aiFunctionProvider;
 147    private static readonly AsyncLocal<IAIFunctionProvider?> _asyncLocalProviderOverride = new();
 48
 49    private static (
 50        Func<IReadOnlyList<Type>> Functions,
 51        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> Groups,
 52        Func<IReadOnlyList<Type>> Agents,
 53        Func<IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason)>>> HandoffTopology,
 54        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> GroupChatGroups,
 55        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> SequentialTopology)? _cachedCombined;
 56
 57    /// <summary>
 58    /// Registers the generated <see cref="IAIFunctionProvider"/> for this assembly.
 59    /// Called automatically by the generator-emitted <c>[ModuleInitializer]</c>.
 60    /// </summary>
 61    public static void RegisterAIFunctionProvider(IAIFunctionProvider provider)
 62    {
 163        ArgumentNullException.ThrowIfNull(provider);
 164        _aiFunctionProvider = provider;
 165    }
 66
 67    /// <summary>
 68    /// Gets the registered <see cref="IAIFunctionProvider"/>, if one has been registered.
 69    /// </summary>
 70    public static bool TryGetAIFunctionProvider([NotNullWhen(true)] out IAIFunctionProvider? provider)
 71    {
 6572        var local = _asyncLocalProviderOverride.Value;
 6573        if (local is not null)
 74        {
 275            provider = local;
 276            return true;
 77        }
 78
 6379        provider = _aiFunctionProvider;
 6380        return provider is not null;
 81    }
 82
 83    /// <summary>
 84    /// Registers the generated type providers for this assembly.
 85    /// Called automatically by the generator-emitted <c>[ModuleInitializer]</c>.
 86    /// </summary>
 87    public static void Register(
 88        Func<IReadOnlyList<Type>> functionTypes,
 89        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> groupTypes,
 90        Func<IReadOnlyList<Type>> agentTypes,
 91        Func<IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason)>>> handoffTopology,
 92        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> groupChatGroups,
 93        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>>? sequentialTopology = null)
 94    {
 195        ArgumentNullException.ThrowIfNull(functionTypes);
 196        ArgumentNullException.ThrowIfNull(groupTypes);
 197        ArgumentNullException.ThrowIfNull(agentTypes);
 198        ArgumentNullException.ThrowIfNull(handoffTopology);
 199        ArgumentNullException.ThrowIfNull(groupChatGroups);
 1100        sequentialTopology ??= static () => new Dictionary<string, IReadOnlyList<Type>>();
 101
 1102        lock (_gate)
 103        {
 1104            _registrations.Add(new Registration(functionTypes, groupTypes, agentTypes, handoffTopology, groupChatGroups,
 1105            _cachedCombined = null;
 1106        }
 1107    }
 108
 109    /// <summary>
 110    /// Gets the combined function type provider from all registered assemblies.
 111    /// </summary>
 112    public static bool TryGetFunctionTypes([NotNullWhen(true)] out Func<IReadOnlyList<Type>>? provider)
 113    {
 37114        var local = _asyncLocalOverride.Value;
 37115        if (local is not null)
 116        {
 0117            provider = local.FunctionTypes;
 0118            return true;
 119        }
 120
 37121        lock (_gate)
 122        {
 37123            if (_registrations.Count == 0)
 124            {
 0125                provider = null;
 0126                return false;
 127            }
 128
 37129            EnsureCombined();
 37130            provider = _cachedCombined!.Value.Functions;
 37131            return true;
 132        }
 37133    }
 134
 135    /// <summary>
 136    /// Gets the combined function group provider from all registered assemblies.
 137    /// </summary>
 138    public static bool TryGetGroupTypes([NotNullWhen(true)] out Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>>? 
 139    {
 52140        var local = _asyncLocalOverride.Value;
 52141        if (local is not null)
 142        {
 2143            provider = local.GroupTypes;
 2144            return true;
 145        }
 146
 50147        lock (_gate)
 148        {
 50149            if (_registrations.Count == 0)
 150            {
 0151                provider = null;
 0152                return false;
 153            }
 154
 50155            EnsureCombined();
 50156            provider = _cachedCombined!.Value.Groups;
 50157            return true;
 158        }
 50159    }
 160
 161    /// <summary>
 162    /// Gets the combined agent type provider from all registered assemblies.
 163    /// </summary>
 164    public static bool TryGetAgentTypes([NotNullWhen(true)] out Func<IReadOnlyList<Type>>? provider)
 165    {
 36166        var local = _asyncLocalOverride.Value;
 36167        if (local is not null)
 168        {
 2169            provider = local.AgentTypes;
 2170            return true;
 171        }
 172
 34173        lock (_gate)
 174        {
 34175            if (_registrations.Count == 0)
 176            {
 0177                provider = null;
 0178                return false;
 179            }
 180
 34181            EnsureCombined();
 34182            provider = _cachedCombined!.Value.Agents;
 34183            return true;
 184        }
 34185    }
 186
 187    /// <summary>
 188    /// Gets the combined handoff topology provider from all registered assemblies.
 189    /// </summary>
 190    public static bool TryGetHandoffTopology(
 191        [NotNullWhen(true)] out Func<IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason)>>>
 192    {
 9193        var local = _asyncLocalOverride.Value;
 9194        if (local is not null)
 195        {
 3196            provider = local.HandoffTopology;
 3197            return true;
 198        }
 199
 6200        lock (_gate)
 201        {
 6202            if (_registrations.Count == 0)
 203            {
 0204                provider = null;
 0205                return false;
 206            }
 207
 6208            EnsureCombined();
 6209            provider = _cachedCombined!.Value.HandoffTopology;
 6210            return true;
 211        }
 6212    }
 213
 214    /// <summary>
 215    /// Gets the combined group chat groups provider from all registered assemblies.
 216    /// </summary>
 217    public static bool TryGetGroupChatGroups(
 218        [NotNullWhen(true)] out Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>>? provider)
 219    {
 8220        var local = _asyncLocalOverride.Value;
 8221        if (local is not null)
 222        {
 3223            provider = local.GroupChatGroups;
 3224            return true;
 225        }
 226
 5227        lock (_gate)
 228        {
 5229            if (_registrations.Count == 0)
 230            {
 0231                provider = null;
 0232                return false;
 233            }
 234
 5235            EnsureCombined();
 5236            provider = _cachedCombined!.Value.GroupChatGroups;
 5237            return true;
 238        }
 5239    }
 240
 241    /// <summary>
 242    /// Gets the combined sequential topology provider from all registered assemblies.
 243    /// </summary>
 244    public static bool TryGetSequentialTopology(
 245        [NotNullWhen(true)] out Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>>? provider)
 246    {
 7247        var local = _asyncLocalOverride.Value;
 7248        if (local is not null)
 249        {
 3250            provider = local.SequentialTopology;
 3251            return true;
 252        }
 253
 4254        lock (_gate)
 255        {
 4256            if (_registrations.Count == 0)
 257            {
 0258                provider = null;
 0259                return false;
 260            }
 261
 4262            EnsureCombined();
 4263            provider = _cachedCombined!.Value.SequentialTopology;
 4264            return true;
 265        }
 4266    }
 267
 268    /// <summary>
 269    /// Creates a test-scoped override that replaces bootstrap discovery for the current async context.
 270    /// Dispose the returned scope to restore the previous state.
 271    /// </summary>
 272    internal static IDisposable BeginTestScope(
 273        Func<IReadOnlyList<Type>> functionTypes,
 274        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> groupTypes,
 275        Func<IReadOnlyList<Type>> agentTypes,
 276        Func<IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason)>>>? handoffTopology = null
 277        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>>? groupChatGroups = null,
 278        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>>? sequentialTopology = null,
 279        IAIFunctionProvider? aiFunctionProvider = null)
 280    {
 11281        handoffTopology ??= static () => new Dictionary<Type, IReadOnlyList<(Type, string?)>>();
 11282        groupChatGroups ??= static () => new Dictionary<string, IReadOnlyList<Type>>();
 11283        sequentialTopology ??= static () => new Dictionary<string, IReadOnlyList<Type>>();
 11284        var prior = _asyncLocalOverride.Value;
 11285        var priorProvider = _asyncLocalProviderOverride.Value;
 11286        _asyncLocalOverride.Value = new Registration(functionTypes, groupTypes, agentTypes, handoffTopology, groupChatGr
 11287        _asyncLocalProviderOverride.Value = aiFunctionProvider;
 11288        return new Scope(prior, priorProvider);
 289    }
 290
 291    private static void EnsureCombined()
 292    {
 136293        if (_cachedCombined.HasValue)
 135294            return;
 295
 2296        var functionProviders = _registrations.Select(r => r.FunctionTypes).ToArray();
 2297        var groupProviders = _registrations.Select(r => r.GroupTypes).ToArray();
 2298        var agentProviders = _registrations.Select(r => r.AgentTypes).ToArray();
 2299        var topologyProviders = _registrations.Select(r => r.HandoffTopology).ToArray();
 2300        var groupChatProviders = _registrations.Select(r => r.GroupChatGroups).ToArray();
 2301        var sequentialProviders = _registrations.Select(r => r.SequentialTopology).ToArray();
 302
 1303        Func<IReadOnlyList<Type>> combinedFunctions = () =>
 1304        {
 37305            var result = new List<Type>();
 37306            var seen = new HashSet<Type>();
 148307            foreach (var p in functionProviders)
 74308                foreach (var t in p())
 0309                    if (seen.Add(t))
 0310                        result.Add(t);
 37311            return result;
 1312        };
 313
 1314        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> combinedGroups = () =>
 1315        {
 50316            var merged = new Dictionary<string, List<Type>>();
 200317            foreach (var p in groupProviders)
 100318                foreach (var (key, types) in p())
 1319                {
 0320                    if (!merged.TryGetValue(key, out var list))
 0321                        merged[key] = list = [];
 0322                    foreach (var t in types)
 0323                        if (!list.Contains(t))
 0324                            list.Add(t);
 1325                }
 50326            return merged.ToDictionary(
 0327                kv => kv.Key,
 50328                kv => (IReadOnlyList<Type>)kv.Value.AsReadOnly());
 1329        };
 330
 1331        Func<IReadOnlyList<Type>> combinedAgents = () =>
 1332        {
 34333            var result = new List<Type>();
 34334            var seen = new HashSet<Type>();
 136335            foreach (var p in agentProviders)
 68336                foreach (var t in p())
 0337                    if (seen.Add(t))
 0338                        result.Add(t);
 34339            return result;
 1340        };
 341
 1342        Func<IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason)>>> combinedTopology = () =
 1343        {
 6344            var merged = new Dictionary<Type, List<(Type, string?)>>();
 24345            foreach (var p in topologyProviders)
 12346                foreach (var (agentType, targets) in p())
 1347                {
 0348                    if (!merged.TryGetValue(agentType, out var list))
 0349                        merged[agentType] = list = [];
 0350                    foreach (var target in targets)
 0351                        if (!list.Contains(target))
 0352                            list.Add(target);
 1353                }
 6354            return merged.ToDictionary(
 0355                kv => kv.Key,
 6356                kv => (IReadOnlyList<(Type, string?)>)kv.Value.AsReadOnly());
 1357        };
 358
 1359        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> combinedGroupChatGroups = () =>
 1360        {
 5361            var merged = new Dictionary<string, List<Type>>();
 20362            foreach (var p in groupChatProviders)
 10363                foreach (var (key, types) in p())
 1364                {
 0365                    if (!merged.TryGetValue(key, out var list))
 0366                        merged[key] = list = [];
 0367                    foreach (var t in types)
 0368                        if (!list.Contains(t))
 0369                            list.Add(t);
 1370                }
 5371            return merged.ToDictionary(
 0372                kv => kv.Key,
 5373                kv => (IReadOnlyList<Type>)kv.Value.AsReadOnly());
 1374        };
 375
 1376        Func<IReadOnlyDictionary<string, IReadOnlyList<Type>>> combinedSequentialTopology = () =>
 1377        {
 4378            var merged = new Dictionary<string, List<Type>>();
 16379            foreach (var p in sequentialProviders)
 8380                foreach (var (key, types) in p())
 1381                {
 0382                    if (!merged.TryGetValue(key, out var list))
 0383                        merged[key] = list = [];
 0384                    foreach (var t in types)
 0385                        if (!list.Contains(t))
 0386                            list.Add(t);
 1387                }
 4388            return merged.ToDictionary(
 0389                kv => kv.Key,
 4390                kv => (IReadOnlyList<Type>)kv.Value.AsReadOnly());
 1391        };
 392
 1393        _cachedCombined = (combinedFunctions, combinedGroups, combinedAgents, combinedTopology, combinedGroupChatGroups,
 1394    }
 395
 396    private sealed class Scope : IDisposable
 397    {
 398        private readonly Registration? _prior;
 399        private readonly IAIFunctionProvider? _priorProvider;
 400
 11401        public Scope(Registration? prior, IAIFunctionProvider? priorProvider)
 402        {
 11403            _prior = prior;
 11404            _priorProvider = priorProvider;
 11405        }
 406
 407        public void Dispose()
 408        {
 11409            _asyncLocalOverride.Value = _prior;
 11410            _asyncLocalProviderOverride.Value = _priorProvider;
 11411        }
 412    }
 413}

Methods/Properties

.ctor(System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>,System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.Type,System.Collections.Generic.IReadOnlyList`1<System.ValueTuple`2<System.Type,System.String>>>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>)
get_FunctionTypes()
get_GroupTypes()
get_AgentTypes()
get_HandoffTopology()
get_GroupChatGroups()
get_SequentialTopology()
.cctor()
RegisterAIFunctionProvider(NexusLabs.Needlr.AgentFramework.IAIFunctionProvider)
TryGetAIFunctionProvider(NexusLabs.Needlr.AgentFramework.IAIFunctionProvider&)
Register(System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>,System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.Type,System.Collections.Generic.IReadOnlyList`1<System.ValueTuple`2<System.Type,System.String>>>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>)
TryGetFunctionTypes(System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>&)
TryGetGroupTypes(System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>&)
TryGetAgentTypes(System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>&)
TryGetHandoffTopology(System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.Type,System.Collections.Generic.IReadOnlyList`1<System.ValueTuple`2<System.Type,System.String>>>>&)
TryGetGroupChatGroups(System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>&)
TryGetSequentialTopology(System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>&)
BeginTestScope(System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>,System.Func`1<System.Collections.Generic.IReadOnlyList`1<System.Type>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.Type,System.Collections.Generic.IReadOnlyList`1<System.ValueTuple`2<System.Type,System.String>>>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>,System.Func`1<System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<System.Type>>>,NexusLabs.Needlr.AgentFramework.IAIFunctionProvider)
EnsureCombined()
.ctor(NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap/Registration,NexusLabs.Needlr.AgentFramework.IAIFunctionProvider)
Dispose()