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
2 changes: 1 addition & 1 deletion src/google/adk/agents/remote_a2a_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ def _construct_message_parts_from_session(
if not event.content or not event.content.parts:
continue

for part in event.content.parts:
for part in reversed(event.content.parts):

converted_part = self._genai_part_converter(part)
if converted_part:
Expand Down
63 changes: 63 additions & 0 deletions tests/unittests/agents/test_remote_a2a_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,69 @@ async def test_handle_a2a_response_success_with_message(self):
assert result.custom_metadata is not None
assert A2A_METADATA_PREFIX + "context_id" in result.custom_metadata


def test_construct_message_parts_from_session_preserves_order(self):
"""Test that message parts are in correct order with multi-part messages.

This test verifies the fix for the bug where _present_other_agent_message
creates multi-part messages with "For context:" prefix, and ensures the
parts are in the correct chronological order (not reversed).
"""
# Create mock events with multiple parts
# Event 1: User message
user_part = Mock()
user_part.text = "User question"
user_content = Mock()
user_content.parts = [user_part]
user_event = Mock()
user_event.content = user_content
user_event.author = "user"

# Event 2: Other agent message (will be transformed by
# _present_other_agent_message)
other_agent_part1 = Mock()
other_agent_part1.text = "For context:"
other_agent_part2 = Mock()
other_agent_part2.text = "[other_agent] said: Response text"
other_agent_content = Mock()
other_agent_content.parts = [other_agent_part1, other_agent_part2]
other_agent_event = Mock()
other_agent_event.content = other_agent_content
other_agent_event.author = "other_agent"

self.mock_session.events = [user_event, other_agent_event]

with patch(
"google.adk.agents.remote_a2a_agent._present_other_agent_message"
) as mock_present:
# Mock _present_other_agent_message to return the transformed event
mock_present.return_value = other_agent_event

# Mock the converter to track the order of parts
converted_parts = []

def mock_converter(part):
mock_a2a_part = Mock()
mock_a2a_part.original_text = part.text
converted_parts.append(mock_a2a_part)
return mock_a2a_part

self.mock_genai_part_converter.side_effect = mock_converter

result = self.agent._construct_message_parts_from_session(self.mock_context)

# Verify the parts are in correct order
assert len(result) == 2 # Returns tuple of (parts, context_id)
assert len(result[0]) == 3 # 1 user part + 2 other agent parts
assert result[1] is None # context_id

# Verify order: user part, then "For context:", then agent message
assert converted_parts[0].original_text == "User question"
assert converted_parts[1].original_text == "For context:"
assert (
converted_parts[2].original_text == "[other_agent] said: Response text"
)

@pytest.mark.asyncio
async def test_handle_a2a_response_with_task_completed_and_no_update(self):
"""Test successful A2A response handling with non-streeaming task and no update."""
Expand Down