@@ -621,10 +621,27 @@ 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+ if (
633+ playback_item_id
634+ and playback_elapsed_ms is not None
635+ ):
636+ truncated_ms = max (int (playback_elapsed_ms ), 0 )
637+ await self ._send_raw_message (
638+ _ConversionHelper .convert_interrupt (
639+ playback_item_id ,
640+ playback_content_index ,
641+ truncated_ms ,
642+ )
643+ )
644+
628645 # Reset trackers so subsequent playback state queries don't
629646 # reference audio that has been interrupted client‑side.
630647 self ._audio_state_tracker .on_interrupted ()
@@ -643,9 +660,6 @@ async def _handle_ws_event(self, event: dict[str, Any]):
643660 )
644661 if not automatic_response_cancellation_enabled :
645662 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.
649663 elif parsed .type == "response.created" :
650664 self ._ongoing_response = True
651665 await self ._emit_event (RealtimeModelTurnStartedEvent ())
0 commit comments