< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Generators.RegistryCodeGenerator
Assembly: NexusLabs.Needlr.AgentFramework.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Generators/CodeGen/RegistryCodeGenerator.cs
Line coverage
100%
Covered lines: 252
Uncovered lines: 0
Coverable lines: 252
Total lines: 365
Line coverage: 100%
Branch coverage
95%
Covered branches: 38
Total branches: 40
Branch coverage: 95%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Generators/CodeGen/RegistryCodeGenerator.cs

#LineLine coverage
 1// Copyright (c) NexusLabs. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System.Collections.Generic;
 5using System.Linq;
 6using System.Text;
 7
 8namespace NexusLabs.Needlr.AgentFramework.Generators;
 9
 10internal static class RegistryCodeGenerator
 11{
 12    public static string GenerateRegistrySource(
 13        List<AgentFunctionTypeInfo> types,
 14        string safeAssemblyName)
 15    {
 10116        var sb = new StringBuilder();
 17
 10118        sb.AppendLine("// <auto-generated/>");
 10119        sb.AppendLine("// Needlr AgentFramework Functions");
 10120        sb.AppendLine("#nullable enable");
 10121        sb.AppendLine();
 10122        sb.AppendLine("using System;");
 10123        sb.AppendLine("using System.Collections.Generic;");
 10124        sb.AppendLine();
 10125        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 10126        sb.AppendLine();
 10127        sb.AppendLine("/// <summary>");
 10128        sb.AppendLine("/// Generated registry for Microsoft Agent Framework function types discovered at compile time.")
 10129        sb.AppendLine("/// Pass <see cref=\"AllFunctionTypes\"/> to");
 10130        sb.AppendLine("/// <c>AgentFrameworkSyringeExtensions.AddAgentFunctionsFromGenerated</c>.");
 10131        sb.AppendLine("/// </summary>");
 10132        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 10133        sb.AppendLine("public static class AgentFrameworkFunctionRegistry");
 10134        sb.AppendLine("{");
 10135        sb.AppendLine("    /// <summary>");
 10136        sb.AppendLine("    /// All types containing methods decorated with [AgentFunction], discovered at compile time."
 10137        sb.AppendLine("    /// </summary>");
 10138        sb.AppendLine("    public static IReadOnlyList<Type> AllFunctionTypes { get; } = new Type[]");
 10139        sb.AppendLine("    {");
 40
 24241        foreach (var type in types)
 42        {
 2043            sb.AppendLine($"        typeof({type.TypeName}),");
 44        }
 45
 10146        sb.AppendLine("    };");
 10147        sb.AppendLine();
 10148        sb.AppendLine("    /// <summary>");
 10149        sb.AppendLine("    /// Gets the number of function types discovered at compile time.");
 10150        sb.AppendLine("    /// </summary>");
 10151        sb.AppendLine($"    public static int Count => {types.Count};");
 10152        sb.AppendLine("}");
 53
 10154        return sb.ToString();
 55    }
 56
 57    public static string GenerateGroupRegistrySource(
 58        Dictionary<string, List<string>> groupedByName,
 59        string safeAssemblyName)
 60    {
 10161        var sb = new StringBuilder();
 62
 10163        sb.AppendLine("// <auto-generated/>");
 10164        sb.AppendLine("// Needlr AgentFramework Function Groups");
 10165        sb.AppendLine("#nullable enable");
 10166        sb.AppendLine();
 10167        sb.AppendLine("using System;");
 10168        sb.AppendLine("using System.Collections.Generic;");
 10169        sb.AppendLine();
 10170        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 10171        sb.AppendLine();
 10172        sb.AppendLine("/// <summary>");
 10173        sb.AppendLine("/// Generated registry for Microsoft Agent Framework function groups discovered at compile time."
 10174        sb.AppendLine("/// Pass <see cref=\"AllGroups\"/> to");
 10175        sb.AppendLine("/// <c>AgentFrameworkSyringeExtensions.AddAgentFunctionGroupsFromGenerated</c>.");
 10176        sb.AppendLine("/// </summary>");
 10177        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 10178        sb.AppendLine("public static class AgentFrameworkFunctionGroupRegistry");
 10179        sb.AppendLine("{");
 10180        sb.AppendLine("    /// <summary>");
 10181        sb.AppendLine("    /// All function groups, mapping group name to the types in that group, discovered at compile
 10182        sb.AppendLine("    /// </summary>");
 10183        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllGroups { get; } =");
 10184        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 10185        sb.AppendLine("        {");
 86
 24187        foreach (var kvp in groupedByName.OrderBy(k => k.Key))
 88        {
 1389            var escapedName = kvp.Key.Replace("\"", "\\\"");
 1390            var typeNames = kvp.Value;
 1391            sb.AppendLine($"            [\"{escapedName}\"] = new Type[]");
 1392            sb.AppendLine("            {");
 5493            foreach (var typeName in typeNames)
 94            {
 1495                sb.AppendLine($"                typeof({typeName}),");
 96            }
 1397            sb.AppendLine("            },");
 98        }
 99
 101100        sb.AppendLine("        };");
 101101        sb.AppendLine("}");
 102
 101103        return sb.ToString();
 104    }
 105
 106    public static string GenerateAgentRegistrySource(
 107        List<NeedlrAiAgentTypeInfo> types,
 108        string safeAssemblyName)
 109    {
 101110        var sb = new StringBuilder();
 111
 101112        sb.AppendLine("// <auto-generated/>");
 101113        sb.AppendLine("// Needlr AgentFramework Agent Registry");
 101114        sb.AppendLine("#nullable enable");
 101115        sb.AppendLine();
 101116        sb.AppendLine("using System;");
 101117        sb.AppendLine("using System.Collections.Generic;");
 101118        sb.AppendLine();
 101119        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 101120        sb.AppendLine();
 101121        sb.AppendLine("/// <summary>");
 101122        sb.AppendLine("/// Generated registry for agent types declared with [NeedlrAiAgent], discovered at compile time.
 101123        sb.AppendLine("/// </summary>");
 101124        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 101125        sb.AppendLine("public static class AgentRegistry");
 101126        sb.AppendLine("{");
 101127        sb.AppendLine("    /// <summary>");
 101128        sb.AppendLine("    /// All types decorated with [NeedlrAiAgent], discovered at compile time.");
 101129        sb.AppendLine("    /// </summary>");
 101130        sb.AppendLine("    public static IReadOnlyList<Type> AllAgentTypes { get; } = new Type[]");
 101131        sb.AppendLine("    {");
 132
 416133        foreach (var type in types)
 134        {
 107135            sb.AppendLine($"        typeof({type.TypeName}),");
 136        }
 137
 101138        sb.AppendLine("    };");
 101139        sb.AppendLine();
 101140        sb.AppendLine("    /// <summary>");
 101141        sb.AppendLine("    /// Gets the number of agent types discovered at compile time.");
 101142        sb.AppendLine("    /// </summary>");
 101143        sb.AppendLine($"    public static int Count => {types.Count};");
 101144        sb.AppendLine("}");
 145
 101146        return sb.ToString();
 147    }
 148
 149    public static string GenerateHandoffTopologyRegistrySource(
 150        Dictionary<(string InitialAgentTypeName, string InitialAgentClassName), List<(string TargetAgentTypeName, string
 151        string safeAssemblyName)
 152    {
 101153        var sb = new StringBuilder();
 101154        sb.AppendLine("// <auto-generated/>");
 101155        sb.AppendLine("// Needlr AgentFramework Handoff Topology Registry");
 101156        sb.AppendLine("#nullable enable");
 101157        sb.AppendLine();
 101158        sb.AppendLine("using System;");
 101159        sb.AppendLine("using System.Collections.Generic;");
 101160        sb.AppendLine();
 101161        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 101162        sb.AppendLine();
 101163        sb.AppendLine("/// <summary>");
 101164        sb.AppendLine("/// Generated registry of agent handoff topology declared via [AgentHandoffsTo] attributes.");
 101165        sb.AppendLine("/// </summary>");
 101166        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 101167        sb.AppendLine("public static class AgentHandoffTopologyRegistry");
 101168        sb.AppendLine("{");
 101169        sb.AppendLine("    /// <summary>");
 101170        sb.AppendLine("    /// All handoff relationships, mapping initial agent type to its declared handoff targets.");
 101171        sb.AppendLine("    /// </summary>");
 101172        sb.AppendLine("    public static IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason
 101173        sb.AppendLine("        new Dictionary<Type, IReadOnlyList<(Type, string?)>>()");
 101174        sb.AppendLine("        {");
 175
 217176        foreach (var kvp in handoffByInitialAgent.OrderBy(k => k.Key.InitialAgentClassName))
 177        {
 5178            sb.AppendLine($"            [typeof({kvp.Key.InitialAgentTypeName})] = new (Type, string?)[]");
 5179            sb.AppendLine("            {");
 20180            foreach (var (targetType, reason) in kvp.Value)
 181            {
 5182                var reasonLiteral = reason is null ? "null" : $"\"{reason.Replace("\"", "\\\"")}\"";
 5183                sb.AppendLine($"                (typeof({targetType}), {reasonLiteral}),");
 184            }
 5185            sb.AppendLine("            },");
 186        }
 187
 101188        sb.AppendLine("        };");
 101189        sb.AppendLine("}");
 101190        return sb.ToString();
 191    }
 192
 193    public static string GenerateGroupChatRegistrySource(
 194        Dictionary<string, List<string>> groupChatByGroupName,
 195        string safeAssemblyName)
 196    {
 101197        var sb = new StringBuilder();
 101198        sb.AppendLine("// <auto-generated/>");
 101199        sb.AppendLine("// Needlr AgentFramework Group Chat Registry");
 101200        sb.AppendLine("#nullable enable");
 101201        sb.AppendLine();
 101202        sb.AppendLine("using System;");
 101203        sb.AppendLine("using System.Collections.Generic;");
 101204        sb.AppendLine();
 101205        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 101206        sb.AppendLine();
 101207        sb.AppendLine("/// <summary>");
 101208        sb.AppendLine("/// Generated registry of agent group chat memberships declared via [AgentGroupChatMember] attrib
 101209        sb.AppendLine("/// </summary>");
 101210        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 101211        sb.AppendLine("public static class AgentGroupChatRegistry");
 101212        sb.AppendLine("{");
 101213        sb.AppendLine("    /// <summary>");
 101214        sb.AppendLine("    /// All group chat groups, mapping group name to the agent types in that group.");
 101215        sb.AppendLine("    /// </summary>");
 101216        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllGroups { get; } =");
 101217        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 101218        sb.AppendLine("        {");
 219
 214220        foreach (var kvp in groupChatByGroupName.OrderBy(k => k.Key))
 221        {
 4222            var escapedName = kvp.Key.Replace("\"", "\\\"");
 4223            sb.AppendLine($"            [\"{escapedName}\"] = new Type[]");
 4224            sb.AppendLine("            {");
 24225            foreach (var typeName in kvp.Value)
 8226                sb.AppendLine($"                typeof({typeName}),");
 4227            sb.AppendLine("            },");
 228        }
 229
 101230        sb.AppendLine("        };");
 101231        sb.AppendLine("}");
 101232        return sb.ToString();
 233    }
 234
 235    public static string GenerateSequentialTopologyRegistrySource(
 236        Dictionary<string, List<string>> sequenceByPipelineName,
 237        string safeAssemblyName)
 238    {
 101239        var sb = new StringBuilder();
 101240        sb.AppendLine("// <auto-generated/>");
 101241        sb.AppendLine("// Needlr AgentFramework Sequential Pipeline Registry");
 101242        sb.AppendLine("#nullable enable");
 101243        sb.AppendLine();
 101244        sb.AppendLine("using System;");
 101245        sb.AppendLine("using System.Collections.Generic;");
 101246        sb.AppendLine();
 101247        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 101248        sb.AppendLine();
 101249        sb.AppendLine("/// <summary>");
 101250        sb.AppendLine("/// Generated registry of sequential pipeline memberships declared via [AgentSequenceMember] attr
 101251        sb.AppendLine("/// Agents within each pipeline are stored in ascending order value order.");
 101252        sb.AppendLine("/// </summary>");
 101253        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 101254        sb.AppendLine("public static class AgentSequentialTopologyRegistry");
 101255        sb.AppendLine("{");
 101256        sb.AppendLine("    /// <summary>");
 101257        sb.AppendLine("    /// All sequential pipelines, mapping pipeline name to the ordered agent types.");
 101258        sb.AppendLine("    /// </summary>");
 101259        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllPipelines { get; } =");
 101260        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 101261        sb.AppendLine("        {");
 262
 241263        foreach (var kvp in sequenceByPipelineName.OrderBy(k => k.Key))
 264        {
 13265            var escapedName = kvp.Key.Replace("\"", "\\\"");
 13266            sb.AppendLine($"            [\"{escapedName}\"] = new Type[]");
 13267            sb.AppendLine("            {");
 74268            foreach (var typeName in kvp.Value)
 24269                sb.AppendLine($"                typeof({typeName}),");
 13270            sb.AppendLine("            },");
 271        }
 272
 101273        sb.AppendLine("        };");
 101274        sb.AppendLine("}");
 101275        return sb.ToString();
 276    }
 277
 278    public static string GenerateGraphTopologyRegistrySource(
 279        Dictionary<string, GraphData> graphDataByName,
 280        string safeAssemblyName)
 281    {
 101282        var sb = new StringBuilder();
 101283        sb.AppendLine("// <auto-generated/>");
 101284        sb.AppendLine("// Needlr AgentFramework Graph Topology Registry");
 101285        sb.AppendLine("#nullable enable");
 101286        sb.AppendLine();
 101287        sb.AppendLine("using System;");
 101288        sb.AppendLine("using System.Collections.Generic;");
 101289        sb.AppendLine();
 101290        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 101291        sb.AppendLine();
 101292        sb.AppendLine("/// <summary>");
 101293        sb.AppendLine("/// Generated registry of graph topologies declared via [AgentGraphEntry], [AgentGraphEdge],");
 101294        sb.AppendLine("/// [AgentGraphNode], and [AgentGraphReducer] attributes.");
 101295        sb.AppendLine("/// </summary>");
 101296        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 101297        sb.AppendLine("public static class AgentGraphTopologyRegistry");
 101298        sb.AppendLine("{");
 101299        sb.AppendLine("    /// <summary>");
 101300        sb.AppendLine("    /// All graph topologies, mapping graph name to its registration data.");
 101301        sb.AppendLine("    /// </summary>");
 101302        sb.AppendLine("    public static IReadOnlyDictionary<string, global::NexusLabs.Needlr.AgentFramework.GraphTopolo
 101303        sb.AppendLine("        new Dictionary<string, global::NexusLabs.Needlr.AgentFramework.GraphTopologyRegistration>
 101304        sb.AppendLine("        {");
 305
 265306        foreach (var kvp in graphDataByName.OrderBy(k => k.Key))
 307        {
 21308            var escapedName = kvp.Key.Replace("\"", "\\\"");
 21309            var graphData = kvp.Value;
 310
 311            // The AgentGraphEntryPointAnalyzer (NDLRMAF017/018) enforces exactly one
 312            // entry point per graph. We take FirstOrDefault here because the analyzer
 313            // guarantees the constraint; the generator doesn't need to re-validate.
 21314            var entryPoint = graphData.EntryPoints.FirstOrDefault();
 21315            var entryTypeExpr = entryPoint.AgentTypeName is not null
 21316                ? $"typeof({entryPoint.AgentTypeName})"
 21317                : "null";
 21318            var routingMode = entryPoint.AgentTypeName is not null
 21319                ? entryPoint.RoutingMode
 21320                : 0;
 321
 21322            sb.AppendLine($"            [\"{escapedName}\"] = new global::NexusLabs.Needlr.AgentFramework.GraphTopologyR
 21323            sb.AppendLine($"                entryType: {entryTypeExpr},");
 21324            sb.AppendLine($"                routingMode: {routingMode},");
 325
 326            // Edges
 21327            sb.AppendLine("                edges: new (Type, Type, string?, bool, int?)[]");
 21328            sb.AppendLine("                {");
 88329            foreach (var edge in graphData.Edges)
 330            {
 23331                var conditionLiteral = edge.Condition is null ? "null" : $"\"{edge.Condition.Replace("\"", "\\\"")}\"";
 23332                var nodeRoutingLiteral = edge.NodeRoutingMode is null ? "(int?)null" : $"(int?){edge.NodeRoutingMode.Val
 23333                sb.AppendLine($"                    (typeof({edge.SourceAgentTypeName}), typeof({edge.TargetAgentTypeNam
 334            }
 21335            sb.AppendLine("                },");
 336
 337            // Nodes
 21338            sb.AppendLine("                nodes: new (Type, int)[]");
 21339            sb.AppendLine("                {");
 46340            foreach (var node in graphData.Nodes)
 341            {
 2342                sb.AppendLine($"                    (typeof({node.AgentTypeName}), {node.JoinMode}),");
 343            }
 21344            sb.AppendLine("                },");
 345
 346            // The AgentGraphEntryPointAnalyzer (NDLRMAF017/018) enforces at most one
 347            // reducer per graph. We take FirstOrDefault here because the analyzer
 348            // guarantees the constraint; the generator doesn't need to re-validate.
 21349            var reducer = graphData.Reducers.FirstOrDefault();
 21350            if (reducer.AgentTypeName is not null)
 351            {
 3352                var methodLiteral = $"\"{reducer.ReducerMethod.Replace("\"", "\\\"")}\"";
 3353                sb.AppendLine($"                reducer: (typeof({reducer.AgentTypeName}), {methodLiteral})),");
 354            }
 355            else
 356            {
 18357                sb.AppendLine("                reducer: null),");
 358            }
 359        }
 360
 101361        sb.AppendLine("        };");
 101362        sb.AppendLine("}");
 101363        return sb.ToString();
 364    }
 365}