2828)
2929from azure .ai .agentserver .responses .hosting import ResponsesAgentServerHost
3030from azure .ai .agentserver .responses .models import (
31+ ApplyPatchToolCallItemParam ,
32+ ApplyPatchToolCallOutputItemParam ,
33+ ComputerCallOutputItemParam ,
3134 ComputerScreenshotContent ,
3235 CreateResponse ,
3336 FunctionCallOutputItemParam ,
3437 FunctionShellAction ,
38+ FunctionShellCallItemParam ,
3539 FunctionShellCallOutputContent ,
3640 FunctionShellCallOutputExitOutcome ,
41+ FunctionShellCallOutputItemParam ,
42+ Item ,
43+ ItemCodeInterpreterToolCall ,
44+ ItemComputerToolCall ,
45+ ItemCustomToolCall ,
46+ ItemCustomToolCallOutput ,
47+ ItemFileSearchToolCall ,
48+ ItemFunctionToolCall ,
49+ ItemImageGenToolCall ,
50+ ItemLocalShellToolCall ,
51+ ItemLocalShellToolCallOutput ,
52+ ItemMcpApprovalRequest ,
53+ ItemMcpToolCall ,
54+ ItemMessage ,
55+ ItemOutputMessage ,
56+ ItemReasoningItem ,
57+ ItemWebSearchToolCall ,
3758 LocalEnvironmentResource ,
59+ MCPApprovalResponse ,
3860 MessageContent ,
3961 MessageContentInputFileContent ,
4062 MessageContentInputImageContent ,
@@ -174,9 +196,11 @@ async def _handle_regular_agent(
174196 context : ResponseContext ,
175197 ) -> AsyncIterable [ResponseStreamEvent | dict [str , Any ]]:
176198 """Handle the creation of a response for a regular (non-workflow) agent."""
177- input_text = await context .get_input_text ()
199+ input_items = await context .get_input_items ()
200+ input_messages = _items_to_messages (input_items )
201+
178202 history = await context .get_history ()
179- messages : list [str | Content | Message ] = [* _to_messages (history ), input_text ]
203+ messages : list [str | Content | Message ] = [* _output_items_to_messages (history ), * input_messages ]
180204
181205 chat_options , are_options_set = _to_chat_options (request )
182206
@@ -243,7 +267,9 @@ async def _handle_workflow_agent(
243267 The sandbox may be deactivated after some period of inactivity, and only data managed
244268 by the hosting infrastructure or files will be preserved upon deactivation.
245269 """
246- input_text = await context .get_input_text ()
270+ input_items = await context .get_input_items ()
271+ input_messages = _items_to_messages (input_items )
272+
247273 is_streaming_request = self ._is_streaming_request (request )
248274
249275 _ , are_options_set = _to_chat_options (request )
@@ -296,7 +322,7 @@ async def _handle_workflow_agent(
296322
297323 if not is_streaming_request :
298324 # Run the agent in non-streaming mode
299- response = await self ._agent .run (input_text , stream = False , checkpoint_storage = checkpoint_storage )
325+ response = await self ._agent .run (input_messages , stream = False , checkpoint_storage = checkpoint_storage )
300326
301327 for message in response .messages :
302328 for content in message .contents :
@@ -308,7 +334,7 @@ async def _handle_workflow_agent(
308334 return
309335
310336 # Run the agent in streaming mode
311- response_stream = self ._agent .run (input_text , stream = True , checkpoint_storage = checkpoint_storage )
337+ response_stream = self ._agent .run (input_messages , stream = True , checkpoint_storage = checkpoint_storage )
312338
313339 # Track the current active output item builder for streaming;
314340 # lazily created on matching content, closed when a different type arrives.
@@ -532,7 +558,260 @@ def _to_chat_options(request: CreateResponse) -> tuple[ChatOptions, bool]:
532558# region Input Message Conversion
533559
534560
535- def _to_messages (history : Sequence [OutputItem ]) -> list [Message ]:
561+ def _items_to_messages (input_items : Sequence [Item ]) -> list [Message ]:
562+ """Converts a sequence of input items to a list of Messages, one per item.
563+
564+ Args:
565+ input_items: The input items to convert.
566+
567+ Returns:
568+ A list of Messages, one per supported input item.
569+ """
570+ messages : list [Message ] = []
571+ for item in input_items :
572+ messages .append (_item_to_message (item ))
573+ return messages
574+
575+
576+ def _item_to_message (item : Item ) -> Message :
577+ """Converts an Item to a Message.
578+
579+ Args:
580+ item: The Item to convert.
581+
582+ Returns:
583+ The converted Message.
584+
585+ Raises:
586+ ValueError: If the Item type is not supported.
587+ """
588+ if item .type == "message" :
589+ msg = cast (ItemMessage , item )
590+ if isinstance (msg .content , str ):
591+ return Message (role = msg .role , contents = [Content .from_text (msg .content )])
592+ return Message (role = msg .role , contents = [_convert_message_content (part ) for part in msg .content ])
593+
594+ if item .type == "output_message" :
595+ output_msg = cast (ItemOutputMessage , item )
596+ return Message (
597+ role = output_msg .role , contents = [_convert_output_message_content (part ) for part in output_msg .content ]
598+ )
599+
600+ if item .type == "function_call" :
601+ fc = cast (ItemFunctionToolCall , item )
602+ return Message (
603+ role = "assistant" ,
604+ contents = [Content .from_function_call (fc .call_id , fc .name , arguments = fc .arguments )],
605+ )
606+
607+ if item .type == "function_call_output" :
608+ fco = cast (FunctionCallOutputItemParam , item )
609+ output = fco .output if isinstance (fco .output , str ) else str (fco .output )
610+ return Message (
611+ role = "tool" ,
612+ contents = [Content .from_function_result (fco .call_id , result = output )],
613+ )
614+
615+ if item .type == "reasoning" :
616+ reasoning = cast (ItemReasoningItem , item )
617+ reason_contents : list [Content ] = []
618+ if reasoning .summary :
619+ for summary in reasoning .summary :
620+ reason_contents .append (Content .from_text (summary .text ))
621+ return Message (role = "assistant" , contents = reason_contents )
622+
623+ if item .type == "mcp_call" :
624+ mcp = cast (ItemMcpToolCall , item )
625+ return Message (
626+ role = "assistant" ,
627+ contents = [
628+ Content .from_mcp_server_tool_call (
629+ mcp .id ,
630+ mcp .name ,
631+ server_name = mcp .server_label ,
632+ arguments = mcp .arguments ,
633+ )
634+ ],
635+ )
636+
637+ if item .type == "mcp_approval_request" :
638+ mcp_req = cast (ItemMcpApprovalRequest , item )
639+ mcp_call_content = Content .from_mcp_server_tool_call (
640+ mcp_req .id ,
641+ mcp_req .name ,
642+ server_name = mcp_req .server_label ,
643+ arguments = mcp_req .arguments ,
644+ )
645+ return Message (
646+ role = "assistant" ,
647+ contents = [Content .from_function_approval_request (mcp_req .id , mcp_call_content )],
648+ )
649+
650+ if item .type == "mcp_approval_response" :
651+ mcp_resp = cast (MCPApprovalResponse , item )
652+ placeholder_content = Content .from_function_call (mcp_resp .approval_request_id , "mcp_approval" )
653+ return Message (
654+ role = "user" ,
655+ contents = [
656+ Content .from_function_approval_response (
657+ mcp_resp .approve , mcp_resp .approval_request_id , placeholder_content
658+ )
659+ ],
660+ )
661+
662+ if item .type == "code_interpreter_call" :
663+ ci = cast (ItemCodeInterpreterToolCall , item )
664+ return Message (
665+ role = "assistant" ,
666+ contents = [Content .from_code_interpreter_tool_call (call_id = ci .id )],
667+ )
668+
669+ if item .type == "image_generation_call" :
670+ ig = cast (ItemImageGenToolCall , item )
671+ return Message (
672+ role = "assistant" ,
673+ contents = [Content .from_image_generation_tool_call (image_id = ig .id )],
674+ )
675+
676+ if item .type == "shell_call" :
677+ sc = cast (FunctionShellCallItemParam , item )
678+ return Message (
679+ role = "assistant" ,
680+ contents = [
681+ Content .from_shell_tool_call (
682+ call_id = sc .call_id ,
683+ commands = sc .action .commands ,
684+ status = str (sc .status ),
685+ )
686+ ],
687+ )
688+
689+ if item .type == "shell_call_output" :
690+ sco = cast (FunctionShellCallOutputItemParam , item )
691+ outputs = [
692+ Content .from_shell_command_output (
693+ stdout = out .stdout or "" ,
694+ stderr = out .stderr or "" ,
695+ exit_code = getattr (out .outcome , "exit_code" , None ) if hasattr (out , "outcome" ) else None ,
696+ )
697+ for out in (sco .output or [])
698+ ]
699+ return Message (
700+ role = "tool" ,
701+ contents = [
702+ Content .from_shell_tool_result (
703+ call_id = sco .call_id ,
704+ outputs = outputs ,
705+ max_output_length = sco .max_output_length ,
706+ )
707+ ],
708+ )
709+
710+ if item .type == "local_shell_call" :
711+ lsc = cast (ItemLocalShellToolCall , item )
712+ commands = lsc .action .command if hasattr (lsc .action , "command" ) and lsc .action .command else []
713+ return Message (
714+ role = "assistant" ,
715+ contents = [
716+ Content .from_shell_tool_call (
717+ call_id = lsc .call_id ,
718+ commands = commands ,
719+ status = str (lsc .status ),
720+ )
721+ ],
722+ )
723+
724+ if item .type == "local_shell_call_output" :
725+ lsco = cast (ItemLocalShellToolCallOutput , item )
726+ return Message (
727+ role = "tool" ,
728+ contents = [
729+ Content .from_shell_tool_result (
730+ call_id = lsco .id ,
731+ outputs = [Content .from_shell_command_output (stdout = lsco .output )],
732+ )
733+ ],
734+ )
735+
736+ if item .type == "file_search_call" :
737+ fs = cast (ItemFileSearchToolCall , item )
738+ return Message (
739+ role = "assistant" ,
740+ contents = [
741+ Content .from_function_call (
742+ fs .id ,
743+ "file_search" ,
744+ arguments = json .dumps ({"queries" : fs .queries }),
745+ )
746+ ],
747+ )
748+
749+ if item .type == "web_search_call" :
750+ ws = cast (ItemWebSearchToolCall , item )
751+ return Message (
752+ role = "assistant" ,
753+ contents = [Content .from_function_call (ws .id , "web_search" )],
754+ )
755+
756+ if item .type == "computer_call" :
757+ cc = cast (ItemComputerToolCall , item )
758+ return Message (
759+ role = "assistant" ,
760+ contents = [
761+ Content .from_function_call (
762+ cc .call_id ,
763+ "computer_use" ,
764+ arguments = str (cc .action ),
765+ )
766+ ],
767+ )
768+
769+ if item .type == "computer_call_output" :
770+ cco = cast (ComputerCallOutputItemParam , item )
771+ return Message (
772+ role = "tool" ,
773+ contents = [Content .from_function_result (cco .call_id , result = str (cco .output ))],
774+ )
775+
776+ if item .type == "custom_tool_call" :
777+ ct = cast (ItemCustomToolCall , item )
778+ return Message (
779+ role = "assistant" ,
780+ contents = [Content .from_function_call (ct .call_id , ct .name , arguments = ct .input )],
781+ )
782+
783+ if item .type == "custom_tool_call_output" :
784+ cto = cast (ItemCustomToolCallOutput , item )
785+ output = cto .output if isinstance (cto .output , str ) else str (cto .output )
786+ return Message (
787+ role = "tool" ,
788+ contents = [Content .from_function_result (cto .call_id , result = output )],
789+ )
790+
791+ if item .type == "apply_patch_call" :
792+ ap = cast (ApplyPatchToolCallItemParam , item )
793+ return Message (
794+ role = "assistant" ,
795+ contents = [
796+ Content .from_function_call (
797+ ap .call_id ,
798+ "apply_patch" ,
799+ arguments = str (ap .operation ),
800+ )
801+ ],
802+ )
803+
804+ if item .type == "apply_patch_call_output" :
805+ apo = cast (ApplyPatchToolCallOutputItemParam , item )
806+ return Message (
807+ role = "tool" ,
808+ contents = [Content .from_function_result (apo .call_id , result = apo .output or "" )],
809+ )
810+
811+ raise ValueError (f"Unsupported Item type: { item .type } " )
812+
813+
814+ def _output_items_to_messages (history : Sequence [OutputItem ]) -> list [Message ]:
536815 """Converts a sequence of OutputItem objects to a list of Message objects.
537816
538817 Args:
@@ -543,11 +822,11 @@ def _to_messages(history: Sequence[OutputItem]) -> list[Message]:
543822 """
544823 messages : list [Message ] = []
545824 for item in history :
546- messages .append (_to_message (item ))
825+ messages .append (_output_item_to_message (item ))
547826 return messages
548827
549828
550- def _to_message (item : OutputItem ) -> Message :
829+ def _output_item_to_message (item : OutputItem ) -> Message :
551830 """Converts an OutputItem to a Message.
552831
553832 Args:
0 commit comments