Skip to content

Commit 626425c

Browse files
committed
Fix: Correct message part ordering in A2A history
The previous logic iterated through session events in reverse and then reversed the resulting list of parts. This caused incorrect ordering for multi-part messages generated by `_present_other_agent_message`. Specifically, introductory text like "For context:" was appearing after the content it was meant to introduce. This change simplifies the implementation by removing both reversals. It now processes events in chronological order, ensuring all message parts are appended in the correct sequence. Signed-off-by: Eran Cohen <[email protected]>
1 parent 955632c commit 626425c

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

src/google/adk/agents/remote_a2a_agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ def _construct_message_parts_from_session(
356356
"""
357357
message_parts: list[A2APart] = []
358358
context_id = None
359-
for event in reversed(ctx.session.events):
359+
for event in ctx.session.events:
360360
if _is_other_agent_reply(self.name, event):
361361
event = _present_other_agent_message(event)
362362
elif event.author == self.name:
@@ -378,7 +378,7 @@ def _construct_message_parts_from_session(
378378
else:
379379
logger.warning("Failed to convert part to A2A format: %s", part)
380380

381-
return message_parts[::-1], context_id
381+
return message_parts, context_id
382382

383383
async def _handle_a2a_response(
384384
self, a2a_response: A2AClientEvent | A2AMessage, ctx: InvocationContext

tests/unittests/agents/test_remote_a2a_agent.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,69 @@ async def test_handle_a2a_response_success_with_message(self):
695695
assert result.custom_metadata is not None
696696
assert A2A_METADATA_PREFIX + "context_id" in result.custom_metadata
697697

698+
699+
def test_construct_message_parts_from_session_preserves_order(self):
700+
"""Test that message parts are in correct order with multi-part messages.
701+
702+
This test verifies the fix for the bug where _present_other_agent_message
703+
creates multi-part messages with "For context:" prefix, and ensures the
704+
parts are in the correct chronological order (not reversed).
705+
"""
706+
# Create mock events with multiple parts
707+
# Event 1: User message
708+
user_part = Mock()
709+
user_part.text = "User question"
710+
user_content = Mock()
711+
user_content.parts = [user_part]
712+
user_event = Mock()
713+
user_event.content = user_content
714+
user_event.author = "user"
715+
716+
# Event 2: Other agent message (will be transformed by
717+
# _present_other_agent_message)
718+
other_agent_part1 = Mock()
719+
other_agent_part1.text = "For context:"
720+
other_agent_part2 = Mock()
721+
other_agent_part2.text = "[other_agent] said: Response text"
722+
other_agent_content = Mock()
723+
other_agent_content.parts = [other_agent_part1, other_agent_part2]
724+
other_agent_event = Mock()
725+
other_agent_event.content = other_agent_content
726+
other_agent_event.author = "other_agent"
727+
728+
self.mock_session.events = [user_event, other_agent_event]
729+
730+
with patch(
731+
"google.adk.agents.remote_a2a_agent._present_other_agent_message"
732+
) as mock_present:
733+
# Mock _present_other_agent_message to return the transformed event
734+
mock_present.return_value = other_agent_event
735+
736+
# Mock the converter to track the order of parts
737+
converted_parts = []
738+
739+
def mock_converter(part):
740+
mock_a2a_part = Mock()
741+
mock_a2a_part.original_text = part.text
742+
converted_parts.append(mock_a2a_part)
743+
return mock_a2a_part
744+
745+
self.mock_genai_part_converter.side_effect = mock_converter
746+
747+
result = self.agent._construct_message_parts_from_session(self.mock_context)
748+
749+
# Verify the parts are in correct order
750+
assert len(result) == 2 # Returns tuple of (parts, context_id)
751+
assert len(result[0]) == 3 # 1 user part + 2 other agent parts
752+
assert result[1] is None # context_id
753+
754+
# Verify order: user part, then "For context:", then agent message
755+
assert converted_parts[0].original_text == "User question"
756+
assert converted_parts[1].original_text == "For context:"
757+
assert (
758+
converted_parts[2].original_text == "[other_agent] said: Response text"
759+
)
760+
698761
@pytest.mark.asyncio
699762
async def test_handle_a2a_response_with_task_completed_and_no_update(self):
700763
"""Test successful A2A response handling with non-streeaming task and no update."""

0 commit comments

Comments
 (0)