Skip to content

Commit b568864

Browse files
mkmeralzastrowm
andauthored
fix(graph): fix cyclic graph behavior (#768)
fix a bug in the Graph multiagent pattern where the reset_on_revisit feature fails to enable cycles and feedback loops. The issue was in the _find_newly_ready_nodes method, which filtered out completed nodes before they could be revisited, making it impossible to implement feedback loops even when reset_on_revisit=True. --------- Co-authored-by: Murat Kaan Meral <[email protected]>Co-authored-by: Mackenzie Zastrow <[email protected]>
1 parent faeb21a commit b568864

File tree

2 files changed

+266
-77
lines changed

2 files changed

+266
-77
lines changed

src/strands/multiagent/graph.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -469,41 +469,32 @@ async def _execute_graph(self) -> None:
469469
ready_nodes.clear()
470470

471471
# Execute current batch of ready nodes concurrently
472-
tasks = [
473-
asyncio.create_task(self._execute_node(node))
474-
for node in current_batch
475-
if node not in self.state.completed_nodes
476-
]
472+
tasks = [asyncio.create_task(self._execute_node(node)) for node in current_batch]
477473

478474
for task in tasks:
479475
await task
480476

481477
# Find newly ready nodes after batch execution
482-
ready_nodes.extend(self._find_newly_ready_nodes())
478+
# We add all nodes in current batch as completed batch,
479+
# because a failure would throw exception and code would not make it here
480+
ready_nodes.extend(self._find_newly_ready_nodes(current_batch))
483481

484-
def _find_newly_ready_nodes(self) -> list["GraphNode"]:
482+
def _find_newly_ready_nodes(self, completed_batch: list["GraphNode"]) -> list["GraphNode"]:
485483
"""Find nodes that became ready after the last execution."""
486484
newly_ready = []
487485
for _node_id, node in self.nodes.items():
488-
if (
489-
node not in self.state.completed_nodes
490-
and node not in self.state.failed_nodes
491-
and self._is_node_ready_with_conditions(node)
492-
):
486+
if self._is_node_ready_with_conditions(node, completed_batch):
493487
newly_ready.append(node)
494488
return newly_ready
495489

496-
def _is_node_ready_with_conditions(self, node: GraphNode) -> bool:
490+
def _is_node_ready_with_conditions(self, node: GraphNode, completed_batch: list["GraphNode"]) -> bool:
497491
"""Check if a node is ready considering conditional edges."""
498492
# Get incoming edges to this node
499493
incoming_edges = [edge for edge in self.edges if edge.to_node == node]
500494

501-
if not incoming_edges:
502-
return node in self.entry_points
503-
504495
# Check if at least one incoming edge condition is satisfied
505496
for edge in incoming_edges:
506-
if edge.from_node in self.state.completed_nodes:
497+
if edge.from_node in completed_batch:
507498
if edge.should_traverse(self.state):
508499
logger.debug(
509500
"from=<%s>, to=<%s> | edge ready via satisfied condition", edge.from_node.node_id, node.node_id

0 commit comments

Comments
 (0)