@@ -112,7 +112,7 @@ def __init__(
112112        }
113113        self ._event_queue : asyncio .Queue [RealtimeSessionEvent ] =  asyncio .Queue ()
114114        self ._closed  =  False 
115-         self ._stored_exception : Exception  |  None  =  None 
115+         self ._stored_exception : BaseException  |  None  =  None 
116116
117117        # Guardrails state tracking 
118118        self ._interrupted_response_ids : set [str ] =  set ()
@@ -123,6 +123,8 @@ def __init__(
123123        )
124124
125125        self ._guardrail_tasks : set [asyncio .Task [Any ]] =  set ()
126+         self ._tool_call_tasks : set [asyncio .Task [Any ]] =  set ()
127+         self ._async_tool_calls : bool  =  bool (self ._run_config .get ("async_tool_calls" , True ))
126128
127129    @property  
128130    def  model (self ) ->  RealtimeModel :
@@ -216,7 +218,11 @@ async def on_event(self, event: RealtimeModelEvent) -> None:
216218        if  event .type  ==  "error" :
217219            await  self ._put_event (RealtimeError (info = self ._event_info , error = event .error ))
218220        elif  event .type  ==  "function_call" :
219-             await  self ._handle_tool_call (event )
221+             agent_snapshot  =  self ._current_agent 
222+             if  self ._async_tool_calls :
223+                 self ._enqueue_tool_call_task (event , agent_snapshot )
224+             else :
225+                 await  self ._handle_tool_call (event , agent_snapshot = agent_snapshot )
220226        elif  event .type  ==  "audio" :
221227            await  self ._put_event (
222228                RealtimeAudio (
@@ -384,11 +390,17 @@ async def _put_event(self, event: RealtimeSessionEvent) -> None:
384390        """Put an event into the queue.""" 
385391        await  self ._event_queue .put (event )
386392
387-     async  def  _handle_tool_call (self , event : RealtimeModelToolCallEvent ) ->  None :
393+     async  def  _handle_tool_call (
394+         self ,
395+         event : RealtimeModelToolCallEvent ,
396+         * ,
397+         agent_snapshot : RealtimeAgent  |  None  =  None ,
398+     ) ->  None :
388399        """Handle a tool call event.""" 
400+         agent  =  agent_snapshot  or  self ._current_agent 
389401        tools , handoffs  =  await  asyncio .gather (
390-             self . _current_agent .get_all_tools (self ._context_wrapper ),
391-             self ._get_handoffs (self . _current_agent , self ._context_wrapper ),
402+             agent .get_all_tools (self ._context_wrapper ),
403+             self ._get_handoffs (agent , self ._context_wrapper ),
392404        )
393405        function_map  =  {tool .name : tool  for  tool  in  tools  if  isinstance (tool , FunctionTool )}
394406        handoff_map  =  {handoff .tool_name : handoff  for  handoff  in  handoffs }
@@ -398,7 +410,7 @@ async def _handle_tool_call(self, event: RealtimeModelToolCallEvent) -> None:
398410                RealtimeToolStart (
399411                    info = self ._event_info ,
400412                    tool = function_map [event .name ],
401-                     agent = self . _current_agent ,
413+                     agent = agent ,
402414                )
403415            )
404416
@@ -423,7 +435,7 @@ async def _handle_tool_call(self, event: RealtimeModelToolCallEvent) -> None:
423435                    info = self ._event_info ,
424436                    tool = func_tool ,
425437                    output = result ,
426-                     agent = self . _current_agent ,
438+                     agent = agent ,
427439                )
428440            )
429441        elif  event .name  in  handoff_map :
@@ -444,7 +456,7 @@ async def _handle_tool_call(self, event: RealtimeModelToolCallEvent) -> None:
444456                )
445457
446458            # Store previous agent for event 
447-             previous_agent  =  self . _current_agent 
459+             previous_agent  =  agent 
448460
449461            # Update current agent 
450462            self ._current_agent  =  result 
@@ -752,10 +764,49 @@ def _cleanup_guardrail_tasks(self) -> None:
752764                task .cancel ()
753765        self ._guardrail_tasks .clear ()
754766
767+     def  _enqueue_tool_call_task (
768+         self , event : RealtimeModelToolCallEvent , agent_snapshot : RealtimeAgent 
769+     ) ->  None :
770+         """Run tool calls in the background to avoid blocking realtime transport.""" 
771+         task  =  asyncio .create_task (self ._handle_tool_call (event , agent_snapshot = agent_snapshot ))
772+         self ._tool_call_tasks .add (task )
773+         task .add_done_callback (self ._on_tool_call_task_done )
774+ 
775+     def  _on_tool_call_task_done (self , task : asyncio .Task [Any ]) ->  None :
776+         self ._tool_call_tasks .discard (task )
777+ 
778+         if  task .cancelled ():
779+             return 
780+ 
781+         exception  =  task .exception ()
782+         if  exception  is  None :
783+             return 
784+ 
785+         logger .exception ("Realtime tool call task failed" , exc_info = exception )
786+ 
787+         if  self ._stored_exception  is  None :
788+             self ._stored_exception  =  exception 
789+ 
790+         asyncio .create_task (
791+             self ._put_event (
792+                 RealtimeError (
793+                     info = self ._event_info ,
794+                     error = {"message" : f"Tool call task failed: { exception }  " },
795+                 )
796+             )
797+         )
798+ 
799+     def  _cleanup_tool_call_tasks (self ) ->  None :
800+         for  task  in  self ._tool_call_tasks :
801+             if  not  task .done ():
802+                 task .cancel ()
803+         self ._tool_call_tasks .clear ()
804+ 
755805    async  def  _cleanup (self ) ->  None :
756806        """Clean up all resources and mark session as closed.""" 
757807        # Cancel and cleanup guardrail tasks 
758808        self ._cleanup_guardrail_tasks ()
809+         self ._cleanup_tool_call_tasks ()
759810
760811        # Remove ourselves as a listener 
761812        self ._model .remove_listener (self )
0 commit comments