@@ -3421,6 +3421,7 @@ def setup_method(self):
34213421 self .mock_config .a2a_artifact_update_converter = Mock ()
34223422 self .mock_config .a2a_message_converter = Mock ()
34233423 self .mock_config .card_request_interceptors = None
3424+ self .mock_config .forward_session_id_as_context_id = False
34243425
34253426 self .agent = RemoteA2aAgent (
34263427 name = "test_agent" ,
@@ -3998,6 +3999,138 @@ async def test_run_async_impl_successful_request(self):
39983999 in mock_event .custom_metadata
39994000 )
40004001
4002+ async def _run_context_id_test (
4003+ self ,
4004+ mock_context_id : str | None ,
4005+ expected_context_id : str | None ,
4006+ * ,
4007+ forward_session_id : bool = False ,
4008+ ):
4009+ """Helper to test context_id handling in _run_async_impl.
4010+
4011+ Args:
4012+ mock_context_id: The context_id to return from
4013+ _construct_message_parts_from_session.
4014+ expected_context_id: The expected context_id in the A2AMessage.
4015+ forward_session_id: Value for forward_session_id_as_context_id config.
4016+ """
4017+ self .agent ._config .forward_session_id_as_context_id = forward_session_id
4018+ with patch .object (self .agent , "_ensure_resolved" ) as mock_ensure_resolved :
4019+ with patch .object (
4020+ self .agent , "_create_a2a_request_for_user_function_response"
4021+ ) as mock_create_func :
4022+ mock_create_func .return_value = None
4023+
4024+ with patch .object (
4025+ self .agent , "_construct_message_parts_from_session"
4026+ ) as mock_construct :
4027+ mock_a2a_part = _compat .make_text_part ("test" )
4028+ mock_construct .return_value = ([mock_a2a_part ], mock_context_id )
4029+
4030+ # Mock A2A client
4031+ mock_a2a_client = create_autospec (spec = A2AClient , instance = True )
4032+ mock_response = _make_stream_message (
4033+ A2AMessage (
4034+ message_id = "m1" ,
4035+ role = _compat .ROLE_USER ,
4036+ parts = [mock_a2a_part ],
4037+ )
4038+ )
4039+ mock_send_message = AsyncMock ()
4040+ mock_send_message .__aiter__ .return_value = [mock_response ]
4041+ mock_a2a_client .send_message .return_value = mock_send_message
4042+ self .agent ._a2a_client = mock_a2a_client
4043+ mock_ensure_resolved .return_value = mock_a2a_client
4044+
4045+ mock_event = Event (
4046+ author = self .agent .name ,
4047+ invocation_id = self .mock_context .invocation_id ,
4048+ branch = self .mock_context .branch ,
4049+ )
4050+
4051+ with patch .object (self .agent , "_handle_a2a_response" ) as mock_handle :
4052+ mock_handle .return_value = mock_event
4053+
4054+ with patch (
4055+ "google.adk.agents.remote_a2a_agent.build_a2a_request_log"
4056+ ) as mock_req_log :
4057+ with patch (
4058+ "google.adk.agents.remote_a2a_agent.build_a2a_response_log"
4059+ ) as mock_resp_log :
4060+ mock_req_log .return_value = "Mock request log"
4061+ mock_resp_log .return_value = "Mock response log"
4062+
4063+ with patch (
4064+ "google.adk.a2a._compat.a2a_to_dict" ,
4065+ return_value = {"k" : "v" },
4066+ ):
4067+ with patch (
4068+ "google.adk.agents.remote_a2a_agent.A2AMessage"
4069+ ) as mock_message_class :
4070+ mock_message = Mock (spec = A2AMessage )
4071+ mock_message_class .return_value = mock_message
4072+
4073+ # Execute
4074+ events = []
4075+ async for event in self .agent ._run_async_impl (
4076+ self .mock_context
4077+ ):
4078+ events .append (event )
4079+
4080+ # Verify A2AMessage was called with expected context_id
4081+ mock_message_class .assert_called_once ()
4082+ call_kwargs = mock_message_class .call_args [1 ]
4083+ assert call_kwargs ["context_id" ] == expected_context_id
4084+
4085+ @pytest .mark .asyncio
4086+ async def test_run_async_impl_does_not_forward_session_id_by_default (self ):
4087+ """Test that session ID is not used as context_id by default.
4088+
4089+ When forward_session_id_as_context_id is False (default) and
4090+ _construct_message_parts_from_session returns None for context_id,
4091+ the agent should not set context_id.
4092+ """
4093+ await self ._run_context_id_test (
4094+ mock_context_id = None ,
4095+ expected_context_id = None ,
4096+ forward_session_id = False ,
4097+ )
4098+
4099+ @pytest .mark .asyncio
4100+ async def test_run_async_impl_uses_session_id_when_opted_in (self ):
4101+ """Test that session ID is used as context_id when opted in.
4102+
4103+ When forward_session_id_as_context_id is True and
4104+ _construct_message_parts_from_session returns None for context_id,
4105+ the agent should use ctx.session.id to maintain session identity across
4106+ local and remote agents.
4107+ """
4108+ await self ._run_context_id_test (
4109+ mock_context_id = None ,
4110+ expected_context_id = self .mock_session .id ,
4111+ forward_session_id = True ,
4112+ )
4113+
4114+ @pytest .mark .asyncio
4115+ async def test_run_async_impl_preserves_existing_context_id (self ):
4116+ """Test that existing context_id is preserved when available.
4117+
4118+ When _construct_message_parts_from_session returns a context_id from
4119+ a previous remote agent response, that context_id should be used
4120+ for conversation continuity regardless of forward_session_id_as_context_id.
4121+ """
4122+ existing_context_id = "existing-context-456"
4123+ await self ._run_context_id_test (
4124+ mock_context_id = existing_context_id ,
4125+ expected_context_id = existing_context_id ,
4126+ forward_session_id = False ,
4127+ )
4128+ await self ._run_context_id_test (
4129+ mock_context_id = existing_context_id ,
4130+ expected_context_id = existing_context_id ,
4131+ forward_session_id = True ,
4132+ )
4133+
40014134 @pytest .mark .asyncio
40024135 async def test_run_async_impl_closes_stream_when_abandoned (self ):
40034136 """The A2A stream is closed when the caller stops consuming early."""
@@ -5147,6 +5280,11 @@ def test_deepcopy_config(self):
51475280 is not config .request_interceptors [0 ]
51485281 )
51495282
5283+ # Verify forward_session_id_as_context_id default and deepcopy
5284+ assert copied_config .forward_session_id_as_context_id is False
5285+ config .forward_session_id_as_context_id = True
5286+ assert copy .deepcopy (config ).forward_session_id_as_context_id is True
5287+
51505288
51515289class TestFindFinishTaskArgsFromHistory :
51525290 """Test _find_finish_task_args_from_history helper function."""
@@ -6374,6 +6512,7 @@ def _make_agent():
63746512def _make_ctx (events ):
63756513 ctx = create_autospec (InvocationContext , instance = True )
63766514 ctx .session = create_autospec (Session , instance = True )
6515+ ctx .session .id = "session-123"
63776516 ctx .session .events = events
63786517 ctx .invocation_id = "inv-1"
63796518 ctx .branch = None
0 commit comments