Repository: livekit/agents
File: livekit-agents/livekit/agents/voice/agent_activity.py (Python)

Code before:
            self._paused_speech
            and (timeout := self._session.options.false_interruption_timeout) is not None
        ):
            # schedule a resume timer when user stops speaking
            self._start_false_interruption_timer(timeout)

    def on_vad_inference_done(self, ev: vad.VADEvent) -> None:
        if self._turn_detection in ("manual", "realtime_llm"):
            # ignore vad inference done event if turn_detection is manual or realtime_llm
            return

        if (
            ev.speech_duration >= self._session.options.min_interruption_duration
            and self._turn_detection != "stt"
        ):
            self._interrupt_by_audio_activity()
        elif (
            ev.speech_duration >= self._session.options.min_interruption_duration
            and self._turn_detection == "stt"
        ):
            # STT often sends end of speech event after final transcript and
            # before VAD end of speech event, we only interrupt if
            # 1. STT EOS hasn't been received yet; or
            # 2. VAD real EOS is not yet triggered (i.e. VAD speech is still ongoing)
            if not self._stt_eos_received or ev.raw_accumulated_silence == 0:
                self._interrupt_by_audio_activity()

        if (
            ev.speaking
            # allow some silence between utterances during active speech
            and ev.raw_accumulated_silence <= self._session.options.min_endpointing_delay / 2
        ):
            self._user_silence_event.clear()
        else:
            self._user_silence_event.set()

    def on_interim_transcript(self, ev: stt.SpeechEvent, *, speaking: bool | None) -> None:
        if isinstance(self.llm, llm.RealtimeModel) and self.llm.capabilities.user_transcription:
            # skip stt transcription if user_transcription is enabled on the realtime model
            return

        self._session._user_input_transcribed(
            UserInputTranscribedEvent(
                language=ev.alternatives[0].language,
                transcript=ev.alternatives[0].text,
                is_final=False,
                speaker_id=ev.alternatives[0].speaker_id,
            ),
        )

        if ev.alternatives[0].text and self._turn_detection not in (

Review comment (suggestion):
```suggestion
        active_speech = ev.speech_duration >= self._session.options.min_interruption_duration
        if active_speech and (
            self._turn_detection != "stt"
            or not self._stt_eos_received
            or ev.raw_accumulated_silence == 0
        ):
            # STT may send EOS before VAD EOS, we only interrupt if:
            # 1. turn detection is not STT; or
            # 2. STT EOS hasn't been received yet; or
            # 3. VAD speech is still ongoing
            self._interrupt_by_audio_activity()
```

Code after:
            self._paused_speech
            and (timeout := self._session.options.false_interruption_timeout) is not None
        ):
            # schedule a resume timer when user stops speaking
            self._start_false_interruption_timer(timeout)

    def on_vad_inference_done(self, ev: vad.VADEvent) -> None:
        if self._turn_detection in ("manual", "realtime_llm"):
            # ignore vad inference done event if turn_detection is manual or realtime_llm
            return

        active_speech = ev.speech_duration >= self._session.options.min_interruption_duration
        if active_speech and (
            self._turn_detection != "stt"
            or not self._stt_eos_received
            or ev.raw_accumulated_silence == 0
        ):
            # STT may send EOS before VAD EOS, we only interrupt if:
            # 1. turn detection is not STT; or
            # 2. STT EOS hasn't been received yet; or
            # 3. VAD speech is still ongoing
            self._interrupt_by_audio_activity()

        if (
            ev.speaking
            # allow some silence between utterances during active speech
            and ev.raw_accumulated_silence <= self._session.options.min_endpointing_delay / 2
        ):
            self._user_silence_event.clear()
        else:
            self._user_silence_event.set()

    def on_interim_transcript(self, ev: stt.SpeechEvent, *, speaking: bool | None) -> None:
        if isinstance(self.llm, llm.RealtimeModel) and self.llm.capabilities.user_transcription:
            # skip stt transcription if user_transcription is enabled on the realtime model
            return

        self._session._user_input_transcribed(
            UserInputTranscribedEvent(
                language=ev.alternatives[0].language,
                transcript=ev.alternatives[0].text,
                is_final=False,
                speaker_id=ev.alternatives[0].speaker_id,
            ),
        )

        if ev.alternatives[0].text and self._turn_detection not in (
            "manual",
            "realtime_llm",
        ):
            self._interrupt_by_audio_activity()