| | | 1 | | using Microsoft.CodeAnalysis; |
| | | 2 | | |
| | | 3 | | namespace NexusLabs.Needlr.AgentFramework.Analyzers; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// Contains diagnostic descriptors for all Needlr Agent Framework analyzers. |
| | | 7 | | /// </summary> |
| | | 8 | | public static class MafDiagnosticDescriptors |
| | | 9 | | { |
| | | 10 | | private const string Category = "NexusLabs.Needlr.AgentFramework"; |
| | | 11 | | private const string HelpLinkBase = "https://github.com/nexus-labs/needlr/blob/main/docs/analyzers/"; |
| | | 12 | | |
| | | 13 | | /// <summary> |
| | | 14 | | /// NDLRMAF001: <c>[AgentHandoffsTo(typeof(X))]</c> target type X is not decorated with <c>[NeedlrAiAgent]</c>. |
| | | 15 | | /// </summary> |
| | 1 | 16 | | public static readonly DiagnosticDescriptor HandoffsToTargetNotNeedlrAgent = new( |
| | 1 | 17 | | id: MafDiagnosticIds.HandoffsToTargetNotNeedlrAgent, |
| | 1 | 18 | | title: "[AgentHandoffsTo] target type is not a declared agent", |
| | 1 | 19 | | messageFormat: "'{0}' is referenced as a handoff target but is not decorated with [NeedlrAiAgent]. Handoff targe |
| | 1 | 20 | | category: Category, |
| | 1 | 21 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 22 | | isEnabledByDefault: true, |
| | 1 | 23 | | description: "Types used as handoff targets in [AgentHandoffsTo] must also be decorated with [NeedlrAiAgent] so |
| | 1 | 24 | | helpLinkUri: HelpLinkBase + "NDLRMAF001.md"); |
| | | 25 | | |
| | | 26 | | /// <summary> |
| | | 27 | | /// NDLRMAF002: <c>[AgentGroupChatMember("g")]</c> group "g" has fewer than two members. |
| | | 28 | | /// </summary> |
| | 1 | 29 | | public static readonly DiagnosticDescriptor GroupChatTooFewMembers = new( |
| | 1 | 30 | | id: MafDiagnosticIds.GroupChatTooFewMembers, |
| | 1 | 31 | | title: "Group chat has fewer than two members", |
| | 1 | 32 | | messageFormat: "Group chat '{0}' has only {1} member(s) in this compilation. A group chat requires at least two |
| | 1 | 33 | | category: Category, |
| | 1 | 34 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 35 | | isEnabledByDefault: true, |
| | 1 | 36 | | description: "IWorkflowFactory.CreateGroupChatWorkflow() throws at runtime when fewer than two agents are regist |
| | 1 | 37 | | helpLinkUri: HelpLinkBase + "NDLRMAF002.md", |
| | 1 | 38 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 39 | | |
| | | 40 | | /// <summary> |
| | | 41 | | /// NDLRMAF003: A class has <c>[AgentHandoffsTo]</c> but is not decorated with <c>[NeedlrAiAgent]</c>. |
| | | 42 | | /// </summary> |
| | 1 | 43 | | public static readonly DiagnosticDescriptor HandoffsToSourceNotNeedlrAgent = new( |
| | 1 | 44 | | id: MafDiagnosticIds.HandoffsToSourceNotNeedlrAgent, |
| | 1 | 45 | | title: "[AgentHandoffsTo] source class is not a declared agent", |
| | 1 | 46 | | messageFormat: "'{0}' has [AgentHandoffsTo] but is not decorated with [NeedlrAiAgent]. The source agent of a han |
| | 1 | 47 | | category: Category, |
| | 1 | 48 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 49 | | isEnabledByDefault: true, |
| | 1 | 50 | | description: "The class that carries [AgentHandoffsTo] is the initial agent of a handoff workflow and must also |
| | 1 | 51 | | helpLinkUri: HelpLinkBase + "NDLRMAF003.md"); |
| | | 52 | | |
| | | 53 | | /// <summary> |
| | | 54 | | /// NDLRMAF004: A cyclic handoff chain was detected. |
| | | 55 | | /// </summary> |
| | 1 | 56 | | public static readonly DiagnosticDescriptor CyclicHandoffChain = new( |
| | 1 | 57 | | id: MafDiagnosticIds.CyclicHandoffChain, |
| | 1 | 58 | | title: "Cyclic handoff chain detected", |
| | 1 | 59 | | messageFormat: "'{0}' participates in a cyclic handoff chain: {1}. This may cause infinite routing loops at runt |
| | 1 | 60 | | category: Category, |
| | 1 | 61 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 62 | | isEnabledByDefault: true, |
| | 1 | 63 | | description: "A handoff cycle exists where an agent can eventually hand off back to itself. While MAF may handle |
| | 1 | 64 | | helpLinkUri: HelpLinkBase + "NDLRMAF004.md", |
| | 1 | 65 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 66 | | |
| | | 67 | | /// <summary> |
| | | 68 | | /// NDLRMAF005: An agent declares a FunctionGroups entry with no matching [AgentFunctionGroup] class. |
| | | 69 | | /// </summary> |
| | 1 | 70 | | public static readonly DiagnosticDescriptor UnresolvedFunctionGroupReference = new( |
| | 1 | 71 | | id: MafDiagnosticIds.UnresolvedFunctionGroupReference, |
| | 1 | 72 | | title: "FunctionGroups references an unregistered group name", |
| | 1 | 73 | | messageFormat: "'{0}' declares FunctionGroups entry '{1}' but no class decorated with [AgentFunctionGroup(\"{1}\ |
| | 1 | 74 | | category: Category, |
| | 1 | 75 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 76 | | isEnabledByDefault: true, |
| | 1 | 77 | | description: "When an agent declares FunctionGroups = new[] { \"name\" }, a class with [AgentFunctionGroup(\"nam |
| | 1 | 78 | | helpLinkUri: HelpLinkBase + "NDLRMAF005.md", |
| | 1 | 79 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 80 | | |
| | | 81 | | /// <summary> |
| | | 82 | | /// NDLRMAF006: Duplicate Order value within the same [AgentSequenceMember] pipeline. |
| | | 83 | | /// </summary> |
| | 1 | 84 | | public static readonly DiagnosticDescriptor DuplicateSequenceOrder = new( |
| | 1 | 85 | | id: MafDiagnosticIds.DuplicateSequenceOrder, |
| | 1 | 86 | | title: "Duplicate Order value in sequential pipeline", |
| | 1 | 87 | | messageFormat: "Pipeline '{0}' has a duplicate Order value ({1}) on '{2}'. Each agent in a sequential pipeline m |
| | 1 | 88 | | category: Category, |
| | 1 | 89 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 90 | | isEnabledByDefault: true, |
| | 1 | 91 | | description: "Two or more agents in the same [AgentSequenceMember] pipeline declare the same Order value. This i |
| | 1 | 92 | | helpLinkUri: HelpLinkBase + "NDLRMAF006.md", |
| | 1 | 93 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 94 | | |
| | | 95 | | /// <summary> |
| | | 96 | | /// NDLRMAF007: Gap in Order sequence within the same [AgentSequenceMember] pipeline. |
| | | 97 | | /// </summary> |
| | 1 | 98 | | public static readonly DiagnosticDescriptor GapInSequenceOrder = new( |
| | 1 | 99 | | id: MafDiagnosticIds.GapInSequenceOrder, |
| | 1 | 100 | | title: "Gap in sequential pipeline Order values", |
| | 1 | 101 | | messageFormat: "Pipeline '{0}' has a gap in its Order sequence — Order {1} is missing. Use contiguous Order valu |
| | 1 | 102 | | category: Category, |
| | 1 | 103 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 104 | | isEnabledByDefault: true, |
| | 1 | 105 | | description: "The Order values declared via [AgentSequenceMember] for this pipeline are not contiguous. This is |
| | 1 | 106 | | helpLinkUri: HelpLinkBase + "NDLRMAF007.md", |
| | 1 | 107 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 108 | | |
| | | 109 | | /// <summary> |
| | | 110 | | /// NDLRMAF008: Agent participates in no topology declaration. |
| | | 111 | | /// </summary> |
| | 1 | 112 | | public static readonly DiagnosticDescriptor OrphanAgent = new( |
| | 1 | 113 | | id: MafDiagnosticIds.OrphanAgent, |
| | 1 | 114 | | title: "Agent participates in no topology declaration", |
| | 1 | 115 | | messageFormat: "'{0}' is decorated with [NeedlrAiAgent] but does not appear in any topology declaration ([AgentH |
| | 1 | 116 | | category: Category, |
| | 1 | 117 | | defaultSeverity: DiagnosticSeverity.Info, |
| | 1 | 118 | | isEnabledByDefault: true, |
| | 1 | 119 | | description: "An agent registered with [NeedlrAiAgent] is not referenced in any topology. It will not appear in |
| | 1 | 120 | | helpLinkUri: HelpLinkBase + "NDLRMAF008.md", |
| | 1 | 121 | | customTags: WellKnownDiagnosticTags.CompilationEnd); |
| | | 122 | | |
| | | 123 | | /// <summary> |
| | | 124 | | /// NDLRMAF009: <c>[WorkflowRunTerminationCondition]</c> declared on a non-agent class. |
| | | 125 | | /// </summary> |
| | 1 | 126 | | public static readonly DiagnosticDescriptor WorkflowRunTerminationConditionOnNonAgent = new( |
| | 1 | 127 | | id: MafDiagnosticIds.WorkflowRunTerminationConditionOnNonAgent, |
| | 1 | 128 | | title: "[WorkflowRunTerminationCondition] declared on a non-agent class", |
| | 1 | 129 | | messageFormat: "'{0}' has [WorkflowRunTerminationCondition] but is not decorated with [NeedlrAiAgent]. Terminati |
| | 1 | 130 | | category: Category, |
| | 1 | 131 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 132 | | isEnabledByDefault: true, |
| | 1 | 133 | | description: "A class carries [WorkflowRunTerminationCondition] but is not decorated with [NeedlrAiAgent], so it |
| | 1 | 134 | | helpLinkUri: HelpLinkBase + "NDLRMAF009.md"); |
| | | 135 | | |
| | | 136 | | /// <summary> |
| | | 137 | | /// NDLRMAF010: Condition type does not implement <c>IWorkflowTerminationCondition</c>. |
| | | 138 | | /// </summary> |
| | 1 | 139 | | public static readonly DiagnosticDescriptor TerminationConditionTypeInvalid = new( |
| | 1 | 140 | | id: MafDiagnosticIds.TerminationConditionTypeInvalid, |
| | 1 | 141 | | title: "Termination condition type does not implement IWorkflowTerminationCondition", |
| | 1 | 142 | | messageFormat: "'{0}' does not implement IWorkflowTerminationCondition and cannot be used as a termination condi |
| | 1 | 143 | | category: Category, |
| | 1 | 144 | | defaultSeverity: DiagnosticSeverity.Error, |
| | 1 | 145 | | isEnabledByDefault: true, |
| | 1 | 146 | | description: "The conditionType argument passed to [WorkflowRunTerminationCondition] or [AgentTerminationConditi |
| | 1 | 147 | | helpLinkUri: HelpLinkBase + "NDLRMAF010.md"); |
| | | 148 | | |
| | | 149 | | /// <summary> |
| | | 150 | | /// NDLRMAF011: Prefer <c>[AgentTerminationCondition]</c> over |
| | | 151 | | /// <c>[WorkflowRunTerminationCondition]</c> for group chat members. |
| | | 152 | | /// </summary> |
| | 1 | 153 | | public static readonly DiagnosticDescriptor PreferAgentTerminationConditionForGroupChat = new( |
| | 1 | 154 | | id: MafDiagnosticIds.PreferAgentTerminationConditionForGroupChat, |
| | 1 | 155 | | title: "Prefer [AgentTerminationCondition] over [WorkflowRunTerminationCondition] for group chat members", |
| | 1 | 156 | | messageFormat: "'{0}' is an [AgentGroupChatMember] with [WorkflowRunTerminationCondition]. Consider using [Agent |
| | 1 | 157 | | category: Category, |
| | 1 | 158 | | defaultSeverity: DiagnosticSeverity.Info, |
| | 1 | 159 | | isEnabledByDefault: true, |
| | 1 | 160 | | description: "For group chat agents, [AgentTerminationCondition] is evaluated inside MAF's group chat loop befor |
| | 1 | 161 | | helpLinkUri: HelpLinkBase + "NDLRMAF011.md"); |
| | | 162 | | |
| | | 163 | | /// <summary> |
| | | 164 | | /// NDLRMAF012: <c>[AgentFunction]</c> method has no <c>[Description]</c> attribute. |
| | | 165 | | /// </summary> |
| | 1 | 166 | | public static readonly DiagnosticDescriptor AgentFunctionMissingDescription = new( |
| | 1 | 167 | | id: MafDiagnosticIds.AgentFunctionMissingDescription, |
| | 1 | 168 | | title: "[AgentFunction] method is missing a [Description] attribute", |
| | 1 | 169 | | messageFormat: "'{0}' is decorated with [AgentFunction] but has no [Description] attribute. Without a descriptio |
| | 1 | 170 | | category: Category, |
| | 1 | 171 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 172 | | isEnabledByDefault: true, |
| | 1 | 173 | | description: "The LLM uses the [Description] text to decide when to invoke an agent function tool. A method with |
| | 1 | 174 | | helpLinkUri: HelpLinkBase + "NDLRMAF012.md"); |
| | | 175 | | |
| | | 176 | | /// <summary> |
| | | 177 | | /// NDLRMAF013: Parameter of an <c>[AgentFunction]</c> method is missing a <c>[Description]</c> attribute. |
| | | 178 | | /// </summary> |
| | 1 | 179 | | public static readonly DiagnosticDescriptor AgentFunctionParameterMissingDescription = new( |
| | 1 | 180 | | id: MafDiagnosticIds.AgentFunctionParameterMissingDescription, |
| | 1 | 181 | | title: "[AgentFunction] method parameter is missing a [Description] attribute", |
| | 1 | 182 | | messageFormat: "Parameter '{0}' on [AgentFunction] method '{1}' has no [Description] attribute. Without a descri |
| | 1 | 183 | | category: Category, |
| | 1 | 184 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 185 | | isEnabledByDefault: true, |
| | 1 | 186 | | description: "Each parameter of an [AgentFunction] method should carry a [Description] attribute so the LLM know |
| | 1 | 187 | | helpLinkUri: HelpLinkBase + "NDLRMAF013.md"); |
| | | 188 | | |
| | | 189 | | /// <summary> |
| | | 190 | | /// NDLRMAF014: A type in <c>FunctionTypes</c> on <c>[NeedlrAiAgent]</c> has no |
| | | 191 | | /// <c>[AgentFunction]</c> methods. |
| | | 192 | | /// </summary> |
| | 1 | 193 | | public static readonly DiagnosticDescriptor AgentFunctionTypesMiswired = new( |
| | 1 | 194 | | id: MafDiagnosticIds.AgentFunctionTypesMiswired, |
| | 1 | 195 | | title: "FunctionTypes entry has no [AgentFunction] methods", |
| | 1 | 196 | | messageFormat: "'{0}' is listed in FunctionTypes on '{1}' but has no methods decorated with [AgentFunction]. The |
| | 1 | 197 | | category: Category, |
| | 1 | 198 | | defaultSeverity: DiagnosticSeverity.Warning, |
| | 1 | 199 | | isEnabledByDefault: true, |
| | 1 | 200 | | description: "A type referenced in FunctionTypes must contain at least one method decorated with [AgentFunction] |
| | 1 | 201 | | helpLinkUri: HelpLinkBase + "NDLRMAF014.md"); |
| | | 202 | | } |