-
Notifications
You must be signed in to change notification settings - Fork 1k
Python: .NET: Executor source gen for workflow executor routing #3131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
0ed461a to
46bb208
Compare
46bb208 to
97c7f78
Compare
3930d93 to
d675f46
Compare
dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Analysis/SemanticAnalyzer.cs
Show resolved
Hide resolved
dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Analysis/SemanticAnalyzer.cs
Outdated
Show resolved
Hide resolved
| /// </summary> | ||
| public static readonly DiagnosticDescriptor MissingWorkflowContext = Register(new( | ||
| id: "MAFGENWF001", | ||
| title: "Handler missing IWorkflowContext parameter", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to localize these strings, right?
dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Analysis/SemanticAnalyzer.cs
Outdated
Show resolved
Hide resolved
| public void Initialize(IncrementalGeneratorInitializationContext context) | ||
| { | ||
| // Step 1: Use ForAttributeWithMetadataName to efficiently find methods with [MessageHandler] attribute. For each method found, build a MethodAnalysisResult. | ||
| var methodAnalysisResults = context.SyntaxProvider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rooting the analysis on the methods annotated by [MessageHandler] is likely simpler than starting with extends(Executor), but it has the drawback that if we only want to use SourceGeneration for the Send/Yield types, and manually create the routes, we ignore that Executor class;
We should also do a bit of perf work here, after we settle on the API surface (attributes, what gets generated / not).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that we should update this to handle Send/Yield types even with manual routing, but I don't think we'd want to root on extends(Executor). Rooting on an attribute with ForAttributeWithMetadataName claims to be at least 99x faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated things to handle this case while still rooting on ForAttributeWithMetadataName .
| sb.AppendLine($"{indent}partial class {info.ClassName}{info.GenericParameters}"); | ||
| sb.AppendLine($"{indent}{{"); | ||
|
|
||
| var memberIndent = indent + " "; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to consider using the AnalyzerConfigOptionsProvider mechanism to get the indent style / spacecount from .editorconfig, so that the generated code matches the general project style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting idea. By default, these generated "files" are not written to actual files and even in VS/Code you have to look in the package dependency to see the virtual file. I'm not sure I see the value in trying to match spacecount etc.
| { | ||
| var builder = ImmutableArray.CreateBuilder<string>(); | ||
|
|
||
| foreach (var attr in classSymbol.GetAttributes()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this always get iterated in the same order?
Since we are creating immutable arrays that are presumably used as some form of comparator somewhere, do we want/need to ensure this is permutation invariant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call out. They are consistent (source order) for a given file, but the partial classes may break that. I've added explicit sort to this and other places that have this issue.
| AppendHandlerGenericArgs(sb, handler); | ||
| sb.Append($"(this.{handler.MethodName})"); | ||
|
|
||
| if (isLast) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you pull this out of the loop you can avoid the conditional and just do sb.AppendLine(";");.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulled it out and added a line to clean up the extra newline
dotnet/tests/Microsoft.Agents.AI.Workflows.Generators.UnitTests/ExecutorRouteGeneratorTests.cs
Show resolved
Hide resolved
dotnet/src/Microsoft.Agents.AI.Workflows/Attributes/YieldsMessageAttribute.cs
Outdated
Show resolved
Hide resolved
…uteGenerator.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
7da1da4 to
7878562
Compare
|
|
||
| foreach (var namedArg in attr.NamedArguments) | ||
| { | ||
| if (namedArg.Key == "Yield" && !namedArg.Value.IsNull) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Should this be a case sensitive comparison just to be safe?
| { | ||
| var sb = new StringBuilder(); | ||
|
|
||
| if (!string.IsNullOrEmpty(info.Namespace)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Probably better to use IsNullOrWhitespace or is whitespace a valid namespace?
| sb.AppendLine(); | ||
|
|
||
| // Namespace | ||
| if (!string.IsNullOrEmpty(info.Namespace)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment for this.
This pull request introduces a new Roslyn source generator project for the Microsoft Agent Framework Workflows, enabling compile-time route configuration for executor classes using the
[MessageHandler]attribute. It includes the generator implementation, diagnostics, build configuration, and integration into the solution and dependency management.Follow-up PRs will update samples and docs to use this new pattern and obsolete the ReflectingExecutor
New Source Generator Project:
Microsoft.Agents.AI.Workflows.Generatorstargetingnetstandard2.0, with all necessary build and NuGet packaging settings, and Roslyn dependencies for source generator development. [1] [2]Source Generator Implementation:
ExecutorRouteGeneratorclass, a Roslyn incremental source generator that scans for[MessageHandler]methods and generates partial classes to configure workflow routes at compile time.SourceBuilderutility to generate the actual C# code for route configuration, including overrides forConfigureRoutes,ConfigureSentTypes, andConfigureYieldTypesas needed.Diagnostics and Analysis:
DiagnosticDescriptorsto define and register custom diagnostics for common usage errors (e.g., missing parameters, invalid return types, non-partial classes, etc.) in handler methods and executor classes.Solution and Dependency Integration:
agent-framework-dotnet.slnx). [1] [2]Directory.Packages.propsto includeMicrosoft.CodeAnalysis.Analyzersas a dependency for code analysis support.### Motivation and ContextContribution Checklist