[BREAKING] Python: Move single-config fluent methods to constructor parameters#3693
[BREAKING] Python: Move single-config fluent methods to constructor parameters#3693moonbox3 wants to merge 1 commit intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements a breaking change that converts 14 fluent builder methods across 6 orchestration builders into constructor parameters. The change eliminates duplicate configuration paths by removing methods like set_start_executor(), with_checkpointing(), with_max_rounds(), with_termination_condition(), with_intermediate_outputs(), and with_plan_review() in favor of constructor parameters.
Changes:
- Moves single-configuration fluent methods to constructor parameters for WorkflowBuilder, SequentialBuilder, ConcurrentBuilder, GroupChatBuilder, HandoffBuilder, and MagenticBuilder
- Updates all samples (60+ files) and tests (20+ files) to use the new constructor syntax
- Converts
set_start_executor()to private_set_start_executor()method and addsstart_executorconstructor parameter - Updates error messages and documentation to reflect the new API
Reviewed changes
Copilot reviewed 103 out of 103 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
_workflow_builder.py |
Adds constructor parameters (start_executor, checkpoint_storage, output_executors), removes fluent methods, makes set_start_executor private |
_sequential.py |
Adds checkpoint_storage and intermediate_outputs constructor parameters, removes corresponding fluent methods |
_concurrent.py |
Adds checkpoint_storage and intermediate_outputs constructor parameters, removes corresponding fluent methods |
_group_chat.py |
Adds termination_condition, max_rounds, checkpoint_storage, and intermediate_outputs constructor parameters |
_handoff.py |
Adds checkpoint_storage and termination_condition constructor parameters |
_magentic.py |
Adds enable_plan_review, checkpoint_storage, and intermediate_outputs constructor parameters |
| Sample files (60+) | Updates all workflow construction to use new constructor parameter syntax |
| Test files (20+) | Updates all test code to use new constructor parameter syntax |
_declarative_builder.py |
Updates to use WorkflowBuilder constructor parameters but calls private _set_start_executor() |
_workflow.py, _orchestration_request_info.py |
Updates documentation and usage examples |
| .register_executor(lambda: database_access, name="database_access") | ||
| ) | ||
|
|
||
| workflow_builder._start_executor = "store_email" # Set start executor |
There was a problem hiding this comment.
Direct assignment to private attribute _start_executor bypasses the validation and wrapping logic in _set_start_executor. This could lead to bugs if the start_executor is an Agent or a string reference that needs to be resolved. Consider using builder._set_start_executor(...) instead to ensure proper validation and wrapping.
| @@ -163,13 +161,8 @@ def test_register_multiple_executors(): | |||
| builder.register_executor(lambda: MockExecutor(id="ExecutorC"), name="ExecutorC") | |||
|
|
|||
| # Build workflow with edges using registered names | |||
| workflow = ( | |||
| builder | |||
| .set_start_executor("ExecutorA") | |||
| .add_edge("ExecutorA", "ExecutorB") | |||
| .add_edge("ExecutorB", "ExecutorC") | |||
| .build() | |||
| ) | |||
| builder._start_executor = "ExecutorA" | |||
| workflow = builder.add_edge("ExecutorA", "ExecutorB").add_edge("ExecutorB", "ExecutorC").build() | |||
|
|
|||
| # Verify all executors are present | |||
| assert "ExecutorA" in workflow.executors | |||
| @@ -193,7 +186,8 @@ def make_executor(): | |||
| builder.register_executor(make_executor, name=["ExecutorA", "ExecutorB"]) | |||
|
|
|||
| # Set up workflow | |||
| workflow = builder.set_start_executor("ExecutorA").add_edge("ExecutorA", "ExecutorB").build() | |||
| builder._start_executor = "ExecutorA" | |||
| workflow = builder.add_edge("ExecutorA", "ExecutorB").build() | |||
|
|
|||
| # Verify both executors are present | |||
| assert "ExecutorA" in workflow.executors | |||
| @@ -220,7 +214,7 @@ def test_register_duplicate_id_raises_error(): | |||
| # Register first executor | |||
| builder.register_executor(lambda: MockExecutor(id="executor"), name="MyExecutor1") | |||
| builder.register_executor(lambda: MockExecutor(id="executor"), name="MyExecutor2") | |||
| builder.set_start_executor("MyExecutor1") | |||
| builder._start_executor = "MyExecutor1" | |||
There was a problem hiding this comment.
Multiple direct assignments to private attribute _start_executor bypass the validation and wrapping logic in _set_start_executor. This pattern appears throughout the test file and could lead to bugs if the start_executor is an Agent or a string reference that needs to be resolved. While acceptable in test code, consider documenting why the private API is being used directly, or preferably use the constructor parameter start_executor consistently.
| if not self._start_executor: | ||
| raise ValueError("Starting executor must be set using set_start_executor before building the workflow.") | ||
| raise ValueError( | ||
| "Starting executor must be set using the start_executor constructor parameter before building." |
There was a problem hiding this comment.
The error message should be updated to match the new API. Instead of "using the start_executor constructor parameter", it should say "via the start_executor constructor parameter" to be consistent with Python conventions and clearer.
| "Starting executor must be set using the start_executor constructor parameter before building." | |
| "Starting executor must be set via the start_executor constructor parameter before building." |
| builder._set_start_executor(entry_node) | ||
| # Use _add_sequential_edge which knows how to wire to structures | ||
| self._add_sequential_edge(builder, entry_node, entry_executor) | ||
| else: | ||
| builder.set_start_executor(entry_executor) | ||
| builder._set_start_executor(entry_executor) |
There was a problem hiding this comment.
The _set_start_executor method is being made internal (prefixed with underscore) but is being called in user-facing code in the declarative builder. This creates an inconsistency - internal methods should not be called from outside the class. The declarative builder should use the constructor parameter start_executor instead, or this method should remain public.
| ) | ||
|
|
||
| # Step 2: Build the workflow graph using fan out and fan in edges. | ||
| workflow_builder._start_executor = "split_data_executor" # Set start executor |
There was a problem hiding this comment.
Direct assignment to private attribute _start_executor bypasses the validation and wrapping logic in _set_start_executor. This could lead to bugs if the start_executor is an Agent or a string reference that needs to be resolved. Consider using builder._set_start_executor(...) instead to ensure proper validation and wrapping.
Motivation and Context
Breaking Changes
An (simple, omitting some config) example of a change:
Methods removed (in favor of constructor arguments):
Description
Contribution Checklist