Merge pull request #835 from pipecat-ai/aleix/even-more-parallel-pipeline-fixes
parallel_pipeline: fix system frames and parallel pipelines again
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from typing import List
|
from typing import Awaitable, Callable, List
|
||||||
|
|
||||||
from pipecat.pipeline.base_pipeline import BasePipeline
|
from pipecat.pipeline.base_pipeline import BasePipeline
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -18,26 +18,37 @@ from loguru import logger
|
|||||||
|
|
||||||
|
|
||||||
class Source(FrameProcessor):
|
class Source(FrameProcessor):
|
||||||
def __init__(self, upstream_queue: asyncio.Queue):
|
def __init__(
|
||||||
|
self,
|
||||||
|
upstream_queue: asyncio.Queue,
|
||||||
|
push_frame_func: Callable[[Frame, FrameDirection], Awaitable[None]],
|
||||||
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._up_queue = upstream_queue
|
self._up_queue = upstream_queue
|
||||||
|
self._push_frame_func = push_frame_func
|
||||||
|
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
await super().process_frame(frame, direction)
|
await super().process_frame(frame, direction)
|
||||||
|
|
||||||
match direction:
|
match direction:
|
||||||
case FrameDirection.UPSTREAM:
|
case FrameDirection.UPSTREAM:
|
||||||
# SystemFrames are pushed directly from ParallelPipeline.
|
if isinstance(frame, SystemFrame):
|
||||||
if not isinstance(frame, SystemFrame):
|
await self._push_frame_func(frame, direction)
|
||||||
|
else:
|
||||||
await self._up_queue.put(frame)
|
await self._up_queue.put(frame)
|
||||||
case FrameDirection.DOWNSTREAM:
|
case FrameDirection.DOWNSTREAM:
|
||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
class Sink(FrameProcessor):
|
class Sink(FrameProcessor):
|
||||||
def __init__(self, downstream_queue: asyncio.Queue):
|
def __init__(
|
||||||
|
self,
|
||||||
|
downstream_queue: asyncio.Queue,
|
||||||
|
push_frame_func: Callable[[Frame, FrameDirection], Awaitable[None]],
|
||||||
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._down_queue = downstream_queue
|
self._down_queue = downstream_queue
|
||||||
|
self._push_frame_func = push_frame_func
|
||||||
|
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
await super().process_frame(frame, direction)
|
await super().process_frame(frame, direction)
|
||||||
@@ -46,8 +57,9 @@ class Sink(FrameProcessor):
|
|||||||
case FrameDirection.UPSTREAM:
|
case FrameDirection.UPSTREAM:
|
||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
case FrameDirection.DOWNSTREAM:
|
case FrameDirection.DOWNSTREAM:
|
||||||
# SystemFrames are pushed directly from ParallelPipeline.
|
if isinstance(frame, SystemFrame):
|
||||||
if not isinstance(frame, SystemFrame):
|
await self._push_frame_func(frame, direction)
|
||||||
|
else:
|
||||||
await self._down_queue.put(frame)
|
await self._down_queue.put(frame)
|
||||||
|
|
||||||
|
|
||||||
@@ -60,6 +72,7 @@ class ParallelPipeline(BasePipeline):
|
|||||||
|
|
||||||
self._sources = []
|
self._sources = []
|
||||||
self._sinks = []
|
self._sinks = []
|
||||||
|
self._seen_ids = set()
|
||||||
|
|
||||||
self._up_queue = asyncio.Queue()
|
self._up_queue = asyncio.Queue()
|
||||||
self._down_queue = asyncio.Queue()
|
self._down_queue = asyncio.Queue()
|
||||||
@@ -74,8 +87,8 @@ class ParallelPipeline(BasePipeline):
|
|||||||
raise TypeError(f"ParallelPipeline argument {processors} is not a list")
|
raise TypeError(f"ParallelPipeline argument {processors} is not a list")
|
||||||
|
|
||||||
# We will add a source before the pipeline and a sink after.
|
# We will add a source before the pipeline and a sink after.
|
||||||
source = Source(self._up_queue)
|
source = Source(self._up_queue, self._parallel_push_frame)
|
||||||
sink = Sink(self._down_queue)
|
sink = Sink(self._down_queue, self._parallel_push_frame)
|
||||||
self._sources.append(source)
|
self._sources.append(source)
|
||||||
self._sinks.append(sink)
|
self._sinks.append(sink)
|
||||||
|
|
||||||
@@ -119,11 +132,6 @@ class ParallelPipeline(BasePipeline):
|
|||||||
# If we get a downstream frame we process it in each source.
|
# If we get a downstream frame we process it in each source.
|
||||||
await asyncio.gather(*[s.queue_frame(frame, direction) for s in self._sources])
|
await asyncio.gather(*[s.queue_frame(frame, direction) for s in self._sources])
|
||||||
|
|
||||||
# If we have a SystemFrame we will push it from this task. Note that the
|
|
||||||
# connected sinks and sources ignore SystemFrames.
|
|
||||||
if isinstance(frame, SystemFrame):
|
|
||||||
await self.push_frame(frame, direction)
|
|
||||||
|
|
||||||
# If we get an EndFrame we stop our queue processing tasks and wait on
|
# If we get an EndFrame we stop our queue processing tasks and wait on
|
||||||
# all the pipelines to finish.
|
# all the pipelines to finish.
|
||||||
if isinstance(frame, (CancelFrame, EndFrame)):
|
if isinstance(frame, (CancelFrame, EndFrame)):
|
||||||
@@ -135,24 +143,25 @@ class ParallelPipeline(BasePipeline):
|
|||||||
if self._down_task:
|
if self._down_task:
|
||||||
await self._down_task
|
await self._down_task
|
||||||
|
|
||||||
|
async def _parallel_push_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
|
if frame.id not in self._seen_ids:
|
||||||
|
self._seen_ids.add(frame.id)
|
||||||
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
async def _process_up_queue(self):
|
async def _process_up_queue(self):
|
||||||
running = True
|
running = True
|
||||||
seen_ids = set()
|
|
||||||
while running:
|
while running:
|
||||||
frame = await self._up_queue.get()
|
frame = await self._up_queue.get()
|
||||||
if frame and frame.id not in seen_ids:
|
if frame:
|
||||||
await self.push_frame(frame, FrameDirection.UPSTREAM)
|
await self._parallel_push_frame(frame, FrameDirection.UPSTREAM)
|
||||||
seen_ids.add(frame.id)
|
|
||||||
running = frame is not None
|
running = frame is not None
|
||||||
self._up_queue.task_done()
|
self._up_queue.task_done()
|
||||||
|
|
||||||
async def _process_down_queue(self):
|
async def _process_down_queue(self):
|
||||||
running = True
|
running = True
|
||||||
seen_ids = set()
|
|
||||||
while running:
|
while running:
|
||||||
frame = await self._down_queue.get()
|
frame = await self._down_queue.get()
|
||||||
if frame and frame.id not in seen_ids:
|
if frame:
|
||||||
await self.push_frame(frame, FrameDirection.DOWNSTREAM)
|
await self._parallel_push_frame(frame, FrameDirection.DOWNSTREAM)
|
||||||
seen_ids.add(frame.id)
|
|
||||||
running = frame is not None
|
running = frame is not None
|
||||||
self._down_queue.task_done()
|
self._down_queue.task_done()
|
||||||
|
|||||||
Reference in New Issue
Block a user