Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
34 changes: 34 additions & 0 deletions src/strands/multiagent/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ class NodeResult:
execution_count: int = 0
interrupts: list[Interrupt] = field(default_factory=list)

def __str__(self) -> str:
"""Return a human-readable string representation of the node result.

Delegates to the inner result's string representation:
- AgentResult: Uses AgentResult.__str__ (extracts text content)
- MultiAgentResult: Uses MultiAgentResult.__str__ (recursive)
- Exception: Uses the exception's string representation

Returns:
String representation of the node's result.
"""
return str(self.result)

def get_agent_results(self) -> list[AgentResult]:
"""Get all AgentResult objects from this node, flattened if nested."""
if isinstance(self.result, Exception):
Expand Down Expand Up @@ -136,6 +149,27 @@ class MultiAgentResult:
execution_time: int = 0
interrupts: list[Interrupt] = field(default_factory=list)

def __str__(self) -> str:
"""Return a human-readable string representation of the multi-agent result.

Priority order:
1. Interrupts (if present) → stringified list of interrupt dicts
2. Node results → each node's string output, prefixed with node name

Returns:
String representation based on the priority order above.
"""
if self.interrupts:
return str([interrupt.to_dict() for interrupt in self.interrupts])

parts = []
for node_name, node_result in self.results.items():
node_str = str(node_result).strip()
if node_str:
parts.append(f"{node_name}: {node_str}")

return "\n".join(parts)

@classmethod
def from_dict(cls, data: dict[str, Any]) -> "MultiAgentResult":
"""Rehydrate a MultiAgentResult from persisted JSON."""
Expand Down
55 changes: 55 additions & 0 deletions tests/strands/multiagent/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,58 @@ def test_serialize_node_result_for_persist(agent_result):
assert "result" in serialized_exception
assert serialized_exception["result"]["type"] == "exception"
assert serialized_exception["result"]["message"] == "Test error"


def test_node_result_str_with_agent_result():
"""Test NodeResult.__str__ delegates to AgentResult.__str__."""
agent_result = AgentResult(
message={"role": "assistant", "content": [{"text": "Hello world"}]},
stop_reason="end_turn",
state={},
metrics={},
)
node_result = NodeResult(result=agent_result)
assert str(node_result) == str(agent_result)
assert "Hello world" in str(node_result)


def test_node_result_str_with_exception():
"""Test NodeResult.__str__ with an Exception result."""
node_result = NodeResult(result=Exception("something broke"), status=Status.FAILED)
assert str(node_result) == "something broke"


def test_multi_agent_result_str_single_node(agent_result):
"""Test MultiAgentResult.__str__ with a single node."""
result = MultiAgentResult(
status=Status.COMPLETED,
results={"writer": NodeResult(result=agent_result)},
)
output = str(result)
assert "writer: Test response" in output


def test_multi_agent_result_str_with_interrupts():
"""Test MultiAgentResult.__str__ prioritizes interrupts over node results."""
from strands.interrupt import Interrupt

ar = AgentResult(
message={"role": "assistant", "content": [{"text": "should not appear"}]},
stop_reason="end_turn",
state={},
metrics={},
)
result = MultiAgentResult(
status=Status.INTERRUPTED,
results={"node": NodeResult(result=ar)},
interrupts=[Interrupt(id="int-1", name="approval", reason="needs review")],
)
output = str(result)
assert "should not appear" not in output
assert "approval" in output


def test_multi_agent_result_str_empty():
"""Test MultiAgentResult.__str__ with no results."""
result = MultiAgentResult(status=Status.COMPLETED, results={})
assert str(result) == ""
Comment thread
dosvk marked this conversation as resolved.