< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.AgentFrameworkSyringe
Assembly: NexusLabs.Needlr.AgentFramework
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework/AgentFrameworkSyringe.cs
Line coverage
100%
Covered lines: 33
Uncovered lines: 0
Coverable lines: 33
Total lines: 111
Line coverage: 100%
Branch coverage
75%
Covered branches: 12
Total branches: 16
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_ServiceProvider()100%11100%
get_ConfigureAgentFactory()100%11100%
get_FunctionTypes()100%11100%
get_FunctionGroupMap()100%11100%
get_AgentTypes()100%11100%
get_Plugins()100%11100%
get_PerAgentResilienceFactory()100%11100%
get_MetricsOptions()100%11100%
get_TokenTrackingWired()100%11100%
BuildAgentFactory()75%1616100%

File(s)

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

#LineLine coverage
 1using Microsoft.Extensions.DependencyInjection;
 2using Microsoft.Extensions.DependencyInjection.Extensions;
 3
 4namespace NexusLabs.Needlr.AgentFramework;
 5
 6/// <summary>
 7/// Fluent builder for configuring the Microsoft Agent Framework with Needlr function discovery.
 8/// </summary>
 9/// <remarks>
 10/// <para>
 11/// When the Needlr source generator is active (the common case), this class uses pre-built
 12/// <see cref="IAIFunctionProvider"/> instances registered by the generated <c>[ModuleInitializer]</c>.
 13/// No reflection is required in that path.
 14/// </para>
 15/// <para>
 16/// When the source generator is not used, this class falls back to reflection to discover
 17/// methods decorated with <see cref="AgentFunctionAttribute"/>. That path carries
 18/// <c>[RequiresDynamicCode]</c> and is not NativeAOT-compatible.
 19/// </para>
 20/// </remarks>
 21/// <example>
 22/// <code>
 23/// // Obtained from SyringeAgentFrameworkExtensions.UsingAgentFramework()
 24/// AgentFrameworkSyringe syringe = app.Services.UsingAgentFramework();
 25///
 26/// // Register function types and build the factory
 27/// IAgentFactory factory = syringe
 28///     .AddAgentFunctionsFromGenerated(GeneratedAgentFunctions.AllFunctionTypes)
 29///     .BuildAgentFactory();
 30///
 31/// // Create agents from the factory
 32/// var supportAgent = factory.CreateAgent&lt;CustomerSupportAgent&gt;();
 33/// </code>
 34/// </example>
 35[DoNotAutoRegister]
 36public sealed record AgentFrameworkSyringe
 37{
 43038    public required IServiceProvider ServiceProvider { get; init; }
 39
 95440    internal List<Action<AgentFrameworkConfigureOptions>>? ConfigureAgentFactory { get; init; } = [];
 41
 104042    internal List<Type>? FunctionTypes { get; init; } = [];
 43
 81344    internal IReadOnlyDictionary<string, IReadOnlyList<Type>>? FunctionGroupMap { get; init; }
 45
 157246    internal List<Type>? AgentTypes { get; init; } = [];
 47
 48    /// <summary>
 49    /// Agent-builder plugins applied to every agent created by the factory.
 50    /// Populated by middleware extension methods such as <c>UsingToolResultMiddleware()</c>
 51    /// and <c>UsingResilience()</c>.
 52    /// </summary>
 29453    internal IReadOnlyList<IAIAgentBuilderPlugin>? Plugins { get; init; }
 54
 55    /// <summary>
 56    /// Factory that creates an <see cref="IAIAgentBuilderPlugin"/> from a
 57    /// <see cref="AgentResilienceAttribute"/> found on an agent type.
 58    /// Set by <c>UsingResilience()</c> to enable per-agent resilience overrides via
 59    /// <c>[AgentResilience]</c>.
 60    /// </summary>
 19761    internal Func<AgentResilienceAttribute, IAIAgentBuilderPlugin>? PerAgentResilienceFactory { get; init; }
 62
 63    /// <summary>
 64    /// Metrics configuration (meter name, ActivitySource name). Populated by
 65    /// <c>ConfigureMetrics()</c>.
 66    /// </summary>
 4467    internal Diagnostics.AgentFrameworkMetricsOptions? MetricsOptions { get; init; }
 68
 69    /// <summary>
 70    /// Whether <c>UsingTokenTracking()</c> has already been called. Prevents
 71    /// double-wiring the recording middleware when both <c>UsingTokenBudget()</c>
 72    /// and <c>UsingDiagnostics()</c> are used together.
 73    /// </summary>
 7374    internal bool TokenTrackingWired { get; init; }
 75
 76    public IAgentFactory BuildAgentFactory()
 77    {
 18978        var groupTypes = (FunctionGroupMap ?? new Dictionary<string, IReadOnlyList<Type>>())
 31979            .SelectMany(kvp => kvp.Value);
 80
 18981        var allFunctionTypes = (FunctionTypes ?? [])
 18982            .Concat(groupTypes)
 18983            .Distinct()
 18984            .ToList();
 85
 18986        var agentTypeMap = new Dictionary<string, Type>(StringComparer.Ordinal);
 105987        foreach (var t in AgentTypes ?? [])
 88        {
 34189            var key = t.FullName ?? t.Name;
 34190            if (!agentTypeMap.TryAdd(key, t))
 91            {
 192                throw new InvalidOperationException(
 193                    $"Duplicate agent registration: '{key}' is already registered as " +
 194                    $"'{agentTypeMap[key].AssemblyQualifiedName}'. Cannot also register " +
 195                    $"'{t.AssemblyQualifiedName}'. Ensure each [NeedlrAiAgent] class has a unique fully-qualified name."
 96            }
 97        }
 98
 18899        AgentFrameworkGeneratedBootstrap.TryGetAIFunctionProvider(out var generatedProvider);
 100
 188101        return new AgentFactory(
 188102            serviceProvider: ServiceProvider,
 188103            configureCallbacks: ConfigureAgentFactory ?? [],
 188104            functionTypes: allFunctionTypes,
 188105            functionGroupMap: FunctionGroupMap,
 188106            agentTypeMap: agentTypeMap,
 188107            generatedProvider: generatedProvider,
 188108            plugins: Plugins ?? [],
 188109            perAgentResilienceFactory: PerAgentResilienceFactory);
 110    }
 111}