@@ -361,14 +361,21 @@ def tool_names(self) -> list[str]:
361
361
all_tools = self .tool_registry .get_all_tools_config ()
362
362
return list (all_tools .keys ())
363
363
364
- def __call__ (self , prompt : Union [ str , list [ContentBlock ]] , ** kwargs : Any ) -> AgentResult :
364
+ def __call__ (self , prompt : str | list [ContentBlock ] | Messages | None = None , ** kwargs : Any ) -> AgentResult :
365
365
"""Process a natural language prompt through the agent's event loop.
366
366
367
- This method implements the conversational interface (e.g., `agent("hello!")`). It adds the user's prompt to
368
- the conversation history, processes it through the model, executes any tool calls, and returns the final result.
367
+ This method implements the conversational interface with multiple input patterns:
368
+ - String input: `agent("hello!")`
369
+ - ContentBlock list: `agent([{"text": "hello"}, {"image": {...}}])`
370
+ - Message list: `agent([{"role": "user", "content": [{"text": "hello"}]}])`
371
+ - No input: `agent()` - uses existing conversation history
369
372
370
373
Args:
371
- prompt: User input as text or list of ContentBlock objects for multi-modal content.
374
+ prompt: User input in various formats:
375
+ - str: Simple text input
376
+ - list[ContentBlock]: Multi-modal content blocks
377
+ - list[Message]: Complete messages with roles
378
+ - None: Use existing conversation history
372
379
**kwargs: Additional parameters to pass through the event loop.
373
380
374
381
Returns:
@@ -387,14 +394,23 @@ def execute() -> AgentResult:
387
394
future = executor .submit (execute )
388
395
return future .result ()
389
396
390
- async def invoke_async (self , prompt : Union [str , list [ContentBlock ]], ** kwargs : Any ) -> AgentResult :
397
+ async def invoke_async (
398
+ self , prompt : str | list [ContentBlock ] | Messages | None = None , ** kwargs : Any
399
+ ) -> AgentResult :
391
400
"""Process a natural language prompt through the agent's event loop.
392
401
393
- This method implements the conversational interface (e.g., `agent("hello!")`). It adds the user's prompt to
394
- the conversation history, processes it through the model, executes any tool calls, and returns the final result.
402
+ This method implements the conversational interface with multiple input patterns:
403
+ - String input: Simple text input
404
+ - ContentBlock list: Multi-modal content blocks
405
+ - Message list: Complete messages with roles
406
+ - No input: Use existing conversation history
395
407
396
408
Args:
397
- prompt: User input as text or list of ContentBlock objects for multi-modal content.
409
+ prompt: User input in various formats:
410
+ - str: Simple text input
411
+ - list[ContentBlock]: Multi-modal content blocks
412
+ - list[Message]: Complete messages with roles
413
+ - None: Use existing conversation history
398
414
**kwargs: Additional parameters to pass through the event loop.
399
415
400
416
Returns:
@@ -411,7 +427,7 @@ async def invoke_async(self, prompt: Union[str, list[ContentBlock]], **kwargs: A
411
427
412
428
return cast (AgentResult , event ["result" ])
413
429
414
- def structured_output (self , output_model : Type [T ], prompt : Optional [ Union [ str , list [ContentBlock ]]] = None ) -> T :
430
+ def structured_output (self , output_model : Type [T ], prompt : str | list [ContentBlock ] | Messages | None = None ) -> T :
415
431
"""This method allows you to get structured output from the agent.
416
432
417
433
If you pass in a prompt, it will be used temporarily without adding it to the conversation history.
@@ -423,7 +439,11 @@ def structured_output(self, output_model: Type[T], prompt: Optional[Union[str, l
423
439
Args:
424
440
output_model: The output model (a JSON schema written as a Pydantic BaseModel)
425
441
that the agent will use when responding.
426
- prompt: The prompt to use for the agent (will not be added to conversation history).
442
+ prompt: The prompt to use for the agent in various formats:
443
+ - str: Simple text input
444
+ - list[ContentBlock]: Multi-modal content blocks
445
+ - list[Message]: Complete messages with roles
446
+ - None: Use existing conversation history
427
447
428
448
Raises:
429
449
ValueError: If no conversation history or prompt is provided.
@@ -437,7 +457,7 @@ def execute() -> T:
437
457
return future .result ()
438
458
439
459
async def structured_output_async (
440
- self , output_model : Type [T ], prompt : Optional [ Union [ str , list [ContentBlock ]]] = None
460
+ self , output_model : Type [T ], prompt : str | list [ContentBlock ] | Messages | None = None
441
461
) -> T :
442
462
"""This method allows you to get structured output from the agent.
443
463
@@ -462,12 +482,8 @@ async def structured_output_async(
462
482
try :
463
483
if not self .messages and not prompt :
464
484
raise ValueError ("No conversation history or prompt provided" )
465
- # Create temporary messages array if prompt is provided
466
- if prompt :
467
- content : list [ContentBlock ] = [{"text" : prompt }] if isinstance (prompt , str ) else prompt
468
- temp_messages = self .messages + [{"role" : "user" , "content" : content }]
469
- else :
470
- temp_messages = self .messages
485
+
486
+ temp_messages : Messages = self .messages + self ._convert_prompt_to_messages (prompt )
471
487
472
488
structured_output_span .set_attributes (
473
489
{
@@ -499,16 +515,25 @@ async def structured_output_async(
499
515
finally :
500
516
self .hooks .invoke_callbacks (AfterInvocationEvent (agent = self ))
501
517
502
- async def stream_async (self , prompt : Union [str , list [ContentBlock ]], ** kwargs : Any ) -> AsyncIterator [Any ]:
518
+ async def stream_async (
519
+ self ,
520
+ prompt : str | list [ContentBlock ] | Messages | None = None ,
521
+ ** kwargs : Any ,
522
+ ) -> AsyncIterator [Any ]:
503
523
"""Process a natural language prompt and yield events as an async iterator.
504
524
505
- This method provides an asynchronous interface for streaming agent events, allowing
506
- consumers to process stream events programmatically through an async iterator pattern
507
- rather than callback functions. This is particularly useful for web servers and other
508
- async environments.
525
+ This method provides an asynchronous interface for streaming agent events with multiple input patterns:
526
+ - String input: Simple text input
527
+ - ContentBlock list: Multi-modal content blocks
528
+ - Message list: Complete messages with roles
529
+ - No input: Use existing conversation history
509
530
510
531
Args:
511
- prompt: User input as text or list of ContentBlock objects for multi-modal content.
532
+ prompt: User input in various formats:
533
+ - str: Simple text input
534
+ - list[ContentBlock]: Multi-modal content blocks
535
+ - list[Message]: Complete messages with roles
536
+ - None: Use existing conversation history
512
537
**kwargs: Additional parameters to pass to the event loop.
513
538
514
539
Yields:
@@ -532,13 +557,15 @@ async def stream_async(self, prompt: Union[str, list[ContentBlock]], **kwargs: A
532
557
"""
533
558
callback_handler = kwargs .get ("callback_handler" , self .callback_handler )
534
559
535
- content : list [ContentBlock ] = [{"text" : prompt }] if isinstance (prompt , str ) else prompt
536
- message : Message = {"role" : "user" , "content" : content }
560
+ # Process input and get message to add (if any)
561
+ messages = self ._convert_prompt_to_messages (prompt )
562
+
563
+ self .trace_span = self ._start_agent_trace_span (messages )
537
564
538
- self .trace_span = self ._start_agent_trace_span (message )
539
565
with trace_api .use_span (self .trace_span ):
540
566
try :
541
- events = self ._run_loop (message , invocation_state = kwargs )
567
+ events = self ._run_loop (messages , invocation_state = kwargs )
568
+
542
569
async for event in events :
543
570
if "callback" in event :
544
571
callback_handler (** event ["callback" ])
@@ -555,12 +582,12 @@ async def stream_async(self, prompt: Union[str, list[ContentBlock]], **kwargs: A
555
582
raise
556
583
557
584
async def _run_loop (
558
- self , message : Message , invocation_state : dict [str , Any ]
585
+ self , messages : Messages , invocation_state : dict [str , Any ]
559
586
) -> AsyncGenerator [dict [str , Any ], None ]:
560
587
"""Execute the agent's event loop with the given message and parameters.
561
588
562
589
Args:
563
- message : The user message to add to the conversation.
590
+ messages : The input messages to add to the conversation.
564
591
invocation_state: Additional parameters to pass to the event loop.
565
592
566
593
Yields:
@@ -571,7 +598,8 @@ async def _run_loop(
571
598
try :
572
599
yield {"callback" : {"init_event_loop" : True , ** invocation_state }}
573
600
574
- self ._append_message (message )
601
+ for message in messages :
602
+ self ._append_message (message )
575
603
576
604
# Execute the event loop cycle with retry logic for context limits
577
605
events = self ._execute_event_loop_cycle (invocation_state )
@@ -629,6 +657,34 @@ async def _execute_event_loop_cycle(self, invocation_state: dict[str, Any]) -> A
629
657
async for event in events :
630
658
yield event
631
659
660
+ def _convert_prompt_to_messages (self , prompt : str | list [ContentBlock ] | Messages | None ) -> Messages :
661
+ messages : Messages | None = None
662
+ if prompt is not None :
663
+ if isinstance (prompt , str ):
664
+ # String input - convert to user message
665
+ messages = [{"role" : "user" , "content" : [{"text" : prompt }]}]
666
+ elif isinstance (prompt , list ):
667
+ if len (prompt ) == 0 :
668
+ # Empty list
669
+ messages = []
670
+ # Check if all item in input list are dictionaries
671
+ elif all (isinstance (item , dict ) for item in prompt ):
672
+ # Check if all items are messages
673
+ if all (all (key in item for key in Message .__annotations__ .keys ()) for item in prompt ):
674
+ # Messages input - add all messages to conversation
675
+ messages = cast (Messages , prompt )
676
+
677
+ # Check if all items are content blocks
678
+ elif all (any (key in ContentBlock .__annotations__ .keys () for key in item ) for item in prompt ):
679
+ # Treat as List[ContentBlock] input - convert to user message
680
+ # This allows invalid structures to be passed through to the model
681
+ messages = [{"role" : "user" , "content" : cast (list [ContentBlock ], prompt )}]
682
+ else :
683
+ messages = []
684
+ if messages is None :
685
+ raise ValueError ("Input prompt must be of type: `str | list[Contentblock] | Messages | None`." )
686
+ return messages
687
+
632
688
def _record_tool_execution (
633
689
self ,
634
690
tool : ToolUse ,
@@ -694,15 +750,15 @@ def _record_tool_execution(
694
750
self ._append_message (tool_result_msg )
695
751
self ._append_message (assistant_msg )
696
752
697
- def _start_agent_trace_span (self , message : Message ) -> trace_api .Span :
753
+ def _start_agent_trace_span (self , messages : Messages ) -> trace_api .Span :
698
754
"""Starts a trace span for the agent.
699
755
700
756
Args:
701
- message : The user message .
757
+ messages : The input messages .
702
758
"""
703
759
model_id = self .model .config .get ("model_id" ) if hasattr (self .model , "config" ) else None
704
760
return self .tracer .start_agent_span (
705
- message = message ,
761
+ messages = messages ,
706
762
agent_name = self .name ,
707
763
model_id = model_id ,
708
764
tools = self .tool_names ,
0 commit comments