@@ -621,10 +621,30 @@ async def _handle_ws_event(self, event: dict[str, Any]):
621621 last_audio = self ._audio_state_tracker .get_last_audio_item ()
622622 if last_audio is not None :
623623 item_id , content_index = last_audio
624+ playback_state = self ._get_playback_state ()
625+ playback_item_id = playback_state .get ("current_item_id" )
626+ playback_content_index = playback_state .get ("current_item_content_index" ) or 0
627+ playback_elapsed_ms = playback_state .get ("elapsed_ms" )
624628 await self ._emit_event (
625629 RealtimeModelAudioInterruptedEvent (item_id = item_id , content_index = content_index )
626630 )
627631
632+ elapsed_override = getattr (parsed , "audio_end_ms" , None )
633+ if elapsed_override is None or elapsed_override <= 0 :
634+ effective_elapsed_ms = playback_elapsed_ms
635+ else :
636+ effective_elapsed_ms = float (elapsed_override )
637+
638+ if playback_item_id and effective_elapsed_ms is not None :
639+ truncated_ms = max (int (round (effective_elapsed_ms )), 0 )
640+ await self ._send_raw_message (
641+ _ConversionHelper .convert_interrupt (
642+ playback_item_id ,
643+ playback_content_index ,
644+ truncated_ms ,
645+ )
646+ )
647+
628648 # Reset trackers so subsequent playback state queries don't
629649 # reference audio that has been interrupted client‑side.
630650 self ._audio_state_tracker .on_interrupted ()
@@ -643,9 +663,6 @@ async def _handle_ws_event(self, event: dict[str, Any]):
643663 )
644664 if not automatic_response_cancellation_enabled :
645665 await self ._cancel_response ()
646- # Avoid sending conversation.item.truncate here. When the session's
647- # turn_detection.interrupt_response is enabled (GA default), the server emits
648- # conversation.item.truncated after the VAD start and takes care of history updates.
649666 elif parsed .type == "response.created" :
650667 self ._ongoing_response = True
651668 await self ._emit_event (RealtimeModelTurnStartedEvent ())
0 commit comments