BaseObserver: added new on_process_frame
This commit is contained in:
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added new `on_process_frame()` observer method which makes it possible to know
|
||||||
|
when a frame is being processed.
|
||||||
|
|
||||||
- Added new `FrameProcessor.entry_processor()` method. This allows you to access
|
- Added new `FrameProcessor.entry_processor()` method. This allows you to access
|
||||||
the first non-compound processor in a pipeline.
|
the first non-compound processor in a pipeline.
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ processors without modifying the pipeline structure. Observers can be used
|
|||||||
for logging, debugging, analytics, and monitoring pipeline behavior.
|
for logging, debugging, analytics, and monitoring pipeline behavior.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from abc import abstractmethod
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from typing_extensions import TYPE_CHECKING
|
from typing_extensions import TYPE_CHECKING
|
||||||
@@ -23,6 +22,28 @@ if TYPE_CHECKING:
|
|||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FrameProcessed:
|
||||||
|
"""Event data for frame processing in the pipeline.
|
||||||
|
|
||||||
|
Represents an event where a frame is being processed by a processor. This
|
||||||
|
data structure is typically used by observers to track the flow of frames
|
||||||
|
through the pipeline for logging, debugging, or analytics purposes.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
processor: The processor processing the frame.
|
||||||
|
frame: The frame being processed.
|
||||||
|
direction: The direction of the frame (e.g., downstream or upstream).
|
||||||
|
timestamp: The time when the frame was pushed, based on the pipeline clock.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
processor: "FrameProcessor"
|
||||||
|
frame: Frame
|
||||||
|
direction: "FrameDirection"
|
||||||
|
timestamp: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FramePushed:
|
class FramePushed:
|
||||||
"""Event data for frame transfers between processors in the pipeline.
|
"""Event data for frame transfers between processors in the pipeline.
|
||||||
@@ -56,7 +77,18 @@ class BaseObserver(BaseObject):
|
|||||||
performance analysis, and analytics collection.
|
performance analysis, and analytics collection.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
async def on_process_frame(self, data: FrameProcessed):
|
||||||
|
"""Handle the event when a frame is being processed by a processor.
|
||||||
|
|
||||||
|
This method should be implemented by subclasses to define specific
|
||||||
|
behavior (e.g., logging, monitoring, debugging) when a frame is
|
||||||
|
being processed by a processor.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: The event data containing details about the frame processing.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
async def on_push_frame(self, data: FramePushed):
|
async def on_push_frame(self, data: FramePushed):
|
||||||
"""Handle the event when a frame is pushed from one processor to another.
|
"""Handle the event when a frame is pushed from one processor to another.
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ the main pipeline execution.
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from attr import dataclass
|
from attr import dataclass
|
||||||
|
|
||||||
from pipecat.observers.base_observer import BaseObserver, FramePushed
|
from pipecat.observers.base_observer import BaseObserver, FrameProcessed, FramePushed
|
||||||
from pipecat.utils.asyncio.task_manager import BaseTaskManager
|
from pipecat.utils.asyncio.task_manager import BaseTaskManager
|
||||||
from pipecat.utils.asyncio.watchdog_queue import WatchdogQueue
|
from pipecat.utils.asyncio.watchdog_queue import WatchdogQueue
|
||||||
|
|
||||||
@@ -120,14 +120,21 @@ class TaskObserver(BaseObserver):
|
|||||||
for proxy in self._proxies.values():
|
for proxy in self._proxies.values():
|
||||||
await self._task_manager.cancel_task(proxy.task)
|
await self._task_manager.cancel_task(proxy.task)
|
||||||
|
|
||||||
|
async def on_process_frame(self, data: FramePushed):
|
||||||
|
"""Queue frame data for all managed observers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: The frame push event data to distribute to observers.
|
||||||
|
"""
|
||||||
|
await self._send_to_proxy(data)
|
||||||
|
|
||||||
async def on_push_frame(self, data: FramePushed):
|
async def on_push_frame(self, data: FramePushed):
|
||||||
"""Queue frame data for all managed observers.
|
"""Queue frame data for all managed observers.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: The frame push event data to distribute to observers.
|
data: The frame push event data to distribute to observers.
|
||||||
"""
|
"""
|
||||||
for proxy in self._proxies.values():
|
await self._send_to_proxy(data)
|
||||||
await proxy.queue.put(data)
|
|
||||||
|
|
||||||
def _started(self) -> bool:
|
def _started(self) -> bool:
|
||||||
"""Check if the task observer has been started."""
|
"""Check if the task observer has been started."""
|
||||||
@@ -151,6 +158,10 @@ class TaskObserver(BaseObserver):
|
|||||||
proxies[observer] = proxy
|
proxies[observer] = proxy
|
||||||
return proxies
|
return proxies
|
||||||
|
|
||||||
|
async def _send_to_proxy(self, data: Any):
|
||||||
|
for proxy in self._proxies.values():
|
||||||
|
await proxy.queue.put(data)
|
||||||
|
|
||||||
async def _proxy_task_handler(self, queue: asyncio.Queue, observer: BaseObserver):
|
async def _proxy_task_handler(self, queue: asyncio.Queue, observer: BaseObserver):
|
||||||
"""Handle frame processing for a single observer."""
|
"""Handle frame processing for a single observer."""
|
||||||
on_push_frame_deprecated = False
|
on_push_frame_deprecated = False
|
||||||
@@ -169,11 +180,15 @@ class TaskObserver(BaseObserver):
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
data = await queue.get()
|
data = await queue.get()
|
||||||
if on_push_frame_deprecated:
|
|
||||||
await observer.on_push_frame(
|
if isinstance(data, FramePushed):
|
||||||
data.src, data.dst, data.frame, data.direction, data.timestamp
|
if on_push_frame_deprecated:
|
||||||
)
|
await observer.on_push_frame(
|
||||||
else:
|
data.src, data.dst, data.frame, data.direction, data.timestamp
|
||||||
await observer.on_push_frame(data)
|
)
|
||||||
|
else:
|
||||||
|
await observer.on_push_frame(data)
|
||||||
|
elif isinstance(data, FrameProcessed):
|
||||||
|
await observer.on_process_frame(data)
|
||||||
|
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from pipecat.frames.frames import (
|
|||||||
SystemFrame,
|
SystemFrame,
|
||||||
)
|
)
|
||||||
from pipecat.metrics.metrics import LLMTokenUsage, MetricsData
|
from pipecat.metrics.metrics import LLMTokenUsage, MetricsData
|
||||||
from pipecat.observers.base_observer import BaseObserver, FramePushed
|
from pipecat.observers.base_observer import BaseObserver, FrameProcessed, FramePushed
|
||||||
from pipecat.processors.metrics.frame_processor_metrics import FrameProcessorMetrics
|
from pipecat.processors.metrics.frame_processor_metrics import FrameProcessorMetrics
|
||||||
from pipecat.utils.asyncio.task_manager import BaseTaskManager
|
from pipecat.utils.asyncio.task_manager import BaseTaskManager
|
||||||
from pipecat.utils.asyncio.watchdog_event import WatchdogEvent
|
from pipecat.utils.asyncio.watchdog_event import WatchdogEvent
|
||||||
@@ -594,6 +594,16 @@ class FrameProcessor(BaseObject):
|
|||||||
frame: The frame to process.
|
frame: The frame to process.
|
||||||
direction: The direction of frame flow.
|
direction: The direction of frame flow.
|
||||||
"""
|
"""
|
||||||
|
if self._observer:
|
||||||
|
timestamp = self._clock.get_time() if self._clock else 0
|
||||||
|
data = FrameProcessed(
|
||||||
|
processor=self,
|
||||||
|
frame=frame,
|
||||||
|
direction=direction,
|
||||||
|
timestamp=timestamp,
|
||||||
|
)
|
||||||
|
await self._observer.on_process_frame(data)
|
||||||
|
|
||||||
if isinstance(frame, StartFrame):
|
if isinstance(frame, StartFrame):
|
||||||
await self.__start(frame)
|
await self.__start(frame)
|
||||||
elif isinstance(frame, StartInterruptionFrame):
|
elif isinstance(frame, StartInterruptionFrame):
|
||||||
|
|||||||
Reference in New Issue
Block a user