OpenAILLMService or CartesiaTTSService, utilities, like LLMTextProcessor, and other things. Largely, you can build most of your application with these built-in FrameProcessors, but commonly, your application code may require custom frame processing logic. For example, you may want to perform an action as a result of a frame that’s pushed in the pipeline.
Example: MetricsFrame logger
This custom FrameProcessor format and logs MetricsFrames:MetricsFrames. When it sees one, it formats the data and logs it.
It uses this format_metrics function:
See this working
example
using the
MetricsFrameLogger FrameProcessorAdd to a Pipeline
MetricsFrameLogger FrameProcessor will receive every MetericsFrame in the pipeline.
Key Requirements
FrameProcessors must inherit from the baseFrameProcessor class. This ensures that your custom FrameProcessor will correctly handle frames like StartFrame, EndFrame, InterruptionFrame without having to write custom logic for those frames. This inheritance also provides it with the ability to process_frame() and push_frame():
process_frame()is what allows the FrameProcessor to receive frames and add custom conditional logic based on the frames that are received.push_frame()allows the FrameProcessor to push frames to the pipeline. Normally, frames are pushed DOWNSTREAM, but based on which processors need the output, you can also push UPSTREAM or in both directions.
Essential Implementation Details
To ensure proper base class inheritance, it’s critical to include:super().__init__()in your__init__methodawait super().process_frame(frame, direction)in yourprocess_frame()method
Setup and cleanup
A processor is set up before any frame reaches it, and cleaned up when the pipeline tears down. This is where a processor that needs a connection, a client, or the pipeline’s audio configuration gets it:FrameProcessorSetup
metrics_enabled, usage_metrics_enabled, and report_only_initial_ttfb are also available as properties on the processor itself, and processor_setup returns the whole object once setup has run.
Processors are set up concurrently, so a resource two of them share — the
client an input and output transport hold between them — needs guarding.
acquires and releases from pipecat.utils.shared do that: the first owner
to acquire runs the decorated method while the rest wait for it, and
releases runs only for the last owner to let go.ErrorFrame upstream, so the application learns its pipeline came up degraded. A processor that raises while being cleaned up no longer costs the rest of the pipeline its teardown — the failure is logged and every other processor is still released.
Holding frames until a condition is met
A processor that establishes a connection during setup may receive frames before it is ready for them.pause_processing_all_frames_until() holds everything arriving at the processor until a condition resolves, then delivers it in order:
StartFrame that triggers it still travels downstream. Both queues are held while it is in force, so give it a timeout; the pause is always lifted at cleanup.
Critical Responsibility: Frame Forwarding
FrameProcessors receive all frames that are pushed through the pipeline. This gives them a lot of power, but also a great responsibility. Critically, they must push all frames through the pipeline; if they don’t, they block frames from moving through the Pipeline, which will cause issues in how your application functions. As well as formatting and logging MetricsFrames,MetricsFrameLogger also has an await self.push_frame(frame, direction) which pushes the frame through to the next processor in the pipeline.
Frame Direction
When pushing frames, you can specify the direction:Best Practices
- Always call the parent methods: Use
super().__init__()andawait super().process_frame() - Forward all frames: Make sure every frame is pushed through with
await self.push_frame(frame, direction) - Handle frames conditionally: Use
isinstance()checks to handle specific frame types - Use proper error handling: Wrap risky operations in try/catch blocks
- Position carefully in pipeline: Consider where in the pipeline your processor needs to be to receive the right frames