@@ -697,6 +697,7 @@ def test_convert_a2a_task_to_event_with_history_message(self):
697697
698698 # Create mock message and task
699699 mock_message = Mock (spec = Message )
700+ mock_message .role = Role .agent
700701 mock_task = Mock (spec = Task )
701702 mock_task .artifacts = None
702703 mock_task .status = None
@@ -799,6 +800,7 @@ def test_convert_a2a_message_to_event_success(self):
799800 mock_convert_part = Mock (return_value = mock_genai_part )
800801
801802 mock_message = Mock (spec = Message , parts = [mock_a2a_part ])
803+ mock_message .role = Role .agent
802804
803805 result = convert_a2a_message_to_event (
804806 mock_message ,
@@ -828,6 +830,7 @@ def test_convert_a2a_message_to_event_with_multiple_parts_returned(self):
828830 mock_convert_part = Mock (return_value = [mock_genai_part1 , mock_genai_part2 ])
829831
830832 mock_message = Mock (spec = Message , parts = [mock_a2a_part ])
833+ mock_message .role = Role .agent
831834
832835 # Act
833836 result = convert_a2a_message_to_event (
@@ -850,6 +853,7 @@ def test_convert_a2a_message_to_event_with_long_running_tools(self):
850853
851854 # Create mock parts and message
852855 mock_message = Mock (spec = Message , parts = [Mock ()])
856+ mock_message .role = Role .agent
853857
854858 # Mock the part conversion to return None to simulate long-running tool detection logic
855859 mock_convert_part = Mock (return_value = None )
@@ -876,6 +880,7 @@ def test_convert_a2a_message_to_event_empty_parts(self):
876880 from google .adk .a2a .converters .event_converter import convert_a2a_message_to_event
877881
878882 mock_message = Mock (spec = Message , parts = [])
883+ mock_message .role = Role .agent
879884
880885 result = convert_a2a_message_to_event (
881886 mock_message , "test-author" , self .mock_invocation_context
@@ -903,6 +908,7 @@ def test_convert_a2a_message_to_event_part_conversion_fails(self):
903908 mock_convert_part = Mock (return_value = None )
904909
905910 mock_message = Mock (spec = Message , parts = [mock_a2a_part ])
911+ mock_message .role = Role .agent
906912
907913 result = convert_a2a_message_to_event (
908914 mock_message ,
@@ -935,6 +941,7 @@ def test_convert_a2a_message_to_event_part_conversion_exception(self):
935941 )
936942
937943 mock_message = Mock (spec = Message , parts = [mock_a2a_part1 , mock_a2a_part2 ])
944+ mock_message .role = Role .agent
938945
939946 result = convert_a2a_message_to_event (
940947 mock_message ,
@@ -956,6 +963,7 @@ def test_convert_a2a_message_to_event_missing_tool_id(self):
956963
957964 # Create mock parts and message
958965 mock_message = Mock (spec = Message , parts = [Mock ()])
966+ mock_message .role = Role .agent
959967
960968 # Mock the part conversion to return None
961969 mock_convert_part = Mock (return_value = None )
@@ -980,6 +988,7 @@ def test_convert_a2a_message_to_event_default_author(self, mock_uuid):
980988 from google .adk .a2a .converters .event_converter import convert_a2a_message_to_event
981989
982990 mock_message = Mock (spec = Message , parts = [])
991+ mock_message .role = Role .agent
983992
984993 # Mock UUID generation
985994 mock_uuid .return_value = "generated-uuid"
@@ -990,3 +999,132 @@ def test_convert_a2a_message_to_event_default_author(self, mock_uuid):
990999 assert result .author == "a2a agent"
9911000 assert result .branch is None
9921001 assert result .invocation_id == "generated-uuid"
1002+
1003+
1004+ class TestRoleMappingRegression :
1005+ """Regression tests for issue #5186: role mapping in A2A→ADK conversion."""
1006+
1007+ def setup_method (self ):
1008+ """Set up test fixtures."""
1009+ self .mock_invocation_context = Mock (spec = InvocationContext )
1010+ self .mock_invocation_context .invocation_id = "test-invocation-id"
1011+ self .mock_invocation_context .branch = "test-branch"
1012+
1013+ def test_user_role_message_maps_to_user_content_role (self ):
1014+ """A2A Role.user must produce content.role='user', not 'model'."""
1015+ from a2a .types import Part
1016+ from a2a .types import TextPart
1017+ from google .adk .a2a .converters .event_converter import convert_a2a_message_to_event
1018+
1019+ message = Message (
1020+ message_id = "msg-1" ,
1021+ role = Role .user ,
1022+ parts = [Part (root = TextPart (text = "user says hi" ))],
1023+ )
1024+
1025+ event = convert_a2a_message_to_event (
1026+ message , "test-author" , self .mock_invocation_context
1027+ )
1028+
1029+ assert event .content .role == "user"
1030+
1031+ def test_agent_role_message_maps_to_model_content_role (self ):
1032+ """A2A Role.agent must produce content.role='model'."""
1033+ from a2a .types import Part
1034+ from a2a .types import TextPart
1035+ from google .adk .a2a .converters .event_converter import convert_a2a_message_to_event
1036+
1037+ message = Message (
1038+ message_id = "msg-1" ,
1039+ role = Role .agent ,
1040+ parts = [Part (root = TextPart (text = "agent reply" ))],
1041+ )
1042+
1043+ event = convert_a2a_message_to_event (
1044+ message , "test-author" , self .mock_invocation_context
1045+ )
1046+
1047+ assert event .content .role == "model"
1048+
1049+ def test_empty_parts_user_message_preserves_user_role (self ):
1050+ """Even with empty parts, Role.user must map to content.role='user'."""
1051+ from google .adk .a2a .converters .event_converter import convert_a2a_message_to_event
1052+
1053+ message = Message (message_id = "msg-1" , role = Role .user , parts = [])
1054+
1055+ event = convert_a2a_message_to_event (
1056+ message , "test-author" , self .mock_invocation_context
1057+ )
1058+
1059+ assert event .content .role == "user"
1060+
1061+ def test_task_history_fallback_skips_trailing_user_message (self ):
1062+ """History fallback must not return a user-role trailing message."""
1063+ from a2a .types import Part
1064+ from a2a .types import TaskStatus
1065+ from a2a .types import TextPart
1066+
1067+ agent_msg = Message (
1068+ message_id = "m1" ,
1069+ role = Role .agent ,
1070+ parts = [Part (root = TextPart (text = "agent reply" ))],
1071+ )
1072+ user_msg = Message (
1073+ message_id = "m2" ,
1074+ role = Role .user ,
1075+ parts = [Part (root = TextPart (text = "follow-up question" ))],
1076+ )
1077+
1078+ task = Task (
1079+ id = "task-1" ,
1080+ status = TaskStatus (
1081+ state = TaskState .submitted , timestamp = "2024-01-01T00:00:00Z"
1082+ ),
1083+ context_id = "ctx-1" ,
1084+ history = [agent_msg , user_msg ],
1085+ )
1086+
1087+ with patch (
1088+ "google.adk.a2a.converters.event_converter.convert_a2a_message_to_event"
1089+ ) as mock_convert :
1090+ mock_event = Mock (spec = Event )
1091+ mock_convert .return_value = mock_event
1092+
1093+ convert_a2a_task_to_event (
1094+ task , "test-author" , self .mock_invocation_context
1095+ )
1096+
1097+ # Must be called with the agent message, not the trailing user message
1098+ mock_convert .assert_called_once ()
1099+ called_message = mock_convert .call_args [0 ][0 ]
1100+ assert called_message .role == Role .agent
1101+ assert called_message .message_id == "m1"
1102+
1103+ def test_task_history_fallback_only_user_messages_creates_minimal_event (self ):
1104+ """History with only user messages must produce a minimal event."""
1105+ from a2a .types import Part
1106+ from a2a .types import TaskStatus
1107+ from a2a .types import TextPart
1108+
1109+ user_msg = Message (
1110+ message_id = "m1" ,
1111+ role = Role .user ,
1112+ parts = [Part (root = TextPart (text = "question" ))],
1113+ )
1114+
1115+ task = Task (
1116+ id = "task-1" ,
1117+ status = TaskStatus (
1118+ state = TaskState .submitted , timestamp = "2024-01-01T00:00:00Z"
1119+ ),
1120+ context_id = "ctx-1" ,
1121+ history = [user_msg ],
1122+ )
1123+
1124+ result = convert_a2a_task_to_event (
1125+ task , "test-author" , self .mock_invocation_context
1126+ )
1127+
1128+ # No agent message to convert → minimal event (no content)
1129+ assert result .author == "test-author"
1130+ assert result .content is None
0 commit comments