Addressing code review comments

This commit is contained in:
marcus-daily
2026-01-09 13:44:44 +00:00
committed by Marcus
parent 35a99f92ab
commit b075502c4c
3 changed files with 22 additions and 18 deletions

View File

@@ -116,6 +116,18 @@ class BaseTurnAnalyzer(ABC):
""" """
pass pass
def on_vad_start_secs_updated(self, vad_start_secs: float):
"""Invoked when the VAD start trigger time is updated.
The turn analyzer may choose to change its buffer size depending
on this value.
Args:
vad_start_secs (float): The number of seconds of voice activity
before triggering the user speaking event.
"""
pass
@abstractmethod @abstractmethod
def clear(self): def clear(self):
"""Reset the turn analyzer to its initial state.""" """Reset the turn analyzer to its initial state."""

View File

@@ -35,15 +35,11 @@ class SmartTurnParams(BaseTurnParams):
Parameters: Parameters:
stop_secs: Maximum silence duration in seconds before ending turn. stop_secs: Maximum silence duration in seconds before ending turn.
pre_speech_ms: Milliseconds of audio to include before speech starts. pre_speech_ms: Milliseconds of audio to include before speech starts.
vad_start_secs: Seconds VAD waits before confirming speech start (e.g. VAD STARTING window).
This is added to `pre_speech_ms` at inference slicing time so Smart Turn can include
the initial audio that occurred while VAD was still confirming speech.
max_duration_secs: Maximum duration in seconds for audio segments. max_duration_secs: Maximum duration in seconds for audio segments.
""" """
stop_secs: float = STOP_SECS stop_secs: float = STOP_SECS
pre_speech_ms: float = PRE_SPEECH_MS pre_speech_ms: float = PRE_SPEECH_MS
vad_start_secs: float = 0.0
max_duration_secs: float = MAX_DURATION_SECONDS max_duration_secs: float = MAX_DURATION_SECONDS
@@ -82,6 +78,7 @@ class BaseSmartTurn(BaseTurnAnalyzer):
# Thread executor that will run the model. We only need one thread per # Thread executor that will run the model. We only need one thread per
# analyzer because one analyzer just handles one audio stream. # analyzer because one analyzer just handles one audio stream.
self._executor = ThreadPoolExecutor(max_workers=1) self._executor = ThreadPoolExecutor(max_workers=1)
self._vad_start_secs: float = 0.0
@property @property
def speech_triggered(self) -> bool: def speech_triggered(self) -> bool:
@@ -165,6 +162,9 @@ class BaseSmartTurn(BaseTurnAnalyzer):
logger.debug(f"End of Turn result: {state}") logger.debug(f"End of Turn result: {state}")
return state, result return state, result
def on_vad_start_secs_updated(self, vad_start_secs: float):
self._vad_start_secs = vad_start_secs
def clear(self): def clear(self):
"""Reset the turn analyzer to its initial state.""" """Reset the turn analyzer to its initial state."""
self._clear(EndOfTurnState.COMPLETE) self._clear(EndOfTurnState.COMPLETE)
@@ -185,7 +185,7 @@ class BaseSmartTurn(BaseTurnAnalyzer):
return state, None return state, None
# Extract recent audio segment for prediction # Extract recent audio segment for prediction
effective_pre_speech_ms = self._params.pre_speech_ms + (self._params.vad_start_secs * 1000) effective_pre_speech_ms = self._params.pre_speech_ms + (self._vad_start_secs * 1000)
start_time = self._speech_start_time - (effective_pre_speech_ms / 1000) start_time = self._speech_start_time - (effective_pre_speech_ms / 1000)
start_index = 0 start_index = 0
for i, (t, _) in enumerate(audio_buffer): for i, (t, _) in enumerate(audio_buffer):

View File

@@ -108,20 +108,12 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy):
async def _handle_speech_control_params(self, frame: SpeechControlParamsFrame): async def _handle_speech_control_params(self, frame: SpeechControlParamsFrame):
"""Sync Smart Turn pre-speech buffering with VAD start delay. """Sync Smart Turn pre-speech buffering with VAD start delay.
In the new user-turn-strategies pipeline, `VADUserStartedSpeakingFrame` `VADUserStartedSpeakingFrame` is emitted only once VAD has confirmed speech
is emitted only once VAD has *confirmed* speech (after `vad_params.start_secs`). (after `vad_params.start_secs`). Smart Turn should still include the initial
Smart Turn should still include the initial audio collected during that audio collected during that confirmation window, so we let the analyzer know
confirmation window, so we record it in `SmartTurnParams.vad_start_secs` and when this value has changed.
add it at inference slicing time (preserving `pre_speech_ms` semantics).
""" """
if not frame.vad_params: self._turn_analyzer.on_vad_start_secs_updated(frame.vad_params.start_secs)
return
params = self._turn_analyzer.params
if not isinstance(params, SmartTurnParams):
return
params.vad_start_secs = frame.vad_params.start_secs
async def _handle_input_audio(self, frame: InputAudioRawFrame): async def _handle_input_audio(self, frame: InputAudioRawFrame):
"""Handle input audio to check if the turn is completed.""" """Handle input audio to check if the turn is completed."""