Merge pull request #4183 from pipecat-ai/aleix/fix-task-scheduling

Yield after create_task to ensure timer tasks are scheduled
This commit is contained in:
Aleix Conchillo Flaqué
2026-03-27 20:32:32 -07:00
committed by GitHub
5 changed files with 13 additions and 0 deletions

1
changelog/4183.fixed.md Normal file
View File

@@ -0,0 +1 @@
- Fixed a timing issue where turn detection timer tasks (idle controller, speech timeout, turn analyzer, and turn completion) could miss their first tick because the newly created asyncio task was not yet scheduled when the caller continued.

View File

@@ -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."""

View File

@@ -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.

View File

@@ -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."""

View File

@@ -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."""