explicit MetadataFrame

This commit is contained in:
Chad Bailey
2025-01-31 19:23:43 +00:00
parent 74b85a450f
commit 0369733f9c
4 changed files with 45 additions and 2 deletions

View File

@@ -17,13 +17,25 @@ from prompts import CUE_USER_TURN, LLM_BASE_PROMPT
from utils.helpers import load_images, load_sounds from utils.helpers import load_images, load_sounds
from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import EndFrame, Frame, ImageRawFrame, TextFrame from pipecat.frames.frames import (
AudioRawFrame,
EndFrame,
Frame,
ImageRawFrame,
MetadataFrame,
SystemFrame,
TextFrame,
TTSStartedFrame,
TTSStoppedFrame,
)
from pipecat.observers.base_observer import BaseObserver from pipecat.observers.base_observer import BaseObserver
from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.sync_parallel_pipeline import SyncParallelPipeline
from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
from pipecat.processors.logger import FrameLogger
from pipecat.services.elevenlabs import ElevenLabsTTSService from pipecat.services.elevenlabs import ElevenLabsTTSService
from pipecat.services.fal import FalImageGenService from pipecat.services.fal import FalImageGenService
from pipecat.services.google import GoogleImageGenService, GoogleLLMService from pipecat.services.google import GoogleImageGenService, GoogleLLMService
@@ -84,6 +96,18 @@ class DebugObserver(BaseObserver):
logger.info( logger.info(
f"⚡ TEXT FRAME: {src} {arrow} {dst} at {time_sec:.2f}s, metadata: {frame.metadata}" f"⚡ TEXT FRAME: {src} {arrow} {dst} at {time_sec:.2f}s, metadata: {frame.metadata}"
) )
elif isinstance(frame, TTSStartedFrame):
logger.info(
f"⚡ TTS STARTED FRAME: {src} {arrow} {dst} at {time_sec:.2f}s, metadata: {frame.metadata}"
)
elif isinstance(frame, TTSStoppedFrame):
logger.info(
f"⚡ TTS STOPPED FRAME: {src} {arrow} {dst} at {time_sec:.2f}s, metadata: {frame.metadata}"
)
elif isinstance(frame, MetadataFrame):
logger.info(
f"⚡ METADATA FRAME: {src} {arrow} {dst} at {time_sec:.2f}s, metadata: {frame.metadata}"
)
async def main(room_url, token=None): async def main(room_url, token=None):
@@ -136,13 +160,18 @@ async def main(room_url, token=None):
runner = PipelineRunner() runner = PipelineRunner()
logger.debug("Waiting for participant...") logger.debug("Waiting for participant...")
after = FrameLogger("After", "red", ignored_frame_types=[SystemFrame, AudioRawFrame])
before = FrameLogger("Before", "cyan", ignored_frame_types=[SystemFrame, AudioRawFrame])
main_pipeline = Pipeline( main_pipeline = Pipeline(
[ [
transport.input(), transport.input(),
context_aggregator.user(), context_aggregator.user(),
llm_service, llm_service,
story_processor, story_processor,
# SyncParallelPipeline([image_processor], [tts_service]),
before,
image_processor, image_processor,
after,
tts_service, tts_service,
transport.output(), transport.output(),
context_aggregator.assistant(), context_aggregator.assistant(),

View File

@@ -17,6 +17,7 @@ from pipecat.frames.frames import (
Frame, Frame,
LLMFullResponseEndFrame, LLMFullResponseEndFrame,
LLMMessagesFrame, LLMMessagesFrame,
MetadataFrame,
TextFrame, TextFrame,
UserStoppedSpeakingFrame, UserStoppedSpeakingFrame,
) )
@@ -154,6 +155,7 @@ class StoryProcessor(FrameProcessor):
# Driven by the prompt, the LLM should have asked the user for input # Driven by the prompt, the LLM should have asked the user for input
elif isinstance(frame, LLMFullResponseEndFrame): elif isinstance(frame, LLMFullResponseEndFrame):
# We use a different frame type, as to avoid image generation ingest # We use a different frame type, as to avoid image generation ingest
await self.push_frame(MetadataFrame())
await self.push_frame(StoryPromptFrame(self._text)) await self.push_frame(StoryPromptFrame(self._text))
self._text = "" self._text = ""
await self.push_frame(frame) await self.push_frame(frame)
@@ -193,6 +195,10 @@ class StoryProcessor(FrameProcessor):
if len(before_break) > 2: if len(before_break) > 2:
self._story.append(before_break) self._story.append(before_break)
mf = MetadataFrame()
mf.metadata = {"story_page_id": self._current_page}
await self.push_frame(mf)
spf = StoryPageFrame(before_break) spf = StoryPageFrame(before_break)
spf.metadata["story_page_id"] = self._current_page spf.metadata["story_page_id"] = self._current_page
self._current_page += 1 self._current_page += 1

View File

@@ -91,6 +91,13 @@ class ControlFrame(Frame):
pass pass
@dataclass
class MetadataFrame(ControlFrame):
"""Used to set default metadata for downstream processors to apply to newly
created frames such as a frame_group_id.
"""
# #
# Mixins # Mixins
# #

View File

@@ -16,6 +16,7 @@ from pipecat.frames.frames import (
CancelFrame, CancelFrame,
ErrorFrame, ErrorFrame,
Frame, Frame,
MetadataFrame,
StartFrame, StartFrame,
StartInterruptionFrame, StartInterruptionFrame,
StopInterruptionFrame, StopInterruptionFrame,
@@ -228,7 +229,7 @@ class FrameProcessor:
async def process_frame(self, frame: Frame, direction: FrameDirection): async def process_frame(self, frame: Frame, direction: FrameDirection):
# System frames skip the queue and blow up determinism # System frames skip the queue and blow up determinism
if not isinstance(frame, SystemFrame): if isinstance(frame, MetadataFrame):
self._input_frame_metadata = frame.metadata self._input_frame_metadata = frame.metadata
# print( # print(
# f"!!! PROCESS: I am {self._name}, input frame is a {frame.name}, metadata is {self._input_frame_metadata}" # f"!!! PROCESS: I am {self._name}, input frame is a {frame.name}, metadata is {self._input_frame_metadata}"