@@ -961,9 +961,6 @@ async def update_session(
961961 if audio_patch :
962962 patch ["audio" ] = audio_patch
963963
964- # Always include session.type in updates
965- patch ["type" ] = "realtime"
966-
967964 # No top-level turn_detection
968965
969966 def _strip_tool_strict (tools_val ):
@@ -1030,7 +1027,8 @@ def _strip_tool_strict(tools_val):
10301027 )
10311028 except Exception :
10321029 pass
1033- await self ._send (payload )
1030+ # Use tracked send to attach an event_id and improve diagnostics
1031+ await self ._send_tracked (payload , label = "session.update:patch" )
10341032
10351033 async def append_audio (self , pcm16_bytes : bytes ) -> None : # pragma: no cover
10361034 b64 = base64 .b64encode (pcm16_bytes ).decode ("ascii" )
@@ -1045,10 +1043,16 @@ async def append_audio(self, pcm16_bytes: bytes) -> None: # pragma: no cover
10451043
10461044 async def commit_input (self ) -> None : # pragma: no cover
10471045 try :
1048- # Skip commits while a response is active to avoid server errors
1046+ # If a previous response is still marked active, wait briefly, then proceed.
1047+ # Skipping commits here can cause new turns to reference old audio and repeat answers.
10491048 if bool (getattr (self , "_response_active" , False )):
1050- logger .warning ("Realtime WS: skipping commit; response active" )
1051- return
1049+ logger .warning (
1050+ "Realtime WS: response active at commit; waiting briefly before proceeding"
1051+ )
1052+ for _ in range (5 ): # up to ~0.5s
1053+ await asyncio .sleep (0.1 )
1054+ if not bool (getattr (self , "_response_active" , False )):
1055+ break
10521056 # Avoid overlapping commits while awaiting server ack
10531057 if bool (getattr (self , "_commit_inflight" , False )):
10541058 logger .warning ("Realtime WS: skipping commit; commit in-flight" )
@@ -1250,6 +1254,24 @@ def iter_output_transcript(self) -> AsyncGenerator[str, None]: # pragma: no cov
12501254 def set_tool_executor (self , executor ): # pragma: no cover
12511255 self ._tool_executor = executor
12521256
1257+ def reset_output_stream (self ) -> None : # pragma: no cover
1258+ """Drain any queued output audio and clear per-response text buffers.
1259+ This avoids replaying stale audio if the client failed to consume previous chunks."""
1260+ try :
1261+ while True :
1262+ try :
1263+ _ = self ._audio_queue .get_nowait ()
1264+ except asyncio .QueueEmpty :
1265+ break
1266+ except Exception :
1267+ break
1268+ try :
1269+ self ._out_text_buffers .clear ()
1270+ except Exception :
1271+ pass
1272+ except Exception :
1273+ pass
1274+
12531275 # Expose whether a function/tool call is currently pending
12541276 def has_pending_tool_call (self ) -> bool : # pragma: no cover
12551277 try :
@@ -1611,3 +1633,7 @@ async def _empty():
16111633 def set_tool_executor (self , executor ): # pragma: no cover
16121634 # Not applicable for transcription-only
16131635 return
1636+
1637+ def reset_output_stream (self ) -> None : # pragma: no cover
1638+ # No audio output stream to reset
1639+ return
0 commit comments