diff --git a/src/google/adk/agents/remote_a2a_agent.py b/src/google/adk/agents/remote_a2a_agent.py index bee8904c7e..e29e8a57ff 100644 --- a/src/google/adk/agents/remote_a2a_agent.py +++ b/src/google/adk/agents/remote_a2a_agent.py @@ -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: diff --git a/tests/unittests/agents/test_remote_a2a_agent.py b/tests/unittests/agents/test_remote_a2a_agent.py index 597b5f1b51..3e75860edd 100644 --- a/tests/unittests/agents/test_remote_a2a_agent.py +++ b/tests/unittests/agents/test_remote_a2a_agent.py @@ -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."""