< Summary

Information
Class: NexusLabs.Needlr.SemanticKernel.SemanticKernelSyringeExtensions
Assembly: NexusLabs.Needlr.SemanticKernel
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.SemanticKernel/SemanticKernelSyringeExtensions.cs
Line coverage
82%
Covered lines: 48
Uncovered lines: 10
Coverable lines: 58
Total lines: 248
Line coverage: 82.7%
Branch coverage
85%
Covered branches: 12
Total branches: 14
Branch coverage: 85.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.SemanticKernel/SemanticKernelSyringeExtensions.cs

#LineLine coverage
 1using Microsoft.Extensions.DependencyInjection;
 2using Microsoft.SemanticKernel;
 3
 4using NexusLabs.Needlr.SemanticKernel.PluginScanners;
 5
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8
 9namespace NexusLabs.Needlr.SemanticKernel;
 10
 11/// <summary>
 12/// Extension methods for <see cref="SemanticKernelSyringe"/> providing fluent configuration of Semantic Kernel integrat
 13/// </summary>
 14public static class SemanticKernelSyringeExtensions
 15{
 16    /// <summary>
 17    /// Registers a callback to further configure <see cref="KernelFactoryOptions"/> when the
 18    /// kernel factory is built.
 19    /// </summary>
 20    /// <param name="syringe">The Semantic Kernel syringe to configure.</param>
 21    /// <param name="configure">The configuration callback to apply.</param>
 22    /// <returns>The configured syringe.</returns>
 23    public static SemanticKernelSyringe Configure(
 24        this SemanticKernelSyringe syringe,
 25        Action<KernelFactoryOptions> configure)
 26    {
 327        ArgumentNullException.ThrowIfNull(syringe);
 328        ArgumentNullException.ThrowIfNull(configure);
 29
 330        return syringe with
 331        {
 332            ConfigureKernelFactory = (syringe.ConfigureKernelFactory ?? []).Append(configure).ToList()
 333        };
 34    }
 35
 36    /// <summary>
 37    /// Adds a single SemanticKernel plugin type to the syringe.
 38    /// </summary>
 39    /// <typeparam name="T">The plugin type that contains <see cref="KernelFunctionAttribute"/>-marked methods.</typepar
 40    /// <param name="syringe">The Semantic Kernel syringe to configure.</param>
 41    /// <returns>The configured syringe.</returns>
 42    public static SemanticKernelSyringe AddSemanticKernelPlugin<T>(
 43        this SemanticKernelSyringe syringe)
 44    {
 1345        ArgumentNullException.ThrowIfNull(syringe);
 46
 1347        return syringe.AddSemanticKernelPlugins([typeof(T)]);
 48    }
 49
 50    /// <summary>
 51    /// Adds SemanticKernel plugins from a pre-discovered list of types.
 52    /// This is the recommended approach for AOT/trimmed applications.
 53    /// </summary>
 54    /// <param name="syringe">The SemanticKernel syringe to configure.</param>
 55    /// <param name="pluginTypes">
 56    /// Pre-discovered plugin types, typically from the generated
 57    /// <c>NexusLabs.Needlr.Generated.SemanticKernelPlugins.AllPluginTypes</c>.
 58    /// </param>
 59    /// <returns>The configured syringe.</returns>
 60    /// <remarks>
 61    /// Example usage with source-generated types:
 62    /// <code>
 63    /// syringe.AddSemanticKernelPluginsFromGenerated(
 64    ///     NexusLabs.Needlr.Generated.SemanticKernelPlugins.AllPluginTypes);
 65    /// </code>
 66    /// </remarks>
 67    public static SemanticKernelSyringe AddSemanticKernelPluginsFromGenerated(
 68        this SemanticKernelSyringe syringe,
 69        IReadOnlyList<Type> pluginTypes)
 70    {
 071        ArgumentNullException.ThrowIfNull(syringe);
 072        ArgumentNullException.ThrowIfNull(pluginTypes);
 73
 074        var scanner = new GeneratedSemanticKernelPluginScanner(pluginTypes);
 075        return syringe.AddSemanticKernelPluginsFromScanner(scanner);
 76    }
 77
 78    /// <summary>
 79    /// Adds SemanticKernel plugins from a custom scanner.
 80    /// </summary>
 81    /// <param name="syringe">The SemanticKernel syringe to configure.</param>
 82    /// <param name="scanner">The scanner to use for plugin discovery.</param>
 83    /// <param name="includeInstancePlugins">Whether to include instance plugins.</param>
 84    /// <param name="includeStaticPlugins">Whether to include static plugins.</param>
 85    /// <returns>The configured syringe.</returns>
 86    public static SemanticKernelSyringe AddSemanticKernelPluginsFromScanner(
 87        this SemanticKernelSyringe syringe,
 88        ISemanticKernelPluginScanner scanner,
 89        bool includeInstancePlugins = true,
 90        bool includeStaticPlugins = true)
 91    {
 092        ArgumentNullException.ThrowIfNull(syringe);
 093        ArgumentNullException.ThrowIfNull(scanner);
 94
 95        // This method doesn't require reflection for scanning since the scanner provides the types
 96        // However, the AddSemanticKernelPlugins method still uses reflection for type inspection
 097        return syringe.AddSemanticKernelPlugins(
 098            scanner,
 099            includeInstancePlugins: includeInstancePlugins,
 0100            includeStaticPlugins: includeStaticPlugins);
 101    }
 102
 103    /// <summary>
 104    /// Adds SemanticKernel plugins discovered from all registered assemblies in the service provider.
 105    /// </summary>
 106    /// <param name="syringe">The Semantic Kernel syringe to configure.</param>
 107    /// <param name="includeInstancePlugins">Whether to include instance-based plugins.</param>
 108    /// <param name="includeStaticPlugins">Whether to include static class plugins.</param>
 109    /// <returns>The configured syringe.</returns>
 110    [RequiresUnreferencedCode("Assembly scanning uses reflection to discover types with [KernelFunction] methods.")]
 111    [RequiresDynamicCode("Assembly scanning uses reflection APIs that may require dynamic code generation.")]
 112    public static SemanticKernelSyringe AddSemanticKernelPluginsFromAssemblies(
 113        this SemanticKernelSyringe syringe,
 114        bool includeInstancePlugins = true,
 115        bool includeStaticPlugins = true)
 116    {
 9117        ArgumentNullException.ThrowIfNull(syringe);
 118
 9119        var assemblies = syringe.ServiceProvider.GetRequiredService<IReadOnlyList<Assembly>>();
 9120        return syringe.AddSemanticKernelPluginsFromAssemblies(
 9121            assemblies,
 9122            includeInstancePlugins: includeInstancePlugins,
 9123            includeStaticPlugins: includeStaticPlugins);
 124    }
 125
 126    /// <summary>
 127    /// Adds SemanticKernel plugins discovered from a specific list of assemblies.
 128    /// </summary>
 129    /// <param name="syringe">The Semantic Kernel syringe to configure.</param>
 130    /// <param name="asssemblies">The assemblies to scan for plugin types.</param>
 131    /// <param name="includeInstancePlugins">Whether to include instance-based plugins.</param>
 132    /// <param name="includeStaticPlugins">Whether to include static class plugins.</param>
 133    /// <returns>The configured syringe.</returns>
 134    [RequiresUnreferencedCode("Assembly scanning uses reflection to discover types with [KernelFunction] methods.")]
 135    [RequiresDynamicCode("Assembly scanning uses reflection APIs that may require dynamic code generation.")]
 136    public static SemanticKernelSyringe AddSemanticKernelPluginsFromAssemblies(
 137        this SemanticKernelSyringe syringe,
 138        IReadOnlyList<Assembly> asssemblies,
 139        bool includeInstancePlugins = true,
 140        bool includeStaticPlugins = true)
 141    {
 9142        ArgumentNullException.ThrowIfNull(syringe);
 9143        ArgumentNullException.ThrowIfNull(asssemblies);
 144
 9145        var scanner = new AssemblySemanticKernelPluginScanner(asssemblies);
 9146        return syringe.AddSemanticKernelPlugins(
 9147            scanner,
 9148            includeInstancePlugins: includeInstancePlugins,
 9149            includeStaticPlugins: includeStaticPlugins);
 150    }
 151
 152    /// <summary>
 153    /// Adds SemanticKernel plugins discovered by scanning all services registered in the service provider.
 154    /// </summary>
 155    /// <param name="syringe">The Semantic Kernel syringe to configure.</param>
 156    /// <returns>The configured syringe.</returns>
 157    [RequiresUnreferencedCode("Service provider scanning uses reflection to discover types with [KernelFunction] methods
 158    [RequiresDynamicCode("Service provider scanning uses reflection APIs that may require dynamic code generation.")]
 159    public static SemanticKernelSyringe AddSemanticKernelPluginsFromProvider(
 160        this SemanticKernelSyringe syringe)
 161    {
 4162        ArgumentNullException.ThrowIfNull(syringe);
 163
 4164        var scanner = new ServiceProviderSemanticKernelPluginScanner(syringe.ServiceProvider);
 4165        return syringe.AddSemanticKernelPlugins(scanner);
 166    }
 167
 168    /// <summary>
 169    /// Adds SemanticKernel plugins by scanning with a custom <see cref="ISemanticKernelPluginScanner"/>.
 170    /// </summary>
 171    /// <param name="syringe">The Semantic Kernel syringe to configure.</param>
 172    /// <param name="scanner">The scanner that produces plugin types.</param>
 173    /// <param name="includeInstancePlugins">Whether to include instance-based plugins.</param>
 174    /// <param name="includeStaticPlugins">Whether to include static class plugins.</param>
 175    /// <returns>The configured syringe.</returns>
 176    [RequiresUnreferencedCode("Plugin scanning uses reflection to discover types with [KernelFunction] methods.")]
 177    [RequiresDynamicCode("Plugin scanning uses reflection APIs that may require dynamic code generation.")]
 178    public static SemanticKernelSyringe AddSemanticKernelPlugins(
 179        this SemanticKernelSyringe syringe,
 180        ISemanticKernelPluginScanner scanner,
 181        bool includeInstancePlugins = true,
 182        bool includeStaticPlugins = true)
 183    {
 13184        ArgumentNullException.ThrowIfNull(syringe);
 13185        ArgumentNullException.ThrowIfNull(scanner);
 186
 13187        var pluginTypes = scanner.ScanForPluginTypes();
 13188        return syringe.AddSemanticKernelPlugins(
 13189            pluginTypes,
 13190            includeInstancePlugins: includeInstancePlugins,
 13191            includeStaticPlugins: includeStaticPlugins);
 192    }
 193
 194    /// <summary>
 195    /// Adds SemanticKernel plugins from an explicit list of plugin types.
 196    /// </summary>
 197    /// <param name="syringe">The Semantic Kernel syringe to configure.</param>
 198    /// <param name="pluginTypes">The plugin types to register.</param>
 199    /// <param name="includeInstancePlugins">Whether to include instance-based plugins.</param>
 200    /// <param name="includeStaticPlugins">Whether to include static class plugins.</param>
 201    /// <returns>The configured syringe.</returns>
 202    [RequiresUnreferencedCode("Plugin type inspection uses reflection to check for [KernelFunction] methods.")]
 203    [RequiresDynamicCode("Plugin type inspection uses reflection APIs that may require dynamic code generation.")]
 204    public static SemanticKernelSyringe AddSemanticKernelPlugins(
 205        this SemanticKernelSyringe syringe,
 206        IReadOnlyList<Type> pluginTypes,
 207        bool includeInstancePlugins = true,
 208        bool includeStaticPlugins = true)
 209    {
 26210        ArgumentNullException.ThrowIfNull(syringe);
 26211        ArgumentNullException.ThrowIfNull(pluginTypes);
 212
 26213        List<Type> typesToAdd = [];
 214
 392215        foreach (var pluginType in pluginTypes)
 216        {
 170217            if (pluginType.IsStatic())
 218            {
 27219                if (!includeStaticPlugins)
 220                {
 221                    continue;
 222                }
 223
 24224                if (!pluginType
 24225                    .GetMethods(BindingFlags.Public | BindingFlags.Static)
 48226                    .Any(m => m.IsDefined(typeof(KernelFunctionAttribute), inherit: true)))
 227                {
 228                    continue;
 229                }
 230
 24231                typesToAdd.Add(pluginType);
 24232                continue;
 233            }
 234
 143235            if (!includeInstancePlugins)
 236            {
 237                continue;
 238            }
 239
 123240            typesToAdd.Add(pluginType);
 241        }
 242
 26243        return syringe with
 26244        {
 26245            PluginTypes = (syringe.PluginTypes ?? []).Concat(typesToAdd).Distinct().ToList()
 26246        };
 247    }
 248}