From ec8a49ec604ca9046224decfb456f986a6dd01a2 Mon Sep 17 00:00:00 2001 From: Enjoy Kumawat Date: Wed, 8 Apr 2026 00:58:11 +0530 Subject: [PATCH 1/2] fix: map A2A Message.role to correct GenAI content role convert_a2a_message_to_event() hard-coded role="model" for all resulting ADK events regardless of the original A2A Message.role. User messages (Role.user) were incorrectly restored as model events. Add _a2a_role_to_content_role() helper that maps A2A Role.user to "user" and Role.agent to "model", and pass the mapped role through _create_event() via a new content_role parameter. Fixes #5186 --- src/google/adk/a2a/converters/to_adk_event.py | 15 ++++++- tests/unittests/a2a/converters/test_to_adk.py | 43 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/google/adk/a2a/converters/to_adk_event.py b/src/google/adk/a2a/converters/to_adk_event.py index 26ae95e1b4..099a48fe45 100644 --- a/src/google/adk/a2a/converters/to_adk_event.py +++ b/src/google/adk/a2a/converters/to_adk_event.py @@ -24,6 +24,7 @@ from a2a.types import Message from a2a.types import Part as A2APart +from a2a.types import Role as A2ARole from a2a.types import Task from a2a.types import TaskArtifactUpdateEvent from a2a.types import TaskState @@ -177,6 +178,7 @@ def _create_event( actions: Optional[EventActions] = None, long_running_function_ids: Optional[set[str]] = None, partial: bool = False, + content_role: str = "model", ) -> Optional[Event]: """Creates an ADK event from parts and metadata.""" event_actions = actions or EventActions() @@ -199,7 +201,7 @@ def _create_event( ), content=( genai_types.Content( - role="model", + role=content_role, parts=output_parts, ) if output_parts @@ -211,6 +213,13 @@ def _create_event( return event +def _a2a_role_to_content_role(role: Optional[A2ARole]) -> str: + """Maps an A2A Role to the corresponding GenAI content role.""" + if role == A2ARole.user: + return "user" + return "model" + + def _parse_adk_metadata_value(value: Any) -> Any: """Parses ADK metadata values serialized through A2A.""" if not isinstance(value, str): @@ -375,11 +384,15 @@ def convert_a2a_message_to_event( output_parts, _ = _convert_a2a_parts_to_adk_parts( a2a_message.parts, part_converter ) + content_role = _a2a_role_to_content_role( + getattr(a2a_message, "role", None) + ) return _create_event( output_parts, invocation_context, author, _extract_event_actions(a2a_message.metadata), + content_role=content_role, ) except Exception as e: diff --git a/tests/unittests/a2a/converters/test_to_adk.py b/tests/unittests/a2a/converters/test_to_adk.py index 12eaf2a75a..cd7750ec70 100644 --- a/tests/unittests/a2a/converters/test_to_adk.py +++ b/tests/unittests/a2a/converters/test_to_adk.py @@ -19,6 +19,7 @@ from a2a.types import Artifact from a2a.types import Message from a2a.types import Part as A2APart +from a2a.types import Role as A2ARole from a2a.types import Task from a2a.types import TaskArtifactUpdateEvent from a2a.types import TaskState @@ -418,3 +419,45 @@ def test_convert_a2a_artifact_update_to_event_none(self): """Test convert_a2a_artifact_update_to_event with None.""" with pytest.raises(ValueError, match="A2A artifact update cannot be None"): convert_a2a_artifact_update_to_event(None) + + def test_convert_a2a_message_to_event_user_role(self): + """Test that A2A user role maps to GenAI content role 'user'.""" + a2a_part = Mock(spec=A2APart) + a2a_part.root = Mock(spec=TextPart) + a2a_part.root.metadata = {} + message = Message( + message_id="msg-1", role=A2ARole.user, parts=[a2a_part] + ) + + mock_genai_part = genai_types.Part.from_text(text="hello from user") + mock_part_converter = Mock(return_value=[mock_genai_part]) + + event = convert_a2a_message_to_event( + message, + author="user", + invocation_context=self.mock_context, + part_converter=mock_part_converter, + ) + + assert event.content.role == "user" + + def test_convert_a2a_message_to_event_agent_role(self): + """Test that A2A agent role maps to GenAI content role 'model'.""" + a2a_part = Mock(spec=A2APart) + a2a_part.root = Mock(spec=TextPart) + a2a_part.root.metadata = {} + message = Message( + message_id="msg-1", role=A2ARole.agent, parts=[a2a_part] + ) + + mock_genai_part = genai_types.Part.from_text(text="hello from agent") + mock_part_converter = Mock(return_value=[mock_genai_part]) + + event = convert_a2a_message_to_event( + message, + author="test-agent", + invocation_context=self.mock_context, + part_converter=mock_part_converter, + ) + + assert event.content.role == "model" From bec671724bd6d9119c455645c6474baad86836d2 Mon Sep 17 00:00:00 2001 From: Enjoy Kumawat Date: Thu, 9 Apr 2026 13:06:05 +0530 Subject: [PATCH 2/2] style: apply pyink formatting and add return type annotations --- src/google/adk/a2a/converters/to_adk_event.py | 4 +--- tests/unittests/a2a/converters/test_to_adk.py | 12 ++++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/google/adk/a2a/converters/to_adk_event.py b/src/google/adk/a2a/converters/to_adk_event.py index 099a48fe45..78a7969eaa 100644 --- a/src/google/adk/a2a/converters/to_adk_event.py +++ b/src/google/adk/a2a/converters/to_adk_event.py @@ -384,9 +384,7 @@ def convert_a2a_message_to_event( output_parts, _ = _convert_a2a_parts_to_adk_parts( a2a_message.parts, part_converter ) - content_role = _a2a_role_to_content_role( - getattr(a2a_message, "role", None) - ) + content_role = _a2a_role_to_content_role(getattr(a2a_message, "role", None)) return _create_event( output_parts, invocation_context, diff --git a/tests/unittests/a2a/converters/test_to_adk.py b/tests/unittests/a2a/converters/test_to_adk.py index cd7750ec70..16f9fe84ec 100644 --- a/tests/unittests/a2a/converters/test_to_adk.py +++ b/tests/unittests/a2a/converters/test_to_adk.py @@ -420,14 +420,12 @@ def test_convert_a2a_artifact_update_to_event_none(self): with pytest.raises(ValueError, match="A2A artifact update cannot be None"): convert_a2a_artifact_update_to_event(None) - def test_convert_a2a_message_to_event_user_role(self): + def test_convert_a2a_message_to_event_user_role(self) -> None: """Test that A2A user role maps to GenAI content role 'user'.""" a2a_part = Mock(spec=A2APart) a2a_part.root = Mock(spec=TextPart) a2a_part.root.metadata = {} - message = Message( - message_id="msg-1", role=A2ARole.user, parts=[a2a_part] - ) + message = Message(message_id="msg-1", role=A2ARole.user, parts=[a2a_part]) mock_genai_part = genai_types.Part.from_text(text="hello from user") mock_part_converter = Mock(return_value=[mock_genai_part]) @@ -441,14 +439,12 @@ def test_convert_a2a_message_to_event_user_role(self): assert event.content.role == "user" - def test_convert_a2a_message_to_event_agent_role(self): + def test_convert_a2a_message_to_event_agent_role(self) -> None: """Test that A2A agent role maps to GenAI content role 'model'.""" a2a_part = Mock(spec=A2APart) a2a_part.root = Mock(spec=TextPart) a2a_part.root.metadata = {} - message = Message( - message_id="msg-1", role=A2ARole.agent, parts=[a2a_part] - ) + message = Message(message_id="msg-1", role=A2ARole.agent, parts=[a2a_part]) mock_genai_part = genai_types.Part.from_text(text="hello from agent") mock_part_converter = Mock(return_value=[mock_genai_part])