< 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    {
 13816        var sb = new StringBuilder();
 17
 13818        sb.AppendLine("// <auto-generated/>");
 13819        sb.AppendLine("// Needlr AgentFramework Functions");
 13820        sb.AppendLine("#nullable enable");
 13821        sb.AppendLine();
 13822        sb.AppendLine("using System;");
 13823        sb.AppendLine("using System.Collections.Generic;");
 13824        sb.AppendLine();
 13825        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 13826        sb.AppendLine();
 13827        sb.AppendLine("/// <summary>");
 13828        sb.AppendLine("/// Generated registry for Microsoft Agent Framework function types discovered at compile time.")
 13829        sb.AppendLine("/// Pass <see cref=\"AllFunctionTypes\"/> to");
 13830        sb.AppendLine("/// <c>AgentFrameworkSyringeExtensions.AddAgentFunctionsFromGenerated</c>.");
 13831        sb.AppendLine("/// </summary>");
 13832        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 13833        sb.AppendLine("public static class AgentFrameworkFunctionRegistry");
 13834        sb.AppendLine("{");
 13835        sb.AppendLine("    /// <summary>");
 13836        sb.AppendLine("    /// All types containing methods decorated with [AgentFunction], discovered at compile time."
 13837        sb.AppendLine("    /// </summary>");
 13838        sb.AppendLine("    public static IReadOnlyList<Type> AllFunctionTypes { get; } = new Type[]");
 13839        sb.AppendLine("    {");
 40
 39041        foreach (var type in types)
 42        {
 5743            sb.AppendLine($"        typeof({type.TypeName}),");
 44        }
 45
 13846        sb.AppendLine("    };");
 13847        sb.AppendLine();
 13848        sb.AppendLine("    /// <summary>");
 13849        sb.AppendLine("    /// Gets the number of function types discovered at compile time.");
 13850        sb.AppendLine("    /// </summary>");
 13851        sb.AppendLine($"    public static int Count => {types.Count};");
 13852        sb.AppendLine("}");
 53
 13854        return sb.ToString();
 55    }
 56
 57    public static string GenerateGroupRegistrySource(
 58        Dictionary<string, List<string>> groupedByName,
 59        string safeAssemblyName)
 60    {
 13861        var sb = new StringBuilder();
 62
 13863        sb.AppendLine("// <auto-generated/>");
 13864        sb.AppendLine("// Needlr AgentFramework Function Groups");
 13865        sb.AppendLine("#nullable enable");
 13866        sb.AppendLine();
 13867        sb.AppendLine("using System;");
 13868        sb.AppendLine("using System.Collections.Generic;");
 13869        sb.AppendLine();
 13870        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 13871        sb.AppendLine();
 13872        sb.AppendLine("/// <summary>");
 13873        sb.AppendLine("/// Generated registry for Microsoft Agent Framework function groups discovered at compile time."
 13874        sb.AppendLine("/// Pass <see cref=\"AllGroups\"/> to");
 13875        sb.AppendLine("/// <c>AgentFrameworkSyringeExtensions.AddAgentFunctionGroupsFromGenerated</c>.");
 13876        sb.AppendLine("/// </summary>");
 13877        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 13878        sb.AppendLine("public static class AgentFrameworkFunctionGroupRegistry");
 13879        sb.AppendLine("{");
 13880        sb.AppendLine("    /// <summary>");
 13881        sb.AppendLine("    /// All function groups, mapping group name to the types in that group, discovered at compile
 13882        sb.AppendLine("    /// </summary>");
 13883        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllGroups { get; } =");
 13884        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 13885        sb.AppendLine("        {");
 86
 42687        foreach (var kvp in groupedByName.OrderBy(k => k.Key))
 88        {
 5089            var escapedName = kvp.Key.Replace("\"", "\\\"");
 5090            var typeNames = kvp.Value;
 5091            sb.AppendLine($"            [\"{escapedName}\"] = new Type[]");
 5092            sb.AppendLine("            {");
 20293            foreach (var typeName in typeNames)
 94            {
 5195                sb.AppendLine($"                typeof({typeName}),");
 96            }
 5097            sb.AppendLine("            },");
 98        }
 99
 138100        sb.AppendLine("        };");
 138101        sb.AppendLine("}");
 102
 138103        return sb.ToString();
 104    }
 105
 106    public static string GenerateAgentRegistrySource(
 107        List<NeedlrAiAgentTypeInfo> types,
 108        string safeAssemblyName)
 109    {
 138110        var sb = new StringBuilder();
 111
 138112        sb.AppendLine("// <auto-generated/>");
 138113        sb.AppendLine("// Needlr AgentFramework Agent Registry");
 138114        sb.AppendLine("#nullable enable");
 138115        sb.AppendLine();
 138116        sb.AppendLine("using System;");
 138117        sb.AppendLine("using System.Collections.Generic;");
 138118        sb.AppendLine();
 138119        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 138120        sb.AppendLine();
 138121        sb.AppendLine("/// <summary>");
 138122        sb.AppendLine("/// Generated registry for agent types declared with [NeedlrAiAgent], discovered at compile time.
 138123        sb.AppendLine("/// </summary>");
 138124        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 138125        sb.AppendLine("public static class AgentRegistry");
 138126        sb.AppendLine("{");
 138127        sb.AppendLine("    /// <summary>");
 138128        sb.AppendLine("    /// All types decorated with [NeedlrAiAgent], discovered at compile time.");
 138129        sb.AppendLine("    /// </summary>");
 138130        sb.AppendLine("    public static IReadOnlyList<Type> AllAgentTypes { get; } = new Type[]");
 138131        sb.AppendLine("    {");
 132
 490133        foreach (var type in types)
 134        {
 107135            sb.AppendLine($"        typeof({type.TypeName}),");
 136        }
 137
 138138        sb.AppendLine("    };");
 138139        sb.AppendLine();
 138140        sb.AppendLine("    /// <summary>");
 138141        sb.AppendLine("    /// Gets the number of agent types discovered at compile time.");
 138142        sb.AppendLine("    /// </summary>");
 138143        sb.AppendLine($"    public static int Count => {types.Count};");
 138144        sb.AppendLine("}");
 145
 138146        return sb.ToString();
 147    }
 148
 149    public static string GenerateHandoffTopologyRegistrySource(
 150        Dictionary<(string InitialAgentTypeName, string InitialAgentClassName), List<(string TargetAgentTypeName, string
 151        string safeAssemblyName)
 152    {
 138153        var sb = new StringBuilder();
 138154        sb.AppendLine("// <auto-generated/>");
 138155        sb.AppendLine("// Needlr AgentFramework Handoff Topology Registry");
 138156        sb.AppendLine("#nullable enable");
 138157        sb.AppendLine();
 138158        sb.AppendLine("using System;");
 138159        sb.AppendLine("using System.Collections.Generic;");
 138160        sb.AppendLine();
 138161        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 138162        sb.AppendLine();
 138163        sb.AppendLine("/// <summary>");
 138164        sb.AppendLine("/// Generated registry of agent handoff topology declared via [AgentHandoffsTo] attributes.");
 138165        sb.AppendLine("/// </summary>");
 138166        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 138167        sb.AppendLine("public static class AgentHandoffTopologyRegistry");
 138168        sb.AppendLine("{");
 138169        sb.AppendLine("    /// <summary>");
 138170        sb.AppendLine("    /// All handoff relationships, mapping initial agent type to its declared handoff targets.");
 138171        sb.AppendLine("    /// </summary>");
 138172        sb.AppendLine("    public static IReadOnlyDictionary<Type, IReadOnlyList<(Type TargetType, string? HandoffReason
 138173        sb.AppendLine("        new Dictionary<Type, IReadOnlyList<(Type, string?)>>()");
 138174        sb.AppendLine("        {");
 175
 291176        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
 138188        sb.AppendLine("        };");
 138189        sb.AppendLine("}");
 138190        return sb.ToString();
 191    }
 192
 193    public static string GenerateGroupChatRegistrySource(
 194        Dictionary<string, List<string>> groupChatByGroupName,
 195        string safeAssemblyName)
 196    {
 138197        var sb = new StringBuilder();
 138198        sb.AppendLine("// <auto-generated/>");
 138199        sb.AppendLine("// Needlr AgentFramework Group Chat Registry");
 138200        sb.AppendLine("#nullable enable");
 138201        sb.AppendLine();
 138202        sb.AppendLine("using System;");
 138203        sb.AppendLine("using System.Collections.Generic;");
 138204        sb.AppendLine();
 138205        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 138206        sb.AppendLine();
 138207        sb.AppendLine("/// <summary>");
 138208        sb.AppendLine("/// Generated registry of agent group chat memberships declared via [AgentGroupChatMember] attrib
 138209        sb.AppendLine("/// </summary>");
 138210        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 138211        sb.AppendLine("public static class AgentGroupChatRegistry");
 138212        sb.AppendLine("{");
 138213        sb.AppendLine("    /// <summary>");
 138214        sb.AppendLine("    /// All group chat groups, mapping group name to the agent types in that group.");
 138215        sb.AppendLine("    /// </summary>");
 138216        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllGroups { get; } =");
 138217        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 138218        sb.AppendLine("        {");
 219
 288220        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
 138230        sb.AppendLine("        };");
 138231        sb.AppendLine("}");
 138232        return sb.ToString();
 233    }
 234
 235    public static string GenerateSequentialTopologyRegistrySource(
 236        Dictionary<string, List<string>> sequenceByPipelineName,
 237        string safeAssemblyName)
 238    {
 138239        var sb = new StringBuilder();
 138240        sb.AppendLine("// <auto-generated/>");
 138241        sb.AppendLine("// Needlr AgentFramework Sequential Pipeline Registry");
 138242        sb.AppendLine("#nullable enable");
 138243        sb.AppendLine();
 138244        sb.AppendLine("using System;");
 138245        sb.AppendLine("using System.Collections.Generic;");
 138246        sb.AppendLine();
 138247        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 138248        sb.AppendLine();
 138249        sb.AppendLine("/// <summary>");
 138250        sb.AppendLine("/// Generated registry of sequential pipeline memberships declared via [AgentSequenceMember] attr
 138251        sb.AppendLine("/// Agents within each pipeline are stored in ascending order value order.");
 138252        sb.AppendLine("/// </summary>");
 138253        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 138254        sb.AppendLine("public static class AgentSequentialTopologyRegistry");
 138255        sb.AppendLine("{");
 138256        sb.AppendLine("    /// <summary>");
 138257        sb.AppendLine("    /// All sequential pipelines, mapping pipeline name to the ordered agent types.");
 138258        sb.AppendLine("    /// </summary>");
 138259        sb.AppendLine("    public static IReadOnlyDictionary<string, IReadOnlyList<Type>> AllPipelines { get; } =");
 138260        sb.AppendLine("        new Dictionary<string, IReadOnlyList<Type>>()");
 138261        sb.AppendLine("        {");
 262
 315263        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
 138273        sb.AppendLine("        };");
 138274        sb.AppendLine("}");
 138275        return sb.ToString();
 276    }
 277
 278    public static string GenerateGraphTopologyRegistrySource(
 279        Dictionary<string, GraphData> graphDataByName,
 280        string safeAssemblyName)
 281    {
 138282        var sb = new StringBuilder();
 138283        sb.AppendLine("// <auto-generated/>");
 138284        sb.AppendLine("// Needlr AgentFramework Graph Topology Registry");
 138285        sb.AppendLine("#nullable enable");
 138286        sb.AppendLine();
 138287        sb.AppendLine("using System;");
 138288        sb.AppendLine("using System.Collections.Generic;");
 138289        sb.AppendLine();
 138290        sb.AppendLine($"namespace {safeAssemblyName}.Generated;");
 138291        sb.AppendLine();
 138292        sb.AppendLine("/// <summary>");
 138293        sb.AppendLine("/// Generated registry of graph topologies declared via [AgentGraphEntry], [AgentGraphEdge],");
 138294        sb.AppendLine("/// [AgentGraphNode], and [AgentGraphReducer] attributes.");
 138295        sb.AppendLine("/// </summary>");
 138296        sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Generat
 138297        sb.AppendLine("public static class AgentGraphTopologyRegistry");
 138298        sb.AppendLine("{");
 138299        sb.AppendLine("    /// <summary>");
 138300        sb.AppendLine("    /// All graph topologies, mapping graph name to its registration data.");
 138301        sb.AppendLine("    /// </summary>");
 138302        sb.AppendLine("    public static IReadOnlyDictionary<string, global::NexusLabs.Needlr.AgentFramework.GraphTopolo
 138303        sb.AppendLine("        new Dictionary<string, global::NexusLabs.Needlr.AgentFramework.GraphTopologyRegistration>
 138304        sb.AppendLine("        {");
 305
 339306        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
 138361        sb.AppendLine("        };");
 138362        sb.AppendLine("}");
 138363        return sb.ToString();
 364    }
 365}