< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.SyringeExtensionsForAgentFramework
Assembly: NexusLabs.Needlr.AgentFramework
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework/SyringeExtensionsForAgentFramework.cs
Line coverage
75%
Covered lines: 40
Uncovered lines: 13
Coverable lines: 53
Total lines: 141
Line coverage: 75.4%
Branch coverage
90%
Covered branches: 20
Total branches: 22
Branch coverage: 90.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
UsingAgentFramework(...)100%11100%
UsingAgentFramework(...)100%2020100%
UsingAgentFramework(...)0%620%

File(s)

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

#LineLine coverage
 1using Microsoft.Extensions.DependencyInjection;
 2
 3using NexusLabs.Needlr.Injection;
 4
 5namespace NexusLabs.Needlr.AgentFramework;
 6
 7/// <summary>
 8/// Extension methods for <see cref="ConfiguredSyringe"/> that enable registering
 9/// Microsoft Agent Framework infrastructure (namely <see cref="IAgentFactory"/>)
 10/// as part of the Needlr build pipeline.
 11/// </summary>
 12/// <remarks>
 13/// <para>
 14/// These helpers defer service registration using the Syringe
 15/// post-plugin registration callback so that function discovery and
 16/// registration are completed before the agent factory is added.
 17/// </para>
 18/// <para>
 19/// When the Needlr source generator is active, the generated <c>[ModuleInitializer]</c> registers
 20/// an <see cref="IAIFunctionProvider"/> that is used instead of reflection. This makes the
 21/// integration NativeAOT-compatible without any code changes.
 22/// </para>
 23/// <para>
 24/// When the source generator is not used, the integration falls back to reflection-based
 25/// <see cref="Microsoft.Extensions.AI.AIFunction"/> schema generation, which requires dynamic code.
 26/// </para>
 27/// </remarks>
 28public static class SyringeExtensionsForAgentFramework
 29{
 30    /// <summary>
 31    /// Registers an <see cref="IAgentFactory"/> built via a
 32    /// <see cref="AgentFrameworkSyringe"/> instance.
 33    /// </summary>
 34    /// <param name="syringe">
 35    /// The <see cref="ConfiguredSyringe"/> to augment with the registration.
 36    /// </param>
 37    /// <returns>
 38    /// A new <see cref="ConfiguredSyringe"/> instance containing the registration.
 39    /// </returns>
 40    public static ConfiguredSyringe UsingAgentFramework(
 41        this ConfiguredSyringe syringe)
 42    {
 243        ArgumentNullException.ThrowIfNull(syringe);
 444        return syringe.UsingAgentFramework(s => s);
 45    }
 46
 47    /// <summary>
 48    /// Registers an <see cref="IAgentFactory"/> built via a configurable
 49    /// <see cref="AgentFrameworkSyringe"/> instance.
 50    /// </summary>
 51    /// <param name="syringe">
 52    /// The <see cref="ConfiguredSyringe"/> to augment with the registration.
 53    /// </param>
 54    /// <param name="configure">
 55    /// A delegate that receives a pre-initialized <see cref="AgentFrameworkSyringe"/>
 56    /// (with its <see cref="AgentFrameworkSyringe.ServiceProvider"/> set) and
 57    /// returns the configured instance used to build the agent factory.
 58    /// </param>
 59    /// <returns>
 60    /// A new <see cref="ConfiguredSyringe"/> instance containing the registration.
 61    /// </returns>
 62    public static ConfiguredSyringe UsingAgentFramework(
 63        this ConfiguredSyringe syringe,
 64        Func<AgentFrameworkSyringe, AgentFrameworkSyringe> configure)
 65    {
 6566        ArgumentNullException.ThrowIfNull(syringe);
 6567        ArgumentNullException.ThrowIfNull(configure);
 68
 6569        return syringe.UsingPostPluginRegistrationCallback(services =>
 6570        {
 6571            services.AddSingleton<IAgentFactory>(provider =>
 6572            {
 6573                AgentFrameworkSyringe afSyringe = new()
 6574                {
 6575                    ServiceProvider = provider,
 6576                };
 6577                afSyringe = configure.Invoke(afSyringe);
 6578
 6579                // Auto-populate from [ModuleInitializer] bootstrap if nothing was explicitly configured.
 6580                // Explicit calls (Add*FromGenerated, AddAgentFunctions*, etc.) take precedence.
 6581                if ((afSyringe.FunctionTypes is null || afSyringe.FunctionTypes.Count == 0) &&
 6582                    AgentFrameworkGeneratedBootstrap.TryGetFunctionTypes(out var functionProvider))
 6583                {
 3784                    afSyringe = afSyringe with { FunctionTypes = functionProvider().ToList() };
 6585                }
 6586
 6587                if ((afSyringe.FunctionGroupMap is null || afSyringe.FunctionGroupMap.Count == 0) &&
 6588                    AgentFrameworkGeneratedBootstrap.TryGetGroupTypes(out var groupProvider))
 6589                {
 5290                    afSyringe = afSyringe with { FunctionGroupMap = groupProvider() };
 6591                }
 6592
 6593                if ((afSyringe.AgentTypes is null || afSyringe.AgentTypes.Count == 0) &&
 6594                    AgentFrameworkGeneratedBootstrap.TryGetAgentTypes(out var agentProvider))
 6595                {
 3696                    afSyringe = afSyringe with { AgentTypes = agentProvider().ToList() };
 6597                }
 6598
 6599                return afSyringe.BuildAgentFactory();
 65100            });
 65101
 65102            services.AddSingleton<IWorkflowFactory>(provider =>
 86103                new WorkflowFactory(provider.GetRequiredService<IAgentFactory>()));
 130104        });
 105    }
 106
 107    /// <summary>
 108    /// Registers an <see cref="IAgentFactory"/> built via an
 109    /// <see cref="AgentFrameworkSyringe"/> created by the supplied delegate.
 110    /// </summary>
 111    /// <param name="syringe">
 112    /// The <see cref="ConfiguredSyringe"/> to augment with the registration.
 113    /// </param>
 114    /// <param name="configure">
 115    /// A factory that creates a fully-configured <see cref="AgentFrameworkSyringe"/>
 116    /// used to build the agent factory. Useful when configuration does
 117    /// not need the service provider.
 118    /// </param>
 119    /// <returns>
 120    /// A new <see cref="ConfiguredSyringe"/> instance containing the registration.
 121    /// </returns>
 122    public static ConfiguredSyringe UsingAgentFramework(
 123        this ConfiguredSyringe syringe,
 124        Func<AgentFrameworkSyringe> configure)
 125    {
 0126        ArgumentNullException.ThrowIfNull(syringe);
 0127        ArgumentNullException.ThrowIfNull(configure);
 128
 0129        return syringe.UsingPostPluginRegistrationCallback(services =>
 0130        {
 0131            services.AddSingleton<IAgentFactory>(provider =>
 0132            {
 0133                var afSyringe = configure.Invoke();
 0134                return afSyringe.BuildAgentFactory();
 0135            });
 0136
 0137            services.AddSingleton<IWorkflowFactory>(provider =>
 0138                new WorkflowFactory(provider.GetRequiredService<IAgentFactory>()));
 0139        });
 140    }
 141}