Refactoring the FrameQueue to be able to track any Frame.

This commit is contained in:
filipi87
2026-04-09 09:02:47 -03:00
parent 0acfb4dd49
commit aeda60f761

View File

@@ -7,17 +7,17 @@
"""Frame queue utilities for Pipecat pipeline processors.""" """Frame queue utilities for Pipecat pipeline processors."""
import asyncio import asyncio
from typing import Any, Callable from typing import Any, Callable, Dict, Type, Union
from pipecat.frames.frames import Frame, UninterruptibleFrame from pipecat.frames.frames import Frame, UninterruptibleFrame
class FrameQueue(asyncio.Queue): class FrameQueue(asyncio.Queue):
"""An asyncio.Queue that tracks whether any UninterruptibleFrame is enqueued. """An asyncio.Queue that tracks the types of frames currently enqueued.
Extends ``asyncio.Queue`` and maintains an O(1) ``has_uninterruptible`` Extends ``asyncio.Queue`` and maintains an internal per-type counter so
flag so interrupt-handling code can decide whether to cancel a task or callers can check whether a frame of a given type (or any subclass) is
merely drain non-uninterruptible items without scanning the queue. present without scanning the queue.
Items may be raw ``Frame`` objects or tuples whose first element is a Items may be raw ``Frame`` objects or tuples whose first element is a
``Frame`` (e.g. ``(frame, direction, callback)``). Pass a ``frame_getter`` ``Frame`` (e.g. ``(frame, direction, callback)``). Pass a ``frame_getter``
@@ -39,22 +39,41 @@ class FrameQueue(asyncio.Queue):
""" """
super().__init__() super().__init__()
self._frame_getter = frame_getter self._frame_getter = frame_getter
self._uninterruptible_count: int = 0 self._frame_counts: Dict[type, int] = {}
@property @property
def has_uninterruptible(self) -> bool: def has_uninterruptible(self) -> bool:
"""Return True if any UninterruptibleFrame is currently in the queue.""" """Return True if any UninterruptibleFrame is currently in the queue."""
return self._uninterruptible_count > 0 return self.has_frame(UninterruptibleFrame)
def has_frame(self, frame_type: Union[Type[Frame], Type[UninterruptibleFrame]]) -> bool:
"""Return True if any frame of the given type (or a subclass) is in the queue.
``frame_type`` may be ``Frame``, ``UninterruptibleFrame`` (a mixin, not a
``Frame`` subclass), or any concrete frame type.
Args:
frame_type: The frame class to check for.
Returns:
True if at least one enqueued frame is an instance of ``frame_type``.
"""
return any(
count > 0 and issubclass(concrete, frame_type)
for concrete, count in self._frame_counts.items()
)
def _put(self, item: Any) -> None: def _put(self, item: Any) -> None:
if isinstance(self._frame_getter(item), UninterruptibleFrame): frame_type = type(self._frame_getter(item))
self._uninterruptible_count += 1 self._frame_counts[frame_type] = self._frame_counts.get(frame_type, 0) + 1
super()._put(item) super()._put(item)
def _get(self) -> Any: def _get(self) -> Any:
item = super()._get() item = super()._get()
if isinstance(self._frame_getter(item), UninterruptibleFrame): frame_type = type(self._frame_getter(item))
self._uninterruptible_count -= 1 self._frame_counts[frame_type] -= 1
if self._frame_counts[frame_type] == 0:
del self._frame_counts[frame_type]
return item return item
def reset(self) -> None: def reset(self) -> None: