@@ -239,6 +239,183 @@ async def mock_run_background(input, adk_agent, user_id, app_name, event_queue):
239
239
expected_instruction = "You are a helpful assistant.\n \n Be very concise in responses."
240
240
assert captured_agent .instruction == expected_instruction
241
241
242
+ @pytest .mark .asyncio
243
+ async def test_system_message_appended_to_instruction_provider (self ):
244
+ """Test that SystemMessage as first message gets appended to agent instructions
245
+ when they are set via instruction provider."""
246
+ # Create an agent with initial instructions
247
+ received_context = None
248
+
249
+ async def instruction_provider (context ) -> str :
250
+ nonlocal received_context
251
+ received_context = context
252
+ return "You are a helpful assistant."
253
+
254
+ mock_agent = Agent (
255
+ name = "test_agent" ,
256
+ instruction = instruction_provider
257
+ )
258
+
259
+ adk_agent = ADKAgent (adk_agent = mock_agent , app_name = "test_app" , user_id = "test_user" )
260
+
261
+ # Create input with SystemMessage as first message
262
+ system_input = RunAgentInput (
263
+ thread_id = "test_thread" ,
264
+ run_id = "test_run" ,
265
+ messages = [
266
+ SystemMessage (id = "sys_1" , role = "system" , content = "Be very concise in responses." ),
267
+ UserMessage (id = "msg_1" , role = "user" , content = "Hello" )
268
+ ],
269
+ context = [],
270
+ state = {},
271
+ tools = [],
272
+ forwarded_props = {}
273
+ )
274
+
275
+ # Mock the background execution to capture the modified agent
276
+ captured_agent = None
277
+ original_run_background = adk_agent ._run_adk_in_background
278
+
279
+ async def mock_run_background (input , adk_agent , user_id , app_name , event_queue ):
280
+ nonlocal captured_agent
281
+ captured_agent = adk_agent
282
+ # Just put a completion event in the queue and return
283
+ await event_queue .put (None )
284
+
285
+ with patch .object (adk_agent , '_run_adk_in_background' , side_effect = mock_run_background ):
286
+ # Start execution to trigger agent modification
287
+ execution = await adk_agent ._start_background_execution (system_input )
288
+
289
+ # Wait briefly for the background task to start
290
+ await asyncio .sleep (0.01 )
291
+
292
+ # Verify the agent's instruction was wrapped correctly
293
+ assert captured_agent is not None
294
+ assert callable (captured_agent .instruction ) is True
295
+
296
+ # Test that the context object received in instruction provider is the same
297
+ test_context = {"test" : "value" }
298
+ expected_instruction = "You are a helpful assistant.\n \n Be very concise in responses."
299
+ agent_instruction = await captured_agent .instruction (test_context )
300
+ assert agent_instruction == expected_instruction
301
+ assert received_context is test_context
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
+
242
419
@pytest .mark .asyncio
243
420
async def test_system_message_not_first_ignored (self ):
244
421
"""Test that SystemMessage not as first message is ignored."""
0 commit comments