task: handle StopFrame and StopTaskFrame gracefully
This commit is contained in:
@@ -22,6 +22,7 @@ from pipecat.frames.frames import (
|
|||||||
HeartbeatFrame,
|
HeartbeatFrame,
|
||||||
MetricsFrame,
|
MetricsFrame,
|
||||||
StartFrame,
|
StartFrame,
|
||||||
|
StopFrame,
|
||||||
StopTaskFrame,
|
StopTaskFrame,
|
||||||
)
|
)
|
||||||
from pipecat.metrics.metrics import ProcessingMetricsData, TTFBMetricsData
|
from pipecat.metrics.metrics import ProcessingMetricsData, TTFBMetricsData
|
||||||
@@ -163,9 +164,9 @@ class PipelineTask(BaseTask):
|
|||||||
# This is the heartbeat queue. When a heartbeat frame is received in the
|
# This is the heartbeat queue. When a heartbeat frame is received in the
|
||||||
# down queue we add it to the heartbeat queue for processing.
|
# down queue we add it to the heartbeat queue for processing.
|
||||||
self._heartbeat_queue = asyncio.Queue()
|
self._heartbeat_queue = asyncio.Queue()
|
||||||
# This event is used to indicate an EndFrame has been received in the
|
# This event is used to indicate a finalize frame (e.g. EndFrame,
|
||||||
# down queue.
|
# StopFrame) has been received in the down queue.
|
||||||
self._endframe_event = asyncio.Event()
|
self._pipeline_end_event = asyncio.Event()
|
||||||
|
|
||||||
self._source = PipelineTaskSource(self._up_queue)
|
self._source = PipelineTaskSource(self._up_queue)
|
||||||
self._source.link(pipeline)
|
self._source.link(pipeline)
|
||||||
@@ -224,9 +225,12 @@ class PipelineTask(BaseTask):
|
|||||||
"""Starts and manages the pipeline execution until completion or cancellation."""
|
"""Starts and manages the pipeline execution until completion or cancellation."""
|
||||||
if self.has_finished():
|
if self.has_finished():
|
||||||
return
|
return
|
||||||
|
cleanup_pipeline = True
|
||||||
try:
|
try:
|
||||||
push_task = await self._create_tasks()
|
push_task = await self._create_tasks()
|
||||||
await self._task_manager.wait_for_task(push_task)
|
await self._task_manager.wait_for_task(push_task)
|
||||||
|
# We have already cleaned up the pipeline inside the task.
|
||||||
|
cleanup_pipeline = False
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
# We are awaiting on the push task and it might be cancelled
|
# We are awaiting on the push task and it might be cancelled
|
||||||
# (e.g. Ctrl-C). This means we will get a CancelledError here as
|
# (e.g. Ctrl-C). This means we will get a CancelledError here as
|
||||||
@@ -234,7 +238,7 @@ class PipelineTask(BaseTask):
|
|||||||
# awaiting a task.
|
# awaiting a task.
|
||||||
pass
|
pass
|
||||||
await self._cancel_tasks()
|
await self._cancel_tasks()
|
||||||
await self._cleanup()
|
await self._cleanup(cleanup_pipeline)
|
||||||
if self._check_dangling_tasks:
|
if self._check_dangling_tasks:
|
||||||
self._print_dangling_tasks()
|
self._print_dangling_tasks()
|
||||||
self._finished = True
|
self._finished = True
|
||||||
@@ -305,13 +309,14 @@ class PipelineTask(BaseTask):
|
|||||||
data.append(ProcessingMetricsData(processor=p.name, value=0.0))
|
data.append(ProcessingMetricsData(processor=p.name, value=0.0))
|
||||||
return MetricsFrame(data=data)
|
return MetricsFrame(data=data)
|
||||||
|
|
||||||
async def _wait_for_endframe(self):
|
async def _wait_for_pipeline_end(self):
|
||||||
await self._endframe_event.wait()
|
await self._pipeline_end_event.wait()
|
||||||
self._endframe_event.clear()
|
self._pipeline_end_event.clear()
|
||||||
|
|
||||||
async def _cleanup(self):
|
async def _cleanup(self, cleanup_pipeline: bool):
|
||||||
await self._source.cleanup()
|
await self._source.cleanup()
|
||||||
await self._pipeline.cleanup()
|
if cleanup_pipeline:
|
||||||
|
await self._pipeline.cleanup()
|
||||||
await self._sink.cleanup()
|
await self._sink.cleanup()
|
||||||
|
|
||||||
async def _process_push_queue(self):
|
async def _process_push_queue(self):
|
||||||
@@ -342,18 +347,16 @@ class PipelineTask(BaseTask):
|
|||||||
await self._source.queue_frame(self._initial_metrics_frame(), FrameDirection.DOWNSTREAM)
|
await self._source.queue_frame(self._initial_metrics_frame(), FrameDirection.DOWNSTREAM)
|
||||||
|
|
||||||
running = True
|
running = True
|
||||||
should_cleanup = True
|
cleanup_pipeline = True
|
||||||
while running:
|
while running:
|
||||||
frame = await self._push_queue.get()
|
frame = await self._push_queue.get()
|
||||||
await self._source.queue_frame(frame, FrameDirection.DOWNSTREAM)
|
await self._source.queue_frame(frame, FrameDirection.DOWNSTREAM)
|
||||||
if isinstance(frame, EndFrame):
|
if isinstance(frame, (EndFrame, StopFrame)):
|
||||||
await self._wait_for_endframe()
|
await self._wait_for_pipeline_end()
|
||||||
running = not isinstance(frame, (CancelFrame, EndFrame, StopTaskFrame))
|
running = not isinstance(frame, (CancelFrame, EndFrame, StopFrame))
|
||||||
should_cleanup = not isinstance(frame, StopTaskFrame)
|
cleanup_pipeline = not isinstance(frame, StopFrame)
|
||||||
self._push_queue.task_done()
|
self._push_queue.task_done()
|
||||||
# Cleanup only if we need to.
|
await self._cleanup(cleanup_pipeline)
|
||||||
if should_cleanup:
|
|
||||||
await self._cleanup()
|
|
||||||
|
|
||||||
async def _process_up_queue(self):
|
async def _process_up_queue(self):
|
||||||
"""This is the task that processes frames coming upstream from the
|
"""This is the task that processes frames coming upstream from the
|
||||||
@@ -371,7 +374,8 @@ class PipelineTask(BaseTask):
|
|||||||
# Tell the task we should end right away.
|
# Tell the task we should end right away.
|
||||||
await self.queue_frame(CancelFrame())
|
await self.queue_frame(CancelFrame())
|
||||||
elif isinstance(frame, StopTaskFrame):
|
elif isinstance(frame, StopTaskFrame):
|
||||||
await self.queue_frame(StopTaskFrame())
|
# Tell the task we should stop nicely.
|
||||||
|
await self.queue_frame(StopFrame())
|
||||||
elif isinstance(frame, ErrorFrame):
|
elif isinstance(frame, ErrorFrame):
|
||||||
logger.error(f"Error running app: {frame}")
|
logger.error(f"Error running app: {frame}")
|
||||||
if frame.fatal:
|
if frame.fatal:
|
||||||
@@ -390,8 +394,8 @@ class PipelineTask(BaseTask):
|
|||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
frame = await self._down_queue.get()
|
frame = await self._down_queue.get()
|
||||||
if isinstance(frame, EndFrame):
|
if isinstance(frame, (EndFrame, StopFrame)):
|
||||||
self._endframe_event.set()
|
self._pipeline_end_event.set()
|
||||||
elif isinstance(frame, HeartbeatFrame):
|
elif isinstance(frame, HeartbeatFrame):
|
||||||
await self._heartbeat_queue.put(frame)
|
await self._heartbeat_queue.put(frame)
|
||||||
self._down_queue.task_done()
|
self._down_queue.task_done()
|
||||||
|
|||||||
Reference in New Issue
Block a user