| | | 1 | | // Copyright (c) NexusLabs. All rights reserved. |
| | | 2 | | // Licensed under the MIT License. |
| | | 3 | | |
| | | 4 | | using System.Collections.Generic; |
| | | 5 | | using System.Linq; |
| | | 6 | | using System.Text; |
| | | 7 | | |
| | | 8 | | namespace NexusLabs.Needlr.AgentFramework.Generators; |
| | | 9 | | |
| | | 10 | | internal static class BootstrapCodeGenerator |
| | | 11 | | { |
| | | 12 | | public static string GenerateBootstrapSource(string safeAssemblyName) |
| | | 13 | | { |
| | 101 | 14 | | return $@"// <auto-generated/> |
| | 101 | 15 | | // Needlr AgentFramework Module Initializer |
| | 101 | 16 | | #nullable enable |
| | 101 | 17 | | |
| | 101 | 18 | | using System.Runtime.CompilerServices; |
| | 101 | 19 | | |
| | 101 | 20 | | namespace {safeAssemblyName}.Generated; |
| | 101 | 21 | | |
| | 101 | 22 | | /// <summary> |
| | 101 | 23 | | /// Auto-registers source-generated Agent Framework types with the Needlr bootstrap. |
| | 101 | 24 | | /// Fires automatically when the assembly loads — no explicit <c>Add*FromGenerated()</c> calls needed. |
| | 101 | 25 | | /// </summary> |
| | 101 | 26 | | [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""NexusLabs.Needlr.AgentFramework.Generators"", ""1.0.0"")] |
| | 101 | 27 | | internal static class NeedlrAgentFrameworkModuleInitializer |
| | 101 | 28 | | {{ |
| | 101 | 29 | | [ModuleInitializer] |
| | 101 | 30 | | internal static void Initialize() |
| | 101 | 31 | | {{ |
| | 101 | 32 | | global::NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap.Register( |
| | 101 | 33 | | () => AgentFrameworkFunctionRegistry.AllFunctionTypes, |
| | 101 | 34 | | () => AgentFrameworkFunctionGroupRegistry.AllGroups, |
| | 101 | 35 | | () => AgentRegistry.AllAgentTypes, |
| | 101 | 36 | | () => AgentHandoffTopologyRegistry.AllHandoffs, |
| | 101 | 37 | | () => AgentGroupChatRegistry.AllGroups, |
| | 101 | 38 | | () => AgentSequentialTopologyRegistry.AllPipelines); |
| | 101 | 39 | | global::NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap.RegisterGraphTopology( |
| | 101 | 40 | | () => AgentGraphTopologyRegistry.AllGraphs); |
| | 101 | 41 | | global::NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap.RegisterAIFunctionProvider( |
| | 101 | 42 | | new global::{safeAssemblyName}.Generated.GeneratedAIFunctionProvider()); |
| | 101 | 43 | | }} |
| | 101 | 44 | | }} |
| | 101 | 45 | | "; |
| | | 46 | | } |
| | | 47 | | |
| | | 48 | | public static string GeneratePartialCompanionSource( |
| | | 49 | | NeedlrAiAgentTypeInfo agentType, |
| | | 50 | | Dictionary<string, List<string>> groupedByName) |
| | | 51 | | { |
| | 11 | 52 | | var sb = new StringBuilder(); |
| | 11 | 53 | | sb.AppendLine("// <auto-generated/>"); |
| | 11 | 54 | | sb.AppendLine("#nullable enable"); |
| | 11 | 55 | | sb.AppendLine(); |
| | | 56 | | |
| | 11 | 57 | | if (agentType.NamespaceName is not null) |
| | | 58 | | { |
| | 11 | 59 | | sb.AppendLine($"namespace {agentType.NamespaceName};"); |
| | 11 | 60 | | sb.AppendLine(); |
| | | 61 | | } |
| | | 62 | | |
| | 11 | 63 | | sb.AppendLine($"partial class {agentType.ClassName}"); |
| | 11 | 64 | | sb.AppendLine("{"); |
| | | 65 | | |
| | 11 | 66 | | sb.AppendLine(" /// <summary>The declared name of this agent, equal to the class name.</summary>"); |
| | | 67 | | |
| | 11 | 68 | | var toolsDocLines = BuildToolsDocComment(agentType, groupedByName); |
| | 74 | 69 | | foreach (var line in toolsDocLines) |
| | 26 | 70 | | sb.AppendLine(line); |
| | | 71 | | |
| | 11 | 72 | | sb.AppendLine($" public static string AgentName => nameof({agentType.ClassName});"); |
| | 11 | 73 | | sb.AppendLine("}"); |
| | | 74 | | |
| | 11 | 75 | | return sb.ToString(); |
| | | 76 | | } |
| | | 77 | | |
| | | 78 | | private static List<string> BuildToolsDocComment( |
| | | 79 | | NeedlrAiAgentTypeInfo agentType, |
| | | 80 | | Dictionary<string, List<string>> groupedByName) |
| | | 81 | | { |
| | | 82 | | static string ShortName(string fqn) |
| | | 83 | | { |
| | 2 | 84 | | var clean = fqn.StartsWith("global::") ? fqn.Substring(8) : fqn; |
| | 2 | 85 | | var dot = clean.LastIndexOf('.'); |
| | 2 | 86 | | return dot >= 0 ? clean.Substring(dot + 1) : clean; |
| | | 87 | | } |
| | | 88 | | |
| | 11 | 89 | | var lines = new List<string>(); |
| | | 90 | | |
| | | 91 | | // FunctionTypes = new Type[0] — explicitly no tools |
| | 11 | 92 | | if (agentType.HasExplicitFunctionTypes && agentType.ExplicitFunctionTypeFQNs.IsEmpty |
| | 11 | 93 | | && agentType.FunctionGroupNames.IsEmpty) |
| | | 94 | | { |
| | 1 | 95 | | lines.Add(" /// <remarks>This agent has no tools assigned (declared with an empty <c>FunctionTypes</c>).< |
| | 1 | 96 | | return lines; |
| | | 97 | | } |
| | | 98 | | |
| | | 99 | | // Neither set — uses all registered function types |
| | 10 | 100 | | if (!agentType.HasExplicitFunctionTypes && agentType.FunctionGroupNames.IsEmpty) |
| | | 101 | | { |
| | 7 | 102 | | lines.Add(" /// <remarks>This agent uses all registered function types.</remarks>"); |
| | 7 | 103 | | return lines; |
| | | 104 | | } |
| | | 105 | | |
| | | 106 | | // Build the resolved list of function types |
| | 3 | 107 | | var entries = new List<(string displayName, string? groupSource)>(); |
| | | 108 | | |
| | 10 | 109 | | foreach (var group in agentType.FunctionGroupNames) |
| | | 110 | | { |
| | 2 | 111 | | if (groupedByName.TryGetValue(group, out var types)) |
| | | 112 | | { |
| | 4 | 113 | | foreach (var typeFqn in types) |
| | 1 | 114 | | entries.Add((ShortName(typeFqn), group)); |
| | | 115 | | } |
| | | 116 | | else |
| | | 117 | | { |
| | 1 | 118 | | entries.Add(($"(unresolved group \"{group}\")", group)); |
| | | 119 | | } |
| | | 120 | | } |
| | | 121 | | |
| | 8 | 122 | | foreach (var typeFqn in agentType.ExplicitFunctionTypeFQNs) |
| | | 123 | | { |
| | 1 | 124 | | var shortName = ShortName(typeFqn); |
| | 1 | 125 | | if (!entries.Any(e => e.displayName == shortName)) |
| | 1 | 126 | | entries.Add((shortName, null)); |
| | | 127 | | } |
| | | 128 | | |
| | 3 | 129 | | lines.Add(" /// <remarks>"); |
| | 3 | 130 | | lines.Add(" /// <para>Agent tools:</para>"); |
| | 3 | 131 | | lines.Add(" /// <list type=\"bullet\">"); |
| | 12 | 132 | | foreach (var (displayName, groupSource) in entries) |
| | | 133 | | { |
| | 3 | 134 | | var source = groupSource is not null ? $" (group <c>\"{groupSource}\"</c>)" : " (explicit type)"; |
| | 3 | 135 | | lines.Add($" /// <item><term><see cref=\"{displayName}\"/>{source}</term></item>"); |
| | | 136 | | } |
| | 3 | 137 | | lines.Add(" /// </list>"); |
| | 3 | 138 | | lines.Add(" /// </remarks>"); |
| | | 139 | | |
| | 3 | 140 | | return lines; |
| | | 141 | | } |
| | | 142 | | } |