@@ -300,6 +300,122 @@ async def mock_run_background(input, adk_agent, user_id, app_name, event_queue):
300
300
assert agent_instruction == expected_instruction
301
301
assert received_context is test_context
302
302
303
+ @pytest .mark .asyncio
304
+ async def test_system_message_appended_to_instruction_provider_with_none (self ):
305
+ """Test that SystemMessage as first message gets appended to agent instructions
306
+ when they are set via instruction provider."""
307
+ # Create an agent with initial instructions, but return None
308
+ async def instruction_provider (context ) -> str :
309
+ return None
310
+
311
+ mock_agent = Agent (
312
+ name = "test_agent" ,
313
+ instruction = instruction_provider
314
+ )
315
+
316
+ adk_agent = ADKAgent (adk_agent = mock_agent , app_name = "test_app" , user_id = "test_user" )
317
+
318
+ # Create input with SystemMessage as first message
319
+ system_input = RunAgentInput (
320
+ thread_id = "test_thread" ,
321
+ run_id = "test_run" ,
322
+ messages = [
323
+ SystemMessage (id = "sys_1" , role = "system" , content = "Be very concise in responses." ),
324
+ UserMessage (id = "msg_1" , role = "user" , content = "Hello" )
325
+ ],
326
+ context = [],
327
+ state = {},
328
+ tools = [],
329
+ forwarded_props = {}
330
+ )
331
+
332
+ # Mock the background execution to capture the modified agent
333
+ captured_agent = None
334
+ original_run_background = adk_agent ._run_adk_in_background
335
+
336
+ async def mock_run_background (input , adk_agent , user_id , app_name , event_queue ):
337
+ nonlocal captured_agent
338
+ captured_agent = adk_agent
339
+ # Just put a completion event in the queue and return
340
+ await event_queue .put (None )
341
+
342
+ with patch .object (adk_agent , '_run_adk_in_background' , side_effect = mock_run_background ):
343
+ # Start execution to trigger agent modification
344
+ execution = await adk_agent ._start_background_execution (system_input )
345
+
346
+ # Wait briefly for the background task to start
347
+ await asyncio .sleep (0.01 )
348
+
349
+ # Verify the agent's instruction was wrapped correctly
350
+ assert captured_agent is not None
351
+ assert callable (captured_agent .instruction ) is True
352
+
353
+ # No empty new lines should be added before the instructions
354
+ expected_instruction = "Be very concise in responses."
355
+ agent_instruction = await captured_agent .instruction ({})
356
+ assert agent_instruction == expected_instruction
357
+
358
+ @pytest .mark .asyncio
359
+ async def test_system_message_appended_to_sync_instruction_provider (self ):
360
+ """Test that SystemMessage as first message gets appended to agent instructions
361
+ when they are set via sync instruction provider."""
362
+ # Create an agent with initial instructions
363
+ received_context = None
364
+
365
+ def instruction_provider (context ) -> str :
366
+ nonlocal received_context
367
+ received_context = context
368
+ return "You are a helpful assistant."
369
+
370
+ mock_agent = Agent (
371
+ name = "test_agent" ,
372
+ instruction = instruction_provider
373
+ )
374
+
375
+ adk_agent = ADKAgent (adk_agent = mock_agent , app_name = "test_app" , user_id = "test_user" )
376
+
377
+ # Create input with SystemMessage as first message
378
+ system_input = RunAgentInput (
379
+ thread_id = "test_thread" ,
380
+ run_id = "test_run" ,
381
+ messages = [
382
+ SystemMessage (id = "sys_1" , role = "system" , content = "Be very concise in responses." ),
383
+ UserMessage (id = "msg_1" , role = "user" , content = "Hello" )
384
+ ],
385
+ context = [],
386
+ state = {},
387
+ tools = [],
388
+ forwarded_props = {}
389
+ )
390
+
391
+ # Mock the background execution to capture the modified agent
392
+ captured_agent = None
393
+ original_run_background = adk_agent ._run_adk_in_background
394
+
395
+ async def mock_run_background (input , adk_agent , user_id , app_name , event_queue ):
396
+ nonlocal captured_agent
397
+ captured_agent = adk_agent
398
+ # Just put a completion event in the queue and return
399
+ await event_queue .put (None )
400
+
401
+ with patch .object (adk_agent , '_run_adk_in_background' , side_effect = mock_run_background ):
402
+ # Start execution to trigger agent modification
403
+ execution = await adk_agent ._start_background_execution (system_input )
404
+
405
+ # Wait briefly for the background task to start
406
+ await asyncio .sleep (0.01 )
407
+
408
+ # Verify agent was captured
409
+ assert captured_agent is not None
410
+ assert callable (captured_agent .instruction )
411
+
412
+ # Test that the context object received in instruction provider is the same
413
+ test_context = {"test" : "value" }
414
+ expected_instruction = "You are a helpful assistant.\n \n Be very concise in responses."
415
+ agent_instruction = captured_agent .instruction (test_context ) # Note: no await for sync function
416
+ assert agent_instruction == expected_instruction
417
+ assert received_context is test_context
418
+
303
419
@pytest .mark .asyncio
304
420
async def test_system_message_not_first_ignored (self ):
305
421
"""Test that SystemMessage not as first message is ignored."""
0 commit comments