From 52ed7137afefb087429b7ea6691af46f01b94996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Fri, 27 Mar 2026 19:03:23 -0700 Subject: [PATCH] Yield after create_task to ensure timer tasks are scheduled Add `await asyncio.sleep(0)` after `create_task()` calls in UserIdleController, SpeechTimeoutUserTurnStopStrategy, TurnAnalyzerUserTurnStopStrategy, and UserTurnCompletionLLMServiceMixin so the event loop schedules the newly created timer tasks before the caller continues. --- src/pipecat/turns/user_idle_controller.py | 2 ++ .../turns/user_stop/speech_timeout_user_turn_stop_strategy.py | 4 ++++ .../turns/user_stop/turn_analyzer_user_turn_stop_strategy.py | 4 ++++ src/pipecat/turns/user_turn_completion_mixin.py | 2 ++ 4 files changed, 12 insertions(+) diff --git a/src/pipecat/turns/user_idle_controller.py b/src/pipecat/turns/user_idle_controller.py index b3b7e8074..188daa327 100644 --- a/src/pipecat/turns/user_idle_controller.py +++ b/src/pipecat/turns/user_idle_controller.py @@ -143,6 +143,8 @@ class UserIdleController(BaseObject): self._idle_timer_expired(), f"{self}::idle_timer", ) + # Make sure the task is scheduled. + await asyncio.sleep(0) async def _cancel_idle_timer(self): """Cancel the idle timer if running.""" diff --git a/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py index 1e127248f..db6ef3c00 100644 --- a/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py @@ -153,6 +153,8 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy): self._timeout_task = self.task_manager.create_task( self._timeout_handler(timeout), f"{self}::_timeout_handler" ) + # Make sure the task is scheduled. + await asyncio.sleep(0) async def _handle_transcription(self, frame: TranscriptionFrame): """Handle user transcription.""" @@ -174,6 +176,8 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy): self._timeout_task = self.task_manager.create_task( self._timeout_handler(timeout), f"{self}::_timeout_handler" ) + # Make sure the task is scheduled. + await asyncio.sleep(0) def _calculate_timeout(self) -> float: """Calculate the timeout value based on current state. diff --git a/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py index 895d0976f..2931965aa 100644 --- a/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py @@ -193,6 +193,8 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy): self._timeout_task = self.task_manager.create_task( self._timeout_handler(timeout), f"{self}::_timeout_handler" ) + # Make sure the task is scheduled. + await asyncio.sleep(0) async def _handle_transcription(self, frame: TranscriptionFrame): """Handle user transcription.""" @@ -217,6 +219,8 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy): self._timeout_task = self.task_manager.create_task( self._timeout_handler(timeout), f"{self}::_timeout_handler" ) + # Make sure the task is scheduled. + await asyncio.sleep(0) async def _handle_prediction_result(self, result: Optional[MetricsData]): """Handle a prediction result event from the turn analyzer.""" diff --git a/src/pipecat/turns/user_turn_completion_mixin.py b/src/pipecat/turns/user_turn_completion_mixin.py index 40ae551b4..fc421e411 100644 --- a/src/pipecat/turns/user_turn_completion_mixin.py +++ b/src/pipecat/turns/user_turn_completion_mixin.py @@ -254,6 +254,8 @@ class UserTurnCompletionLLMServiceMixin: self._incomplete_timeout_handler(incomplete_type, timeout), f"_incomplete_timeout_{incomplete_type}", ) + # Make sure the task is scheduled. + await asyncio.sleep(0) async def _cancel_incomplete_timeout(self): """Cancel any pending incomplete timeout task."""