Skip to content

Commit dcd5840

Browse files
authored
Merge pull request pipecat-ai#3455 from pipecat-ai/aleix/reset-user-turn-start-strategies
UserTurnController: reset user turn start strategies when turn triggered
2 parents 965466c + 9e705ce commit dcd5840

3 files changed

Lines changed: 49 additions & 0 deletions

File tree

changelog/3455.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fixed an issue where user turn start strategies were not being reset after a user turn started, causing incorrect strategy behavior.

src/pipecat/turns/user_turn_controller.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ async def _trigger_user_turn_start(
251251
self._user_turn = True
252252
self._user_turn_stop_timeout_event.set()
253253

254+
# Reset all user turn start strategies to start fresh.
255+
for s in self._user_turn_strategies.start or []:
256+
await s.reset()
257+
254258
await self._call_event_handler("on_user_turn_started", strategy, params)
255259

256260
async def _trigger_user_turn_stop(

tests/test_user_turn_controller.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
import unittest
99

1010
from pipecat.frames.frames import (
11+
BotStartedSpeakingFrame,
1112
TranscriptionFrame,
1213
UserStartedSpeakingFrame,
1314
UserStoppedSpeakingFrame,
1415
VADUserStartedSpeakingFrame,
1516
VADUserStoppedSpeakingFrame,
1617
)
18+
from pipecat.turns.user_start.min_words_user_turn_start_strategy import (
19+
MinWordsUserTurnStartStrategy,
20+
)
1721
from pipecat.turns.user_turn_controller import UserTurnController
1822
from pipecat.turns.user_turn_strategies import ExternalUserTurnStrategies, UserTurnStrategies
1923
from pipecat.utils.asyncio.task_manager import TaskManager, TaskManagerParams
@@ -58,6 +62,46 @@ async def on_user_turn_stopped(controller, strategy, params):
5862
self.assertTrue(should_start)
5963
self.assertTrue(should_stop)
6064

65+
async def test_user_turn_start_reset(self):
66+
controller = UserTurnController(
67+
user_turn_strategies=UserTurnStrategies(
68+
start=[MinWordsUserTurnStartStrategy(min_words=3)]
69+
),
70+
user_turn_stop_timeout=USER_TURN_STOP_TIMEOUT,
71+
)
72+
73+
await controller.setup(self.task_manager)
74+
75+
should_start = 0
76+
77+
@controller.event_handler("on_user_turn_started")
78+
async def on_user_turn_started(controller, strategy, params):
79+
nonlocal should_start
80+
should_start += 1
81+
82+
await controller.process_frame(BotStartedSpeakingFrame())
83+
await controller.process_frame(TranscriptionFrame(text="One", user_id="cat", timestamp=""))
84+
self.assertEqual(should_start, 0)
85+
86+
await controller.process_frame(
87+
TranscriptionFrame(text=" two three!", user_id="cat", timestamp="")
88+
)
89+
self.assertEqual(should_start, 1)
90+
91+
# Trigger user stop turn so we can trigger user start turn again.
92+
await asyncio.sleep(USER_TURN_STOP_TIMEOUT + 0.1)
93+
94+
await controller.process_frame(BotStartedSpeakingFrame())
95+
await controller.process_frame(
96+
TranscriptionFrame(text="Hello", user_id="cat", timestamp="")
97+
)
98+
self.assertEqual(should_start, 1)
99+
100+
await controller.process_frame(
101+
TranscriptionFrame(text=" there friend!", user_id="cat", timestamp="")
102+
)
103+
self.assertEqual(should_start, 2)
104+
61105
async def test_user_turn_stop_timeout_no_transcription(self):
62106
controller = UserTurnController(
63107
user_turn_strategies=UserTurnStrategies(),

0 commit comments

Comments
 (0)