diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f4d47273..b41efdcf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated +- `TTSService.say()` is deprecated, push a `TTSSpeakFrame` instead. Calling + functions directly is a discouraged pattern in Pipecat because, for example, + it might cause issues with frame ordering. + - `LLMMessagesFrame` is deprecated, in favor of either: - `LLMMessagesUpdateFrame` with `run_llm=True` @@ -67,7 +71,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Chinese, Japanese, Korean word timestamp support to `CartesiaTTSService`. -- Added `region` parameter to `GladiaSTTService`. Accepted values: eu-west (default), us-west. +- Added `region` parameter to `GladiaSTTService`. Accepted values: eu-west + (default), us-west. ### Changed diff --git a/examples/foundational/12-describe-video.py b/examples/foundational/12-describe-video.py index 8bba87d0c..fad52c2de 100644 --- a/examples/foundational/12-describe-video.py +++ b/examples/foundational/12-describe-video.py @@ -11,7 +11,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame +from pipecat.frames.frames import Frame, TextFrame, TTSSpeakFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask @@ -119,7 +119,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): image_requester.set_participant_id(client_id) # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await task.queue_frame(TTSSpeakFrame("Hi there! Feel free to ask me what I see.")) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/12a-describe-video-gemini-flash.py b/examples/foundational/12a-describe-video-gemini-flash.py index c1ba73812..090dd6db6 100644 --- a/examples/foundational/12a-describe-video-gemini-flash.py +++ b/examples/foundational/12a-describe-video-gemini-flash.py @@ -11,7 +11,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame +from pipecat.frames.frames import Frame, TextFrame, TTSSpeakFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -123,7 +123,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): image_requester.set_participant_id(client_id) # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await task.queue_frame(TTSSpeakFrame("Hi there! Feel free to ask me what I see.")) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/12b-describe-video-gpt-4o.py b/examples/foundational/12b-describe-video-gpt-4o.py index a5578538d..f495801c7 100644 --- a/examples/foundational/12b-describe-video-gpt-4o.py +++ b/examples/foundational/12b-describe-video-gpt-4o.py @@ -11,7 +11,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame +from pipecat.frames.frames import Frame, TextFrame, TTSSpeakFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -123,7 +123,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): image_requester.set_participant_id(client_id) # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await task.queue_frame(TTSSpeakFrame("Hi there! Feel free to ask me what I see.")) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/12c-describe-video-anthropic.py b/examples/foundational/12c-describe-video-anthropic.py index 53e719457..cc48d1567 100644 --- a/examples/foundational/12c-describe-video-anthropic.py +++ b/examples/foundational/12c-describe-video-anthropic.py @@ -11,7 +11,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame +from pipecat.frames.frames import Frame, TextFrame, TTSSpeakFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -123,7 +123,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): image_requester.set_participant_id(client_id) # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await task.queue_frame(TTSSpeakFrame("Hi there! Feel free to ask me what I see.")) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index eda10e5f4..78b4fdf65 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -14,6 +14,7 @@ from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import TTSSpeakFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -34,7 +35,6 @@ load_dotenv(override=True) BASE_FILENAME = "/tmp/pipecat_conversation_" -tts = None async def fetch_weather_from_api(params: FunctionCallParams): @@ -87,7 +87,7 @@ async def load_conversation(params: FunctionCallParams): logger.debug( f"loaded conversation from {filename}\n{json.dumps(params.context.messages, indent=4)}" ) - await tts.say("Ok, I've loaded that conversation.") + await params.llm.queue_frame(TTSSpeakFrame("Ok, I've loaded that conversation.")) except Exception as e: await params.result_callback({"success": False, "error": str(e)}) @@ -190,7 +190,7 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - global tts + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 9599527fa..9086ae37a 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -14,6 +14,7 @@ from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import TTSSpeakFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -88,7 +89,7 @@ async def load_conversation(params: FunctionCallParams): logger.debug( f"loaded conversation from {filename}\n{json.dumps(params.context.messages, indent=4)}" ) - await tts.say("Ok, I've loaded that conversation.") + await params.llm.queue_frame(TTSSpeakFrame("Ok, I've loaded that conversation.")) except Exception as e: await params.result_callback({"success": False, "error": str(e)}) diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index bcb485d33..9be28f319 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -269,9 +269,20 @@ class TTSService(AIService): async def say(self, text: str): """Immediately speak the provided text. + .. deprecated:: 0.0.79 + Push a `TTSSpeakFrame` instead to ensure frame ordering is maintained. + Args: text: The text to speak. """ + import warnings + + warnings.warn( + "`TTSService.say()` is deprecated. Push a `TTSSpeakFrame` instead.", + DeprecationWarning, + stacklevel=2, + ) + await self.queue_frame(TTSSpeakFrame(text)) async def process_frame(self, frame: Frame, direction: FrameDirection):