Skip to content

Commit f2ed5b5

Browse files
authored
Python: WorkflowBuilder registry (#2486)
* Add workflow builder factory pattern * Add internal edge groups to registered executors; next samples * Update samples: Part 1 * register -> register_executor * update hil samples * Update other samples * Update agent samples * Update doc string * Add new sample * Fix mypy * Address comments * Fix mypy
1 parent 6809510 commit f2ed5b5

33 files changed

Lines changed: 1585 additions & 672 deletions

python/packages/core/agent_framework/_workflows/_edge.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def __init__(self) -> None:
232232
"""
233233

234234
condition: Callable[[Any], bool]
235-
target: Executor
235+
target: Executor | str
236236

237237

238238
@dataclass
@@ -255,7 +255,7 @@ def __init__(self) -> None:
255255
assert fallback.target.id == "dead_letter"
256256
"""
257257

258-
target: Executor
258+
target: Executor | str
259259

260260

261261
@dataclass(init=False)

python/packages/core/agent_framework/_workflows/_validation.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,44 +101,41 @@ class WorkflowGraphValidator:
101101
def __init__(self) -> None:
102102
self._edges: list[Edge] = []
103103
self._executors: dict[str, Executor] = {}
104-
self._start_executor_ref: Executor | str | None = None
105104

106105
# region Core Validation Methods
107106
def validate_workflow(
108107
self,
109108
edge_groups: Sequence[EdgeGroup],
110109
executors: dict[str, Executor],
111-
start_executor: Executor | str,
110+
start_executor: Executor,
112111
) -> None:
113112
"""Validate the entire workflow graph.
114113
115114
Args:
116115
edge_groups: list of edge groups in the workflow
117116
executors: Map of executor IDs to executor instances
118-
start_executor: The starting executor (can be instance or ID)
117+
start_executor: The starting executor
119118
120119
Raises:
121120
WorkflowValidationError: If any validation fails
122121
"""
123122
self._executors = executors
124123
self._edges = [edge for group in edge_groups for edge in group.edges]
125124
self._edge_groups = edge_groups
126-
self._start_executor_ref = start_executor
127125

128126
# If only the start executor exists, add it to the executor map
129127
# Handle the special case where the workflow consists of only a single executor and no edges.
130128
# In this scenario, the executor map will be empty because there are no edge groups to reference executors.
131129
# Adding the start executor to the map ensures that single-executor workflows (without any edges) are supported,
132130
# allowing validation and execution to proceed for workflows that do not require inter-executor communication.
133-
if not self._executors and start_executor and isinstance(start_executor, Executor):
131+
if not self._executors:
134132
self._executors[start_executor.id] = start_executor
135133

136134
# Validate that start_executor exists in the graph
137135
# It should because we check for it in the WorkflowBuilder
138136
# but we do it here for completeness.
139-
start_executor_id = start_executor.id if isinstance(start_executor, Executor) else start_executor
140-
if start_executor_id not in self._executors:
141-
raise GraphConnectivityError(f"Start executor '{start_executor_id}' is not present in the workflow graph")
137+
if start_executor.id not in self._executors:
138+
raise GraphConnectivityError(f"Start executor '{start_executor.id}' is not present in the workflow graph")
142139

143140
# Additional presence verification:
144141
# A start executor that is only injected via the builder (present in the executors map)
@@ -152,16 +149,16 @@ def validate_workflow(
152149
for e in self._edges:
153150
edge_executor_ids.add(e.source_id)
154151
edge_executor_ids.add(e.target_id)
155-
if start_executor_id not in edge_executor_ids:
152+
if start_executor.id not in edge_executor_ids:
156153
raise GraphConnectivityError(
157-
f"Start executor '{start_executor_id}' is not present in the workflow graph"
154+
f"Start executor '{start_executor.id}' is not present in the workflow graph"
158155
)
159156

160157
# Run all checks
161158
self._validate_edge_duplication()
162159
self._validate_handler_output_annotations()
163160
self._validate_type_compatibility()
164-
self._validate_graph_connectivity(start_executor_id)
161+
self._validate_graph_connectivity(start_executor.id)
165162
self._validate_self_loops()
166163
self._validate_dead_ends()
167164

@@ -398,7 +395,7 @@ def _validate_dead_ends(self) -> None:
398395
def validate_workflow_graph(
399396
edge_groups: Sequence[EdgeGroup],
400397
executors: dict[str, Executor],
401-
start_executor: Executor | str,
398+
start_executor: Executor,
402399
) -> None:
403400
"""Convenience function to validate a workflow graph.
404401

python/packages/core/agent_framework/_workflows/_workflow.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def __init__(
180180
self,
181181
edge_groups: list[EdgeGroup],
182182
executors: dict[str, Executor],
183-
start_executor: Executor | str,
183+
start_executor: Executor,
184184
runner_context: RunnerContext,
185185
max_iterations: int = DEFAULT_MAX_ITERATIONS,
186186
name: str | None = None,
@@ -192,19 +192,16 @@ def __init__(
192192
Args:
193193
edge_groups: A list of EdgeGroup instances that define the workflow edges.
194194
executors: A dictionary mapping executor IDs to Executor instances.
195-
start_executor: The starting executor for the workflow, which can be an Executor instance or its ID.
195+
start_executor: The starting executor for the workflow.
196196
runner_context: The RunnerContext instance to be used during workflow execution.
197197
max_iterations: The maximum number of iterations the workflow will run for convergence.
198198
name: Optional human-readable name for the workflow.
199199
description: Optional description of what the workflow does.
200200
kwargs: Additional keyword arguments. Unused in this implementation.
201201
"""
202-
# Convert start_executor to string ID if it's an Executor instance
203-
start_executor_id = start_executor.id if isinstance(start_executor, Executor) else start_executor
204-
205202
self.edge_groups = list(edge_groups)
206203
self.executors = dict(executors)
207-
self.start_executor_id = start_executor_id
204+
self.start_executor_id = start_executor.id
208205
self.max_iterations = max_iterations
209206
self.id = str(uuid.uuid4())
210207
self.name = name

0 commit comments

Comments
 (0)