Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/strands/multiagent/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,15 @@ def reset_executor_state(self) -> None:

This is useful when nodes are executed multiple times and need to start
fresh on each execution, providing stateless behavior.

For MultiAgentBase executors (e.g. nested Graph or Swarm), the state is a
GraphState/SwarmState dataclass — not an AgentState dict — so we skip the
state reset to avoid corrupting it with an incompatible type.
"""
if hasattr(self.executor, "messages"):
self.executor.messages = copy.deepcopy(self._initial_messages)

if hasattr(self.executor, "state"):
if hasattr(self.executor, "state") and not isinstance(self.executor, MultiAgentBase):
self.executor.state = AgentState(self._initial_state.get())

# Reset execution status
Expand Down
51 changes: 50 additions & 1 deletion tests/strands/multiagent/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,8 +738,14 @@ async def test_node_reset_executor_state():

# Also modify execution status and result
node.execution_status = Status.COMPLETED
agent_result = AgentResult(
message={"role": "assistant", "content": [{"text": "test"}]},
stop_reason="end_turn",
state={},
metrics=None,
)
node.result = NodeResult(
result="test result",
result=agent_result,
execution_time=100,
status=Status.COMPLETED,
accumulated_usage={"inputTokens": 10, "outputTokens": 20, "totalTokens": 30},
Expand Down Expand Up @@ -793,6 +799,49 @@ async def test_node_reset_executor_state():
assert multi_agent_node.result is None


def test_reset_executor_state_preserves_multiagent_state_type():
"""Test that reset_executor_state does not overwrite MultiAgentBase.state with AgentState.

Regression test for https://github.com/strands-agents/sdk-python/issues/1775.
GraphNode.reset_executor_state() was unconditionally assigning AgentState(...) to
executor.state, corrupting the GraphState dataclass on nested Graph/Swarm executors.
"""
multi_agent = create_mock_multi_agent("nested_graph")
# Simulate a MultiAgentBase that has a GraphState (like a nested Graph)
original_state = GraphState(task="original task")
multi_agent.state = original_state

node = GraphNode("nested_node", multi_agent)

# Modify execution status as if the node had run
node.execution_status = Status.COMPLETED
agent_result = AgentResult(
message={"role": "assistant", "content": [{"text": "test"}]},
stop_reason="end_turn",
state={},
metrics=None,
)
node.result = NodeResult(
result=agent_result,
execution_time=100,
status=Status.COMPLETED,
accumulated_usage={},
accumulated_metrics={},
execution_count=1,
)

# Reset should NOT corrupt the state type
node.reset_executor_state()

# The state must still be a GraphState, not an AgentState dict
assert isinstance(multi_agent.state, GraphState), (
f"Expected GraphState but got {type(multi_agent.state).__name__}; "
"reset_executor_state must not overwrite MultiAgentBase state with AgentState"
)
assert node.execution_status == Status.PENDING
assert node.result is None


def test_graph_dataclasses_and_enums():
"""Test dataclass initialization, properties, and enum behavior."""
# Test Status enum
Expand Down