fix: Correct message part ordering in A2A history

Merges test case from https://github.com/google/adk-python/pull/3262

Fixes: #3260

Co-authored-by: Ayush Agrawal <ayushagra@google.com>
PiperOrigin-RevId: 826119626
This commit is contained in:
Ayush Agrawal
2025-10-30 11:36:38 -07:00
committed by Copybara-Service
parent b0017aed44
commit 5eca72f9bf
2 changed files with 74 additions and 5 deletions
+9 -5
View File
@@ -356,29 +356,33 @@ class RemoteA2aAgent(BaseAgent):
"""
message_parts: list[A2APart] = []
context_id = None
events_to_process = []
for event in reversed(ctx.session.events):
if _is_other_agent_reply(self.name, event):
event = _present_other_agent_message(event)
elif event.author == self.name:
if event.author == self.name:
# stop on content generated by current a2a agent given it should already
# be in remote session
if event.custom_metadata:
metadata = event.custom_metadata
context_id = metadata.get(A2A_METADATA_PREFIX + "context_id")
break
events_to_process.append(event)
for event in reversed(events_to_process):
if _is_other_agent_reply(self.name, event):
event = _present_other_agent_message(event)
if not event.content or not event.content.parts:
continue
for part in event.content.parts:
converted_part = self._genai_part_converter(part)
if converted_part:
message_parts.append(converted_part)
else:
logger.warning("Failed to convert part to A2A format: %s", part)
return message_parts[::-1], context_id
return message_parts, context_id
async def _handle_a2a_response(
self, a2a_response: A2AClientEvent | A2AMessage, ctx: InvocationContext
@@ -735,6 +735,71 @@ class TestRemoteA2aAgentMessageHandling:
assert A2A_METADATA_PREFIX + "task_id" in result.custom_metadata
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_submitted_and_no_update(self):
"""Test successful A2A response handling with streaming task and no update."""