improve task creation and cancellation
If a FrameProcessor needs to create a task it should use FrameProcessor.create_task() and FrameProcessor.cancel_task(). This gives Pipecat more control over all the tasks that are created in Pipecat. Both functions internally use the utils module: utils.create_task() and utils.cancel_task() which should also be used outside of FrameProcessors. That is, unless strictly necessary, we should avoid using asyncio.create_task().
This commit is contained in:
@@ -253,20 +253,18 @@ class TTSService(AIService):
|
||||
async def start(self, frame: StartFrame):
|
||||
await super().start(frame)
|
||||
if self._push_stop_frames:
|
||||
self._stop_frame_task = self.get_event_loop().create_task(self._stop_frame_handler())
|
||||
self._stop_frame_task = self.create_task(self._stop_frame_handler())
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
await super().stop(frame)
|
||||
if self._stop_frame_task:
|
||||
self._stop_frame_task.cancel()
|
||||
await self._stop_frame_task
|
||||
await self.cancel_task(self._stop_frame_task)
|
||||
self._stop_frame_task = None
|
||||
|
||||
async def cancel(self, frame: CancelFrame):
|
||||
await super().cancel(frame)
|
||||
if self._stop_frame_task:
|
||||
self._stop_frame_task.cancel()
|
||||
await self._stop_frame_task
|
||||
await self.cancel_task(self._stop_frame_task)
|
||||
self._stop_frame_task = None
|
||||
|
||||
async def _update_settings(self, settings: Dict[str, Any]):
|
||||
@@ -364,23 +362,20 @@ class TTSService(AIService):
|
||||
await self.push_frame(TTSTextFrame(text))
|
||||
|
||||
async def _stop_frame_handler(self):
|
||||
try:
|
||||
has_started = False
|
||||
while True:
|
||||
try:
|
||||
frame = await asyncio.wait_for(
|
||||
self._stop_frame_queue.get(), self._stop_frame_timeout_s
|
||||
)
|
||||
if isinstance(frame, TTSStartedFrame):
|
||||
has_started = True
|
||||
elif isinstance(frame, (TTSStoppedFrame, StartInterruptionFrame)):
|
||||
has_started = False
|
||||
except asyncio.TimeoutError:
|
||||
if has_started:
|
||||
await self.push_frame(TTSStoppedFrame())
|
||||
has_started = False
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
has_started = False
|
||||
while True:
|
||||
try:
|
||||
frame = await asyncio.wait_for(
|
||||
self._stop_frame_queue.get(), self._stop_frame_timeout_s
|
||||
)
|
||||
if isinstance(frame, TTSStartedFrame):
|
||||
has_started = True
|
||||
elif isinstance(frame, (TTSStoppedFrame, StartInterruptionFrame)):
|
||||
has_started = False
|
||||
except asyncio.TimeoutError:
|
||||
if has_started:
|
||||
await self.push_frame(TTSStoppedFrame())
|
||||
has_started = False
|
||||
|
||||
|
||||
class WordTTSService(TTSService):
|
||||
@@ -388,7 +383,7 @@ class WordTTSService(TTSService):
|
||||
super().__init__(**kwargs)
|
||||
self._initial_word_timestamp = -1
|
||||
self._words_queue = asyncio.Queue()
|
||||
self._words_task = self.get_event_loop().create_task(self._words_task_handler())
|
||||
self._words_task = self.create_task(self._words_task_handler())
|
||||
|
||||
def start_word_timestamps(self):
|
||||
if self._initial_word_timestamp == -1:
|
||||
@@ -421,35 +416,29 @@ class WordTTSService(TTSService):
|
||||
|
||||
async def _stop_words_task(self):
|
||||
if self._words_task:
|
||||
self._words_task.cancel()
|
||||
await self._words_task
|
||||
await self.cancel_task(self._words_task)
|
||||
self._words_task = None
|
||||
|
||||
async def _words_task_handler(self):
|
||||
last_pts = 0
|
||||
while True:
|
||||
try:
|
||||
(word, timestamp) = await self._words_queue.get()
|
||||
if word == "Reset" and timestamp == 0:
|
||||
self.reset_word_timestamps()
|
||||
frame = None
|
||||
elif word == "LLMFullResponseEndFrame" and timestamp == 0:
|
||||
frame = LLMFullResponseEndFrame()
|
||||
frame.pts = last_pts
|
||||
elif word == "TTSStoppedFrame" and timestamp == 0:
|
||||
frame = TTSStoppedFrame()
|
||||
frame.pts = last_pts
|
||||
else:
|
||||
frame = TTSTextFrame(word)
|
||||
frame.pts = self._initial_word_timestamp + timestamp
|
||||
if frame:
|
||||
last_pts = frame.pts
|
||||
await self.push_frame(frame)
|
||||
self._words_queue.task_done()
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.exception(f"{self} exception: {e}")
|
||||
(word, timestamp) = await self._words_queue.get()
|
||||
if word == "Reset" and timestamp == 0:
|
||||
self.reset_word_timestamps()
|
||||
frame = None
|
||||
elif word == "LLMFullResponseEndFrame" and timestamp == 0:
|
||||
frame = LLMFullResponseEndFrame()
|
||||
frame.pts = last_pts
|
||||
elif word == "TTSStoppedFrame" and timestamp == 0:
|
||||
frame = TTSStoppedFrame()
|
||||
frame.pts = last_pts
|
||||
else:
|
||||
frame = TTSTextFrame(word)
|
||||
frame.pts = self._initial_word_timestamp + timestamp
|
||||
if frame:
|
||||
last_pts = frame.pts
|
||||
await self.push_frame(frame)
|
||||
self._words_queue.task_done()
|
||||
|
||||
|
||||
class STTService(AIService):
|
||||
|
||||
Reference in New Issue
Block a user