Trigger turn stop immediately when transcript arrives after p99 timeout

When the STT p99 timeout fires without a transcript, the turn stop
strategy previously did nothing — falling through to the 5-second
user_turn_stop_timeout. Now, a _timeout_expired flag tracks when the
timeout has elapsed so that a late transcript triggers the turn stop
immediately instead of waiting for the fallback.
This commit is contained in:
Mark Backman
2026-04-13 18:11:32 -04:00
parent be81dac723
commit 804e3ea9ec
3 changed files with 55 additions and 0 deletions

View File

@@ -61,6 +61,7 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._transcript_finalized = False
self._vad_stopped_time: Optional[float] = None
self._timeout_task: Optional[asyncio.Task] = None
self._timeout_expired: bool = False
async def reset(self):
"""Reset the strategy to its initial state."""
@@ -69,6 +70,7 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._vad_user_speaking = False
self._transcript_finalized = False
self._vad_stopped_time = None
self._timeout_expired = False
if self._timeout_task:
await self.task_manager.cancel_task(self._timeout_task)
self._timeout_task = None
@@ -117,6 +119,7 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._vad_user_speaking = True
self._transcript_finalized = False
self._vad_stopped_time = None
self._timeout_expired = False
# Cancel any pending timeout
if self._timeout_task:
await self.task_manager.cancel_task(self._timeout_task)
@@ -163,6 +166,11 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._transcript_finalized = True
# For finalized transcripts, check if we can trigger early
await self._maybe_trigger_user_turn_stopped()
elif self._timeout_expired:
# The p99 timeout already elapsed without a transcript. Now that
# we have one, trigger the turn stop immediately.
await self.trigger_user_turn_stopped()
return
# Fallback: handle transcripts when no VAD stop was received.
# This handles edge cases where transcripts arrive without VAD firing.
@@ -207,6 +215,7 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy):
finally:
self._timeout_task = None
self._timeout_expired = True
await self._maybe_trigger_user_turn_stopped()
async def _maybe_trigger_user_turn_stopped(self):

View File

@@ -65,6 +65,7 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._vad_stopped_time: Optional[float] = None # Track when VAD stopped was received
self._transcript_finalized = False
self._timeout_task: Optional[asyncio.Task] = None
self._timeout_expired: bool = False
async def reset(self):
"""Reset the strategy to its initial state."""
@@ -74,6 +75,7 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._vad_user_speaking = False
self._vad_stopped_time = None
self._transcript_finalized = False
self._timeout_expired = False
if self._timeout_task:
await self.task_manager.cancel_task(self._timeout_task)
self._timeout_task = None
@@ -149,6 +151,7 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._vad_user_speaking = True
self._vad_stopped_time = None
self._transcript_finalized = False
self._timeout_expired = False
# Cancel any pending timeout
if self._timeout_task:
await self.task_manager.cancel_task(self._timeout_task)
@@ -204,6 +207,13 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy):
self._transcript_finalized = True
# For finalized transcripts, trigger immediately if turn is complete
await self._maybe_trigger_user_turn_stopped()
elif self._timeout_expired and self._turn_complete:
# The p99 timeout already elapsed without a transcript. Now that
# we have one, trigger the turn stop immediately. This handles the
# case where the transcript is slower to arrive than the p99 timeout,
# trigger the user turn to stop immediately.
await self.trigger_user_turn_stopped()
return
# Fallback: handle transcripts when no VAD stop was received.
# This handles edge cases where transcripts arrive without VAD firing.
@@ -240,6 +250,7 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy):
finally:
self._timeout_task = None
self._timeout_expired = True
await self._maybe_trigger_user_turn_stopped()
async def _maybe_trigger_user_turn_stopped(self):