diff --git a/CHANGELOG.md b/CHANGELOG.md index 93dc505adc..ff567fec2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Bug Fixes + +* fix(agents): tolerate empty A2A message parts in RemoteA2aAgent; add unit test ([fix/a2a-handle-empty-parts]) + ## [1.20.0](https://github.com/google/adk-python/compare/v1.19.0...v1.20.0) (2025-12-01) @@ -40,7 +46,6 @@ * Add Code Wiki badge to README ([caf23ac](https://github.com/google/adk-python/commit/caf23ac49fe08bc7f625c61eed4635c26852c3ba)) - ## [1.19.0](https://github.com/google/adk-python/compare/v1.18.0...v1.19.0) (2025-11-19) ### Features diff --git a/src/google/adk/agents/remote_a2a_agent.py b/src/google/adk/agents/remote_a2a_agent.py index 953812a296..e9ae935663 100644 --- a/src/google/adk/agents/remote_a2a_agent.py +++ b/src/google/adk/agents/remote_a2a_agent.py @@ -412,8 +412,14 @@ async def _handle_a2a_response( ) # for streaming task, we update the event with the task status. # We update the event as Thought updates. - if task and task.status and task.status.state == TaskState.submitted: - event.content.parts[0].thought = True + if ( + task + and task.status + and task.status.state == TaskState.submitted + and event.content.parts + ): + for part in event.content.parts: + part.thought = True elif ( isinstance(update, A2ATaskStatusUpdateEvent) and update.status diff --git a/tests/unittests/agents/test_remote_a2a_agent.py b/tests/unittests/agents/test_remote_a2a_agent.py index b3894d73d0..c4fe01cdb5 100644 --- a/tests/unittests/agents/test_remote_a2a_agent.py +++ b/tests/unittests/agents/test_remote_a2a_agent.py @@ -1010,6 +1010,45 @@ async def test_handle_a2a_response_with_partial_artifact_update(self): assert result is None + @pytest.mark.asyncio + async def test_handle_a2a_response_with_task_submitted_and_empty_parts(self): + """Test handling of a task submitted response with empty parts.""" + mock_a2a_task = Mock(spec=A2ATask) + mock_a2a_task.id = "task-123" + mock_a2a_task.context_id = "context-123" + mock_a2a_task.status = Mock(spec=A2ATaskStatus) + mock_a2a_task.status.state = TaskState.submitted + + # Create a proper Event mock that can handle custom_metadata + # Content with empty parts + mock_event = Event( + author=self.agent.name, + invocation_id=self.mock_context.invocation_id, + branch=self.mock_context.branch, + content=genai_types.Content(role="model", parts=[]), + ) + + with patch( + "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event" + ) as mock_convert: + mock_convert.return_value = mock_event + + result = await self.agent._handle_a2a_response( + (mock_a2a_task, None), self.mock_context + ) + + assert result == mock_event + mock_convert.assert_called_once_with( + mock_a2a_task, + self.agent.name, + self.mock_context, + self.mock_a2a_part_converter, + ) + # Check that metadata was added + assert result.custom_metadata is not None + assert A2A_METADATA_PREFIX + "task_id" in result.custom_metadata + assert A2A_METADATA_PREFIX + "context_id" in result.custom_metadata + class TestRemoteA2aAgentMessageHandlingFromFactory: """Test message handling functionality."""