Repository navigation
Description
Summary
The .NET Agent Framework uses a different API pattern for workflow composition compared to the Python SDK, creating an inconsistency that hinders cross-platform development and documentation portability.
Python SDK (Simple & Explicit):
# Wrap workflow as an executor
sub_exec = WorkflowExecutor(sub, id="sub_workflow")
# Use in parent workflow
main_workflow = Workflow(...).add_edge(source, sub_exec)
.NET SDK (Less Discoverable):
// Extension method on Workflow instance
ExecutorIsh subWorkflowExecutor = subWorkflow.ConfigureSubWorkflow("subWorkflow");
// Use in parent workflow
var mainWorkflow = new WorkflowBuilder(...)
.AddEdge(source, subWorkflowExecutor)
Issues:
- Different mental models - Python uses explicit wrapper class, .NET uses extension method
- Discoverability - Python's WorkflowExecutor is self-documenting, .NET's ConfigureSubWorkflow is hidden as extension method
- Documentation mismatch - Python migration guide references WorkflowExecutor, but it doesn't exist in .NET
- Naming inconsistency - Python uses "Executor" terminology, .NET uses "SubWorkflow" terminology
- API surface - Python's class-based approach is more explicit and testable
Current Behavior (.NET)
In .NET, workflows are converted to executors using an extension method:
// Build a sub-workflow
var subWorkflow = new WorkflowBuilder(uppercaseExecutor)
.AddEdge(uppercaseExecutor, reverseExecutor)
.AddEdge(reverseExecutor, appendExecutor)
.WithOutputFrom(appendExecutor)
.Build();
// ❓ Non-obvious: must call extension method
ExecutorIsh subWorkflowExecutor = subWorkflow.ConfigureSubWorkflow("subWorkflow");
// Use in parent workflow
var mainWorkflow = new WorkflowBuilder(prefixExecutor)
.AddEdge(prefixExecutor, subWorkflowExecutor)
.AddEdge(subWorkflowExecutor, postProcessExecutor)
.Build();
Problems:
• ConfigureSubWorkflow is an extension method - not discoverable through IntelliSense on Workflow
• Lack of clarity: Method name doesn't clearly indicate "convert to executor"
• Returns opaque ExecutorIsh type instead of clearly typed wrapper
• No clear connection to "executor" concept (uses "SubWorkflow" terminology)
Python Behavior (Reference)
Python SDK uses an explicit wrapper class as documented in the migration guide:
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat, Swarm
from autogen_agentchat.ui import Console
# Create sub-workflow
sub = Workflow()
sub.add_node(uppercase_executor)
sub.add_node(reverse_executor)
sub.add_edge(uppercase_executor, reverse_executor)
# ✅ Explicit wrapper class
**sub_exec = WorkflowExecutor(sub, id="sub_workflow")**
# Use in parent workflow
main = Workflow()
main.add_node(prefix_executor)
main.add_node(sub_exec) # ✅ Clear that it's an executor
main.add_edge(prefix_executor, sub_exec)
Advantages:
- Explicit and discoverable - WorkflowExecutor class is easily found in API docs
- Self-documenting - Name clearly indicates "this wraps a workflow as an executor"
- Testable - Can mock/test WorkflowExecutor independently
- Consistent terminology - Uses "Executor" throughout
- Type-safe - Returns concrete WorkflowExecutor type, not opaque interface
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
[-].NET: API inconsistency with Python - ConfigureSubWorkflow vs WorkflowExecutor for workflow composition[/-][+]Python: .NET: API inconsistency with Python - ConfigureSubWorkflow vs WorkflowExecutor for workflow composition[/+][-]Python: .NET: API inconsistency with Python - ConfigureSubWorkflow vs WorkflowExecutor for workflow composition[/-][+].NET: API inconsistency with Python - ConfigureSubWorkflow vs WorkflowExecutor for workflow composition[/+]lokitoth commentedon Oct 22, 2025
We do not allow the user to instantiate
WorkflowExecutoras it depends on being factory-initialized to support all the various execution modes correctly. The burden of making it work in a cross-run shareable manner would be fairly significant.I expect this difference to remain, at least in the short term.
joslat commentedon Oct 22, 2025
Hi Jacob Alber (@lokitoth), after looking into it, I agree with your concerns about direct instantiation breaking the factory pattern and cross-run shareability.
But, The Real Problem Isn't the Class - It's the API Surface ;) - The issue isn't that WorkflowExecutor cannot exist - it's that users shouldn't directly instantiate it.
However, we can achieve the Python SDK's discoverability benefits without exposing public constructors.
Here is a proposed compromise:
Add workflow.AsExecutor(id) as an extension method that internally maintains the factory pattern. This gives us:
• ✅ Discoverable API (shows in IntelliSense)
• ✅ Python-aligned naming ("Executor" terminology)
• ✅ Factory pattern preserved (no direct instantiation)
• ✅ Backward compatible (deprecate ConfigureSubWorkflow)
Users get a better API surface, we keep architectural integrity. Win-win?
Would you consider this middle-ground approach?
lokitoth commentedon Oct 23, 2025
Oh, I'd do more than consider: #1637 :)
joslat commentedon Oct 23, 2025
I... like it! :)
TaoChenOSU commentedon Dec 5, 2025
Closed by #1637