Skip to content
Closed
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
30 changes: 30 additions & 0 deletions src/strands/agent/agent_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,36 @@ def __str__(self) -> str:

return result

def to_node_output(self) -> str:
"""Get a complete representation for graph node transitions.

This method generates output suitable for passing between graph nodes,
including both text content and structured output when present. Unlike
__str__, this method always includes structured output alongside text
to ensure no data is lost during graph transitions.

Returns:
A string representation containing both text and structured output.
"""
parts = []

# Extract text content from message
content_array = self.message.get("content", [])
text_content = ""
for item in content_array:
if isinstance(item, dict) and "text" in item:
text_content += item.get("text", "") + "\n"

if text_content:
parts.append(text_content.rstrip("\n"))

# Include structured output when present
if self.structured_output:
structured_json = self.structured_output.model_dump_json()
parts.append(f"[Structured Output]: {structured_json}")

return "\n".join(parts) if parts else ""

@classmethod
def from_dict(cls, data: dict[str, Any]) -> "AgentResult":
"""Rehydrate an AgentResult from persisted JSON.
Expand Down
2 changes: 1 addition & 1 deletion src/strands/multiagent/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ def _build_node_input(self, node: GraphNode) -> list[ContentBlock]:
agent_results = node_result.get_agent_results()
for result in agent_results:
agent_name = getattr(result, "agent_name", "Agent")
result_text = str(result)
result_text = result.to_node_output() if hasattr(result, "to_node_output") else str(result)
node_input.append(ContentBlock(text=f" - {agent_name}: {result_text}"))

return node_input
Expand Down
83 changes: 83 additions & 0 deletions tests/strands/agent/test_agent_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,86 @@ def test__str__empty_message_with_structured_output(mock_metrics, empty_message:
assert "example" in message_string
assert "123" in message_string
assert "optional" in message_string


# Tests for to_node_output() method - graph node transitions
def test_to_node_output_text_only(mock_metrics, simple_message: Message):
"""Test to_node_output() with text-only message returns just the text."""
result = AgentResult(stop_reason="end_turn", message=simple_message, metrics=mock_metrics, state={})

node_output = result.to_node_output()
assert node_output == "Hello world!"
assert "[Structured Output]" not in node_output


def test_to_node_output_structured_output_only(mock_metrics, empty_message: Message):
"""Test to_node_output() with only structured_output."""
structured_output = StructuredOutputModel(name="test", value=42)

result = AgentResult(
stop_reason="end_turn",
message=empty_message,
metrics=mock_metrics,
state={},
structured_output=structured_output,
)

node_output = result.to_node_output()
assert "[Structured Output]:" in node_output
assert '"name":"test"' in node_output
assert '"value":42' in node_output


def test_to_node_output_text_and_structured_output(mock_metrics, simple_message: Message):
"""Test to_node_output() includes BOTH text AND structured_output when present."""
structured_output = StructuredOutputModel(name="test", value=42)

result = AgentResult(
stop_reason="end_turn",
message=simple_message,
metrics=mock_metrics,
state={},
structured_output=structured_output,
)

node_output = result.to_node_output()

# Should contain both text and structured output
assert "Hello world!" in node_output
assert "[Structured Output]:" in node_output
assert '"name":"test"' in node_output
assert '"value":42' in node_output


def test_to_node_output_empty_message_no_structured_output(mock_metrics, empty_message: Message):
"""Test to_node_output() with empty message and no structured_output returns empty string."""
result = AgentResult(stop_reason="end_turn", message=empty_message, metrics=mock_metrics, state={})

node_output = result.to_node_output()
assert node_output == ""


def test_to_node_output_complex_message(mock_metrics, complex_message: Message):
"""Test to_node_output() with complex message containing multiple text blocks."""
structured_output = StructuredOutputModel(name="complex", value=999, optional_field="extra")

result = AgentResult(
stop_reason="end_turn",
message=complex_message,
metrics=mock_metrics,
state={},
structured_output=structured_output,
)

node_output = result.to_node_output()

# Should contain all text blocks
assert "First paragraph" in node_output
assert "Second paragraph" in node_output
assert "Third paragraph" in node_output

# Should contain structured output
assert "[Structured Output]:" in node_output
assert '"name":"complex"' in node_output
assert '"value":999' in node_output
assert '"optional_field":"extra"' in node_output
Loading