From 607e3040d47698e058150fbdfd74247c58e381af Mon Sep 17 00:00:00 2001 From: LucasStringPay <160168817+LucasStringPay@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:16:11 -0700 Subject: [PATCH 001/158] Ignore None 'completion_tokens' or similar Similar as https://github.com/pipecat-ai/pipecat/commit/144ea36c81e0698163493e945a33d5b5b4ebd406 , reported in https://github.com/pipecat-ai/pipecat/issues/2207 --- src/pipecat/services/google/llm_openai.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/google/llm_openai.py b/src/pipecat/services/google/llm_openai.py index 81d124cb7..fde62154e 100644 --- a/src/pipecat/services/google/llm_openai.py +++ b/src/pipecat/services/google/llm_openai.py @@ -94,9 +94,9 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService): async for chunk in chunk_stream: if chunk.usage: tokens = LLMTokenUsage( - prompt_tokens=chunk.usage.prompt_tokens, - completion_tokens=chunk.usage.completion_tokens, - total_tokens=chunk.usage.total_tokens, + prompt_tokens=chunk.usage.prompt_tokens or 0, + completion_tokens=chunk.usage.completion_tokens or 0, + total_tokens=chunk.usage.total_tokens or 0, ) await self.start_llm_usage_metrics(tokens) From 28929a47f73b5e8ae4f0c70cb78cb3fcee6a8d19 Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Mon, 6 Oct 2025 16:16:59 -0300 Subject: [PATCH 002/158] Verifying WhatsApp signature to ensure the webhook request is from WhatsApp. --- env.example | 6 +++++ src/pipecat/runner/run.py | 16 +++++++++--- src/pipecat/transports/whatsapp/client.py | 31 +++++++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/env.example b/env.example index 9c9ee5d8c..22768494b 100644 --- a/env.example +++ b/env.example @@ -158,3 +158,9 @@ NVIDIA_API_KEY=... # Qwen QWEN_API_KEY=... + +# WhatsApp +WHATSAPP_TOKEN= +WHATSAPP_WEBHOOK_VERIFICATION_TOKEN= +WHATSAPP_PHONE_NUMBER_ID= +WHATSAPP_APP_SECRET= \ No newline at end of file diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index edcfbe9c4..1ab923314 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -84,7 +84,7 @@ from pipecat.runner.types import ( try: import uvicorn from dotenv import load_dotenv - from fastapi import BackgroundTasks, FastAPI, HTTPException, Request, WebSocket + from fastapi import BackgroundTasks, FastAPI, Header, HTTPException, Request, WebSocket from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse, RedirectResponse except ImportError as e: @@ -275,6 +275,7 @@ def _setup_whatsapp_routes(app: FastAPI): WHATSAPP_TOKEN = os.getenv("WHATSAPP_TOKEN") WHATSAPP_PHONE_NUMBER_ID = os.getenv("WHATSAPP_PHONE_NUMBER_ID") WHATSAPP_WEBHOOK_VERIFICATION_TOKEN = os.getenv("WHATSAPP_WEBHOOK_VERIFICATION_TOKEN") + WHATSAPP_APP_SECRET = os.getenv("WHATSAPP_APP_SECRET") if not all( [ @@ -325,7 +326,12 @@ def _setup_whatsapp_routes(app: FastAPI): summary="Handle WhatsApp webhook events", description="Processes incoming WhatsApp messages and call events", ) - async def whatsapp_webhook(body: WhatsAppWebhookRequest, background_tasks: BackgroundTasks): + async def whatsapp_webhook( + body: WhatsAppWebhookRequest, + background_tasks: BackgroundTasks, + request: Request, + x_hub_signature_256: str = Header(None), + ): """Handle incoming WhatsApp webhook events. For call events, establishes WebRTC connections and spawns bot instances @@ -357,7 +363,10 @@ def _setup_whatsapp_routes(app: FastAPI): try: # Process the webhook request - result = await whatsapp_client.handle_webhook_request(body, connection_callback) + raw_body = await request.body() + result = await whatsapp_client.handle_webhook_request( + body, connection_callback, sha256_signature=x_hub_signature_256, raw_body=raw_body + ) logger.debug(f"Webhook processed successfully: {result}") return {"status": "success", "message": "Webhook processed successfully"} except ValueError as ve: @@ -376,6 +385,7 @@ def _setup_whatsapp_routes(app: FastAPI): async with aiohttp.ClientSession() as session: whatsapp_client = WhatsAppClient( whatsapp_token=WHATSAPP_TOKEN, + whatsapp_secret=WHATSAPP_APP_SECRET, phone_number_id=WHATSAPP_PHONE_NUMBER_ID, session=session, ) diff --git a/src/pipecat/transports/whatsapp/client.py b/src/pipecat/transports/whatsapp/client.py index 7c86ca2ce..f6fd83e7b 100644 --- a/src/pipecat/transports/whatsapp/client.py +++ b/src/pipecat/transports/whatsapp/client.py @@ -12,6 +12,8 @@ WhatsApp call events. """ import asyncio +import hashlib +import hmac from typing import Awaitable, Callable, Dict, List, Optional import aiohttp @@ -47,6 +49,7 @@ class WhatsAppClient: phone_number_id: str, session: aiohttp.ClientSession, ice_servers: Optional[List[IceServer]] = None, + whatsapp_secret: Optional[str] = None, ) -> None: """Initialize the WhatsApp client. @@ -56,10 +59,12 @@ class WhatsAppClient: session: aiohttp session for making HTTP requests ice_servers: List of ICE servers for WebRTC connections. If None, defaults to Google's public STUN server + whatsapp_secret: WhatsApp APP secret for validating that the webhook request came from WhatsApp. """ self._whatsapp_api = WhatsAppApi( whatsapp_token=whatsapp_token, phone_number_id=phone_number_id, session=session ) + self._whatsapp_secret = whatsapp_secret self._ongoing_calls_map: Dict[str, SmallWebRTCConnection] = {} # Set default ICE servers if none provided @@ -133,10 +138,32 @@ class WhatsAppClient: return int(challenge) + async def _validate_whatsapp_webhook_request(self, raw_body: bytes, sha256_signature: str): + """Common handler for both /start and /connect endpoints.""" + # Compute HMAC SHA256 using your App Secret + expected_signature = hmac.new( + key=self._whatsapp_secret.encode("utf-8"), + msg=raw_body, + digestmod=hashlib.sha256, + ).hexdigest() + + # Extract signature from header (strip 'sha256=' prefix) + if not sha256_signature: + raise Exception("Missing X-Hub-Signature-256 header") + received_signature = sha256_signature.split("sha256=")[-1] + + # Compare signatures securely + if not hmac.compare_digest(expected_signature, received_signature): + raise Exception("Invalid webhook signature") + + logger.debug(f"Webhook signature verified!") + async def handle_webhook_request( self, request: WhatsAppWebhookRequest, connection_callback: Optional[Callable[[SmallWebRTCConnection], Awaitable[None]]] = None, + raw_body: Optional[bytes] = None, + sha256_signature: Optional[str] = None, ) -> bool: """Handle a webhook request from WhatsApp. @@ -150,6 +177,8 @@ class WhatsAppClient: connection_callback: Optional callback function to invoke when a new WebRTC connection is established. The callback receives the SmallWebRTCConnection instance. + raw_body: Optional bytes containing the raw request body. + sha256_signature: Optional X-Hub-Signature-256 header value from the request. Returns: bool: True if the webhook request was handled successfully, False otherwise @@ -159,6 +188,8 @@ class WhatsAppClient: Exception: If connection establishment or API calls fail """ try: + if self._whatsapp_secret: + await self._validate_whatsapp_webhook_request(raw_body, sha256_signature) for entry in request.entry: for change in entry.changes: # Handle connect events From fe168e3c68e0aafda44c8b317ae003daa3943b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sun, 5 Oct 2025 21:27:17 -0700 Subject: [PATCH 003/158] GoogleLLMService: added support for image generation --- CHANGELOG.md | 3 + .../07n-interruptible-gemini-image.py | 151 ++++++++++++++++++ src/pipecat/services/google/llm.py | 14 ++ 3 files changed, 168 insertions(+) create mode 100644 examples/foundational/07n-interruptible-gemini-image.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ae7e3dcb1..0ff7a6118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for Nano Banana models to `GoogleLLMService`. For example, you + can now use the `gemini-2.5-flash-image` model to generate images. + - `PermissionError` is now caught if NLTK's `punkt_tab` can't be downloaded. - Added `HumeTTSService` for text-to-speech synthesis using Hume AI's diff --git a/examples/foundational/07n-interruptible-gemini-image.py b/examples/foundational/07n-interruptible-gemini-image.py new file mode 100644 index 000000000..61b8e650a --- /dev/null +++ b/examples/foundational/07n-interruptible-gemini-image.py @@ -0,0 +1,151 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +""" +A conversational AI bot using Gemini for both LLM, STT and TTS. + +This example demonstrates how to use Gemini's image generation capabilities. + +Features showcased: +- Gemini LLM for conversation and image generation +- Google TTS and STT + +Run with: + python examples/foundational/07n-interruptible-gemini-image.py + +Make sure to set your environment variables: + export GOOGLE_API_KEY=your_api_key_here +""" + +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams +from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import LLMRunFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.google.llm import GoogleLLMService +from pipecat.services.google.stt import GoogleSTTService +from pipecat.services.google.tts import GoogleTTSService +from pipecat.transcriptions.language import Language +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = GoogleSTTService( + params=GoogleSTTService.InputParams(languages=Language.EN_US), + credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"), + ) + + tts = GoogleTTSService( + voice_id="en-US-Chirp3-HD-Charon", + params=GoogleTTSService.InputParams(language=Language.EN_US), + credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"), + ) + + llm = GoogleLLMService( + api_key=os.getenv("GOOGLE_API_KEY"), + model="gemini-2.5-flash-image", + ) + + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = LLMContext(messages) + context_aggregator = LLMContextAggregatorPair(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, # STT + context_aggregator.user(), # User responses + llm, # LLM + tts, # Gemini TTS + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation with a styled introduction + messages.append({"role": "system", "content": "Please introduce yourself to the user."}) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index 70d4ca2bf..b7b3bb50c 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -35,6 +35,7 @@ from pipecat.frames.frames import ( LLMMessagesFrame, LLMTextFrame, LLMUpdateSettingsFrame, + OutputImageRawFrame, UserImageRawFrame, ) from pipecat.metrics.metrics import LLMTokenUsage @@ -72,6 +73,9 @@ try: HttpOptions, Part, ) + + # Temporary hack to be able to process Nano Banana returned images. + genai._api_client.READ_BUFFER_SIZE = 5 * 1024 * 1024 except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") @@ -710,6 +714,7 @@ class GoogleLLMService(LLMService): self._api_key = api_key self._system_instruction = system_instruction self._http_options = http_options + self._create_client(api_key, http_options) self._settings = { "max_tokens": params.max_tokens, @@ -788,6 +793,9 @@ class GoogleLLMService(LLMService): # and can be configured to turn it off. if not self._model_name.startswith("gemini-2.5-flash"): return + # If we have an image model, we don't use a budget either. + if "image" in self._model_name: + return # If thinking_config is already set, don't override it. if "thinking_config" in generation_params: return @@ -927,6 +935,12 @@ class GoogleLLMService(LLMService): arguments=function_call.args or {}, ) ) + elif part.inline_data and part.inline_data.data: + image = Image.open(io.BytesIO(part.inline_data.data)) + frame = OutputImageRawFrame( + image=image.tobytes(), size=image.size, format="RGB" + ) + await self.push_frame(frame) if ( candidate.grounding_metadata From 312959f97ec1e305f967bf75e06d9a941d700381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sun, 5 Oct 2025 21:37:37 -0700 Subject: [PATCH 004/158] GoogleLLMService: update default model to gemini-2.5-flash --- CHANGELOG.md | 4 ++++ src/pipecat/services/google/llm.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ff7a6118..3d585698b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `hume` optional dependency group for Hume AI TTS integration. +### Changed + +- Updated default `GoogleLLMService` model to `gemini-2.5-flash`. + ### Fixed - Fixed RTVI incoming message handling, broken in 0.0.87. diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index b7b3bb50c..b45c276d0 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -686,7 +686,7 @@ class GoogleLLMService(LLMService): self, *, api_key: str, - model: str = "gemini-2.0-flash", + model: str = "gemini-2.5-flash", params: Optional[InputParams] = None, system_instruction: Optional[str] = None, tools: Optional[List[Dict[str, Any]]] = None, From bd13a80da7b2760808a4e74285c10b78a88b5303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 6 Oct 2025 01:06:28 -0700 Subject: [PATCH 005/158] pyproject: update google dependencies --- pyproject.toml | 2 +- uv.lock | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0adbf281e..375ca5245 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ fal = [ "fal-client~=0.5.9" ] fireworks = [] fish = [ "ormsgpack~=1.7.0", "pipecat-ai[websockets-base]" ] gladia = [ "pipecat-ai[websockets-base]" ] -google = [ "google-cloud-speech~=2.32.0", "google-cloud-texttospeech~=2.26.0", "google-genai~=1.24.0", "pipecat-ai[websockets-base]" ] +google = [ "google-cloud-speech>=2.33.0,<3", "google-cloud-texttospeech>=2.31.0,<3", "google-genai>=1.41.0,<2", "pipecat-ai[websockets-base]" ] grok = [] groq = [ "groq~=0.23.0" ] gstreamer = [ "pygobject~=3.50.0" ] diff --git a/uv.lock b/uv.lock index bef2f3a60..8e4b59009 100644 --- a/uv.lock +++ b/uv.lock @@ -1809,7 +1809,7 @@ wheels = [ [[package]] name = "google-cloud-speech" -version = "2.32.0" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -1817,14 +1817,14 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/fc/7e47328069850f084ee17e26b5572de067e30fdab862e381702222d237b7/google_cloud_speech-2.32.0.tar.gz", hash = "sha256:89c2618b131d310c6c00e7c04d290ffa9a5d68c20191030766a7737850f04e77", size = 387621, upload-time = "2025-04-14T10:16:35.386Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/74/9c5a556f8af19cab461058aa15e1409e7afa453ca2383473a24a12801ef7/google_cloud_speech-2.33.0.tar.gz", hash = "sha256:fd08511b5124fdaa768d71a4054e84a5d8eb02531cb6f84f311c0387ea1314ed", size = 389072, upload-time = "2025-06-11T23:56:37.231Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/a4/f41f2737cd0597f2aa5855b0a12f353fad4506868887590671230df81c77/google_cloud_speech-2.32.0-py3-none-any.whl", hash = "sha256:537b279d8697fe5b5bc5f485f2d48a6b343fc76f73385b5776806c37bc5f8ea1", size = 334148, upload-time = "2025-04-14T10:16:33.89Z" }, + { url = "https://files.pythonhosted.org/packages/12/1d/880342b2541b4bad888ad8ab2ac77d4b5dad25b32a2a1c5f21140c14c8e3/google_cloud_speech-2.33.0-py3-none-any.whl", hash = "sha256:4ba16c8517c24a6abcde877289b0f40b719090504bf06b1adea248198ccd50a5", size = 335681, upload-time = "2025-06-11T23:56:36.026Z" }, ] [[package]] name = "google-cloud-texttospeech" -version = "2.26.0" +version = "2.31.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -1832,9 +1832,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/3d/214506e1163138159a3ba172adc0945970843ce9a8c5332db06772806dff/google_cloud_texttospeech-2.26.0.tar.gz", hash = "sha256:43af1b88a6b9becde69a3bbf8aa80cdfa5f12f8999e56bcf9dec374354ed7f6a", size = 181084, upload-time = "2025-04-14T10:16:39.737Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/4b/7ccadbec28ee255a3176c3de0a14705c4b6469777f1c7ddbf4452fa893e3/google_cloud_texttospeech-2.31.0.tar.gz", hash = "sha256:1f0c0c6448f175e1e2f63d96fb13af5d9abee6970bbb22c1e4036f53136a5588", size = 184880, upload-time = "2025-09-25T14:03:22.786Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/eb/fb3a2c16f5612c4a131b2bfa242aaf7800ec0cee479759d9de2cc919ba70/google_cloud_texttospeech-2.26.0-py3-none-any.whl", hash = "sha256:837835aadeb261983d139ef1c5e60c99f80199e22330bf4f62e217360b9e07b8", size = 188122, upload-time = "2025-04-14T10:16:38.466Z" }, + { url = "https://files.pythonhosted.org/packages/18/3e/54ff1a5af26f90c5d76e7e80b9208f8484035b5bd8fb6a06c819fed6a8c9/google_cloud_texttospeech-2.31.0-py3-none-any.whl", hash = "sha256:9442134b4b8e7e3d179dfd3850a5a953a6a6a9cf000a3640caddb85cf97ab69b", size = 191280, upload-time = "2025-09-25T14:03:16.667Z" }, ] [[package]] @@ -1874,7 +1874,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.24.0" +version = "1.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1886,9 +1886,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8d/cf/37ac8cd4752e28e547b8a52765fe48a2ada2d0d286ea03f46e4d8c69ff4f/google_genai-1.24.0.tar.gz", hash = "sha256:bc896e30ad26d05a2af3d17c2ba10ea214a94f1c0cdb93d5c004dc038774e75a", size = 226740, upload-time = "2025-07-01T22:14:24.365Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/8b/ee20bcf707769b3b0e1106c3b5c811507736af7e8a60f29a70af1750ba19/google_genai-1.41.0.tar.gz", hash = "sha256:134f861bb0ace4e34af0501ecb75ceee15f7662fd8120698cd185e8cb39f2800", size = 245812, upload-time = "2025-10-02T22:30:29.699Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/28/a35f64fc02e599808101617a21d447d241dadeba2aac1f4dc2d1179b8218/google_genai-1.24.0-py3-none-any.whl", hash = "sha256:98be8c51632576289ecc33cd84bcdaf4356ef0bef04ac7578660c49175af22b9", size = 226065, upload-time = "2025-07-01T22:14:23.177Z" }, + { url = "https://files.pythonhosted.org/packages/15/14/e5e8fbca8863fee718208566c4e927b8e9f45fd46ec5cf89e24759da545b/google_genai-1.41.0-py3-none-any.whl", hash = "sha256:111a3ee64c1a0927d3879faddb368234594432479a40c311e5fe4db338ca8778", size = 245931, upload-time = "2025-10-02T22:30:27.885Z" }, ] [[package]] @@ -4558,9 +4558,9 @@ requires-dist = [ { name = "fastapi", marker = "extra == 'runner'", specifier = ">=0.115.6,<0.117.0" }, { name = "fastapi", marker = "extra == 'websocket'", specifier = ">=0.115.6,<0.117.0" }, { name = "faster-whisper", marker = "extra == 'whisper'", specifier = "~=1.1.1" }, - { name = "google-cloud-speech", marker = "extra == 'google'", specifier = "~=2.32.0" }, - { name = "google-cloud-texttospeech", marker = "extra == 'google'", specifier = "~=2.26.0" }, - { name = "google-genai", marker = "extra == 'google'", specifier = "~=1.24.0" }, + { name = "google-cloud-speech", marker = "extra == 'google'", specifier = ">=2.33.0,<3" }, + { name = "google-cloud-texttospeech", marker = "extra == 'google'", specifier = ">=2.31.0,<3" }, + { name = "google-genai", marker = "extra == 'google'", specifier = ">=1.41.0,<2" }, { name = "groq", marker = "extra == 'groq'", specifier = "~=0.23.0" }, { name = "hume", marker = "extra == 'hume'", specifier = ">=0.11.2" }, { name = "langchain", marker = "extra == 'langchain'", specifier = "~=0.3.20" }, From de8ee9692737225c9dbadb31201bf7d197587eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 6 Oct 2025 14:58:47 -0700 Subject: [PATCH 006/158] fix context order when using interruption strategies --- CHANGELOG.md | 3 +++ src/pipecat/frames/frames.py | 23 ++++++++++++++++++----- src/pipecat/pipeline/task.py | 16 ++++++++++++++-- src/pipecat/processors/frame_processor.py | 16 +++++++++------- tests/test_context_aggregators.py | 3 +++ tests/test_dtmf_aggregator.py | 11 ++++++++++- tests/test_frame_processor.py | 2 ++ 7 files changed, 59 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d585698b..b12667248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue that would cause wrong user/assistant context ordering when + using interruption strategies. + - Fixed RTVI incoming message handling, broken in 0.0.87. ## [0.0.87] - 2025-10-02 diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 2a9651b83..a871fe74b 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -849,13 +849,15 @@ class FrameProcessorResumeUrgentFrame(SystemFrame): class InterruptionFrame(SystemFrame): """Frame indicating user started speaking (interruption detected). - Emitted by the BaseInputTransport to indicate that a user has started - speaking (i.e. is interrupting). This is similar to - UserStartedSpeakingFrame except that it should be pushed concurrently - with other frames (so the order is not guaranteed). + Usually emitted by the pipeline task to indicate that all frame processors + should be interrrupted. + + Parameters: + pushed_by_task: Whether this interruption was pushed from the pipeline + task. """ - pass + pushed_by_task: bool = False @dataclass @@ -886,6 +888,17 @@ class StartInterruptionFrame(InterruptionFrame): ) +@dataclass +class InterruptionCompletedFrame(SystemFrame): + """Frame indicating that the whole pipeline has been interrupted. + + This is emitted by the pipeline task when an InterruptionFrame has made it + all the way to the end of pipeline, interrupting all the frame processors. + """ + + pass + + @dataclass class UserStartedSpeakingFrame(SystemFrame): """Frame indicating user has started speaking. diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index 9ce3baf7f..d040beac1 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -30,6 +30,7 @@ from pipecat.frames.frames import ( ErrorFrame, Frame, HeartbeatFrame, + InterruptionCompletedFrame, InterruptionFrame, InterruptionTaskFrame, MetricsFrame, @@ -691,8 +692,11 @@ class PipelineTask(BasePipelineTask): # bypassing the push queue and directly queue into the # pipeline. This is in case the push task is blocked waiting for a # pipeline-ending frame to finish traversing the pipeline. - logger.debug(f"{self}: received interruption task frame {frame}") - await self._pipeline.queue_frame(InterruptionFrame()) + interruption_frame = InterruptionFrame(pushed_by_task=True) + logger.debug( + f"{self}: received interruption task frame {frame}, pushing {interruption_frame}" + ) + await self._pipeline.queue_frame(interruption_frame) elif isinstance(frame, ErrorFrame): if frame.fatal: logger.error(f"A fatal error occurred: {frame}") @@ -736,6 +740,14 @@ class PipelineTask(BasePipelineTask): self._pipeline_end_event.set() elif isinstance(frame, CancelFrame): self._pipeline_end_event.set() + elif isinstance(frame, InterruptionFrame) and frame.pushed_by_task: + # If an interruption frame made it all the way to the end of the + # pipeline, send an InterruptionCompleteFrame. Note that we are + # bypassing the push queue and directly queue into the + # pipeline. This is in case the push task is blocked waiting for a + # pipeline-ending frame to finish traversing the pipeline. + logger.debug(f"{self}: interruption completed with {frame}") + await self._pipeline.queue_frame(InterruptionCompletedFrame()) elif isinstance(frame, HeartbeatFrame): await self._heartbeat_queue.put(frame) diff --git a/src/pipecat/processors/frame_processor.py b/src/pipecat/processors/frame_processor.py index 0b55082aa..25dc2716e 100644 --- a/src/pipecat/processors/frame_processor.py +++ b/src/pipecat/processors/frame_processor.py @@ -28,6 +28,7 @@ from pipecat.frames.frames import ( FrameProcessorPauseUrgentFrame, FrameProcessorResumeFrame, FrameProcessorResumeUrgentFrame, + InterruptionCompletedFrame, InterruptionFrame, InterruptionTaskFrame, StartFrame, @@ -571,7 +572,9 @@ class FrameProcessor(BaseObject): # frames and we will process the frame right away. This is because a # previous system frame might be waiting for the interruption frame and # it's blocking the input task. - if self._wait_for_interruption and isinstance(frame, InterruptionFrame): + if self._wait_for_interruption and isinstance( + frame, (InterruptionFrame, InterruptionCompletedFrame) + ): await self.__process_frame(frame, direction, callback) return @@ -661,18 +664,17 @@ class FrameProcessor(BaseObject): await self._call_event_handler("on_after_push_frame", frame) - # If we are waiting for an interruption and we get an interruption, then - # we can unblock `push_interruption_task_frame_and_wait()`. - if self._wait_for_interruption and isinstance(frame, InterruptionFrame): + # If we are waiting for an interruption and one completed, then we can + # unblock `push_interruption_task_frame_and_wait()`. + if self._wait_for_interruption and isinstance(frame, InterruptionCompletedFrame): self._wait_interruption_event.set() async def push_interruption_task_frame_and_wait(self): """Push an interruption task frame upstream and wait for the interruption. This function sends an `InterruptionTaskFrame` upstream to the pipeline - task and waits to receive the corresponding `InterruptionFrame`. When - the function finishes it is guaranteed that the `InterruptionFrame` has - been pushed downstream. + task and waits to receive an `InterruptionCompletedFrame`. This + guarantees the whole pipeline has been interrupted. """ self._wait_for_interruption = True diff --git a/tests/test_context_aggregators.py b/tests/test_context_aggregators.py index 77b6acc87..accbfd669 100644 --- a/tests/test_context_aggregators.py +++ b/tests/test_context_aggregators.py @@ -5,6 +5,7 @@ # import json +import sys import unittest from typing import Any @@ -20,6 +21,7 @@ from pipecat.frames.frames import ( FunctionCallResultFrame, FunctionCallResultProperties, InterimTranscriptionFrame, + InterruptionCompletedFrame, InterruptionFrame, InterruptionTaskFrame, LLMFullResponseEndFrame, @@ -568,6 +570,7 @@ class BaseTestUserContextAggregator: BotStartedSpeakingFrame, UserStartedSpeakingFrame, InterruptionFrame, + InterruptionCompletedFrame, UserStoppedSpeakingFrame, *self.EXPECTED_CONTEXT_FRAMES, ] diff --git a/tests/test_dtmf_aggregator.py b/tests/test_dtmf_aggregator.py index c7590ae47..18082a992 100644 --- a/tests/test_dtmf_aggregator.py +++ b/tests/test_dtmf_aggregator.py @@ -10,6 +10,7 @@ from pipecat.audio.dtmf.types import KeypadEntry from pipecat.frames.frames import ( EndFrame, InputDTMFFrame, + InterruptionCompletedFrame, InterruptionFrame, TranscriptionFrame, ) @@ -30,6 +31,7 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, InputDTMFFrame, InputDTMFFrame, InputDTMFFrame, @@ -62,10 +64,12 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, # First aggregation "12" InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, TranscriptionFrame, # Second aggregation "3" ] @@ -98,11 +102,13 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, InputDTMFFrame, InputDTMFFrame, TranscriptionFrame, # "12#" InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, # "45" ] @@ -132,6 +138,7 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, # Should flush before EndFrame EndFrame, @@ -160,6 +167,7 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, ] @@ -187,6 +195,7 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, + InterruptionCompletedFrame, InputDTMFFrame, InputDTMFFrame, TranscriptionFrame, @@ -224,7 +233,7 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): # All the InputDTMFFrames plus one TranscriptionFrame expected_down_frames = ( - [InputDTMFFrame, InterruptionFrame] + [InputDTMFFrame, InterruptionFrame, InterruptionCompletedFrame] + [InputDTMFFrame] * (len(frames_to_send) - 1) + [TranscriptionFrame] ) diff --git a/tests/test_frame_processor.py b/tests/test_frame_processor.py index d0072e5fb..5c1ee1b38 100644 --- a/tests/test_frame_processor.py +++ b/tests/test_frame_processor.py @@ -10,6 +10,7 @@ import unittest from pipecat.frames.frames import ( EndFrame, Frame, + InterruptionCompletedFrame, InterruptionFrame, OutputTransportMessageUrgentFrame, TextFrame, @@ -101,6 +102,7 @@ class TestFrameProcessor(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InterruptionFrame, InterruptionFrame, + InterruptionCompletedFrame, OutputTransportMessageUrgentFrame, EndFrame, ] From b1ccec74b218aa5e51d1f97aae7097d07984886a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 7 Oct 2025 09:03:49 -0400 Subject: [PATCH 007/158] Fix AWS Nova Sonic authentication --- CHANGELOG.md | 3 ++ pyproject.toml | 2 +- src/pipecat/services/aws_nova_sonic/aws.py | 20 ++++----- uv.lock | 50 ++++++++++++---------- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b12667248..827e1fb88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue with `AWSNovaSonicLLMService` where the client wouldn't + connect due to a breaking change in the AWS dependency chain. + - Fixed an issue that would cause wrong user/assistant context ordering when using interruption strategies. diff --git a/pyproject.toml b/pyproject.toml index 375ca5245..c04dd2433 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ anthropic = [ "anthropic~=0.49.0" ] assemblyai = [ "pipecat-ai[websockets-base]" ] asyncai = [ "pipecat-ai[websockets-base]" ] aws = [ "aioboto3~=15.0.0", "pipecat-ai[websockets-base]" ] -aws-nova-sonic = [ "aws_sdk_bedrock_runtime~=0.0.2; python_version>='3.12'" ] +aws-nova-sonic = [ "aws_sdk_bedrock_runtime~=0.1.0; python_version>='3.12'" ] azure = [ "azure-cognitiveservices-speech~=1.42.0"] cartesia = [ "cartesia~=2.0.3", "pipecat-ai[websockets-base]" ] cerebras = [] diff --git a/src/pipecat/services/aws_nova_sonic/aws.py b/src/pipecat/services/aws_nova_sonic/aws.py index 432db553f..66b020055 100644 --- a/src/pipecat/services/aws_nova_sonic/aws.py +++ b/src/pipecat/services/aws_nova_sonic/aws.py @@ -70,7 +70,7 @@ try: BedrockRuntimeClient, InvokeModelWithBidirectionalStreamOperationInput, ) - from aws_sdk_bedrock_runtime.config import Config, HTTPAuthSchemeResolver, SigV4AuthScheme + from aws_sdk_bedrock_runtime.config import Config from aws_sdk_bedrock_runtime.models import ( BidirectionalInputPayloadPart, InvokeModelWithBidirectionalStreamInput, @@ -78,8 +78,8 @@ try: InvokeModelWithBidirectionalStreamOperationOutput, InvokeModelWithBidirectionalStreamOutput, ) - from smithy_aws_core.credentials_resolvers.static import StaticCredentialsResolver - from smithy_aws_core.identity import AWSCredentialsIdentity + from smithy_aws_core.auth.sigv4 import SigV4AuthScheme + from smithy_aws_core.identity.static import StaticCredentialsResolver from smithy_core.aio.eventstream import DuplexEventStream except ModuleNotFoundError as e: logger.error(f"Exception: {e}") @@ -528,15 +528,11 @@ class AWSNovaSonicLLMService(LLMService): config = Config( endpoint_uri=f"https://bedrock-runtime.{self._region}.amazonaws.com", region=self._region, - aws_credentials_identity_resolver=StaticCredentialsResolver( - credentials=AWSCredentialsIdentity( - access_key_id=self._access_key_id, - secret_access_key=self._secret_access_key, - session_token=self._session_token, - ) - ), - http_auth_scheme_resolver=HTTPAuthSchemeResolver(), - http_auth_schemes={"aws.auth#sigv4": SigV4AuthScheme()}, + aws_access_key_id=self._access_key_id, + aws_secret_access_key=self._secret_access_key, + aws_session_token=self._session_token, + aws_credentials_identity_resolver=StaticCredentialsResolver(), + auth_schemes={"aws.auth#sigv4": SigV4AuthScheme(service="bedrock")}, ) return BedrockRuntimeClient(config=config) diff --git a/uv.lock b/uv.lock index 8e4b59009..d7f8aebee 100644 --- a/uv.lock +++ b/uv.lock @@ -410,18 +410,16 @@ wheels = [ [[package]] name = "aws-sdk-bedrock-runtime" -version = "0.0.2" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "smithy-aws-core", marker = "python_full_version >= '3.12'" }, - { name = "smithy-aws-event-stream", marker = "python_full_version >= '3.12'" }, + { name = "smithy-aws-core", extra = ["eventstream", "json"], marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, { name = "smithy-http", extra = ["awscrt"], marker = "python_full_version >= '3.12'" }, - { name = "smithy-json", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/8d/52ba543d5d2dbafbbb762ed7d87afd1b86d0e9abb6309d5956fcc92debf7/aws_sdk_bedrock_runtime-0.0.2.tar.gz", hash = "sha256:7a45752060713fccdc4ae560d34666c225c937e798f90fd1739566431e3c79dc", size = 76377, upload-time = "2025-04-09T20:37:21.192Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/e1/39971b907c83a7525bab112c9b395e1bb6d4bc23bc1712d6d7a050662217/aws_sdk_bedrock_runtime-0.1.0.tar.gz", hash = "sha256:bd062de5a48404f64e1dfe6fb8841fbbf68e8f1798c357d14eb427274cb96a2b", size = 85419, upload-time = "2025-09-29T19:40:01.855Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/1e/cd601cf90c7344dbfaeb6cf4d23c001cfe001ea212e681be16bab134cf50/aws_sdk_bedrock_runtime-0.0.2-py3-none-any.whl", hash = "sha256:4d954d103e8e2d304e1c87ccb8c4c77fae27f4c31b70c0ca50817eb30423f9f7", size = 72205, upload-time = "2025-04-09T20:37:19.956Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e1/5b36bffe85010cdcd44730d1c2d5244653d57c002f440141d7fc3b9f1347/aws_sdk_bedrock_runtime-0.1.0-py3-none-any.whl", hash = "sha256:aac6ff47069d456ca5e23083d96a01e3e0cbc215414e6753c289d7d9efef3335", size = 78853, upload-time = "2025-09-29T19:40:00.341Z" }, ] [[package]] @@ -4546,7 +4544,7 @@ requires-dist = [ { name = "aiortc", marker = "extra == 'webrtc'", specifier = ">=1.13.0,<2" }, { name = "anthropic", marker = "extra == 'anthropic'", specifier = "~=0.49.0" }, { name = "audioop-lts", marker = "python_full_version >= '3.13'", specifier = "~=0.2.1" }, - { name = "aws-sdk-bedrock-runtime", marker = "python_full_version >= '3.12' and extra == 'aws-nova-sonic'", specifier = "~=0.0.2" }, + { name = "aws-sdk-bedrock-runtime", marker = "python_full_version >= '3.12' and extra == 'aws-nova-sonic'", specifier = "~=0.1.0" }, { name = "azure-cognitiveservices-speech", marker = "extra == 'azure'", specifier = "~=1.42.0" }, { name = "cartesia", marker = "extra == 'cartesia'", specifier = "~=2.0.3" }, { name = "coremltools", marker = "extra == 'local-smart-turn'", specifier = ">=8.0" }, @@ -6562,49 +6560,57 @@ wheels = [ [[package]] name = "smithy-aws-core" -version = "0.0.3" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aws-sdk-signers", marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, { name = "smithy-http", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/20/262da16a1e41ffaa2865ad8bf5c1b98f92a5ecdc19022f2e9016c0acc21d/smithy_aws_core-0.0.3.tar.gz", hash = "sha256:ba891626798eec914a6b73c9cb5e1155f0767cc6b34190be582a49caba06c43d", size = 8370, upload-time = "2025-06-17T18:12:31.359Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/e8/8cef48be92ed09a112c54747a4515313ba96e767e7e0118a769aeb147e07/smithy_aws_core-0.1.0.tar.gz", hash = "sha256:5f197b69ad1380e9118e1e3c9032e0e305525ef56fb4fc97dea6414281065526", size = 11135, upload-time = "2025-09-29T19:37:13.072Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/de/4d9652a3199dbed6f82d6f6a9f4f15f8f88e941a3f84b9479c0cb1009981/smithy_aws_core-0.0.3-py3-none-any.whl", hash = "sha256:cb17d9c0fc4e810c34ea67fcc60f9c72407c176dde9360fa3f2795e63677a75a", size = 15271, upload-time = "2025-06-17T18:12:30.599Z" }, + { url = "https://files.pythonhosted.org/packages/46/7e/6d05275646bc2cdf7b0749e9bd54958a4e808aafeee4d8ff2fdaa8233dc2/smithy_aws_core-0.1.0-py3-none-any.whl", hash = "sha256:a8cda4011562f45f1fc5957c3a981b6016d736178450e5f2a1586937632af487", size = 18959, upload-time = "2025-09-29T19:37:12.041Z" }, +] + +[package.optional-dependencies] +eventstream = [ + { name = "smithy-aws-event-stream", marker = "python_full_version >= '3.12'" }, +] +json = [ + { name = "smithy-json", marker = "python_full_version >= '3.12'" }, ] [[package]] name = "smithy-aws-event-stream" -version = "0.0.1" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/97/0e4ad1cdf92e46502262b507e9d4c28fe3559ff7dcffb7358db1d57ef043/smithy_aws_event_stream-0.0.1.tar.gz", hash = "sha256:4c4369146a0194790d0169cf86ffaa9feefe4f5ffec393ddeae55e86b56c088e", size = 11399, upload-time = "2025-04-07T19:44:39.336Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/26/8ff24194efed60b2df18f610ea05fa2a4c6546858b80a0a51335a4943b9b/smithy_aws_event_stream-0.1.0.tar.gz", hash = "sha256:6634691a3bf5d4801a2c29f0761db2dc4771f3ae43cdee50c10d4b4bb2f86475", size = 12216, upload-time = "2025-09-29T19:37:14.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/b4/8237238261927aee8b583fe3bde95c51367725a59213f3f39e3e979207eb/smithy_aws_event_stream-0.0.1-py3-none-any.whl", hash = "sha256:d5c0dd9a117e9dd927bafa857072fd26ccfa208a2768e5600ea628aebc93696b", size = 14992, upload-time = "2025-04-07T19:44:37.877Z" }, + { url = "https://files.pythonhosted.org/packages/90/c4/2b63d31af58fc359577e5515bf730348a235f2f2fa10e17af8640495c81c/smithy_aws_event_stream-0.1.0-py3-none-any.whl", hash = "sha256:17a7300a85cb90df4c6c23f895ca6343361fa419203c3cf80019edd7d3b5f036", size = 15581, upload-time = "2025-09-29T19:37:13.589Z" }, ] [[package]] name = "smithy-core" -version = "0.0.2" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/30/cf766866ff5536cea5b4eec71efe97326a54f62a10d9e2615d303e9ff3fe/smithy_core-0.0.2.tar.gz", hash = "sha256:f81bc9a3008bc791dd025ce150802ccf564f2289afd093589b38f506e91327b5", size = 41327, upload-time = "2025-04-09T16:02:09.289Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/8d/16028d03456071d21de7591f1e1e6a1cc81b2389e53ef8663dbf59caf9cd/smithy_core-0.1.0.tar.gz", hash = "sha256:b159b8905264e1e4c613eab9f74cec0b2f5b8119c42fbadddb4da0a8ed8050e9", size = 48415, upload-time = "2025-09-29T19:37:16.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/2c/cc1c09352df57cc2f0a53e670042fc58902236c72b29ec1eed462695cddf/smithy_core-0.0.2-py3-none-any.whl", hash = "sha256:9f2a929e3c04d581448c5437cf69681c77bbc24656c3c8e30f9c2479963a4941", size = 53860, upload-time = "2025-04-09T16:02:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5b/563cb2beadcfa40597b0c3ff3f2d42e21f065b14782c4ba9cb41a44b745f/smithy_core-0.1.0-py3-none-any.whl", hash = "sha256:cb44e9355fb89e89f2c6ba6a1d59c5db4f2f7282c72d31d9307b6202d66cd0fa", size = 62895, upload-time = "2025-09-29T19:37:15.917Z" }, ] [[package]] name = "smithy-http" -version = "0.0.1" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/29/670680189584f6f282bf97c3d016660a04037119600fa1e1eb438e2a76b7/smithy_http-0.0.1.tar.gz", hash = "sha256:214d0f45a75078654c80ec13d518dcb690dcbec8b11a9a65b4cc2fe108c9bc33", size = 25050, upload-time = "2025-04-07T19:43:59.388Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/62/5ba46c7432fbb0852acf8340402879ba53bb4c009b875e1b5b2e9df844ff/smithy_http-0.1.0.tar.gz", hash = "sha256:ed44552531f594e31101f7186c7b01b508ecd38a860b45390a1cce7da700df4b", size = 28269, upload-time = "2025-09-29T19:37:18.629Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/7b/64efb9237630e6ec64f6c6ee6eef1dfbfd1aa5013788ea72edee378cdf39/smithy_http-0.0.1-py3-none-any.whl", hash = "sha256:b25ff39604de6998adc842455138c58411d2adac9b1130d58f115eea2f109f77", size = 35604, upload-time = "2025-04-07T19:43:58.024Z" }, + { url = "https://files.pythonhosted.org/packages/5b/23/d18076ea45b3000c5e9eb8ebd75a4ea1b65b5c59e5c2080a119e2679dfba/smithy_http-0.1.0-py3-none-any.whl", hash = "sha256:7657aaf4b9e025cb9d317406f417b49cf19fba9d1b2ab4f5e6d9dc5a2dd7cdba", size = 38995, upload-time = "2025-09-29T19:37:17.506Z" }, ] [package.optional-dependencies] @@ -6614,15 +6620,15 @@ awscrt = [ [[package]] name = "smithy-json" -version = "0.0.1" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ijson", marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/51/a0d795aac06233c93fbf97cfd54c6962e868f5611e5fb20e8a45e4bcc56f/smithy_json-0.0.1.tar.gz", hash = "sha256:97c559e559654892dbcf561a3e5fb73ebffc45ed6329cba08792f2a12e6487ff", size = 6095, upload-time = "2025-04-07T19:44:16.41Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/5b/0ecb10007475e1b8faca3bbff1be2fc6edb3ea12ffc5e939e2249be95325/smithy_json-0.1.0.tar.gz", hash = "sha256:84fb48e445b87d850c240d837702c16b259ea53bad76b655ac1bbd8094d48912", size = 7086, upload-time = "2025-09-29T19:37:20.432Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/b4/c5d207759ad967684a97d6ff2f24835d926a5fe4f96db8ea244e141a71b4/smithy_json-0.0.1-py3-none-any.whl", hash = "sha256:50d3b441c369bc16507f271699ad4f73e3961fec762c0d827a0e17709424948c", size = 8903, upload-time = "2025-04-07T19:44:15.138Z" }, + { url = "https://files.pythonhosted.org/packages/62/95/e11c04e56aae12b62e38c49000004a1dc598a64dc207018c08448efde322/smithy_json-0.1.0-py3-none-any.whl", hash = "sha256:80ff64734dccdabf1ba6a2908555b97e60f62c07c3a27df48e421ee058413cb9", size = 9914, upload-time = "2025-09-29T19:37:19.459Z" }, ] [[package]] From 7bbdb4f9918415b593a58c2887d6d60166d91a08 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 7 Oct 2025 12:32:32 -0400 Subject: [PATCH 008/158] Remove deprecation warning for ElevenLabsSTTService --- examples/foundational/07d-interruptible-elevenlabs-http.py | 1 - src/pipecat/services/elevenlabs/__init__.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/foundational/07d-interruptible-elevenlabs-http.py b/examples/foundational/07d-interruptible-elevenlabs-http.py index cd922ddc2..8a144dab3 100644 --- a/examples/foundational/07d-interruptible-elevenlabs-http.py +++ b/examples/foundational/07d-interruptible-elevenlabs-http.py @@ -23,7 +23,6 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.stt import ElevenLabsSTTService from pipecat.services.elevenlabs.tts import ElevenLabsHttpTTSService from pipecat.services.openai.llm import OpenAILLMService diff --git a/src/pipecat/services/elevenlabs/__init__.py b/src/pipecat/services/elevenlabs/__init__.py index e5a76e71a..fca70eaec 100644 --- a/src/pipecat/services/elevenlabs/__init__.py +++ b/src/pipecat/services/elevenlabs/__init__.py @@ -8,6 +8,7 @@ import sys from pipecat.services import DeprecatedModuleProxy +from .stt import * from .tts import * -sys.modules[__name__] = DeprecatedModuleProxy(globals(), "elevenlabs", "elevenlabs.tts") +sys.modules[__name__] = DeprecatedModuleProxy(globals(), "elevenlabs", "elevenlabs.[stt,tts]") From da4f30cb6dad37d9e85ed605bf75c54b85bb6a36 Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Tue, 7 Oct 2025 17:28:14 -0300 Subject: [PATCH 009/158] Allowing to update smallwebrtc and whatsapp properties. --- .../transports/smallwebrtc/request_handler.py | 4 ++++ src/pipecat/transports/whatsapp/api.py | 8 ++++++++ src/pipecat/transports/whatsapp/client.py | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/pipecat/transports/smallwebrtc/request_handler.py b/src/pipecat/transports/smallwebrtc/request_handler.py index 3a6c77843..00d9ebb6a 100644 --- a/src/pipecat/transports/smallwebrtc/request_handler.py +++ b/src/pipecat/transports/smallwebrtc/request_handler.py @@ -116,6 +116,10 @@ class SmallWebRTCRequestHandler: detail="Cannot create new connection with existing connection active", ) + def update_ice_servers(self, ice_servers: Optional[List[IceServer]] = None): + """Update the list of ICE servers used for WebRTC connections.""" + self._ice_servers = ice_servers + async def handle_web_request( self, request: SmallWebRTCRequest, diff --git a/src/pipecat/transports/whatsapp/api.py b/src/pipecat/transports/whatsapp/api.py index b7e9388ee..b06ac5c6b 100644 --- a/src/pipecat/transports/whatsapp/api.py +++ b/src/pipecat/transports/whatsapp/api.py @@ -241,6 +241,14 @@ class WhatsAppApi: self._whatsapp_url = f"{self.BASE_URL}{phone_number_id}/calls" self._whatsapp_token = whatsapp_token + def update_whatsapp_token(self, whatsapp_token: str): + """Update the WhatsApp access token for authentication.""" + self._whatsapp_token = whatsapp_token + + def update_whatsapp_phone_number_id(self, phone_number_id: str): + """Update the WhatsApp phone number ID for authentication.""" + self._phone_number_id = phone_number_id + async def answer_call_to_whatsapp(self, call_id: str, action: str, sdp: str, from_: str): """Answer an incoming WhatsApp call. diff --git a/src/pipecat/transports/whatsapp/client.py b/src/pipecat/transports/whatsapp/client.py index f6fd83e7b..f48174f90 100644 --- a/src/pipecat/transports/whatsapp/client.py +++ b/src/pipecat/transports/whatsapp/client.py @@ -73,6 +73,22 @@ class WhatsAppClient: else: self._ice_servers = ice_servers + def update_ice_servers(self, ice_servers: Optional[List[IceServer]] = None): + """Update the list of ICE servers used for WebRTC connections.""" + self._ice_servers = ice_servers + + def update_whatsapp_secret(self, whatsapp_secret: Optional[str] = None): + """Update the WhatsApp APP secret for validating that the webhook request came from WhatsApp.""" + self._whatsapp_secret = whatsapp_secret + + def update_whatsapp_token(self, whatsapp_token: str): + """Update the WhatsApp API access token.""" + self._whatsapp_api.update_whatsapp_token(whatsapp_token) + + def update_whatsapp_phone_number_id(self, phone_number_id: str): + """Update the WhatsApp phone number ID for authentication.""" + self._whatsapp_api.update_whatsapp_phone_number_id(phone_number_id) + async def terminate_all_calls(self) -> None: """Terminate all ongoing WhatsApp calls. From eddce460da1e01b8f2ca84b53a642d11d9c6928b Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 7 Oct 2025 16:40:01 -0400 Subject: [PATCH 010/158] Deprecate PlayHT TTS services --- CHANGELOG.md | 6 ++++++ scripts/evals/run-release-evals.py | 2 -- src/pipecat/services/playht/tts.py | 31 ++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 827e1fb88..ce801c3d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated default `GoogleLLMService` model to `gemini-2.5-flash`. +### Deprecated + +- PlayHT is shutting down their API on December 31st, 2025. As a result, + `PlayHTTTSService` and `PlayHTHttpTTSService` are deprecated and will be + removed in a future version. + ### Fixed - Fixed an issue with `AWSNovaSonicLLMService` where the client wouldn't diff --git a/scripts/evals/run-release-evals.py b/scripts/evals/run-release-evals.py index 3fc55d983..206cf848e 100644 --- a/scripts/evals/run-release-evals.py +++ b/scripts/evals/run-release-evals.py @@ -75,8 +75,6 @@ TESTS_07 = [ EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST, ), - ("07e-interruptible-playht.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), - ("07e-interruptible-playht-http.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), ("07f-interruptible-azure.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), ("07g-interruptible-openai.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), ("07h-interruptible-openpipe.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), diff --git a/src/pipecat/services/playht/tts.py b/src/pipecat/services/playht/tts.py index 0f23b7b5e..9288ebd59 100644 --- a/src/pipecat/services/playht/tts.py +++ b/src/pipecat/services/playht/tts.py @@ -14,6 +14,7 @@ import io import json import struct import uuid +import warnings from typing import AsyncGenerator, Optional import aiohttp @@ -110,6 +111,11 @@ def language_to_playht_language(language: Language) -> Optional[str]: class PlayHTTTSService(InterruptibleTTSService): """PlayHT WebSocket-based text-to-speech service. + .. deprecated:: 0.0.88 + + This class is deprecated and will be removed in a future version. + PlayHT is shutting down their API on December 31st, 2025. + Provides real-time text-to-speech synthesis using PlayHT's WebSocket API. Supports streaming audio generation with configurable voice engines and language settings. @@ -158,6 +164,15 @@ class PlayHTTTSService(InterruptibleTTSService): **kwargs, ) + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "PlayHT is shutting down their API on December 31st, 2025. " + "'PlayHTTTSService' is deprecated and will be removed in a future version.", + DeprecationWarning, + stacklevel=2, + ) + params = params or PlayHTTTSService.InputParams() self._api_key = api_key @@ -401,6 +416,11 @@ class PlayHTTTSService(InterruptibleTTSService): class PlayHTHttpTTSService(TTSService): """PlayHT HTTP-based text-to-speech service. + .. deprecated:: 0.0.88 + + This class is deprecated and will be removed in a future version. + PlayHT is shutting down their API on December 31st, 2025. + Provides text-to-speech synthesis using PlayHT's HTTP API for simpler, non-streaming synthesis. Suitable for use cases where streaming is not required and simpler integration is preferred. @@ -454,8 +474,6 @@ class PlayHTHttpTTSService(TTSService): # Warn about deprecated protocol parameter if explicitly provided if protocol: - import warnings - with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( @@ -464,6 +482,15 @@ class PlayHTHttpTTSService(TTSService): stacklevel=2, ) + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "PlayHT is shutting down their API on December 31st, 2025. " + "'PlayHTHttpTTSService' is deprecated and will be removed in a future version.", + DeprecationWarning, + stacklevel=2, + ) + params = params or PlayHTHttpTTSService.InputParams() self._user_id = user_id From 36da623352bdd6dc8e1d7e526ccbc317c35278db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 7 Oct 2025 14:12:12 -0700 Subject: [PATCH 011/158] update CHANGELOG for 0.0.88 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce801c3d0..0eff9c990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.0.88] - 2025-10-07 ### Added From d9aef5f916e4e66df370c3613144295ec3499127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 7 Oct 2025 14:29:27 -0700 Subject: [PATCH 012/158] some last release CHANGELOG updates --- CHANGELOG.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eff9c990..eed99d367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,14 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for Nano Banana models to `GoogleLLMService`. For example, you can now use the `gemini-2.5-flash-image` model to generate images. -- `PermissionError` is now caught if NLTK's `punkt_tab` can't be downloaded. - -- Added `HumeTTSService` for text-to-speech synthesis using Hume AI's - expressive voice models. Provides high-quality, emotionally expressive speech - synthesis with support for various voice models. Includes example in - `examples/foundational/07ad-interruptible-hume.py`. - -- Added `hume` optional dependency group for Hume AI TTS integration. +- Added `HumeTTSService` for text-to-speech synthesis using Hume AI's expressive + voice models. Provides high-quality, emotionally expressive speech synthesis + with support for various voice models. Includes example in + `examples/foundational/07ad-interruptible-hume.py`. Use with `uv pip install + pipecat-ai[hume]`. ### Changed @@ -36,6 +33,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue with `AWSNovaSonicLLMService` where the client wouldn't connect due to a breaking change in the AWS dependency chain. +- `PermissionError` is now caught if NLTK's `punkt_tab` can't be downloaded. + - Fixed an issue that would cause wrong user/assistant context ordering when using interruption strategies. From 066b77fba04047af9814c986b737c4603c21b47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 7 Oct 2025 15:01:28 -0700 Subject: [PATCH 013/158] README: add Pipecat TV reference --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e5f4b270a..3eb9bf346 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,10 @@ Looking for help debugging your pipeline and processors? Check out [Whisker](htt Love terminal applications? Check out [Tail](https://github.com/pipecat-ai/tail), a terminal dashboard for Pipecat. +### πŸ“ΊοΈ Pipecat TV Channel + +Catch new features, interviews, and how-tos on our [Pipecat TV](https://www.youtube.com/playlist?list=PLzU2zoMTQIHjqC3v4q2XVSR3hGSzwKFwH) channel. + ## 🎬 See it in action

From 7c15a8c800c63349812c91aa18dcffcb3badb76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 7 Oct 2025 17:42:35 -0700 Subject: [PATCH 014/158] Revert "fix context order when using interruption strategies" This reverts commit de8ee9692737225c9dbadb31201bf7d197587eda. --- src/pipecat/frames/frames.py | 23 +++++------------------ src/pipecat/pipeline/task.py | 16 ++-------------- src/pipecat/processors/frame_processor.py | 16 +++++++--------- tests/test_context_aggregators.py | 3 --- tests/test_dtmf_aggregator.py | 11 +---------- tests/test_frame_processor.py | 2 -- 6 files changed, 15 insertions(+), 56 deletions(-) diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index a871fe74b..2a9651b83 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -849,15 +849,13 @@ class FrameProcessorResumeUrgentFrame(SystemFrame): class InterruptionFrame(SystemFrame): """Frame indicating user started speaking (interruption detected). - Usually emitted by the pipeline task to indicate that all frame processors - should be interrrupted. - - Parameters: - pushed_by_task: Whether this interruption was pushed from the pipeline - task. + Emitted by the BaseInputTransport to indicate that a user has started + speaking (i.e. is interrupting). This is similar to + UserStartedSpeakingFrame except that it should be pushed concurrently + with other frames (so the order is not guaranteed). """ - pushed_by_task: bool = False + pass @dataclass @@ -888,17 +886,6 @@ class StartInterruptionFrame(InterruptionFrame): ) -@dataclass -class InterruptionCompletedFrame(SystemFrame): - """Frame indicating that the whole pipeline has been interrupted. - - This is emitted by the pipeline task when an InterruptionFrame has made it - all the way to the end of pipeline, interrupting all the frame processors. - """ - - pass - - @dataclass class UserStartedSpeakingFrame(SystemFrame): """Frame indicating user has started speaking. diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index d040beac1..9ce3baf7f 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -30,7 +30,6 @@ from pipecat.frames.frames import ( ErrorFrame, Frame, HeartbeatFrame, - InterruptionCompletedFrame, InterruptionFrame, InterruptionTaskFrame, MetricsFrame, @@ -692,11 +691,8 @@ class PipelineTask(BasePipelineTask): # bypassing the push queue and directly queue into the # pipeline. This is in case the push task is blocked waiting for a # pipeline-ending frame to finish traversing the pipeline. - interruption_frame = InterruptionFrame(pushed_by_task=True) - logger.debug( - f"{self}: received interruption task frame {frame}, pushing {interruption_frame}" - ) - await self._pipeline.queue_frame(interruption_frame) + logger.debug(f"{self}: received interruption task frame {frame}") + await self._pipeline.queue_frame(InterruptionFrame()) elif isinstance(frame, ErrorFrame): if frame.fatal: logger.error(f"A fatal error occurred: {frame}") @@ -740,14 +736,6 @@ class PipelineTask(BasePipelineTask): self._pipeline_end_event.set() elif isinstance(frame, CancelFrame): self._pipeline_end_event.set() - elif isinstance(frame, InterruptionFrame) and frame.pushed_by_task: - # If an interruption frame made it all the way to the end of the - # pipeline, send an InterruptionCompleteFrame. Note that we are - # bypassing the push queue and directly queue into the - # pipeline. This is in case the push task is blocked waiting for a - # pipeline-ending frame to finish traversing the pipeline. - logger.debug(f"{self}: interruption completed with {frame}") - await self._pipeline.queue_frame(InterruptionCompletedFrame()) elif isinstance(frame, HeartbeatFrame): await self._heartbeat_queue.put(frame) diff --git a/src/pipecat/processors/frame_processor.py b/src/pipecat/processors/frame_processor.py index 25dc2716e..0b55082aa 100644 --- a/src/pipecat/processors/frame_processor.py +++ b/src/pipecat/processors/frame_processor.py @@ -28,7 +28,6 @@ from pipecat.frames.frames import ( FrameProcessorPauseUrgentFrame, FrameProcessorResumeFrame, FrameProcessorResumeUrgentFrame, - InterruptionCompletedFrame, InterruptionFrame, InterruptionTaskFrame, StartFrame, @@ -572,9 +571,7 @@ class FrameProcessor(BaseObject): # frames and we will process the frame right away. This is because a # previous system frame might be waiting for the interruption frame and # it's blocking the input task. - if self._wait_for_interruption and isinstance( - frame, (InterruptionFrame, InterruptionCompletedFrame) - ): + if self._wait_for_interruption and isinstance(frame, InterruptionFrame): await self.__process_frame(frame, direction, callback) return @@ -664,17 +661,18 @@ class FrameProcessor(BaseObject): await self._call_event_handler("on_after_push_frame", frame) - # If we are waiting for an interruption and one completed, then we can - # unblock `push_interruption_task_frame_and_wait()`. - if self._wait_for_interruption and isinstance(frame, InterruptionCompletedFrame): + # If we are waiting for an interruption and we get an interruption, then + # we can unblock `push_interruption_task_frame_and_wait()`. + if self._wait_for_interruption and isinstance(frame, InterruptionFrame): self._wait_interruption_event.set() async def push_interruption_task_frame_and_wait(self): """Push an interruption task frame upstream and wait for the interruption. This function sends an `InterruptionTaskFrame` upstream to the pipeline - task and waits to receive an `InterruptionCompletedFrame`. This - guarantees the whole pipeline has been interrupted. + task and waits to receive the corresponding `InterruptionFrame`. When + the function finishes it is guaranteed that the `InterruptionFrame` has + been pushed downstream. """ self._wait_for_interruption = True diff --git a/tests/test_context_aggregators.py b/tests/test_context_aggregators.py index accbfd669..77b6acc87 100644 --- a/tests/test_context_aggregators.py +++ b/tests/test_context_aggregators.py @@ -5,7 +5,6 @@ # import json -import sys import unittest from typing import Any @@ -21,7 +20,6 @@ from pipecat.frames.frames import ( FunctionCallResultFrame, FunctionCallResultProperties, InterimTranscriptionFrame, - InterruptionCompletedFrame, InterruptionFrame, InterruptionTaskFrame, LLMFullResponseEndFrame, @@ -570,7 +568,6 @@ class BaseTestUserContextAggregator: BotStartedSpeakingFrame, UserStartedSpeakingFrame, InterruptionFrame, - InterruptionCompletedFrame, UserStoppedSpeakingFrame, *self.EXPECTED_CONTEXT_FRAMES, ] diff --git a/tests/test_dtmf_aggregator.py b/tests/test_dtmf_aggregator.py index 18082a992..c7590ae47 100644 --- a/tests/test_dtmf_aggregator.py +++ b/tests/test_dtmf_aggregator.py @@ -10,7 +10,6 @@ from pipecat.audio.dtmf.types import KeypadEntry from pipecat.frames.frames import ( EndFrame, InputDTMFFrame, - InterruptionCompletedFrame, InterruptionFrame, TranscriptionFrame, ) @@ -31,7 +30,6 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, InputDTMFFrame, InputDTMFFrame, InputDTMFFrame, @@ -64,12 +62,10 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, # First aggregation "12" InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, TranscriptionFrame, # Second aggregation "3" ] @@ -102,13 +98,11 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, InputDTMFFrame, InputDTMFFrame, TranscriptionFrame, # "12#" InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, # "45" ] @@ -138,7 +132,6 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, # Should flush before EndFrame EndFrame, @@ -167,7 +160,6 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, InputDTMFFrame, TranscriptionFrame, ] @@ -195,7 +187,6 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InputDTMFFrame, InterruptionFrame, - InterruptionCompletedFrame, InputDTMFFrame, InputDTMFFrame, TranscriptionFrame, @@ -233,7 +224,7 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): # All the InputDTMFFrames plus one TranscriptionFrame expected_down_frames = ( - [InputDTMFFrame, InterruptionFrame, InterruptionCompletedFrame] + [InputDTMFFrame, InterruptionFrame] + [InputDTMFFrame] * (len(frames_to_send) - 1) + [TranscriptionFrame] ) diff --git a/tests/test_frame_processor.py b/tests/test_frame_processor.py index 5c1ee1b38..d0072e5fb 100644 --- a/tests/test_frame_processor.py +++ b/tests/test_frame_processor.py @@ -10,7 +10,6 @@ import unittest from pipecat.frames.frames import ( EndFrame, Frame, - InterruptionCompletedFrame, InterruptionFrame, OutputTransportMessageUrgentFrame, TextFrame, @@ -102,7 +101,6 @@ class TestFrameProcessor(unittest.IsolatedAsyncioTestCase): expected_down_frames = [ InterruptionFrame, InterruptionFrame, - InterruptionCompletedFrame, OutputTransportMessageUrgentFrame, EndFrame, ] From 0669daec3dbae394e5466b039abd0d44fde1091e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 7 Oct 2025 17:44:10 -0700 Subject: [PATCH 015/158] update CHANGELOG for 0.0.89 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eed99d367..926cff71d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.0.89] - 2025-10-07 + +### Fixed + +- Reverted a change that was causing pipelines to be frozen when using + interruption strategies. + ## [0.0.88] - 2025-10-07 ### Added From 92087bdfa8b02fc3fb56b16cae0787e0820bba00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 7 Oct 2025 18:08:18 -0700 Subject: [PATCH 016/158] update CHANGELOG --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 926cff71d..5bbbe624a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Reverted a change that was causing pipelines to be frozen when using - interruption strategies. +- Reverted a change introduced in 0.0.88 that was causing pipelines to be frozen + when using interruption strategies and processors that block interruption + frames (e.g. `STTMuteFilter`). ## [0.0.88] - 2025-10-07 From 62b7c3d3b286f76e3c63f754c838d45f8708c7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 7 Oct 2025 18:36:38 -0700 Subject: [PATCH 017/158] PipelineTask: add on_pipeline_error event --- CHANGELOG.md | 13 +++++++++++++ src/pipecat/pipeline/task.py | 14 ++++++++++++-- tests/test_pipeline.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bbbe624a..0edd55d07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Added `on_pipeline_error` event to `PipelineTask`. This event will get fired + when an `ErrorFrame` is pushed (use `FrameProcessor.push_error()`). + + ```python + @task.event_handler("on_pipeline_error") + async def on_pipeline_error(task: PipelineTask, frame: ErrorFrame): + ... + ``` + ## [0.0.89] - 2025-10-07 ### Fixed diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index 9ce3baf7f..f6d17262e 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -138,6 +138,8 @@ class PipelineTask(BasePipelineTask): Use this event for cleanup, logging, or post-processing tasks. Users can inspect the frame if they need to handle specific cases. + - on_pipeline_error: Called when an error occurs with ErrorFrame + Example:: @task.event_handler("on_frame_reached_upstream") @@ -148,9 +150,17 @@ class PipelineTask(BasePipelineTask): async def on_pipeline_idle_timeout(task): ... + @task.event_handler("on_pipeline_started") + async def on_pipeline_started(task, frame): + ... + @task.event_handler("on_pipeline_finished") async def on_pipeline_finished(task, frame): ... + + @task.event_handler("on_pipeline_error") + async def on_pipeline_error(task, frame): + ... """ def __init__( @@ -288,6 +298,7 @@ class PipelineTask(BasePipelineTask): self._register_event_handler("on_pipeline_ended") self._register_event_handler("on_pipeline_cancelled") self._register_event_handler("on_pipeline_finished") + self._register_event_handler("on_pipeline_error") @property def params(self) -> PipelineParams: @@ -694,12 +705,11 @@ class PipelineTask(BasePipelineTask): logger.debug(f"{self}: received interruption task frame {frame}") await self._pipeline.queue_frame(InterruptionFrame()) elif isinstance(frame, ErrorFrame): + await self._call_event_handler("on_pipeline_error", frame) if frame.fatal: logger.error(f"A fatal error occurred: {frame}") # Cancel all tasks downstream. await self.queue_frame(CancelFrame()) - # Tell the task we should stop. - await self.queue_frame(StopTaskFrame()) else: logger.warning(f"{self}: Something went wrong: {frame}") diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index fda6fa63e..df4af49b9 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -11,6 +11,7 @@ import unittest from pipecat.frames.frames import ( CancelFrame, EndFrame, + ErrorFrame, Frame, HeartbeatFrame, InputAudioRawFrame, @@ -450,3 +451,34 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) except asyncio.CancelledError: assert cancelled + + async def test_task_error(self): + class ErrorProcessor(FrameProcessor): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + + if isinstance(frame, TextFrame): + await self.push_error(ErrorFrame("Boo!")) + + await self.push_frame(frame, direction) + + error_received = False + + pipeline = Pipeline([ErrorProcessor()]) + task = PipelineTask(pipeline) + + @task.event_handler("on_pipeline_error") + async def on_pipeline_error(task: PipelineTask, frame: ErrorFrame): + nonlocal error_received + error_received = True + await task.cancel() + + await task.queue_frame(TextFrame(text="Hello from Pipecat!")) + + try: + await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) + except asyncio.CancelledError: + assert error_received From c85a3f0b947af4dcff34fa39603f8c3264ab225f Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 8 Oct 2025 08:32:35 -0400 Subject: [PATCH 018/158] Add service_tier to BaseOpenAILLMService --- CHANGELOG.md | 8 ++++++-- src/pipecat/services/openai/base_llm.py | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0edd55d07..75bb875da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ... ``` +- Added a `service_tier` `InputParam` to the `BaseOpenAILLMService`. This + parameter can influence the latency of the response. For example `"priority"` + will result in faster completions, but in exchange for a higher price. + ## [0.0.89] - 2025-10-07 ### Fixed @@ -36,8 +40,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `HumeTTSService` for text-to-speech synthesis using Hume AI's expressive voice models. Provides high-quality, emotionally expressive speech synthesis with support for various voice models. Includes example in - `examples/foundational/07ad-interruptible-hume.py`. Use with `uv pip install - pipecat-ai[hume]`. + `examples/foundational/07ad-interruptible-hume.py`. Use with: + `uv pip install pipecat-ai[hume]`. ### Changed diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 5c48f86e9..d020e1106 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -66,6 +66,7 @@ class BaseOpenAILLMService(LLMService): top_p: Top-p (nucleus) sampling parameter (0.0 to 1.0). max_tokens: Maximum tokens in response (deprecated, use max_completion_tokens). max_completion_tokens: Maximum completion tokens to generate. + service_tier: Service tier to use (e.g., "auto", "flex", "priority"). extra: Additional model-specific parameters. """ @@ -83,6 +84,7 @@ class BaseOpenAILLMService(LLMService): top_p: Optional[float] = Field(default_factory=lambda: NOT_GIVEN, ge=0.0, le=1.0) max_tokens: Optional[int] = Field(default_factory=lambda: NOT_GIVEN, ge=1) max_completion_tokens: Optional[int] = Field(default_factory=lambda: NOT_GIVEN, ge=1) + service_tier: Optional[str] = Field(default_factory=lambda: NOT_GIVEN) extra: Optional[Dict[str, Any]] = Field(default_factory=dict) def __init__( @@ -125,6 +127,7 @@ class BaseOpenAILLMService(LLMService): "top_p": params.top_p, "max_tokens": params.max_tokens, "max_completion_tokens": params.max_completion_tokens, + "service_tier": params.service_tier, "extra": params.extra if isinstance(params.extra, dict) else {}, } self._retry_timeout_secs = retry_timeout_secs @@ -236,6 +239,7 @@ class BaseOpenAILLMService(LLMService): "top_p": self._settings["top_p"], "max_tokens": self._settings["max_tokens"], "max_completion_tokens": self._settings["max_completion_tokens"], + "service_tier": self._settings["service_tier"], } # Messages, tools, tool_choice From d87b6189ba29b57c51d97ed6df98058af869c3fe Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 1 Oct 2025 15:31:50 -0400 Subject: [PATCH 019/158] Update `GeminiMultimodalLiveLLMService` to use the `google-genai` library, which is the new recommended approach for interfacing with Gemini Live. --- .../services/gemini_multimodal_live/events.py | 541 +---------------- .../services/gemini_multimodal_live/gemini.py | 542 ++++++++---------- 2 files changed, 269 insertions(+), 814 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/events.py b/src/pipecat/services/gemini_multimodal_live/events.py index 1766cf806..0d5ce4de5 100644 --- a/src/pipecat/services/gemini_multimodal_live/events.py +++ b/src/pipecat/services/gemini_multimodal_live/events.py @@ -6,525 +6,22 @@ """Event models and utilities for Google Gemini Multimodal Live API.""" -import base64 -import io -import json -from enum import Enum -from typing import List, Literal, Optional - -from PIL import Image -from pydantic import BaseModel, Field - -from pipecat.frames.frames import ImageRawFrame - -# -# Client events -# - - -class MediaChunk(BaseModel): - """Represents a chunk of media data for transmission. - - Parameters: - mimeType: MIME type of the media content. - data: Base64-encoded media data. - """ - - mimeType: str - data: str - - -class ContentPart(BaseModel): - """Represents a part of content that can contain text or media. - - Parameters: - text: Text content. Defaults to None. - inlineData: Inline media data. Defaults to None. - """ - - text: Optional[str] = Field(default=None, validate_default=False) - inlineData: Optional[MediaChunk] = Field(default=None, validate_default=False) - fileData: Optional["FileData"] = Field(default=None, validate_default=False) - - -class FileData(BaseModel): - """Represents a file reference in the Gemini File API.""" - - mimeType: str - fileUri: str - - -ContentPart.model_rebuild() # Rebuild model to resolve forward reference - - -class Turn(BaseModel): - """Represents a conversational turn in the dialogue. - - Parameters: - role: The role of the speaker, either "user" or "model". Defaults to "user". - parts: List of content parts that make up the turn. - """ - - role: Literal["user", "model"] = "user" - parts: List[ContentPart] - - -class StartSensitivity(str, Enum): - """Determines how start of speech is detected.""" - - UNSPECIFIED = "START_SENSITIVITY_UNSPECIFIED" # Default is HIGH - HIGH = "START_SENSITIVITY_HIGH" # Detect start of speech more often - LOW = "START_SENSITIVITY_LOW" # Detect start of speech less often - - -class EndSensitivity(str, Enum): - """Determines how end of speech is detected.""" - - UNSPECIFIED = "END_SENSITIVITY_UNSPECIFIED" # Default is HIGH - HIGH = "END_SENSITIVITY_HIGH" # End speech more often - LOW = "END_SENSITIVITY_LOW" # End speech less often - - -class AutomaticActivityDetection(BaseModel): - """Configures automatic detection of voice activity. - - Parameters: - disabled: Whether automatic activity detection is disabled. Defaults to None. - start_of_speech_sensitivity: Sensitivity for detecting speech start. Defaults to None. - prefix_padding_ms: Padding before speech start in milliseconds. Defaults to None. - end_of_speech_sensitivity: Sensitivity for detecting speech end. Defaults to None. - silence_duration_ms: Duration of silence to detect speech end. Defaults to None. - """ - - disabled: Optional[bool] = None - start_of_speech_sensitivity: Optional[StartSensitivity] = None - prefix_padding_ms: Optional[int] = None - end_of_speech_sensitivity: Optional[EndSensitivity] = None - silence_duration_ms: Optional[int] = None - - -class RealtimeInputConfig(BaseModel): - """Configures the realtime input behavior. - - Parameters: - automatic_activity_detection: Voice activity detection configuration. Defaults to None. - """ - - automatic_activity_detection: Optional[AutomaticActivityDetection] = None - - -class RealtimeInput(BaseModel): - """Contains realtime input media chunks and text. - - Parameters: - mediaChunks: List of media chunks for realtime processing. - text: Text for realtime processing. - """ - - mediaChunks: Optional[List[MediaChunk]] = None - text: Optional[str] = None - - -class ClientContent(BaseModel): - """Content sent from client to the Gemini Live API. - - Parameters: - turns: List of conversation turns. Defaults to None. - turnComplete: Whether the client's turn is complete. Defaults to False. - """ - - turns: Optional[List[Turn]] = None - turnComplete: bool = False - - -class AudioInputMessage(BaseModel): - """Message containing audio input data. - - Parameters: - realtimeInput: Realtime input containing audio chunks. - """ - - realtimeInput: RealtimeInput - - @classmethod - def from_raw_audio(cls, raw_audio: bytes, sample_rate: int) -> "AudioInputMessage": - """Create an audio input message from raw audio data. - - Args: - raw_audio: Raw audio bytes. - sample_rate: Audio sample rate in Hz. - - Returns: - AudioInputMessage instance with encoded audio data. - """ - data = base64.b64encode(raw_audio).decode("utf-8") - return cls( - realtimeInput=RealtimeInput( - mediaChunks=[MediaChunk(mimeType=f"audio/pcm;rate={sample_rate}", data=data)] - ) - ) - - -class VideoInputMessage(BaseModel): - """Message containing video/image input data. - - Parameters: - realtimeInput: Realtime input containing video/image chunks. - """ - - realtimeInput: RealtimeInput - - @classmethod - def from_image_frame(cls, frame: ImageRawFrame) -> "VideoInputMessage": - """Create a video input message from an image frame. - - Args: - frame: Image frame to encode. - - Returns: - VideoInputMessage instance with encoded image data. - """ - buffer = io.BytesIO() - Image.frombytes(frame.format, frame.size, frame.image).save(buffer, format="JPEG") - data = base64.b64encode(buffer.getvalue()).decode("utf-8") - return cls( - realtimeInput=RealtimeInput(mediaChunks=[MediaChunk(mimeType=f"image/jpeg", data=data)]) - ) - - -class TextInputMessage(BaseModel): - """Message containing text input data.""" - - realtimeInput: RealtimeInput - - @classmethod - def from_text(cls, text: str) -> "TextInputMessage": - """Create a text input message from a string. - - Args: - text: The text to send. - - Returns: - A TextInputMessage instance. - """ - return cls(realtimeInput=RealtimeInput(text=text)) - - -class ClientContentMessage(BaseModel): - """Message containing client content for the API. - - Parameters: - clientContent: The client content to send. - """ - - clientContent: ClientContent - - -class SystemInstruction(BaseModel): - """System instruction for the model. - - Parameters: - parts: List of content parts that make up the system instruction. - """ - - parts: List[ContentPart] - - -class AudioTranscriptionConfig(BaseModel): - """Configuration for audio transcription.""" - - pass - - -class Setup(BaseModel): - """Setup configuration for the Gemini Live session. - - Parameters: - model: Model identifier to use. - system_instruction: System instruction for the model. Defaults to None. - tools: List of available tools/functions. Defaults to None. - generation_config: Generation configuration parameters. Defaults to None. - input_audio_transcription: Input audio transcription config. Defaults to None. - output_audio_transcription: Output audio transcription config. Defaults to None. - realtime_input_config: Realtime input configuration. Defaults to None. - """ - - model: str - system_instruction: Optional[SystemInstruction] = None - tools: Optional[List[dict]] = None - generation_config: Optional[dict] = None - input_audio_transcription: Optional[AudioTranscriptionConfig] = None - output_audio_transcription: Optional[AudioTranscriptionConfig] = None - realtime_input_config: Optional[RealtimeInputConfig] = None - - -class Config(BaseModel): - """Configuration message for session setup. - - Parameters: - setup: Setup configuration for the session. - """ - - setup: Setup - - -# -# Grounding metadata models -# - - -class SearchEntryPoint(BaseModel): - """Represents the search entry point with rendered content for search suggestions.""" - - renderedContent: Optional[str] = None - - -class WebSource(BaseModel): - """Represents a web source from grounding chunks.""" - - uri: Optional[str] = None - title: Optional[str] = None - - -class GroundingChunk(BaseModel): - """Represents a grounding chunk containing web source information.""" - - web: Optional[WebSource] = None - - -class GroundingSegment(BaseModel): - """Represents a segment of text that is grounded.""" - - startIndex: Optional[int] = None - endIndex: Optional[int] = None - text: Optional[str] = None - - -class GroundingSupport(BaseModel): - """Represents support information for grounded text segments.""" - - segment: Optional[GroundingSegment] = None - groundingChunkIndices: Optional[List[int]] = None - confidenceScores: Optional[List[float]] = None - - -class GroundingMetadata(BaseModel): - """Represents grounding metadata from Google Search.""" - - searchEntryPoint: Optional[SearchEntryPoint] = None - groundingChunks: Optional[List[GroundingChunk]] = None - groundingSupports: Optional[List[GroundingSupport]] = None - webSearchQueries: Optional[List[str]] = None - - -# -# Server events -# - - -class SetupComplete(BaseModel): - """Indicates that session setup is complete.""" - - pass - - -class InlineData(BaseModel): - """Inline data embedded in server responses. - - Parameters: - mimeType: MIME type of the data. - data: Base64-encoded data content. - """ - - mimeType: str - data: str - - -class Part(BaseModel): - """Part of a server response containing data or text. - - Parameters: - inlineData: Inline binary data. Defaults to None. - text: Text content. Defaults to None. - """ - - inlineData: Optional[InlineData] = None - text: Optional[str] = None - - -class ModelTurn(BaseModel): - """Represents a turn from the model in the conversation. - - Parameters: - parts: List of content parts in the model's response. - """ - - parts: List[Part] - - -class ServerContentInterrupted(BaseModel): - """Indicates server content was interrupted. - - Parameters: - interrupted: Whether the content was interrupted. - """ - - interrupted: bool - - -class ServerContentTurnComplete(BaseModel): - """Indicates the server's turn is complete. - - Parameters: - turnComplete: Whether the turn is complete. - """ - - turnComplete: bool - - -class BidiGenerateContentTranscription(BaseModel): - """Transcription data from bidirectional content generation. - - Parameters: - text: The transcribed text content. - """ - - text: str - - -class ServerContent(BaseModel): - """Content sent from server to client. - - Parameters: - modelTurn: Model's conversational turn. Defaults to None. - interrupted: Whether content was interrupted. Defaults to None. - turnComplete: Whether the turn is complete. Defaults to None. - inputTranscription: Transcription of input audio. Defaults to None. - outputTranscription: Transcription of output audio. Defaults to None. - """ - - modelTurn: Optional[ModelTurn] = None - interrupted: Optional[bool] = None - turnComplete: Optional[bool] = None - inputTranscription: Optional[BidiGenerateContentTranscription] = None - outputTranscription: Optional[BidiGenerateContentTranscription] = None - groundingMetadata: Optional[GroundingMetadata] = None - - -class FunctionCall(BaseModel): - """Represents a function call from the model. - - Parameters: - id: Unique identifier for the function call. - name: Name of the function to call. - args: Arguments to pass to the function. - """ - - id: str - name: str - args: dict - - -class ToolCall(BaseModel): - """Contains one or more function calls. - - Parameters: - functionCalls: List of function calls to execute. - """ - - functionCalls: List[FunctionCall] - - -class Modality(str, Enum): - """Modality types in token counts.""" - - UNSPECIFIED = "MODALITY_UNSPECIFIED" - TEXT = "TEXT" - IMAGE = "IMAGE" - AUDIO = "AUDIO" - VIDEO = "VIDEO" - - -class ModalityTokenCount(BaseModel): - """Token count for a specific modality. - - Parameters: - modality: The modality type. - tokenCount: Number of tokens for this modality. - """ - - modality: Modality - tokenCount: int - - -class UsageMetadata(BaseModel): - """Usage metadata about the API response. - - Parameters: - promptTokenCount: Number of tokens in the prompt. Defaults to None. - cachedContentTokenCount: Number of cached content tokens. Defaults to None. - responseTokenCount: Number of tokens in the response. Defaults to None. - toolUsePromptTokenCount: Number of tokens for tool use prompts. Defaults to None. - thoughtsTokenCount: Number of tokens for model thoughts. Defaults to None. - totalTokenCount: Total number of tokens used. Defaults to None. - promptTokensDetails: Detailed breakdown of prompt tokens by modality. Defaults to None. - cacheTokensDetails: Detailed breakdown of cache tokens by modality. Defaults to None. - responseTokensDetails: Detailed breakdown of response tokens by modality. Defaults to None. - toolUsePromptTokensDetails: Detailed breakdown of tool use tokens by modality. Defaults to None. - """ - - promptTokenCount: Optional[int] = None - cachedContentTokenCount: Optional[int] = None - responseTokenCount: Optional[int] = None - toolUsePromptTokenCount: Optional[int] = None - thoughtsTokenCount: Optional[int] = None - totalTokenCount: Optional[int] = None - promptTokensDetails: Optional[List[ModalityTokenCount]] = None - cacheTokensDetails: Optional[List[ModalityTokenCount]] = None - responseTokensDetails: Optional[List[ModalityTokenCount]] = None - toolUsePromptTokensDetails: Optional[List[ModalityTokenCount]] = None - - -class ServerEvent(BaseModel): - """Server event received from the Gemini Live API. - - Parameters: - setupComplete: Setup completion notification. Defaults to None. - serverContent: Content from the server. Defaults to None. - toolCall: Tool/function call request. Defaults to None. - usageMetadata: Token usage metadata. Defaults to None. - """ - - setupComplete: Optional[SetupComplete] = None - serverContent: Optional[ServerContent] = None - toolCall: Optional[ToolCall] = None - usageMetadata: Optional[UsageMetadata] = None - - -def parse_server_event(str): - """Parse a server event from JSON string. - - Args: - str: JSON string containing the server event. - - Returns: - ServerEvent instance if parsing succeeds, None otherwise. - """ - try: - evt = json.loads(str) - return ServerEvent.model_validate(evt) - except Exception as e: - print(f"Error parsing server event: {e}") - return None - - -class ContextWindowCompressionConfig(BaseModel): - """Configuration for context window compression. - - Parameters: - sliding_window: Whether to use sliding window compression. Defaults to True. - trigger_tokens: Token count threshold to trigger compression. Defaults to None. - """ - - sliding_window: Optional[bool] = Field(default=True) - trigger_tokens: Optional[int] = Field(default=None) +from loguru import logger + +try: + from google.genai.types import ( + EndSensitivity as _EndSensitivity, + ) + from google.genai.types import ( + StartSensitivity as _StartSensitivity, + ) +except ModuleNotFoundError as e: + logger.error(f"Exception: {e}") + logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") + raise Exception(f"Missing module: {e}") + +# These aliases are just here for backward compatibility, since we used to +# define public-facing StartSensitivity and EndSensitivity enums in this +# module. +StartSensitivity = _StartSensitivity +EndSensitivity = _EndSensitivity diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index df560358f..7980f76ba 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -12,6 +12,7 @@ voice transcription, streaming responses, and tool usage. """ import base64 +import io import json import time from dataclasses import dataclass @@ -19,6 +20,7 @@ from enum import Enum from typing import Any, Dict, List, Optional, Union from loguru import logger +from PIL import Image from pydantic import BaseModel, Field from pipecat.adapters.schemas.tools_schema import ToolsSchema @@ -28,7 +30,6 @@ from pipecat.frames.frames import ( BotStoppedSpeakingFrame, CancelFrame, EndFrame, - ErrorFrame, Frame, InputAudioRawFrame, InputImageRawFrame, @@ -72,11 +73,33 @@ from pipecat.utils.string import match_endofsentence from pipecat.utils.time import time_now_iso8601 from pipecat.utils.tracing.service_decorators import traced_gemini_live, traced_stt -from . import events from .file_api import GeminiFileAPI try: - from websockets.asyncio.client import connect as websocket_connect + from google.genai import Client + from google.genai.live import AsyncSession + from google.genai.types import ( + AudioTranscriptionConfig, + AutomaticActivityDetection, + Blob, + Content, + ContextWindowCompressionConfig, + EndSensitivity, + FileData, + FunctionResponse, + GenerationConfig, + GroundingMetadata, + HttpOptions, + LiveConnectConfig, + LiveServerMessage, + Modality, + Part, + RealtimeInputConfig, + SlidingWindow, + SpeechConfig, + StartSensitivity, + VoiceConfig, + ) except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") @@ -246,13 +269,13 @@ class GeminiMultimodalLiveContext(OpenAILLMContext): self.messages.append(message) logger.info(f"Added file reference to context: {file_uri}") - def get_messages_for_initializing_history(self): + def get_messages_for_initializing_history(self) -> List[Content]: """Get messages formatted for Gemini history initialization. Returns: List of messages in Gemini format for conversation history. """ - messages = [] + messages: List[Content] = [] for item in self.messages: role = item.get("role") @@ -263,29 +286,28 @@ class GeminiMultimodalLiveContext(OpenAILLMContext): role = "model" content = item.get("content") - parts = [] + parts: List[Part] = [] if isinstance(content, str): - parts = [{"text": content}] + parts = [Part(text=content)] elif isinstance(content, list): for part in content: if part.get("type") == "text": - parts.append({"text": part.get("text")}) + parts.append(Part(text=part.get("text"))) elif part.get("type") == "file_data": file_data = part.get("file_data", {}) - parts.append( - { - "fileData": { - "mimeType": file_data.get("mime_type"), - "fileUri": file_data.get("file_uri"), - } - } + Part( + file_data=FileData( + mime_type=file_data.get("mime_type"), + file_uri=file_data.get("file_uri"), + ) + ) ) else: logger.warning(f"Unsupported content type: {str(part)[:80]}") else: logger.warning(f"Unsupported content type: {str(content)[:80]}") - messages.append({"role": role, "parts": parts}) + messages.append(Content(role=role, parts=parts)) return messages @@ -411,8 +433,8 @@ class GeminiVADParams(BaseModel): """ disabled: Optional[bool] = Field(default=None) - start_sensitivity: Optional[events.StartSensitivity] = Field(default=None) - end_sensitivity: Optional[events.EndSensitivity] = Field(default=None) + start_sensitivity: Optional[StartSensitivity] = Field(default=None) + end_sensitivity: Optional[EndSensitivity] = Field(default=None) prefix_padding_ms: Optional[int] = Field(default=None) silence_duration_ms: Optional[int] = Field(default=None) @@ -441,7 +463,7 @@ class InputParams(BaseModel): temperature: Sampling temperature (0.0-2.0). Defaults to None. top_k: Top-k sampling parameter. Must be >= 0. Defaults to None. top_p: Top-p sampling parameter (0.0-1.0). Defaults to None. - modalities: Response modalities. Defaults to AUDIO. + modalities: Response modalities. Defaults to "AUDIO". language: Language for generation. Defaults to EN_US. media_resolution: Media resolution setting. Defaults to UNSPECIFIED. vad: Voice activity detection parameters. Defaults to None. @@ -455,8 +477,8 @@ class InputParams(BaseModel): temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0) top_k: Optional[int] = Field(default=None, ge=0) top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0) - modalities: Optional[GeminiMultimodalModalities] = Field( - default=GeminiMultimodalModalities.AUDIO + modalities: Optional[Union[List[GeminiMultimodalModalities], GeminiMultimodalModalities]] = ( + Field(default=GeminiMultimodalModalities.AUDIO) ) language: Optional[Language] = Field(default=Language.EN_US) media_resolution: Optional[GeminiMediaResolution] = Field( @@ -492,6 +514,7 @@ class GeminiMultimodalLiveLLMService(LLMService): params: Optional[InputParams] = None, inference_on_context_initialization: bool = True, file_api_base_url: str = "https://generativelanguage.googleapis.com/v1beta/files", + http_options: Optional[HttpOptions] = None, **kwargs, ): """Initialize the Gemini Multimodal Live LLM service. @@ -509,6 +532,7 @@ class GeminiMultimodalLiveLLMService(LLMService): inference_on_context_initialization: Whether to generate a response when context is first set. Defaults to True. file_api_base_url: Base URL for the Gemini File API. Defaults to the official endpoint. + http_options: HTTP options for the client. **kwargs: Additional arguments passed to parent LLMService. """ super().__init__(base_url=base_url, **kwargs) @@ -516,7 +540,6 @@ class GeminiMultimodalLiveLLMService(LLMService): params = params or InputParams() self._last_sent_time = 0 - self._api_key = api_key self._base_url = base_url self.set_model_name(model) self._voice_id = voice_id @@ -530,12 +553,12 @@ class GeminiMultimodalLiveLLMService(LLMService): self._audio_input_paused = start_audio_paused self._video_input_paused = start_video_paused self._context = None - self._websocket = None - self._receive_task = None + self._create_client(api_key, http_options) + self._session: AsyncSession = None + self._connection_task = None self._disconnecting = False - self._api_session_ready = False - self._run_llm_when_api_session_ready = False + self._run_llm_when_session_ready = False self._user_is_speaking = False self._bot_is_speaking = False @@ -578,6 +601,9 @@ class GeminiMultimodalLiveLLMService(LLMService): self._search_result_buffer = "" self._accumulated_grounding_metadata = None + def _create_client(self, api_key: str, http_options: Optional[HttpOptions] = None): + self._client = Client(api_key=api_key, http_options=http_options) + def can_generate_metrics(self) -> bool: """Check if the service can generate usage metrics. @@ -613,7 +639,9 @@ class GeminiMultimodalLiveLLMService(LLMService): """ self._video_input_paused = paused - def set_model_modalities(self, modalities: GeminiMultimodalModalities): + def set_model_modalities( + self, modalities: Union[List[GeminiMultimodalModalities], GeminiMultimodalModalities] + ): """Set the model response modalities. Args: @@ -656,7 +684,7 @@ class GeminiMultimodalLiveLLMService(LLMService): # async def start(self, frame: StartFrame): - """Start the service and establish websocket connection. + """Start the service and establish connection. Args: frame: The start frame. @@ -701,10 +729,9 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.start_ttfb_metrics() if self._needs_turn_complete_message: self._needs_turn_complete_message = False - evt = events.ClientContentMessage.model_validate( - {"clientContent": {"turnComplete": True}} - ) - await self.send_client_event(evt) + # NOTE: without this, the model ignores the context it's been + # seeded with before the user started speaking + await self._session.send_client_content(turn_complete=True) # # frame processing @@ -768,6 +795,11 @@ class GeminiMultimodalLiveLLMService(LLMService): # ignore this frame. Use the serverContent.turnComplete API message await self.push_frame(frame, direction) elif isinstance(frame, LLMMessagesAppendFrame): + # NOTE: handling LLMMessagesAppendFrame here in the LLMService is + # unusual - typically this would be handled in the user context + # aggregator. Leaving this handling here so that user code that + # uses this frame *without* a user context aggregator still works + # (we have an example that does just that, actually). await self._create_single_response(frame.messages) elif isinstance(frame, LLMUpdateSettingsFrame): await self._update_settings(frame.settings) @@ -776,199 +808,171 @@ class GeminiMultimodalLiveLLMService(LLMService): else: await self.push_frame(frame, direction) - # - # websocket communication - # - - async def send_client_event(self, event): - """Send a client event to the Gemini Live API. - - Args: - event: The event to send. - """ - await self._ws_send(event.model_dump(exclude_none=True)) - async def _connect(self): - """Establish WebSocket connection to Gemini Live API.""" - if self._websocket: - # Here we assume that if we have a websocket, we are connected. We + """Establish client connection to Gemini Live API.""" + if self._session: + # Here we assume that if we have a client, we are connected. We # handle disconnections in the send/recv code paths. return logger.info("Connecting to Gemini service") try: - logger.info(f"Connecting to wss://{self._base_url}") - uri = f"wss://{self._base_url}?key={self._api_key}" - self._websocket = await websocket_connect(uri=uri) - self._receive_task = self.create_task(self._receive_task_handler()) + # Handle modalities being specified as either a list or a single value + modalities = self._settings["modalities"] + if isinstance(modalities, GeminiMultimodalModalities): + modalities = [modalities] - # Create the basic configuration - config_data = { - "setup": { - "model": self._model_name, - "generation_config": { - "frequency_penalty": self._settings["frequency_penalty"], - "max_output_tokens": self._settings["max_tokens"], - "presence_penalty": self._settings["presence_penalty"], - "temperature": self._settings["temperature"], - "top_k": self._settings["top_k"], - "top_p": self._settings["top_p"], - "response_modalities": self._settings["modalities"].value, - "speech_config": { - "voice_config": { - "prebuilt_voice_config": {"voice_name": self._voice_id} - }, - "language_code": self._settings["language"], - }, - "media_resolution": self._settings["media_resolution"].value, - }, - "input_audio_transcription": {}, - "output_audio_transcription": {}, - } - } + # Assemble basic configuration + config = LiveConnectConfig( + generation_config=GenerationConfig( + frequency_penalty=self._settings["frequency_penalty"], + max_output_tokens=self._settings["max_tokens"], + presence_penalty=self._settings["presence_penalty"], + temperature=self._settings["temperature"], + top_k=self._settings["top_k"], + top_p=self._settings["top_p"], + response_modalities=[Modality(modality.value) for modality in modalities], + speech_config=SpeechConfig( + voice_config=VoiceConfig( + prebuilt_voice_config={"voice_name": self._voice_id} + ), + language_code=self._settings["language"], + ), + media_resolution=self._settings["media_resolution"].value, + ), + input_audio_transcription=AudioTranscriptionConfig(), + output_audio_transcription=AudioTranscriptionConfig(), + ) - # Add context window compression if enabled + # Add context window compression to configuration, if enabled if self._settings.get("context_window_compression", {}).get("enabled", False): - compression_config = {} + compression_config = ContextWindowCompressionConfig() + # Add sliding window (always true if compression is enabled) - compression_config["sliding_window"] = {} + compression_config.sliding_window = SlidingWindow() # Add trigger_tokens if specified trigger_tokens = self._settings.get("context_window_compression", {}).get( "trigger_tokens" ) if trigger_tokens is not None: - compression_config["trigger_tokens"] = trigger_tokens + compression_config.trigger_tokens = trigger_tokens - config_data["setup"]["context_window_compression"] = compression_config + config.context_window_compression = compression_config - # Add VAD configuration if provided + # Add VAD configuration to configuration, if provided if self._settings.get("vad"): - vad_config = {} + vad_config = AutomaticActivityDetection() vad_params = self._settings["vad"] + has_vad_settings = False # Only add parameters that are explicitly set if vad_params.disabled is not None: - vad_config["disabled"] = vad_params.disabled + vad_config.disabled = vad_params.disabled + has_vad_settings = True if vad_params.start_sensitivity: - vad_config["start_of_speech_sensitivity"] = vad_params.start_sensitivity.value + vad_config.start_of_speech_sensitivity = vad_params.start_sensitivity.value + has_vad_settings = True if vad_params.end_sensitivity: - vad_config["end_of_speech_sensitivity"] = vad_params.end_sensitivity.value + vad_config.end_of_speech_sensitivity = vad_params.end_sensitivity.value + has_vad_settings = True if vad_params.prefix_padding_ms is not None: - vad_config["prefix_padding_ms"] = vad_params.prefix_padding_ms + vad_config.prefix_padding_ms = vad_params.prefix_padding_ms + has_vad_settings = True if vad_params.silence_duration_ms is not None: - vad_config["silence_duration_ms"] = vad_params.silence_duration_ms + vad_config.silence_duration_ms = vad_params.silence_duration_ms + has_vad_settings = True # Only add automatic_activity_detection if we have VAD settings - if vad_config: - realtime_config = {"automatic_activity_detection": vad_config} + if has_vad_settings: + config.realtime_input_config = RealtimeInputConfig( + automatic_activity_detection=vad_config + ) - config_data["setup"]["realtime_input_config"] = realtime_config - - config = events.Config.model_validate(config_data) - - # Add system instruction if available + # Add system instruction to configuration, if provided system_instruction = self._system_instruction or "" if self._context and hasattr(self._context, "extract_system_instructions"): system_instruction += "\n" + self._context.extract_system_instructions() if system_instruction: logger.debug(f"Setting system instruction: {system_instruction}") - config.setup.system_instruction = events.SystemInstruction( - parts=[events.ContentPart(text=system_instruction)] - ) + config.system_instruction = system_instruction - # Add tools if available + # Add tools to configuration, if provided if self._tools: - logger.debug(f"Gemini is configuring to use tools{self._tools}") - config.setup.tools = self.get_llm_adapter().from_standard_tools(self._tools) + logger.debug(f"Setting tools: {self._tools}") + config.tools = self.get_llm_adapter().from_standard_tools(self._tools) - # Send the configuration - await self.send_client_event(config) + # Start the connection + self._connection_task = self.create_task(self._connection_task_handler(config=config)) except Exception as e: logger.error(f"{self} initialization error: {e}") - self._websocket = None + self._session = None + + async def _connection_task_handler(self, config: LiveConnectConfig): + async with self._client.aio.live.connect(model=self._model_name, config=config) as session: + logger.info("Connected to Gemini service") + + await self._handle_session_ready(session) + + while True: + try: + turn = self._session.receive() + async for message in turn: + if message.server_content and message.server_content.model_turn: + await self._handle_msg_model_turn(message) + elif ( + message.server_content + and message.server_content.turn_complete + and message.usage_metadata + ): + await self._handle_msg_turn_complete(message) + await self._handle_msg_usage_metadata(message) + elif message.server_content and message.server_content.input_transcription: + await self._handle_msg_input_transcription(message) + elif message.server_content and message.server_content.output_transcription: + await self._handle_msg_output_transcription(message) + elif message.server_content and message.server_content.grounding_metadata: + await self._handle_msg_grounding_metadata(message) + elif message.tool_call: + await self._handle_msg_tool_call(message) + except Exception as e: + logger.error(f"Error in receive loop: {type(e)}: {e}") + break async def _disconnect(self): """Disconnect from Gemini Live API and clean up resources.""" logger.info("Disconnecting from Gemini service") try: self._disconnecting = True - self._api_session_ready = False await self.stop_all_metrics() - if self._websocket: - await self._websocket.close() - self._websocket = None - if self._receive_task: - await self.cancel_task(self._receive_task, timeout=1.0) - self._receive_task = None + if self._connection_task: + await self.cancel_task(self._connection_task, timeout=1.0) + self._connection_task = None + if self._session: + await self._session.close() + self._session = None self._disconnecting = False except Exception as e: logger.error(f"{self} error disconnecting: {e}") - async def _ws_send(self, message): - """Send a message to the WebSocket connection.""" - # logger.debug(f"Sending message to websocket: {message}") - try: - if self._websocket: - await self._websocket.send(json.dumps(message)) - except Exception as e: - if self._disconnecting: - return - logger.error(f"Error sending message to websocket: {e}") - # In server-to-server contexts, a WebSocket error should be quite rare. Given how hard - # it is to recover from a send-side error with proper state management, and that exponential - # backoff for retries can have cost/stability implications for a service cluster, let's just - # treat a send-side error as fatal. - await self.push_error(ErrorFrame(error=f"Error sending client event: {e}", fatal=True)) - - # - # inbound server event handling - # todo: docs link here - # - - async def _receive_task_handler(self): - """Handle incoming messages from the WebSocket connection.""" - async for message in self._websocket: - evt = events.parse_server_event(message) - # logger.debug(f"Received event: {message[:500]}") - # logger.debug(f"Received event: {evt}") - - if evt.setupComplete: - await self._handle_evt_setup_complete(evt) - elif evt.serverContent and evt.serverContent.modelTurn: - await self._handle_evt_model_turn(evt) - elif evt.serverContent and evt.serverContent.turnComplete and evt.usageMetadata: - await self._handle_evt_turn_complete(evt) - await self._handle_evt_usage_metadata(evt) - elif evt.serverContent and evt.serverContent.inputTranscription: - await self._handle_evt_input_transcription(evt) - elif evt.serverContent and evt.serverContent.outputTranscription: - await self._handle_evt_output_transcription(evt) - elif evt.serverContent and evt.serverContent.groundingMetadata: - await self._handle_evt_grounding_metadata(evt) - elif evt.toolCall: - await self._handle_evt_tool_call(evt) - elif False: # !!! todo: error events? - await self._handle_evt_error(evt) - # errors are fatal, so exit the receive loop - return - - # - # - # - async def _send_user_audio(self, frame): """Send user audio frame to Gemini Live API.""" if self._audio_input_paused: return + + if not self._session: + return + # Send all audio to Gemini - evt = events.AudioInputMessage.from_raw_audio(frame.audio, frame.sample_rate) - await self.send_client_event(evt) + await self._session.send_realtime_input( + audio=Blob(data=frame.audio, mime_type=f"audio/pcm;rate={frame.sample_rate}") + ) + # Manage a buffer of audio to use for transcription audio = frame.audio if self._user_is_speaking: @@ -993,27 +997,34 @@ class GeminiMultimodalLiveLLMService(LLMService): Args: text: The text to send as user input. """ - evt = events.TextInputMessage.from_text(text) - await self.send_client_event(evt) + if not self._session: + return + + await self._session.send_realtime_input(text=text) async def _send_user_video(self, frame): """Send user video frame to Gemini Live API.""" if self._video_input_paused: return + if not self._session: + return + now = time.time() if now - self._last_sent_time < 1: return # Ignore if less than 1 second has passed self._last_sent_time = now # Update last sent time logger.debug(f"Sending video frame to Gemini: {frame}") - evt = events.VideoInputMessage.from_image_frame(frame) - await self.send_client_event(evt) + buffer = io.BytesIO() + Image.frombytes(frame.format, frame.size, frame.image).save(buffer, format="JPEG") + data = base64.b64encode(buffer.getvalue()).decode("utf-8") + await self._session.send_realtime_input(video=Blob(data=data, mime_type="image/jpeg")) async def _create_initial_response(self): """Create initial response based on context history.""" - if not self._api_session_ready: - self._run_llm_when_api_session_ready = True + if not self._session: + self._run_llm_when_session_ready = True return messages = self._context.get_messages_for_initializing_history() @@ -1024,70 +1035,31 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.start_ttfb_metrics() - evt = events.ClientContentMessage.model_validate( - { - "clientContent": { - "turns": messages, - "turnComplete": self._inference_on_context_initialization, - } - } + await self._session.send_client_content( + turns=messages, turn_complete=self._inference_on_context_initialization ) - await self.send_client_event(evt) + + # If we're generating a response right away upon initializing + # conversation history, set a flag saying that we need a turn complete + # message when the user stops speaking. if not self._inference_on_context_initialization: self._needs_turn_complete_message = True async def _create_single_response(self, messages_list): """Create a single response from a list of messages.""" - # Refactor to combine this logic with same logic in GeminiMultimodalLiveContext - messages = [] - for item in messages_list: - role = item.get("role") + # Create a throwaway context just for the purpose of getting messages + # in the right format + context = GeminiMultimodalLiveContext.upgrade(OpenAILLMContext(messages=messages_list)) + messages = context.get_messages_for_initializing_history() - if role == "system": - continue - - elif role == "assistant": - role = "model" - - content = item.get("content") - parts = [] - if isinstance(content, str): - parts = [{"text": content}] - elif isinstance(content, list): - for part in content: - if part.get("type") == "text": - parts.append({"text": part.get("text")}) - elif part.get("type") == "file_data": - file_data = part.get("file_data", {}) - - parts.append( - { - "fileData": { - "mimeType": file_data.get("mime_type"), - "fileUri": file_data.get("file_uri"), - } - } - ) - else: - logger.warning(f"Unsupported content type: {str(part)[:80]}") - else: - logger.warning(f"Unsupported content type: {str(content)[:80]}") - messages.append({"role": role, "parts": parts}) if not messages: return + logger.debug(f"Creating response: {messages}") await self.start_ttfb_metrics() - evt = events.ClientContentMessage.model_validate( - { - "clientContent": { - "turns": messages, - "turnComplete": True, - } - } - ) - await self.send_client_event(evt) + await self._session.send_client_content(turns=messages, turn_complete=True) @traced_gemini_live(operation="llm_tool_result") async def _tool_result(self, tool_result_message): @@ -1097,37 +1069,22 @@ class GeminiMultimodalLiveLLMService(LLMService): id = tool_result_message.get("tool_call_id") name = tool_result_message.get("tool_call_name") result = json.loads(tool_result_message.get("content") or "") - response_message = json.dumps( - { - "toolResponse": { - "functionResponses": [ - { - "id": id, - "name": name, - "response": { - "result": result, - }, - } - ], - } - } - ) - await self._websocket.send(response_message) - # await self._websocket.send(json.dumps({"clientContent": {"turnComplete": True}})) + response = FunctionResponse(name=name, id=id, response=result) + await self._session.send_tool_response(function_responses=response) @traced_gemini_live(operation="llm_setup") - async def _handle_evt_setup_complete(self, evt): - """Handle the setup complete event.""" - # If this is our first context frame, run the LLM - self._api_session_ready = True - # Now that we've configured the session, we can run the LLM if we need to. - if self._run_llm_when_api_session_ready: - self._run_llm_when_api_session_ready = False + async def _handle_session_ready(self, session: AsyncSession): + """Handle the session being ready.""" + self._session = session + # If we were just waititng for the session to be ready to run the LLM, + # do that now. + if self._run_llm_when_session_ready: + self._run_llm_when_session_ready = False await self._create_initial_response() - async def _handle_evt_model_turn(self, evt): - """Handle the model turn event.""" - part = evt.serverContent.modelTurn.parts[0] + async def _handle_msg_model_turn(self, msg: LiveServerMessage): + """Handle the model turn message.""" + part = msg.server_content.model_turn.parts[0] if not part: return @@ -1144,17 +1101,17 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.push_frame(LLMTextFrame(text=text)) # Check for grounding metadata in server content - if evt.serverContent and evt.serverContent.groundingMetadata: - self._accumulated_grounding_metadata = evt.serverContent.groundingMetadata + if msg.server_content and msg.server_content.grounding_metadata: + self._accumulated_grounding_metadata = msg.server_content.grounding_metadata - inline_data = part.inlineData + inline_data = part.inline_data if not inline_data: return - if inline_data.mimeType != f"audio/pcm;rate={self._sample_rate}": - logger.warning(f"Unrecognized server_content format {inline_data.mimeType}") + if inline_data.mime_type != f"audio/pcm;rate={self._sample_rate}": + logger.warning(f"Unrecognized server_content format {inline_data.mime_type}") return - audio = base64.b64decode(inline_data.data) + audio = inline_data.data if not audio: return @@ -1172,9 +1129,9 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.push_frame(frame) @traced_gemini_live(operation="llm_tool_call") - async def _handle_evt_tool_call(self, evt): - """Handle tool call events.""" - function_calls = evt.toolCall.functionCalls + async def _handle_msg_tool_call(self, message: LiveServerMessage): + """Handle tool call messages.""" + function_calls = message.tool_call.function_calls if not function_calls: return if not self._context: @@ -1193,12 +1150,13 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.run_function_calls(function_calls_llm) @traced_gemini_live(operation="llm_response") - async def _handle_evt_turn_complete(self, evt): - """Handle the turn complete event.""" + async def _handle_msg_turn_complete(self, message: LiveServerMessage): + """Handle the turn complete message.""" self._bot_is_speaking = False text = self._bot_text_buffer # Determine output and modality for tracing + # TODO: looks like there's a bug here - output_text and output_modality are unused if text: # TEXT modality output_text = text @@ -1239,17 +1197,17 @@ class GeminiMultimodalLiveLLMService(LLMService): """Handle a transcription result with tracing.""" pass - async def _handle_evt_input_transcription(self, evt): - """Handle the input transcription event. + async def _handle_msg_input_transcription(self, message: LiveServerMessage): + """Handle the input transcription message. Gemini Live sends user transcriptions in either single words or multi-word phrases. As a result, we have to aggregate the input transcription. This handler aggregates into sentences, splitting on the end of sentence markers. """ - if not evt.serverContent.inputTranscription: + if not message.server_content.input_transcription: return - text = evt.serverContent.inputTranscription.text + text = message.server_content.input_transcription.text if not text: return @@ -1282,20 +1240,20 @@ class GeminiMultimodalLiveLLMService(LLMService): text=complete_sentence, user_id="", timestamp=time_now_iso8601(), - result=evt, + result=message, ), FrameDirection.UPSTREAM, ) - async def _handle_evt_output_transcription(self, evt): - """Handle the output transcription event.""" - if not evt.serverContent.outputTranscription: + async def _handle_msg_output_transcription(self, message: LiveServerMessage): + """Handle the output transcription message.""" + if not message.server_content.output_transcription: return # This is the output transcription text when modalities is set to AUDIO. # In this case, we push LLMTextFrame and TTSTextFrame to be handled by the # downstream assistant context aggregator. - text = evt.serverContent.outputTranscription.text + text = message.server_content.output_transcription.text if not text: return @@ -1304,23 +1262,23 @@ class GeminiMultimodalLiveLLMService(LLMService): self._search_result_buffer += text # Check for grounding metadata in server content - if evt.serverContent and evt.serverContent.groundingMetadata: - self._accumulated_grounding_metadata = evt.serverContent.groundingMetadata + if message.server_content and message.server_content.grounding_metadata: + self._accumulated_grounding_metadata = message.server_content.grounding_metadata # Collect text for tracing self._llm_output_buffer += text await self.push_frame(LLMTextFrame(text=text)) await self.push_frame(TTSTextFrame(text=text)) - async def _handle_evt_grounding_metadata(self, evt): - """Handle dedicated grounding metadata events.""" - if evt.serverContent and evt.serverContent.groundingMetadata: - grounding_metadata = evt.serverContent.groundingMetadata + async def _handle_msg_grounding_metadata(self, message: LiveServerMessage): + """Handle dedicated grounding metadata messages.""" + if message.server_content and message.server_content.grounding_metadata: + grounding_metadata = message.server_content.grounding_metadata # Process the grounding metadata immediately await self._process_grounding_metadata(grounding_metadata, self._search_result_buffer) async def _process_grounding_metadata( - self, grounding_metadata: events.GroundingMetadata, search_result: str = "" + self, grounding_metadata: GroundingMetadata, search_result: str = "" ): """Process grounding metadata and emit LLMSearchResponseFrame.""" if not grounding_metadata: @@ -1329,19 +1287,19 @@ class GeminiMultimodalLiveLLMService(LLMService): # Extract rendered content for search suggestions rendered_content = None if ( - grounding_metadata.searchEntryPoint - and grounding_metadata.searchEntryPoint.renderedContent + grounding_metadata.search_entry_point + and grounding_metadata.search_entry_point.rendered_content ): - rendered_content = grounding_metadata.searchEntryPoint.renderedContent + rendered_content = grounding_metadata.search_entry_point.rendered_content # Convert grounding chunks and supports to LLMSearchOrigin format origins = [] - if grounding_metadata.groundingChunks and grounding_metadata.groundingSupports: + if grounding_metadata.grounding_chunks and grounding_metadata.grounding_supports: # Create a mapping of chunk indices to origins - chunk_to_origin = {} + chunk_to_origin: Dict[int, LLMSearchOrigin] = {} - for index, chunk in enumerate(grounding_metadata.groundingChunks): + for index, chunk in enumerate(grounding_metadata.grounding_chunks): if chunk.web: origin = LLMSearchOrigin( site_uri=chunk.web.uri, site_title=chunk.web.title, results=[] @@ -1350,13 +1308,13 @@ class GeminiMultimodalLiveLLMService(LLMService): origins.append(origin) # Add grounding support results to the appropriate origins - for support in grounding_metadata.groundingSupports: - if support.segment and support.groundingChunkIndices: + for support in grounding_metadata.grounding_supports: + if support.segment and support.grounding_chunk_indices: text = support.segment.text or "" - confidence_scores = support.confidenceScores or [] + confidence_scores = support.confidence_scores or [] # Add this result to all origins referenced by this support - for chunk_index in support.groundingChunkIndices: + for chunk_index in support.grounding_chunk_indices: if chunk_index in chunk_to_origin: result = LLMSearchResult(text=text, confidence=confidence_scores) chunk_to_origin[chunk_index].results.append(result) @@ -1368,17 +1326,17 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.push_frame(search_frame) - async def _handle_evt_usage_metadata(self, evt): - """Handle the usage metadata event.""" - if not evt.usageMetadata: + async def _handle_msg_usage_metadata(self, message: LiveServerMessage): + """Handle the usage metadata message.""" + if not message.usage_metadata: return - usage = evt.usageMetadata + usage = message.usage_metadata # Ensure we have valid integers for all token counts - prompt_tokens = usage.promptTokenCount or 0 - completion_tokens = usage.responseTokenCount or 0 - total_tokens = usage.totalTokenCount or (prompt_tokens + completion_tokens) + prompt_tokens = usage.prompt_token_count or 0 + completion_tokens = usage.response_token_count or 0 + total_tokens = usage.total_token_count or (prompt_tokens + completion_tokens) tokens = LLMTokenUsage( prompt_tokens=prompt_tokens, From 453ce715a657386b1f2a40db93da712ca9e4d888 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 2 Oct 2025 16:42:53 -0400 Subject: [PATCH 020/158] Add some error handling to `GeminiMultimodalLiveLLMService` --- .../services/gemini_multimodal_live/gemini.py | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 7980f76ba..feed669a8 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -30,6 +30,7 @@ from pipecat.frames.frames import ( BotStoppedSpeakingFrame, CancelFrame, EndFrame, + ErrorFrame, Frame, InputAudioRawFrame, InputImageRawFrame, @@ -910,8 +911,7 @@ class GeminiMultimodalLiveLLMService(LLMService): self._connection_task = self.create_task(self._connection_task_handler(config=config)) except Exception as e: - logger.error(f"{self} initialization error: {e}") - self._session = None + await self.push_error(ErrorFrame(error=f"{self} Initialization error: {e}", fatal=True)) async def _connection_task_handler(self, config: LiveConnectConfig): async with self._client.aio.live.connect(model=self._model_name, config=config) as session: @@ -941,7 +941,10 @@ class GeminiMultimodalLiveLLMService(LLMService): elif message.tool_call: await self._handle_msg_tool_call(message) except Exception as e: - logger.error(f"Error in receive loop: {type(e)}: {e}") + if not self._disconnecting: + await self.push_error( + ErrorFrame(error=f"{self} Error in receive loop: {e}", fatal=True) + ) break async def _disconnect(self): @@ -969,9 +972,12 @@ class GeminiMultimodalLiveLLMService(LLMService): return # Send all audio to Gemini - await self._session.send_realtime_input( - audio=Blob(data=frame.audio, mime_type=f"audio/pcm;rate={frame.sample_rate}") - ) + try: + await self._session.send_realtime_input( + audio=Blob(data=frame.audio, mime_type=f"audio/pcm;rate={frame.sample_rate}") + ) + except Exception as e: + self._handle_send_error(e) # Manage a buffer of audio to use for transcription audio = frame.audio @@ -1000,7 +1006,10 @@ class GeminiMultimodalLiveLLMService(LLMService): if not self._session: return - await self._session.send_realtime_input(text=text) + try: + await self._session.send_realtime_input(text=text) + except Exception as e: + self._handle_send_error(e) async def _send_user_video(self, frame): """Send user video frame to Gemini Live API.""" @@ -1016,10 +1025,15 @@ class GeminiMultimodalLiveLLMService(LLMService): self._last_sent_time = now # Update last sent time logger.debug(f"Sending video frame to Gemini: {frame}") + buffer = io.BytesIO() Image.frombytes(frame.format, frame.size, frame.image).save(buffer, format="JPEG") data = base64.b64encode(buffer.getvalue()).decode("utf-8") - await self._session.send_realtime_input(video=Blob(data=data, mime_type="image/jpeg")) + + try: + await self._session.send_realtime_input(video=Blob(data=data, mime_type="image/jpeg")) + except Exception as e: + self._handle_send_error(e) async def _create_initial_response(self): """Create initial response based on context history.""" @@ -1035,9 +1049,12 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.start_ttfb_metrics() - await self._session.send_client_content( - turns=messages, turn_complete=self._inference_on_context_initialization - ) + try: + await self._session.send_client_content( + turns=messages, turn_complete=self._inference_on_context_initialization + ) + except Exception as e: + self._handle_send_error(e) # If we're generating a response right away upon initializing # conversation history, set a flag saying that we need a turn complete @@ -1059,7 +1076,10 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.start_ttfb_metrics() - await self._session.send_client_content(turns=messages, turn_complete=True) + try: + await self._session.send_client_content(turns=messages, turn_complete=True) + except Exception as e: + self._handle_send_error(e) @traced_gemini_live(operation="llm_tool_result") async def _tool_result(self, tool_result_message): @@ -1070,7 +1090,11 @@ class GeminiMultimodalLiveLLMService(LLMService): name = tool_result_message.get("tool_call_name") result = json.loads(tool_result_message.get("content") or "") response = FunctionResponse(name=name, id=id, response=result) - await self._session.send_tool_response(function_responses=response) + + try: + await self._session.send_tool_response(function_responses=response) + except Exception as e: + self._handle_send_error(e) @traced_gemini_live(operation="llm_setup") async def _handle_session_ready(self, session: AsyncSession): @@ -1346,6 +1370,15 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.start_llm_usage_metrics(tokens) + async def _handle_send_error(self, error: Exception): + # In server-to-server contexts, a WebSocket error should be quite rare. + # Given how hard it is to recover from a send-side error with proper + # state management, and that exponential backoff for retries can have + # cost/stability implications for a service cluster, let's just treat a + # send-side error as fatal. + if not self._disconnecting: + await self.push_error(ErrorFrame(error=f"{self} Send error: {error}", fatal=True)) + def create_context_aggregator( self, context: OpenAILLMContext, From 9b4ca12f497ed4cef5cf7a8dafc34a2040495ab0 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Sat, 4 Oct 2025 15:21:53 -0400 Subject: [PATCH 021/158] Revert to only supporting providing a single modality - looks like specifying a list of modalities results in an API error. Also, fix some missing `await`s in error handling. --- .../services/gemini_multimodal_live/gemini.py | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index feed669a8..ee43ece18 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -478,8 +478,8 @@ class InputParams(BaseModel): temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0) top_k: Optional[int] = Field(default=None, ge=0) top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0) - modalities: Optional[Union[List[GeminiMultimodalModalities], GeminiMultimodalModalities]] = ( - Field(default=GeminiMultimodalModalities.AUDIO) + modalities: Optional[GeminiMultimodalModalities] = Field( + default=GeminiMultimodalModalities.AUDIO ) language: Optional[Language] = Field(default=Language.EN_US) media_resolution: Optional[GeminiMediaResolution] = Field( @@ -640,9 +640,7 @@ class GeminiMultimodalLiveLLMService(LLMService): """ self._video_input_paused = paused - def set_model_modalities( - self, modalities: Union[List[GeminiMultimodalModalities], GeminiMultimodalModalities] - ): + def set_model_modalities(self, modalities: GeminiMultimodalModalities): """Set the model response modalities. Args: @@ -818,11 +816,6 @@ class GeminiMultimodalLiveLLMService(LLMService): logger.info("Connecting to Gemini service") try: - # Handle modalities being specified as either a list or a single value - modalities = self._settings["modalities"] - if isinstance(modalities, GeminiMultimodalModalities): - modalities = [modalities] - # Assemble basic configuration config = LiveConnectConfig( generation_config=GenerationConfig( @@ -832,7 +825,7 @@ class GeminiMultimodalLiveLLMService(LLMService): temperature=self._settings["temperature"], top_k=self._settings["top_k"], top_p=self._settings["top_p"], - response_modalities=[Modality(modality.value) for modality in modalities], + response_modalities=[Modality(self._settings["modalities"].value)], speech_config=SpeechConfig( voice_config=VoiceConfig( prebuilt_voice_config={"voice_name": self._voice_id} @@ -977,7 +970,7 @@ class GeminiMultimodalLiveLLMService(LLMService): audio=Blob(data=frame.audio, mime_type=f"audio/pcm;rate={frame.sample_rate}") ) except Exception as e: - self._handle_send_error(e) + await self._handle_send_error(e) # Manage a buffer of audio to use for transcription audio = frame.audio @@ -1009,7 +1002,7 @@ class GeminiMultimodalLiveLLMService(LLMService): try: await self._session.send_realtime_input(text=text) except Exception as e: - self._handle_send_error(e) + await self._handle_send_error(e) async def _send_user_video(self, frame): """Send user video frame to Gemini Live API.""" @@ -1033,7 +1026,7 @@ class GeminiMultimodalLiveLLMService(LLMService): try: await self._session.send_realtime_input(video=Blob(data=data, mime_type="image/jpeg")) except Exception as e: - self._handle_send_error(e) + await self._handle_send_error(e) async def _create_initial_response(self): """Create initial response based on context history.""" @@ -1054,7 +1047,7 @@ class GeminiMultimodalLiveLLMService(LLMService): turns=messages, turn_complete=self._inference_on_context_initialization ) except Exception as e: - self._handle_send_error(e) + await self._handle_send_error(e) # If we're generating a response right away upon initializing # conversation history, set a flag saying that we need a turn complete @@ -1079,7 +1072,7 @@ class GeminiMultimodalLiveLLMService(LLMService): try: await self._session.send_client_content(turns=messages, turn_complete=True) except Exception as e: - self._handle_send_error(e) + await self._handle_send_error(e) @traced_gemini_live(operation="llm_tool_result") async def _tool_result(self, tool_result_message): @@ -1094,7 +1087,7 @@ class GeminiMultimodalLiveLLMService(LLMService): try: await self._session.send_tool_response(function_responses=response) except Exception as e: - self._handle_send_error(e) + await self._handle_send_error(e) @traced_gemini_live(operation="llm_setup") async def _handle_session_ready(self, session: AsyncSession): From 04a68f2c57576e7e0ae0478f1eb4c32449197f38 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Sat, 4 Oct 2025 15:33:01 -0400 Subject: [PATCH 022/158] Fix tracing in `GeminiMultimodalLiveLLMService` --- .../services/gemini_multimodal_live/gemini.py | 13 +----- .../utils/tracing/service_decorators.py | 42 +++++++++---------- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index ee43ece18..87fc72f3a 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -1172,19 +1172,8 @@ class GeminiMultimodalLiveLLMService(LLMService): self._bot_is_speaking = False text = self._bot_text_buffer - # Determine output and modality for tracing - # TODO: looks like there's a bug here - output_text and output_modality are unused - if text: - # TEXT modality - output_text = text - output_modality = "TEXT" - else: - # AUDIO modality - output_text = self._llm_output_buffer - output_modality = "AUDIO" - # Trace the complete LLM response (this will be handled by the decorator) - # The decorator will extract the output text and usage metadata from the event + # The decorator will extract the output text and usage metadata from the message self._bot_text_buffer = "" self._llm_output_buffer = "" diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index 2edec9862..cf1ba912c 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -651,9 +651,9 @@ def traced_gemini_live(operation: str) -> Callable: elif operation == "llm_tool_call" and args: # Extract tool call information - evt = args[0] if args else None - if evt and hasattr(evt, "toolCall") and evt.toolCall.functionCalls: - function_calls = evt.toolCall.functionCalls + msg = args[0] if args else None + if msg and hasattr(msg, "tool_call") and msg.tool_call.function_calls: + function_calls = msg.tool_call.function_calls if function_calls: # Add information about the first function call call = function_calls[0] @@ -722,19 +722,19 @@ def traced_gemini_live(operation: str) -> Callable: elif operation == "llm_response" and args: # Extract usage and response metadata from turn complete event - evt = args[0] if args else None - if evt and hasattr(evt, "usageMetadata") and evt.usageMetadata: - usage = evt.usageMetadata + msg = args[0] if args else None + if msg and hasattr(msg, "usage_metadata") and msg.usage_metadata: + usage = msg.usage_metadata # Token usage - basic attributes for span visibility - if hasattr(usage, "promptTokenCount"): - operation_attrs["tokens.prompt"] = usage.promptTokenCount or 0 - if hasattr(usage, "responseTokenCount"): + if hasattr(usage, "prompt_token_count"): + operation_attrs["tokens.prompt"] = usage.prompt_token_count or 0 + if hasattr(usage, "response_token_count"): operation_attrs["tokens.completion"] = ( - usage.responseTokenCount or 0 + usage.response_token_count or 0 ) - if hasattr(usage, "totalTokenCount"): - operation_attrs["tokens.total"] = usage.totalTokenCount or 0 + if hasattr(usage, "total_token_count"): + operation_attrs["tokens.total"] = usage.total_token_count or 0 # Get output text and modality from service state text = getattr(self, "_bot_text_buffer", "") @@ -751,9 +751,9 @@ def traced_gemini_live(operation: str) -> Callable: # Add turn completion status if ( - evt - and hasattr(evt, "serverContent") - and evt.serverContent.turnComplete + msg + and hasattr(msg, "server_content") + and msg.server_content.turn_complete ): operation_attrs["turn_complete"] = True @@ -772,16 +772,16 @@ def traced_gemini_live(operation: str) -> Callable: # For llm_response operation, also handle token usage metrics if operation == "llm_response" and hasattr(self, "start_llm_usage_metrics"): - evt = args[0] if args else None - if evt and hasattr(evt, "usageMetadata") and evt.usageMetadata: - usage = evt.usageMetadata + msg = args[0] if args else None + if msg and hasattr(msg, "usage_metadata") and msg.usage_metadata: + usage = msg.usage_metadata # Create LLMTokenUsage object from pipecat.metrics.metrics import LLMTokenUsage tokens = LLMTokenUsage( - prompt_tokens=usage.promptTokenCount or 0, - completion_tokens=usage.responseTokenCount or 0, - total_tokens=usage.totalTokenCount or 0, + prompt_tokens=usage.prompt_token_count or 0, + completion_tokens=usage.response_token_count or 0, + total_tokens=usage.total_token_count or 0, ) _add_token_usage_to_span(current_span, tokens) From 12e7613d5fbd622481c3b9b3241a001664d45629 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Sat, 4 Oct 2025 15:53:30 -0400 Subject: [PATCH 023/158] Deprecate the `base_url` argument to `GeminiMultimodalLiveLLMService`. It expected a WebSocket URL, but we're no longer (directly) using WebSockets to talk to Gemini. Instead of trying to (potentially erroneously) map a given custom WebSocket URL to an `HttpOptions` object (the new preferred way of customizing requests made by the Gemini API client), we're simply deprecating `base_url` and pointing users to the `http_options` argument instead. --- .../services/gemini_multimodal_live/gemini.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 87fc72f3a..c04736c83 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -523,6 +523,12 @@ class GeminiMultimodalLiveLLMService(LLMService): Args: api_key: Google AI API key for authentication. base_url: API endpoint base URL. Defaults to the official Gemini Live endpoint. + + .. deprecated:: 0.0.88 + This parameter is deprecated and no longer has any effect. + Please use `http_options` to customize requests made by the + API client. + model: Model identifier to use. Defaults to "models/gemini-2.0-flash-live-001". voice_id: TTS voice identifier. Defaults to "Charon". start_audio_paused: Whether to start with audio input paused. Defaults to False. @@ -536,6 +542,21 @@ class GeminiMultimodalLiveLLMService(LLMService): http_options: HTTP options for the client. **kwargs: Additional arguments passed to parent LLMService. """ + # Check for deprecated parameter usage + if ( + base_url + != "generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent" + ): + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Parameter 'base_url' is deprecated and no longer has any effect. Please use 'http_options' to customize requests made by the API client.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(base_url=base_url, **kwargs) params = params or InputParams() From 566af6b0b8fd9219533e4f4f45e49fd90a488360 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Sat, 4 Oct 2025 16:20:23 -0400 Subject: [PATCH 024/158] Minor comment improvement --- src/pipecat/services/gemini_multimodal_live/gemini.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index c04736c83..a1c7899c6 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -464,7 +464,7 @@ class InputParams(BaseModel): temperature: Sampling temperature (0.0-2.0). Defaults to None. top_k: Top-k sampling parameter. Must be >= 0. Defaults to None. top_p: Top-p sampling parameter (0.0-1.0). Defaults to None. - modalities: Response modalities. Defaults to "AUDIO". + modalities: Response modalities. Defaults to AUDIO. language: Language for generation. Defaults to EN_US. media_resolution: Media resolution setting. Defaults to UNSPECIFIED. vad: Voice activity detection parameters. Defaults to None. From 48e92a22fed92b7adcc4782fd6bdccbc58fd94fc Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 7 Oct 2025 09:56:55 -0400 Subject: [PATCH 025/158] Add thinking settings to `GeminiMultimodalLiveLLMService` --- .../services/gemini_multimodal_live/gemini.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index a1c7899c6..0e1722829 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -99,6 +99,7 @@ try: SlidingWindow, SpeechConfig, StartSensitivity, + ThinkingConfig, VoiceConfig, ) except ModuleNotFoundError as e: @@ -454,6 +455,9 @@ class ContextWindowCompressionParams(BaseModel): ) # None = use default (80% of context window) +ThinkingParams = ThinkingConfig + + class InputParams(BaseModel): """Input parameters for Gemini Multimodal Live generation. @@ -469,6 +473,9 @@ class InputParams(BaseModel): media_resolution: Media resolution setting. Defaults to UNSPECIFIED. vad: Voice activity detection parameters. Defaults to None. context_window_compression: Context compression settings. Defaults to None. + thinking: Thinking settings. Defaults to None. + Note that these settings aren't accepted by all models, including + the current default one. extra: Additional parameters. Defaults to empty dict. """ @@ -487,6 +494,7 @@ class InputParams(BaseModel): ) vad: Optional[GeminiVADParams] = Field(default=None) context_window_compression: Optional[ContextWindowCompressionParams] = Field(default=None) + thinking: Optional[ThinkingParams] = Field(default=None) extra: Optional[Dict[str, Any]] = Field(default_factory=dict) @@ -613,6 +621,7 @@ class GeminiMultimodalLiveLLMService(LLMService): "context_window_compression": params.context_window_compression.model_dump() if params.context_window_compression else {}, + "thinking": params.thinking or {}, "extra": params.extra if isinstance(params.extra, dict) else {}, } @@ -875,6 +884,10 @@ class GeminiMultimodalLiveLLMService(LLMService): config.context_window_compression = compression_config + # Add thinking configuration to configuration, if provided + if self._settings.get("thinking"): + config.thinking_config = self._settings["thinking"] + # Add VAD configuration to configuration, if provided if self._settings.get("vad"): vad_config = AutomaticActivityDetection() From 43915937f2ec026e0fe6cbee9e8613f34fd7839d Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 7 Oct 2025 10:05:24 -0400 Subject: [PATCH 026/158] Update how we import and re-export some types in `GeminiMultimodalLiveLLMService` --- .../services/gemini_multimodal_live/gemini.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 0e1722829..e15b47b8a 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -90,7 +90,6 @@ try: FunctionResponse, GenerationConfig, GroundingMetadata, - HttpOptions, LiveConnectConfig, LiveServerMessage, Modality, @@ -99,9 +98,14 @@ try: SlidingWindow, SpeechConfig, StartSensitivity, - ThinkingConfig, VoiceConfig, ) + from google.genai.types import ( + HttpOptions as _HttpOptions, + ) + from google.genai.types import ( + ThinkingConfig as _ThinkingConfig, + ) except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") @@ -455,7 +459,8 @@ class ContextWindowCompressionParams(BaseModel): ) # None = use default (80% of context window) -ThinkingParams = ThinkingConfig +ThinkingConfig = _ThinkingConfig +HttpOptions = _HttpOptions class InputParams(BaseModel): @@ -494,7 +499,7 @@ class InputParams(BaseModel): ) vad: Optional[GeminiVADParams] = Field(default=None) context_window_compression: Optional[ContextWindowCompressionParams] = Field(default=None) - thinking: Optional[ThinkingParams] = Field(default=None) + thinking: Optional[ThinkingConfig] = Field(default=None) extra: Optional[Dict[str, Any]] = Field(default_factory=dict) From 8a6d504a7e9c160567aab15f1cf490d9eafa39c7 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 7 Oct 2025 10:34:48 -0400 Subject: [PATCH 027/158] Add `enable_affective_dialog` and `proactivity` settings to `GeminiMultimodalLiveLLMService` --- .../services/gemini_multimodal_live/gemini.py | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index e15b47b8a..205b298e4 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -103,6 +103,7 @@ try: from google.genai.types import ( HttpOptions as _HttpOptions, ) + from google.genai.types import ProactivityConfig as _ProactivityConfig from google.genai.types import ( ThinkingConfig as _ThinkingConfig, ) @@ -461,6 +462,7 @@ class ContextWindowCompressionParams(BaseModel): ThinkingConfig = _ThinkingConfig HttpOptions = _HttpOptions +ProactivityConfig = _ProactivityConfig class InputParams(BaseModel): @@ -479,8 +481,21 @@ class InputParams(BaseModel): vad: Voice activity detection parameters. Defaults to None. context_window_compression: Context compression settings. Defaults to None. thinking: Thinking settings. Defaults to None. - Note that these settings aren't accepted by all models, including - the current default one. + Note that these settings may require specifying a model that + supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". + enable_affective_dialog: Enable affective dialog, which allows Gemini + to adapt to expression and tone. Defaults to None. + Note that these settings may require specifying a model that + supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". + Also note that this setting may require specifying an API version that + supports it, e.g. HttpOptions(api_version="v1alpha"). + proactivity: Proactivity settings, which allows Gemini to proactively + decide how to behave, such as whether to avoid responding to + content that is not relevant. Defaults to None. + Note that these settings may require specifying a model that + supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". + Also note that this setting may require specifying an API version that + supports it, e.g. HttpOptions(api_version="v1alpha"). extra: Additional parameters. Defaults to empty dict. """ @@ -500,6 +515,8 @@ class InputParams(BaseModel): vad: Optional[GeminiVADParams] = Field(default=None) context_window_compression: Optional[ContextWindowCompressionParams] = Field(default=None) thinking: Optional[ThinkingConfig] = Field(default=None) + enable_affective_dialog: Optional[bool] = Field(default=None) + proactivity: Optional[ProactivityConfig] = Field(default=None) extra: Optional[Dict[str, Any]] = Field(default_factory=dict) @@ -627,6 +644,8 @@ class GeminiMultimodalLiveLLMService(LLMService): if params.context_window_compression else {}, "thinking": params.thinking or {}, + "enable_affective_dialog": params.enable_affective_dialog or False, + "proactivity": params.proactivity or {}, "extra": params.extra if isinstance(params.extra, dict) else {}, } @@ -893,6 +912,14 @@ class GeminiMultimodalLiveLLMService(LLMService): if self._settings.get("thinking"): config.thinking_config = self._settings["thinking"] + # Add affective dialog setting, if provided + if self._settings.get("enable_affective_dialog", False): + config.enable_affective_dialog = self._settings["enable_affective_dialog"] + + # Add proactivity configuration to configuration, if provided + if self._settings.get("proactivity"): + config.proactivity = self._settings["proactivity"] + # Add VAD configuration to configuration, if provided if self._settings.get("vad"): vad_config = AutomaticActivityDetection() From d19e6111c39a23c440f6f0f2595c18a61c933af4 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 7 Oct 2025 11:18:14 -0400 Subject: [PATCH 028/158] Bumping deprecation version of `GeminiMultimodalLiveLLMService`'s `base_url` arg --- src/pipecat/services/gemini_multimodal_live/gemini.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 205b298e4..53890a5e7 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -554,7 +554,7 @@ class GeminiMultimodalLiveLLMService(LLMService): api_key: Google AI API key for authentication. base_url: API endpoint base URL. Defaults to the official Gemini Live endpoint. - .. deprecated:: 0.0.88 + .. deprecated:: 0.0.89 This parameter is deprecated and no longer has any effect. Please use `http_options` to customize requests made by the API client. From fa663abbbc7a4fa2d0fb708298cf240dda86b16c Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 7 Oct 2025 11:58:59 -0400 Subject: [PATCH 029/158] Add CHANGELOG entry for new `GeminiMultimodalLiveLLMService` configuration options --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75bb875da..429daa844 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added some new configuration options to `GeminiMultimodalLiveLLMService`: + + - `thinking` + - `enable_affective_dialog` + - `proactivity` + + Note that these new configuration options require using a newer model than + the default, like "gemini-2.5-flash-native-audio-preview-09-2025". The last + two require specifying `http_options=HttpOptions(api_version="v1alpha")`. + - Added `on_pipeline_error` event to `PipelineTask`. This event will get fired when an `ErrorFrame` is pushed (use `FrameProcessor.push_error()`). From e4b01a58446eac057fd5473ecf8ff91963408cc2 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 14:58:25 -0400 Subject: [PATCH 030/158] Bumping deprecation version of `GeminiMultimodalLiveLLMService`'s `base_url` arg --- src/pipecat/services/gemini_multimodal_live/gemini.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 53890a5e7..08e6a1875 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -554,7 +554,7 @@ class GeminiMultimodalLiveLLMService(LLMService): api_key: Google AI API key for authentication. base_url: API endpoint base URL. Defaults to the official Gemini Live endpoint. - .. deprecated:: 0.0.89 + .. deprecated:: 0.0.90 This parameter is deprecated and no longer has any effect. Please use `http_options` to customize requests made by the API client. From ac1582c906a7166d719e641410c84b4c446e6c6a Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 15:13:20 -0400 Subject: [PATCH 031/158] Let users directly use `google-genai` types rather than aliased re-exported types --- .../services/gemini_multimodal_live/gemini.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 08e6a1875..ff804a547 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -90,23 +90,19 @@ try: FunctionResponse, GenerationConfig, GroundingMetadata, + HttpOptions, LiveConnectConfig, LiveServerMessage, Modality, Part, + ProactivityConfig, RealtimeInputConfig, SlidingWindow, SpeechConfig, StartSensitivity, + ThinkingConfig, VoiceConfig, ) - from google.genai.types import ( - HttpOptions as _HttpOptions, - ) - from google.genai.types import ProactivityConfig as _ProactivityConfig - from google.genai.types import ( - ThinkingConfig as _ThinkingConfig, - ) except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") @@ -460,11 +456,6 @@ class ContextWindowCompressionParams(BaseModel): ) # None = use default (80% of context window) -ThinkingConfig = _ThinkingConfig -HttpOptions = _HttpOptions -ProactivityConfig = _ProactivityConfig - - class InputParams(BaseModel): """Input parameters for Gemini Multimodal Live generation. From 2921162b3b397f078c4aa5fe9ca1256b82bca694 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 15:26:19 -0400 Subject: [PATCH 032/158] Add deprecation warning around importing StartSensitivity and EndSensitivity from pipecat.services.gemini_multimodal_live.events --- .../services/gemini_multimodal_live/events.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/gemini_multimodal_live/events.py b/src/pipecat/services/gemini_multimodal_live/events.py index 0d5ce4de5..9705cf9f2 100644 --- a/src/pipecat/services/gemini_multimodal_live/events.py +++ b/src/pipecat/services/gemini_multimodal_live/events.py @@ -4,7 +4,14 @@ # SPDX-License-Identifier: BSD 2-Clause License # -"""Event models and utilities for Google Gemini Multimodal Live API.""" +"""Event models and utilities for Google Gemini Multimodal Live API. + +.. deprecated:: 0.0.90 + Importing StartSensitivity and EndSensitivity from this module is deprecated. + Import them directly from google.genai.types instead. +""" + +import warnings from loguru import logger @@ -23,5 +30,12 @@ except ModuleNotFoundError as e: # These aliases are just here for backward compatibility, since we used to # define public-facing StartSensitivity and EndSensitivity enums in this # module. +warnings.warn( + "Importing StartSensitivity and EndSensitivity from " + "pipecat.services.gemini_multimodal_live.events is deprecated. " + "Please import them directly from google.genai.types instead.", + DeprecationWarning, + stacklevel=2, +) StartSensitivity = _StartSensitivity EndSensitivity = _EndSensitivity From f09e4e238b18c008d34beb330e133ebfb8411c07 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 15:29:01 -0400 Subject: [PATCH 033/158] Fix some mishandling of enum values --- src/pipecat/services/gemini_multimodal_live/gemini.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index ff804a547..eecb712a4 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -93,6 +93,7 @@ try: HttpOptions, LiveConnectConfig, LiveServerMessage, + MediaResolution, Modality, Part, ProactivityConfig, @@ -877,7 +878,7 @@ class GeminiMultimodalLiveLLMService(LLMService): ), language_code=self._settings["language"], ), - media_resolution=self._settings["media_resolution"].value, + media_resolution=MediaResolution(self._settings["media_resolution"].value), ), input_audio_transcription=AudioTranscriptionConfig(), output_audio_transcription=AudioTranscriptionConfig(), @@ -923,11 +924,11 @@ class GeminiMultimodalLiveLLMService(LLMService): has_vad_settings = True if vad_params.start_sensitivity: - vad_config.start_of_speech_sensitivity = vad_params.start_sensitivity.value + vad_config.start_of_speech_sensitivity = vad_params.start_sensitivity has_vad_settings = True if vad_params.end_sensitivity: - vad_config.end_of_speech_sensitivity = vad_params.end_sensitivity.value + vad_config.end_of_speech_sensitivity = vad_params.end_sensitivity has_vad_settings = True if vad_params.prefix_padding_ms is not None: From 402e019ae22f8dafd184cdc3bc647bfd3773cad7 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 16:09:10 -0400 Subject: [PATCH 034/158] Make a bit of code clearer --- src/pipecat/services/gemini_multimodal_live/gemini.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index eecb712a4..d045e1f02 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -527,7 +527,7 @@ class GeminiMultimodalLiveLLMService(LLMService): self, *, api_key: str, - base_url: str = "generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent", + base_url: Optional[str] = None, model="models/gemini-2.0-flash-live-001", voice_id: str = "Charon", start_audio_paused: bool = False, @@ -565,10 +565,7 @@ class GeminiMultimodalLiveLLMService(LLMService): **kwargs: Additional arguments passed to parent LLMService. """ # Check for deprecated parameter usage - if ( - base_url - != "generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent" - ): + if base_url is not None: import warnings with warnings.catch_warnings(): From 86127c6a6e8928aaed7971647031857ac3d989e7 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 16:17:28 -0400 Subject: [PATCH 035/158] Add to the changelog the `GeminiMultimodalLiveLLMService` update to use `google-genai` --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 429daa844..337e0ddd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 parameter can influence the latency of the response. For example `"priority"` will result in faster completions, but in exchange for a higher price. +### Changed + +- Updated `GeminiMultimodalLiveLLMService` to use the `google-genai` library + rather than use WebSockets directly. + ## [0.0.89] - 2025-10-07 ### Fixed From 7efb22a323f0e80760832f3d5870497e30241d6a Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 7 Oct 2025 15:20:57 -0400 Subject: [PATCH 036/158] Add (relatively spartan) reconnection logic to `GeminiMultimodalLiveLLMService`, leveraging the Gemini Live session resumption mechanism --- .../services/gemini_multimodal_live/gemini.py | 113 +++++++++++++++--- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index d045e1f02..9f3994121 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -14,6 +14,7 @@ voice transcription, streaming responses, and tool usage. import base64 import io import json +import random import time from dataclasses import dataclass from enum import Enum @@ -98,6 +99,7 @@ try: Part, ProactivityConfig, RealtimeInputConfig, + SessionResumptionConfig, SlidingWindow, SpeechConfig, StartSensitivity, @@ -618,6 +620,12 @@ class GeminiMultimodalLiveLLMService(LLMService): ) self._vad_params = params.vad + # Reconnection tracking + self._consecutive_failures = 0 + self._max_consecutive_failures = 3 + self._connection_established_threshold = 10.0 # seconds + self._connection_start_time = None + self._settings = { "frequency_penalty": params.frequency_penalty, "max_tokens": params.max_tokens, @@ -645,6 +653,9 @@ class GeminiMultimodalLiveLLMService(LLMService): self._search_result_buffer = "" self._accumulated_grounding_metadata = None + # Session resumption + self._session_resumption_handle: Optional[str] = None + def _create_client(self, api_key: str, http_options: Optional[HttpOptions] = None): self._client = Client(api_key=api_key, http_options=http_options) @@ -850,14 +861,19 @@ class GeminiMultimodalLiveLLMService(LLMService): else: await self.push_frame(frame, direction) - async def _connect(self): + async def _connect(self, session_resumption_handle: Optional[str] = None): """Establish client connection to Gemini Live API.""" if self._session: # Here we assume that if we have a client, we are connected. We # handle disconnections in the send/recv code paths. return - logger.info("Connecting to Gemini service") + if session_resumption_handle: + logger.info( + f"Connecting to Gemini service with session_resumption_handle: {session_resumption_handle}" + ) + else: + logger.info("Connecting to Gemini service") try: # Assemble basic configuration config = LiveConnectConfig( @@ -879,6 +895,7 @@ class GeminiMultimodalLiveLLMService(LLMService): ), input_audio_transcription=AudioTranscriptionConfig(), output_audio_transcription=AudioTranscriptionConfig(), + session_resumption=SessionResumptionConfig(handle=session_resumption_handle), ) # Add context window compression to configuration, if enabled @@ -965,12 +982,18 @@ class GeminiMultimodalLiveLLMService(LLMService): async with self._client.aio.live.connect(model=self._model_name, config=config) as session: logger.info("Connected to Gemini service") + # Mark connection start time + self._connection_start_time = time.time() + await self._handle_session_ready(session) while True: try: turn = self._session.receive() async for message in turn: + # Reset failure counter if connection has been stable + self._check_and_reset_failure_counter() + if message.server_content and message.server_content.model_turn: await self._handle_msg_model_turn(message) elif ( @@ -988,13 +1011,67 @@ class GeminiMultimodalLiveLLMService(LLMService): await self._handle_msg_grounding_metadata(message) elif message.tool_call: await self._handle_msg_tool_call(message) + elif message.session_resumption_update: + self._handle_msg_resumption_update(message) except Exception as e: if not self._disconnecting: - await self.push_error( - ErrorFrame(error=f"{self} Error in receive loop: {e}", fatal=True) - ) + should_reconnect = await self._handle_connection_error(e) + if should_reconnect: + await self._reconnect() + return # Exit this connection handler, _reconnect will start a new one break + def _check_and_reset_failure_counter(self): + """Check if connection has been stable long enough to reset the failure counter. + + If the connection has been active for longer than the established threshold + and there are accumulated failures, reset the counter to 0. + """ + if ( + self._connection_start_time + and self._consecutive_failures > 0 + and time.time() - self._connection_start_time >= self._connection_established_threshold + ): + logger.info( + f"Connection stable for {self._connection_established_threshold}s, " + f"resetting failure counter from {self._consecutive_failures} to 0" + ) + self._consecutive_failures = 0 + + async def _handle_connection_error(self, error: Exception) -> bool: + """Handle a connection error and determine if reconnection should be attempted. + + Args: + error: The exception that caused the connection error. + + Returns: + True if reconnection should be attempted, False if a fatal error should be pushed. + """ + self._consecutive_failures += 1 + logger.warning( + f"Connection error (failure {self._consecutive_failures}/{self._max_consecutive_failures}): {error}" + ) + + if self._consecutive_failures >= self._max_consecutive_failures: + logger.error( + f"Max consecutive failures ({self._max_consecutive_failures}) reached, " + "treating as fatal error" + ) + await self.push_error( + ErrorFrame(error=f"{self} Error in receive loop: {error}", fatal=True) + ) + return False + else: + logger.info( + f"Attempting reconnection ({self._consecutive_failures}/{self._max_consecutive_failures})" + ) + return True + + async def _reconnect(self): + """Reconnect to Gemini Live API.""" + await self._disconnect() + await self._connect(session_resumption_handle=self._session_resumption_handle) + async def _disconnect(self): """Disconnect from Gemini Live API and clean up resources.""" logger.info("Disconnecting from Gemini service") @@ -1013,10 +1090,7 @@ class GeminiMultimodalLiveLLMService(LLMService): async def _send_user_audio(self, frame): """Send user audio frame to Gemini Live API.""" - if self._audio_input_paused: - return - - if not self._session: + if self._audio_input_paused or self._disconnecting or not self._session: return # Send all audio to Gemini @@ -1051,7 +1125,7 @@ class GeminiMultimodalLiveLLMService(LLMService): Args: text: The text to send as user input. """ - if not self._session: + if self._disconnecting or not self._session: return try: @@ -1061,10 +1135,7 @@ class GeminiMultimodalLiveLLMService(LLMService): async def _send_user_video(self, frame): """Send user video frame to Gemini Live API.""" - if self._video_input_paused: - return - - if not self._session: + if self._video_input_paused or self._disconnecting or not self._session: return now = time.time() @@ -1085,6 +1156,9 @@ class GeminiMultimodalLiveLLMService(LLMService): async def _create_initial_response(self): """Create initial response based on context history.""" + if self._disconnecting: + return + if not self._session: self._run_llm_when_session_ready = True return @@ -1112,6 +1186,9 @@ class GeminiMultimodalLiveLLMService(LLMService): async def _create_single_response(self, messages_list): """Create a single response from a list of messages.""" + if self._disconnecting or not self._session: + return + # Create a throwaway context just for the purpose of getting messages # in the right format context = GeminiMultimodalLiveContext.upgrade(OpenAILLMContext(messages=messages_list)) @@ -1132,6 +1209,9 @@ class GeminiMultimodalLiveLLMService(LLMService): @traced_gemini_live(operation="llm_tool_result") async def _tool_result(self, tool_result_message): """Send tool result back to the API.""" + if self._disconnecting or not self._session: + return + # For now we're shoving the name into the tool_call_id field, so this # will work until we revisit that. id = tool_result_message.get("tool_call_id") @@ -1407,6 +1487,11 @@ class GeminiMultimodalLiveLLMService(LLMService): await self.start_llm_usage_metrics(tokens) + def _handle_msg_resumption_update(self, message: LiveServerMessage): + update = message.session_resumption_update + if update.resumable and update.new_handle: + self._session_resumption_handle = update.new_handle + async def _handle_send_error(self, error: Exception): # In server-to-server contexts, a WebSocket error should be quite rare. # Given how hard it is to recover from a send-side error with proper From 7da37a0d1f4e678160a363fa04398bb9efcecb21 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 17:04:05 -0400 Subject: [PATCH 037/158] Pull `_connection_established_threshold` and `_max_consecutive_failures` into file-level constants --- .../services/gemini_multimodal_live/gemini.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 9f3994121..f48fd3324 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -112,6 +112,11 @@ except ModuleNotFoundError as e: raise Exception(f"Missing module: {e}") +# Connection management constants +MAX_CONSECUTIVE_FAILURES = 3 +CONNECTION_ESTABLISHED_THRESHOLD = 10.0 # seconds + + def language_to_gemini_language(language: Language) -> Optional[str]: """Maps a Language enum value to a Gemini Live supported language code. @@ -622,8 +627,6 @@ class GeminiMultimodalLiveLLMService(LLMService): # Reconnection tracking self._consecutive_failures = 0 - self._max_consecutive_failures = 3 - self._connection_established_threshold = 10.0 # seconds self._connection_start_time = None self._settings = { @@ -1030,10 +1033,10 @@ class GeminiMultimodalLiveLLMService(LLMService): if ( self._connection_start_time and self._consecutive_failures > 0 - and time.time() - self._connection_start_time >= self._connection_established_threshold + and time.time() - self._connection_start_time >= CONNECTION_ESTABLISHED_THRESHOLD ): logger.info( - f"Connection stable for {self._connection_established_threshold}s, " + f"Connection stable for {CONNECTION_ESTABLISHED_THRESHOLD}s, " f"resetting failure counter from {self._consecutive_failures} to 0" ) self._consecutive_failures = 0 @@ -1049,12 +1052,12 @@ class GeminiMultimodalLiveLLMService(LLMService): """ self._consecutive_failures += 1 logger.warning( - f"Connection error (failure {self._consecutive_failures}/{self._max_consecutive_failures}): {error}" + f"Connection error (failure {self._consecutive_failures}/{MAX_CONSECUTIVE_FAILURES}): {error}" ) - if self._consecutive_failures >= self._max_consecutive_failures: + if self._consecutive_failures >= MAX_CONSECUTIVE_FAILURES: logger.error( - f"Max consecutive failures ({self._max_consecutive_failures}) reached, " + f"Max consecutive failures ({MAX_CONSECUTIVE_FAILURES}) reached, " "treating as fatal error" ) await self.push_error( @@ -1063,7 +1066,7 @@ class GeminiMultimodalLiveLLMService(LLMService): return False else: logger.info( - f"Attempting reconnection ({self._consecutive_failures}/{self._max_consecutive_failures})" + f"Attempting reconnection ({self._consecutive_failures}/{MAX_CONSECUTIVE_FAILURES})" ) return True From a666327d702d6d5013bdddefad5cf45aaf76e76e Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 14:42:45 -0400 Subject: [PATCH 038/158] Implement ending `GeminiMultimodalLiveLLMService` gracefully (i.e. after the bot is finished) --- CHANGELOG.md | 3 + ...26i-gemini-multimodal-live-graceful-end.py | 206 ++++++++++++++++++ .../services/gemini_multimodal_live/gemini.py | 26 ++- 3 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 examples/foundational/26i-gemini-multimodal-live-graceful-end.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 337e0ddd3..5b80bf907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- `GeminiMultimodalLiveLLMService` will now end gracefully (i.e. after the bot + has finished) upon receiving an `EndFrame`. + - Fixed an issue with `AWSNovaSonicLLMService` where the client wouldn't connect due to a breaking change in the AWS dependency chain. diff --git a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py new file mode 100644 index 000000000..e178997b8 --- /dev/null +++ b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py @@ -0,0 +1,206 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + + +import asyncio +import os +from datetime import datetime + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import AdapterType, ToolsSchema +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import EndTaskFrame, LLMRunFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.processors.frame_processor import FrameDirection +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.llm_service import FunctionCallParams +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + + +async def fetch_weather_from_api(params: FunctionCallParams): + temperature = 75 if params.arguments["format"] == "fahrenheit" else 24 + await params.result_callback( + { + "conditions": "nice", + "temperature": temperature, + "format": params.arguments["format"], + "timestamp": datetime.now().strftime("%Y%m%d_%H%M%S"), + } + ) + + +async def fetch_restaurant_recommendation(params: FunctionCallParams): + await params.result_callback({"name": "The Golden Dragon"}) + + +async def end_conversation(params: FunctionCallParams): + await params.result_callback({"success": True}) + await params.llm.push_frame(EndTaskFrame(), FrameDirection.UPSTREAM) + + +system_instruction = """ +You are a helpful assistant who can answer questions and use tools. + +You have three tools available to you: +1. get_current_weather: Use this tool to get the current weather in a specific location. +2. get_restaurant_recommendation: Use this tool to get a restaurant recommendation in a specific location. +3. end_conversation: Use this tool to gracefully end the conversation. + +After you've responded to the user three times, do two things, in order: +1. Politely let them know that that's all the time you have today and say goodbye. +2. Call the end_conversation tool to gracefully end the conversation. +""" + + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + weather_function = FunctionSchema( + name="get_current_weather", + description="Get the current weather", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "format": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "The temperature unit to use. Infer this from the user's location.", + }, + }, + required=["location", "format"], + ) + restaurant_function = FunctionSchema( + name="get_restaurant_recommendation", + description="Get a restaurant recommendation", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + }, + required=["location"], + ) + end_conversation_function = FunctionSchema( + name="end_conversation", + description="Gracefully end the conversation", + properties={}, + required=[], + ) + search_tool = {"google_search": {}} + tools = ToolsSchema( + standard_tools=[weather_function, restaurant_function, end_conversation_function], + custom_tools={AdapterType.GEMINI: [search_tool]}, + ) + + llm = GeminiMultimodalLiveLLMService( + api_key=os.getenv("GOOGLE_API_KEY"), + system_instruction=system_instruction, + tools=tools, + ) + + llm.register_function("get_current_weather", fetch_weather_from_api) + llm.register_function("get_restaurant_recommendation", fetch_restaurant_recommendation) + llm.register_function("end_conversation", end_conversation) + + context = OpenAILLMContext( + [{"role": "user", "content": "Say hello."}], + ) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), + context_aggregator.user(), + llm, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index f48fd3324..c3358cd2c 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -658,6 +658,9 @@ class GeminiMultimodalLiveLLMService(LLMService): # Session resumption self._session_resumption_handle: Optional[str] = None + + # Bookkeeping for ending gracefully (i.e. after the bot is finished) + self._end_frame_pending_bot_turn_finished: Optional[EndFrame] = None def _create_client(self, api_key: str, http_options: Optional[HttpOptions] = None): self._client = Client(api_key=api_key, http_options=http_options) @@ -771,7 +774,7 @@ class GeminiMultimodalLiveLLMService(LLMService): # async def _handle_interruption(self): - self._bot_is_speaking = False + await self._set_bot_is_speaking(False) await self.push_frame(TTSStoppedFrame()) await self.push_frame(LLMFullResponseEndFrame()) @@ -802,6 +805,13 @@ class GeminiMultimodalLiveLLMService(LLMService): frame: The frame to process. direction: The frame processing direction. """ + # Defer EndFrame handling until after the bot turn is finished + if isinstance(frame, EndFrame): + if self._bot_is_speaking: + logger.debug("Deferring handling EndFrame until bot turn is finished") + self._end_frame_pending_bot_turn_finished = frame + return + await super().process_frame(frame, direction) if isinstance(frame, TranscriptionFrame): @@ -864,6 +874,16 @@ class GeminiMultimodalLiveLLMService(LLMService): else: await self.push_frame(frame, direction) + async def _set_bot_is_speaking(self, speaking: bool): + if self._bot_is_speaking == speaking: + return + + self._bot_is_speaking = speaking + + if not self._bot_is_speaking and self._end_frame_pending_bot_turn_finished: + await self.queue_frame(self._end_frame_pending_bot_turn_finished) + self._end_frame_pending_bot_turn_finished = None + async def _connect(self, session_resumption_handle: Optional[str] = None): """Establish client connection to Gemini Live API.""" if self._session: @@ -1271,7 +1291,7 @@ class GeminiMultimodalLiveLLMService(LLMService): return if not self._bot_is_speaking: - self._bot_is_speaking = True + await self._set_bot_is_speaking(True) await self.push_frame(TTSStartedFrame()) await self.push_frame(LLMFullResponseStartFrame()) @@ -1307,7 +1327,7 @@ class GeminiMultimodalLiveLLMService(LLMService): @traced_gemini_live(operation="llm_response") async def _handle_msg_turn_complete(self, message: LiveServerMessage): """Handle the turn complete message.""" - self._bot_is_speaking = False + await self._set_bot_is_speaking(False) text = self._bot_text_buffer # Trace the complete LLM response (this will be handled by the decorator) From deaeb75a1f900eec0f3569a4daf2013a93860ecd Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 17:16:31 -0400 Subject: [PATCH 039/158] Fix changelog after rebase (and add a missing item) --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b80bf907..10cd8738f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `GeminiMultimodalLiveLLMService` to use the `google-genai` library rather than use WebSockets directly. +### Fixed + +- `GeminiMultimodalLiveLLMService` will now end gracefully (i.e. after the bot + has finished) upon receiving an `EndFrame`. + +- `GeminiMultimodalLiveLLMService` will try to seamlessly reconnect when it + loses its connection. + ## [0.0.89] - 2025-10-07 ### Fixed @@ -70,9 +78,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- `GeminiMultimodalLiveLLMService` will now end gracefully (i.e. after the bot - has finished) upon receiving an `EndFrame`. - - Fixed an issue with `AWSNovaSonicLLMService` where the client wouldn't connect due to a breaking change in the AWS dependency chain. From cf90071926f20ecb44906c1e7033c15989932232 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 8 Oct 2025 17:19:46 -0400 Subject: [PATCH 040/158] Format fix --- src/pipecat/services/gemini_multimodal_live/gemini.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index c3358cd2c..65b5eb781 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -658,7 +658,7 @@ class GeminiMultimodalLiveLLMService(LLMService): # Session resumption self._session_resumption_handle: Optional[str] = None - + # Bookkeeping for ending gracefully (i.e. after the bot is finished) self._end_frame_pending_bot_turn_finished: Optional[EndFrame] = None From 728361a6a7d83f4074e62581ae86ee91b4c00e18 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 6 Oct 2025 10:17:50 -0400 Subject: [PATCH 041/158] Add `GeminiVertexMultimodalLiveLLMService` --- CHANGELOG.md | 3 + .../26h-gemini-multimodal-live-vertex.py | 134 +++++++++++++ .../services/gemini_multimodal_live/gemini.py | 42 +++- .../services/gemini_multimodal_live/vertex.py | 187 ++++++++++++++++++ 4 files changed, 360 insertions(+), 6 deletions(-) create mode 100644 examples/foundational/26h-gemini-multimodal-live-vertex.py create mode 100644 src/pipecat/services/gemini_multimodal_live/vertex.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 10cd8738f..6999bd37c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `GeminiVertexMultimodalLiveLLMService`, for accessing Gemini Live via + Google Vertex AI. + - Added some new configuration options to `GeminiMultimodalLiveLLMService`: - `thinking` diff --git a/examples/foundational/26h-gemini-multimodal-live-vertex.py b/examples/foundational/26h-gemini-multimodal-live-vertex.py new file mode 100644 index 000000000..581d2524a --- /dev/null +++ b/examples/foundational/26h-gemini-multimodal-live-vertex.py @@ -0,0 +1,134 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import LLMMessagesAppendFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_multimodal_live.vertex import GeminiVertexMultimodalLiveLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +# Load environment variables +load_dotenv(override=True) + + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + # Create the Gemini Vertex Multimodal Live LLM service + system_instruction = f""" + You are a helpful AI assistant. + Your goal is to demonstrate your capabilities in a helpful and engaging way. + Your output will be converted to audio so don't include special characters in your answers. + Respond to what the user said in a creative and helpful way. + """ + + llm = GeminiVertexMultimodalLiveLLMService( + credentials=os.getenv("GOOGLE_VERTEX_TEST_CREDENTIALS"), + project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), + location=os.getenv("GOOGLE_CLOUD_LOCATION"), + system_instruction=system_instruction, + voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck + ) + + # Build the pipeline + pipeline = Pipeline( + [ + transport.input(), + llm, + transport.output(), + ] + ) + + # Configure the pipeline task + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + # Handle client connection event + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + await task.queue_frames( + [ + LLMMessagesAppendFrame( + messages=[ + { + "role": "user", + "content": f"Greet the user and introduce yourself.", + } + ] + ) + ] + ) + + # Handle client disconnection events + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + # Run the pipeline + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 65b5eb781..0de76fbf1 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -601,7 +601,8 @@ class GeminiMultimodalLiveLLMService(LLMService): self._audio_input_paused = start_audio_paused self._video_input_paused = start_video_paused self._context = None - self._create_client(api_key, http_options) + self._api_key = api_key + self._http_options = http_options self._session: AsyncSession = None self._connection_task = None @@ -649,8 +650,8 @@ class GeminiMultimodalLiveLLMService(LLMService): "extra": params.extra if isinstance(params.extra, dict) else {}, } - # Initialize the File API client - self.file_api = GeminiFileAPI(api_key=api_key, base_url=file_api_base_url) + self._file_api_base_url = file_api_base_url + self._file_api: Optional[GeminiFileAPI] = None # Grounding metadata tracking self._search_result_buffer = "" @@ -662,8 +663,23 @@ class GeminiMultimodalLiveLLMService(LLMService): # Bookkeeping for ending gracefully (i.e. after the bot is finished) self._end_frame_pending_bot_turn_finished: Optional[EndFrame] = None - def _create_client(self, api_key: str, http_options: Optional[HttpOptions] = None): - self._client = Client(api_key=api_key, http_options=http_options) + # Initialize the API client. Subclasses can override this if needed. + self.create_client() + + def create_client(self): + """Create the Gemini API client instance. Subclasses can override this.""" + self._client = Client(api_key=self._api_key, http_options=self._http_options) + + @property + def file_api(self) -> GeminiFileAPI: + """Get the Gemini File API client instance. Subclasses can override this. + + Returns: + The Gemini File API client. + """ + if not self._file_api: + self._file_api = GeminiFileAPI(api_key=self._api_key, base_url=self._file_api_base_url) + return self._file_api def can_generate_metrics(self) -> bool: """Check if the service can generate usage metrics. @@ -1282,7 +1298,21 @@ class GeminiMultimodalLiveLLMService(LLMService): inline_data = part.inline_data if not inline_data: return - if inline_data.mime_type != f"audio/pcm;rate={self._sample_rate}": + + # Check if mime type matches expected format + expected_mime_type = f"audio/pcm;rate={self._sample_rate}" + if inline_data.mime_type == expected_mime_type: + # Perfect match, continue processing + pass + elif inline_data.mime_type == "audio/pcm": + # Sample rate not provided in mime type, assume default + if not hasattr(self, "_sample_rate_warning_logged"): + logger.warning( + f"Sample rate not provided in mime type '{inline_data.mime_type}', assuming rate of {self._sample_rate}" + ) + self._sample_rate_warning_logged = True + else: + # Unrecognized format logger.warning(f"Unrecognized server_content format {inline_data.mime_type}") return diff --git a/src/pipecat/services/gemini_multimodal_live/vertex.py b/src/pipecat/services/gemini_multimodal_live/vertex.py new file mode 100644 index 000000000..e07eb474f --- /dev/null +++ b/src/pipecat/services/gemini_multimodal_live/vertex.py @@ -0,0 +1,187 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Google Vertex AI Gemini Multimodal Live service. + +This module provides integration with Google's Gemini Multimodal Live model via +Vertex AI, supporting both text and audio modalities with voice transcription, +streaming responses, and tool usage. +""" + +import json +import os +from typing import List, Optional, Union + +from loguru import logger + +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.services.gemini_multimodal_live.gemini import ( + GeminiMultimodalLiveLLMService, + HttpOptions, + InputParams, +) + +try: + from google.auth import default + from google.auth.exceptions import GoogleAuthError + from google.auth.transport.requests import Request + from google.genai import Client + from google.oauth2 import service_account + +except ModuleNotFoundError as e: + logger.error(f"Exception: {e}") + logger.error( + "In order to use Google Vertex AI, you need to `pip install pipecat-ai[google]`. Also, set up your Google credentials properly." + ) + raise Exception(f"Missing module: {e}") + + +class GeminiVertexMultimodalLiveLLMService(GeminiMultimodalLiveLLMService): + """Google Vertex AI Gemini Multimodal Live service. + + Provides access to Google's Gemini Multimodal Live model via Vertex AI, + supporting both text and audio modalities. It handles voice transcription, + streaming audio responses, and tool usage. + """ + + def __init__( + self, + *, + credentials: Optional[str] = None, + credentials_path: Optional[str] = None, + location: str = "us-east4", + project_id: str, + model="google/gemini-2.0-flash-live-preview-04-09", + voice_id: str = "Charon", + start_audio_paused: bool = False, + start_video_paused: bool = False, + system_instruction: Optional[str] = None, + tools: Optional[Union[List[dict], ToolsSchema]] = None, + params: Optional[InputParams] = None, + inference_on_context_initialization: bool = True, + file_api_base_url: str = "https://generativelanguage.googleapis.com/v1beta/files", + http_options: Optional[HttpOptions] = None, + **kwargs, + ): + """Initialize the Google Vertex AI Gemini Multimodal Live service. + + Args: + credentials: JSON string of service account credentials. + credentials_path: Path to the service account JSON file. + location: GCP region for Vertex AI endpoint (e.g., "us-east4"). + project_id: Google Cloud project ID. + model: Model identifier to use. Defaults to "models/gemini-2.0-flash-live-preview-04-09". + voice_id: TTS voice identifier. Defaults to "Charon". + start_audio_paused: Whether to start with audio input paused. Defaults to False. + start_video_paused: Whether to start with video input paused. Defaults to False. + system_instruction: System prompt for the model. Defaults to None. + tools: Tools/functions available to the model. Defaults to None. + params: Configuration parameters for the model along with Vertex AI + location and project ID. + inference_on_context_initialization: Whether to generate a response when context + is first set. Defaults to True. + file_api_base_url: Base URL for the Gemini File API. Defaults to the official endpoint. + http_options: HTTP options for the client. + **kwargs: Additional arguments passed to parent GeminiMultimodalLiveLLMService. + """ + # Check if user incorrectly passed api_key, which is used by parent + # class but not here. + if "api_key" in kwargs: + logger.error( + "GeminiVertexMultimodalLiveLLMService does not accept 'api_key' parameter. " + "Use 'credentials' or 'credentials_path' instead for Vertex AI authentication." + ) + raise ValueError( + "Invalid parameter 'api_key'. Use 'credentials' or 'credentials_path' for Vertex AI authentication." + ) + + # These need to be set before calling super().__init__() because + # super().__init__() invokes create_client(), which needs these. + self._credentials = self._get_credentials(credentials, credentials_path) + self._project_id = project_id + self._location = location + + # Call parent constructor with the obtained API key + super().__init__( + # api_key is required by parent class, but actually not used with + # Vertex + api_key="dummy", + model=model, + voice_id=voice_id, + start_audio_paused=start_audio_paused, + start_video_paused=start_video_paused, + system_instruction=system_instruction, + tools=tools, + params=params, + inference_on_context_initialization=inference_on_context_initialization, + file_api_base_url=file_api_base_url, + http_options=http_options, + **kwargs, + ) + + def create_client(self): + """Create the Gemini client instance.""" + self._client = Client( + vertexai=True, + credentials=self._credentials, + project=self._project_id, + location=self._location, + ) + + @property + def file_api(self): + """Gemini File API is not supported with Vertex AI.""" + raise NotImplementedError( + "When using Vertex AI, the recommended approach is to use Google Cloud Storage for file handling. The Gemini File API is not directly supported in this context." + ) + + @staticmethod + def _get_credentials(credentials: Optional[str], credentials_path: Optional[str]) -> str: + """Retrieve Credentials using Google service account credentials JSON. + + Supports multiple authentication methods: + 1. Direct JSON credentials string + 2. Path to service account JSON file + 3. Default application credentials (ADC) + + Args: + credentials: JSON string of service account credentials. + credentials_path: Path to the service account JSON file. + + Returns: + OAuth token for API authentication. + + Raises: + ValueError: If no valid credentials are provided or found. + """ + creds: Optional[service_account.Credentials] = None + + if credentials: + # Parse and load credentials from JSON string + creds = service_account.Credentials.from_service_account_info( + json.loads(credentials), + scopes=["https://www.googleapis.com/auth/cloud-platform"], + ) + elif credentials_path: + # Load credentials from JSON file + creds = service_account.Credentials.from_service_account_file( + credentials_path, + scopes=["https://www.googleapis.com/auth/cloud-platform"], + ) + else: + try: + creds, project_id = default( + scopes=["https://www.googleapis.com/auth/cloud-platform"] + ) + except GoogleAuthError: + pass + + if not creds: + raise ValueError("No valid credentials provided.") + + creds.refresh(Request()) # Ensure token is up-to-date, lifetime is 1 hour. + + return creds From a14fb20d15e976b49e41fe09e27326db5515e58e Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 6 Oct 2025 15:10:23 -0400 Subject: [PATCH 042/158] Fix Gemini Live w/Vertex AI not being able to handle an empty list provided for "function_declarations" --- src/pipecat/adapters/services/gemini_adapter.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pipecat/adapters/services/gemini_adapter.py b/src/pipecat/adapters/services/gemini_adapter.py index 63a86e6d2..abe33a8ec 100644 --- a/src/pipecat/adapters/services/gemini_adapter.py +++ b/src/pipecat/adapters/services/gemini_adapter.py @@ -87,9 +87,11 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): Includes both converted standard tools and any custom Gemini-specific tools. """ functions_schema = tools_schema.standard_tools - formatted_standard_tools = [ - {"function_declarations": [func.to_default_dict() for func in functions_schema]} - ] + formatted_standard_tools = ( + [{"function_declarations": [func.to_default_dict() for func in functions_schema]}] + if functions_schema + else [] + ) custom_gemini_tools = [] if tools_schema.custom_tools: custom_gemini_tools = tools_schema.custom_tools.get(AdapterType.GEMINI, []) From 0b6dd98000e2426c7a7bd8539f2b31d65288d6b0 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 6 Oct 2025 15:13:05 -0400 Subject: [PATCH 043/158] Make a note in our examples that there's an issue with Gemini Live + Vertex around using "google_search" alongside other tools --- .../26b-gemini-multimodal-live-function-calling.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index f14713a5c..a5024d4a6 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -122,6 +122,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): required=["location"], ) search_tool = {"google_search": {}} + # KNOWN ISSUE: If using GeminiVertexMultimodalLiveLLMService, it appears + # you cannot use the "google_search" tool alongside other tools. + # See https://github.com/googleapis/python-genai/issues/941. tools = ToolsSchema( standard_tools=[weather_function, restaurant_function], custom_tools={AdapterType.GEMINI: [search_tool]}, From 2699f0c2a61fee9ffb8e60766f90a75caee5ff8d Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 6 Oct 2025 15:39:08 -0400 Subject: [PATCH 044/158] Fix tool calls when using Gemini Live + Vertex AI --- src/pipecat/services/gemini_multimodal_live/gemini.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 0de76fbf1..8ab836173 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -16,6 +16,7 @@ import io import json import random import time +import uuid from dataclasses import dataclass from enum import Enum from typing import Any, Dict, List, Optional, Union @@ -1345,7 +1346,11 @@ class GeminiMultimodalLiveLLMService(LLMService): function_calls_llm = [ FunctionCallFromLLM( context=self._context, - tool_call_id=f.id, + tool_call_id=( + # NOTE: when using Vertex AI we don't get server-provided + # tool call IDs here + f.id or str(uuid.uuid4()) + ), function_name=f.name, arguments=f.args, ) From 99f008e9279b9565c21fc9585d4aaabf0aa7991b Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 6 Oct 2025 15:54:56 -0400 Subject: [PATCH 045/158] Make a note in our examples that there's an issue with Gemini Live + Vertex around specifying a modality other than AUDIO --- examples/foundational/26d-gemini-multimodal-live-text.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index 667887b03..dd3e0d65b 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -80,6 +80,8 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") + # KNOWN ISSUE: If using GeminiVertexMultimodalLiveLLMService, it appears + # you cannot specify a modality other than AUDIO. llm = GeminiMultimodalLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=SYSTEM_INSTRUCTION, From 7c1e2793c57aecfca2cea910d97dcf6907b8e876 Mon Sep 17 00:00:00 2001 From: shreyas-sarvam Date: Thu, 9 Oct 2025 18:26:22 +0530 Subject: [PATCH 046/158] feat: Add support for bulbul:v3 and bulbul:v3-beta --- src/pipecat/services/sarvam/tts.py | 136 ++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 31 deletions(-) diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index a9fedcc58..5bacffdf0 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -20,9 +20,9 @@ from pipecat.frames.frames import ( EndFrame, ErrorFrame, Frame, - InterruptionFrame, LLMFullResponseEndFrame, StartFrame, + StartInterruptionFrame, TTSAudioRawFrame, TTSStartedFrame, TTSStoppedFrame, @@ -76,17 +76,29 @@ class SarvamHttpTTSService(TTSService): Example:: - tts = SarvamTTSService( + tts = SarvamHttpTTSService( api_key="your-api-key", voice_id="anushka", model="bulbul:v2", aiohttp_session=session, - params=SarvamTTSService.InputParams( + params=SarvamHttpTTSService.InputParams( language=Language.HI, pitch=0.1, pace=1.2 ) ) + + # For bulbul v3 beta with any speaker: + tts_v3 = SarvamHttpTTSService( + api_key="your-api-key", + voice_id="speaker_name", + model="bulbul:v3, + aiohttp_session=session, + params=SarvamHttpTTSService.InputParams( + language=Language.HI, + temperature=0.8 + ) + ) """ class InputParams(BaseModel): @@ -105,6 +117,11 @@ class SarvamHttpTTSService(TTSService): pace: Optional[float] = Field(default=1.0, ge=0.3, le=3.0) loudness: Optional[float] = Field(default=1.0, ge=0.1, le=3.0) enable_preprocessing: Optional[bool] = False + temperature: Optional[float] = Field( + default=0.6, + ge=0.01, + le=1.0, + ) def __init__( self, @@ -124,7 +141,7 @@ class SarvamHttpTTSService(TTSService): api_key: Sarvam AI API subscription key. aiohttp_session: Shared aiohttp session for making requests. voice_id: Speaker voice ID (e.g., "anushka", "meera"). Defaults to "anushka". - model: TTS model to use ("bulbul:v1" or "bulbul:v2"). Defaults to "bulbul:v2". + model: TTS model to use ("bulbul:v2" or "bulbul:v3-beta" or "bulbul:v3"). Defaults to "bulbul:v2". base_url: Sarvam AI API base URL. Defaults to "https://api.sarvam.ai". sample_rate: Audio sample rate in Hz (8000, 16000, 22050, 24000). If None, uses default. params: Additional voice and preprocessing parameters. If None, uses defaults. @@ -138,15 +155,31 @@ class SarvamHttpTTSService(TTSService): self._base_url = base_url self._session = aiohttp_session - self._settings = { - "language": ( - self.language_to_service_language(params.language) if params.language else "en-IN" - ), - "pitch": params.pitch, - "pace": params.pace, - "loudness": params.loudness, - "enable_preprocessing": params.enable_preprocessing, - } + if model == "bulbul:v3-beta" or model == "bulbul:v3": + # For bulbul v3 beta, exclude pace and loudness parameters + self._settings = { + "language": ( + self.language_to_service_language(params.language) + if params.language + else "en-IN" + ), + "enable_preprocessing": params.enable_preprocessing, + "temperature": getattr(params, "temperature", 0.6), + "model": model, # Include model in settings for v3 beta + } + else: + # For bulbul v2, include all parameters including pace and loudness + self._settings = { + "language": ( + self.language_to_service_language(params.language) + if params.language + else "en-IN" + ), + "pitch": params.pitch, + "pace": params.pace, + "loudness": params.loudness, + "enable_preprocessing": params.enable_preprocessing, + } self.set_model_name(model) self.set_voice(voice_id) @@ -275,6 +308,18 @@ class SarvamTTSService(InterruptibleTTSService): pace=1.2 ) ) + + # For bulbul v3 beta with any speaker and temperature: + # Note: pace and loudness are not supported for bulbul v3 beta + tts_v3 = SarvamTTSService( + api_key="your-api-key", + voice_id="speaker_name", + model="bulbul:v3", + params=SarvamTTSService.InputParams( + language=Language.HI, + temperature=0.8 + ) + ) """ class InputParams(BaseModel): @@ -310,6 +355,14 @@ class SarvamTTSService(InterruptibleTTSService): output_audio_codec: Optional[str] = "linear16" output_audio_bitrate: Optional[str] = "128k" language: Optional[Language] = Language.EN + temperature: Optional[float] = Field( + default=0.6, + ge=0.01, + le=1.0, + description="Controls the randomness of the output for bulbul v3 beta. " + "Lower values make the output more focused and deterministic, while " + "higher values make it more random. Range: 0.01 to 1.0. Default: 0.6.", + ) def __init__( self, @@ -329,13 +382,12 @@ class SarvamTTSService(InterruptibleTTSService): Args: api_key: Sarvam API key for authenticating TTS requests. model: Identifier of the Sarvam speech model (default "bulbul:v2"). + Supports "bulbul:v2", "bulbul:v3-beta" and "bulbul:v3". voice_id: Voice identifier for synthesis (default "anushka"). url: WebSocket URL for connecting to the TTS backend (default production URL). aiohttp_session: Optional shared aiohttp session. To maintain backward compatibility. - .. deprecated:: 0.0.81 aiohttp_session is no longer used. This parameter will be removed in a future version. - aggregate_sentences: Whether to merge multiple sentences into one audio chunk (default True). sample_rate: Desired sample rate for the output audio in Hz (overrides default if set). params: Optional input parameters to override global configuration. @@ -372,21 +424,43 @@ class SarvamTTSService(InterruptibleTTSService): self.set_model_name(model) self.set_voice(voice_id) # Configuration parameters - self._settings = { - "target_language_code": ( - self.language_to_service_language(params.language) if params.language else "en-IN" - ), - "pitch": params.pitch, - "pace": params.pace, - "speaker": voice_id, - "loudness": params.loudness, - "speech_sample_rate": 0, - "enable_preprocessing": params.enable_preprocessing, - "min_buffer_size": params.min_buffer_size, - "max_chunk_length": params.max_chunk_length, - "output_audio_codec": params.output_audio_codec, - "output_audio_bitrate": params.output_audio_bitrate, - } + if model == "bulbul:v3-beta" or model == "bulbul:v3": + # For bulbul v3 beta, exclude pace and loudness parameters + self._settings = { + "target_language_code": ( + self.language_to_service_language(params.language) + if params.language + else "en-IN" + ), + "speaker": voice_id, + "speech_sample_rate": 0, + "enable_preprocessing": params.enable_preprocessing, + "min_buffer_size": params.min_buffer_size, + "max_chunk_length": params.max_chunk_length, + "output_audio_codec": params.output_audio_codec, + "output_audio_bitrate": params.output_audio_bitrate, + "temperature": getattr(params, "temperature", 0.6), + "model": model, # Include model in settings for v3 beta + } + else: + # For bulbul v2, include all parameters including pace and loudness + self._settings = { + "target_language_code": ( + self.language_to_service_language(params.language) + if params.language + else "en-IN" + ), + "pitch": params.pitch, + "pace": params.pace, + "speaker": voice_id, + "loudness": params.loudness, + "speech_sample_rate": 0, + "enable_preprocessing": params.enable_preprocessing, + "min_buffer_size": params.min_buffer_size, + "max_chunk_length": params.max_chunk_length, + "output_audio_codec": params.output_audio_codec, + "output_audio_bitrate": params.output_audio_bitrate, + } self._started = False self._receive_task = None @@ -455,7 +529,7 @@ class SarvamTTSService(InterruptibleTTSService): direction: The direction to push the frame. """ await super().push_frame(frame, direction) - if isinstance(frame, (TTSStoppedFrame, InterruptionFrame)): + if isinstance(frame, (TTSStoppedFrame, StartInterruptionFrame)): self._started = False async def process_frame(self, frame: Frame, direction: FrameDirection): From f2d90639845b90d25c9f8218697a32936c4e856e Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 9 Oct 2025 09:40:47 -0400 Subject: [PATCH 047/158] Renames: remove "multimodal" from Gemini Live types --- .../foundational/26-gemini-multimodal-live.py | 4 +- ...6a-gemini-multimodal-live-transcription.py | 4 +- ...gemini-multimodal-live-function-calling.py | 4 +- .../26c-gemini-multimodal-live-video.py | 4 +- .../26d-gemini-multimodal-live-text.py | 10 +- .../26e-gemini-multimodal-google-search.py | 4 +- .../26f-gemini-multimodal-live-files-api.py | 6 +- ...emini-multimodal-live-groundingMetadata.py | 4 +- .../26h-gemini-multimodal-live-vertex.py | 6 +- ...26i-gemini-multimodal-live-graceful-end.py | 4 +- examples/foundational/46-video-processing.py | 4 +- src/pipecat/services/gemini_live/__init__.py | 3 + src/pipecat/services/gemini_live/file_api.py | 189 ++ src/pipecat/services/gemini_live/gemini.py | 1582 ++++++++++++++++ .../vertex.py | 24 +- .../services/gemini_multimodal_live/events.py | 17 +- .../gemini_multimodal_live/file_api.py | 194 +- .../services/gemini_multimodal_live/gemini.py | 1617 +---------------- 18 files changed, 1887 insertions(+), 1793 deletions(-) create mode 100644 src/pipecat/services/gemini_live/__init__.py create mode 100644 src/pipecat/services/gemini_live/file_api.py create mode 100644 src/pipecat/services/gemini_live/gemini.py rename src/pipecat/services/{gemini_multimodal_live => gemini_live}/vertex.py (89%) diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index b446a9b8c..f063c247f 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -17,7 +17,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): Respond to what the user said in a creative and helpful way. """ - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=system_instruction, voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index ad3cd06ee..3c0eb19ce 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -20,7 +20,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams @@ -65,7 +65,7 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede # system_instruction="Talk like a pirate." diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index a5024d4a6..a86822a20 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -22,7 +22,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -130,7 +130,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): custom_tools={AdapterType.GEMINI: [search_tool]}, ) - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=system_instruction, tools=tools, diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index a28eaaacf..7eac0bc0b 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -24,7 +24,7 @@ from pipecat.runner.utils import ( maybe_capture_participant_camera, maybe_capture_participant_screen, ) -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -58,7 +58,7 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede # system_instruction="Talk like a pirate." diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index dd3e0d65b..625ca17d0 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -20,9 +20,9 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.services.gemini_multimodal_live.gemini import ( - GeminiMultimodalLiveLLMService, - GeminiMultimodalModalities, +from pipecat.services.gemini_live.gemini import ( + GeminiLiveLLMService, + GeminiModalities, InputParams, ) from pipecat.transports.base_transport import BaseTransport, TransportParams @@ -82,11 +82,11 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): # KNOWN ISSUE: If using GeminiVertexMultimodalLiveLLMService, it appears # you cannot specify a modality other than AUDIO. - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=SYSTEM_INSTRUCTION, tools=[{"google_search": {}}, {"code_execution": {}}], - params=InputParams(modalities=GeminiMultimodalModalities.TEXT), + params=InputParams(modalities=GeminiModalities.TEXT), ) # Optionally, you can set the response modalities via a function diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index 31fad54fe..1bcf02db9 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -19,7 +19,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams @@ -83,7 +83,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") # Initialize the Gemini Multimodal Live model - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck system_instruction=system_instruction, diff --git a/examples/foundational/26f-gemini-multimodal-live-files-api.py b/examples/foundational/26f-gemini-multimodal-live-files-api.py index b01c21803..7ce4a483d 100644 --- a/examples/foundational/26f-gemini-multimodal-live-files-api.py +++ b/examples/foundational/26f-gemini-multimodal-live-files-api.py @@ -19,8 +19,8 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import ( - GeminiMultimodalLiveLLMService, +from pipecat.services.gemini_live.gemini import ( + GeminiLiveLLMService, ) from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -110,7 +110,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): """ # Initialize Gemini service with File API support - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=system_instruction, voice_id="Charon", # Aoede, Charon, Fenrir, Kore, Puck diff --git a/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py b/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py index 6cfeed51b..9b8b25315 100644 --- a/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py +++ b/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py @@ -14,7 +14,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.services.google.frames import LLMSearchResponseFrame from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -105,7 +105,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): custom_tools={AdapterType.GEMINI: [{"google_search": {}}, {"code_execution": {}}]}, ) - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=SYSTEM_INSTRUCTION, voice_id="Charon", # Aoede, Charon, Fenrir, Kore, Puck diff --git a/examples/foundational/26h-gemini-multimodal-live-vertex.py b/examples/foundational/26h-gemini-multimodal-live-vertex.py index 581d2524a..4210dc0e2 100644 --- a/examples/foundational/26h-gemini-multimodal-live-vertex.py +++ b/examples/foundational/26h-gemini-multimodal-live-vertex.py @@ -17,8 +17,8 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService -from pipecat.services.gemini_multimodal_live.vertex import GeminiVertexMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.gemini_live.vertex import GeminiLiveVertexLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): Respond to what the user said in a creative and helpful way. """ - llm = GeminiVertexMultimodalLiveLLMService( + llm = GeminiLiveVertexLLMService( credentials=os.getenv("GOOGLE_VERTEX_TEST_CREDENTIALS"), project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), location=os.getenv("GOOGLE_CLOUD_LOCATION"), diff --git a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py index e178997b8..9451d0ef0 100644 --- a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py +++ b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py @@ -24,7 +24,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -144,7 +144,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): custom_tools={AdapterType.GEMINI: [search_tool]}, ) - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=system_instruction, tools=tools, diff --git a/examples/foundational/46-video-processing.py b/examples/foundational/46-video-processing.py index bfb718ff2..7f7363538 100644 --- a/examples/foundational/46-video-processing.py +++ b/examples/foundational/46-video-processing.py @@ -20,7 +20,7 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.processors.frameworks.rtvi import RTVIObserver, RTVIProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_multimodal_live import GeminiMultimodalLiveLLMService +from pipecat.services.gemini_live import GeminiLiveLLMService from pipecat.transports.base_transport import TransportParams from pipecat.transports.daily.transport import DailyParams, DailyTransport @@ -94,7 +94,7 @@ Respond to what the user said in a creative and helpful way. Keep your responses async def run_bot(pipecat_transport): - llm = GeminiMultimodalLiveLLMService( + llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck transcribe_user_audio=True, diff --git a/src/pipecat/services/gemini_live/__init__.py b/src/pipecat/services/gemini_live/__init__.py new file mode 100644 index 000000000..6a2d33bd8 --- /dev/null +++ b/src/pipecat/services/gemini_live/__init__.py @@ -0,0 +1,3 @@ +from .file_api import GeminiFileAPI +from .gemini import GeminiLiveLLMService +from .vertex import GeminiLiveVertexLLMService diff --git a/src/pipecat/services/gemini_live/file_api.py b/src/pipecat/services/gemini_live/file_api.py new file mode 100644 index 000000000..5ae7fdbb7 --- /dev/null +++ b/src/pipecat/services/gemini_live/file_api.py @@ -0,0 +1,189 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Gemini File API client for uploading and managing files. + +This module provides a client for Google's Gemini File API, enabling file +uploads, metadata retrieval, listing, and deletion. Files uploaded through +this API can be referenced in Gemini generative model calls. +""" + +import mimetypes +from typing import Any, Dict, Optional + +import aiohttp +from loguru import logger + + +class GeminiFileAPI: + """Client for the Gemini File API. + + This class provides methods for uploading, fetching, listing, and deleting files + through Google's Gemini File API. + + Files uploaded through this API remain available for 48 hours and can be referenced + in calls to the Gemini generative models. Maximum file size is 2GB, with total + project storage limited to 20GB. + """ + + def __init__( + self, api_key: str, base_url: str = "https://generativelanguage.googleapis.com/v1beta/files" + ): + """Initialize the Gemini File API client. + + Args: + api_key: Google AI API key + base_url: Base URL for the Gemini File API (default is the v1beta endpoint) + """ + self._api_key = api_key + self._base_url = base_url + # Upload URL uses the /upload/ path + self.upload_base_url = "https://generativelanguage.googleapis.com/upload/v1beta/files" + + async def upload_file( + self, file_path: str, display_name: Optional[str] = None + ) -> Dict[str, Any]: + """Upload a file to the Gemini File API using the correct resumable upload protocol. + + Args: + file_path: Path to the file to upload + display_name: Optional display name for the file + + Returns: + File metadata including uri, name, and display_name + """ + logger.info(f"Uploading file: {file_path}") + + async with aiohttp.ClientSession() as session: + # Determine the file's MIME type + mime_type, _ = mimetypes.guess_type(file_path) + if not mime_type: + mime_type = "application/octet-stream" + + # Read the file + with open(file_path, "rb") as f: + file_data = f.read() + + # Create the metadata payload + metadata = {} + if display_name: + metadata = {"file": {"display_name": display_name}} + + # Step 1: Initial resumable request to get upload URL + headers = { + "X-Goog-Upload-Protocol": "resumable", + "X-Goog-Upload-Command": "start", + "X-Goog-Upload-Header-Content-Length": str(len(file_data)), + "X-Goog-Upload-Header-Content-Type": mime_type, + "Content-Type": "application/json", + } + + logger.debug(f"Step 1: Getting upload URL from {self.upload_base_url}") + async with session.post( + f"{self.upload_base_url}?key={self._api_key}", headers=headers, json=metadata + ) as response: + if response.status != 200: + error_text = await response.text() + logger.error(f"Error initiating file upload: {error_text}") + raise Exception(f"Failed to initiate upload: {response.status} - {error_text}") + + # Get the upload URL from the response header + upload_url = response.headers.get("X-Goog-Upload-URL") + if not upload_url: + logger.error(f"Response headers: {dict(response.headers)}") + raise Exception("No upload URL in response headers") + + logger.debug(f"Got upload URL: {upload_url}") + + # Step 2: Upload the actual file data + upload_headers = { + "Content-Length": str(len(file_data)), + "X-Goog-Upload-Offset": "0", + "X-Goog-Upload-Command": "upload, finalize", + } + + logger.debug(f"Step 2: Uploading file data to {upload_url}") + async with session.post(upload_url, headers=upload_headers, data=file_data) as response: + if response.status != 200: + error_text = await response.text() + logger.error(f"Error uploading file data: {error_text}") + raise Exception(f"Failed to upload file: {response.status} - {error_text}") + + file_info = await response.json() + logger.info(f"File uploaded successfully: {file_info.get('file', {}).get('name')}") + return file_info + + async def get_file(self, name: str) -> Dict[str, Any]: + """Get metadata for a file. + + Args: + name: File name (or full path) + + Returns: + File metadata + """ + # Extract just the name part if a full path is provided + if "/" in name: + name = name.split("/")[-1] + + async with aiohttp.ClientSession() as session: + async with session.get(f"{self._base_url}/{name}?key={self._api_key}") as response: + if response.status != 200: + error_text = await response.text() + logger.error(f"Error getting file metadata: {error_text}") + raise Exception(f"Failed to get file metadata: {response.status}") + + file_info = await response.json() + return file_info + + async def list_files( + self, page_size: int = 10, page_token: Optional[str] = None + ) -> Dict[str, Any]: + """List uploaded files. + + Args: + page_size: Number of files to return per page + page_token: Token for pagination + + Returns: + List of files and next page token if available + """ + params = {"key": self._api_key, "pageSize": page_size} + + if page_token: + params["pageToken"] = page_token + + async with aiohttp.ClientSession() as session: + async with session.get(self._base_url, params=params) as response: + if response.status != 200: + error_text = await response.text() + logger.error(f"Error listing files: {error_text}") + raise Exception(f"Failed to list files: {response.status}") + + result = await response.json() + return result + + async def delete_file(self, name: str) -> bool: + """Delete a file. + + Args: + name: File name (or full path) + + Returns: + True if deleted successfully + """ + # Extract just the name part if a full path is provided + if "/" in name: + name = name.split("/")[-1] + + async with aiohttp.ClientSession() as session: + async with session.delete(f"{self._base_url}/{name}?key={self._api_key}") as response: + if response.status != 200: + error_text = await response.text() + logger.error(f"Error deleting file: {error_text}") + raise Exception(f"Failed to delete file: {response.status}") + + return True diff --git a/src/pipecat/services/gemini_live/gemini.py b/src/pipecat/services/gemini_live/gemini.py new file mode 100644 index 000000000..4b5f05209 --- /dev/null +++ b/src/pipecat/services/gemini_live/gemini.py @@ -0,0 +1,1582 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Google Gemini Live API service implementation. + +This module provides real-time conversational AI capabilities using Google's +Gemini Live API, supporting both text and audio modalities with +voice transcription, streaming responses, and tool usage. +""" + +import base64 +import io +import json +import random +import time +import uuid +from dataclasses import dataclass +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from loguru import logger +from PIL import Image +from pydantic import BaseModel, Field + +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.adapters.services.gemini_adapter import GeminiLLMAdapter +from pipecat.frames.frames import ( + BotStartedSpeakingFrame, + BotStoppedSpeakingFrame, + CancelFrame, + EndFrame, + ErrorFrame, + Frame, + InputAudioRawFrame, + InputImageRawFrame, + InputTextRawFrame, + InterruptionFrame, + LLMContextFrame, + LLMFullResponseEndFrame, + LLMFullResponseStartFrame, + LLMMessagesAppendFrame, + LLMSetToolsFrame, + LLMTextFrame, + LLMUpdateSettingsFrame, + StartFrame, + TranscriptionFrame, + TTSAudioRawFrame, + TTSStartedFrame, + TTSStoppedFrame, + TTSTextFrame, + UserImageRawFrame, + UserStartedSpeakingFrame, + UserStoppedSpeakingFrame, +) +from pipecat.metrics.metrics import LLMTokenUsage +from pipecat.processors.aggregators.llm_response import ( + LLMAssistantAggregatorParams, + LLMUserAggregatorParams, +) +from pipecat.processors.aggregators.openai_llm_context import ( + OpenAILLMContext, + OpenAILLMContextFrame, +) +from pipecat.processors.frame_processor import FrameDirection +from pipecat.services.google.frames import LLMSearchOrigin, LLMSearchResponseFrame, LLMSearchResult +from pipecat.services.llm_service import FunctionCallFromLLM, LLMService +from pipecat.services.openai.llm import ( + OpenAIAssistantContextAggregator, + OpenAIUserContextAggregator, +) +from pipecat.transcriptions.language import Language +from pipecat.utils.string import match_endofsentence +from pipecat.utils.time import time_now_iso8601 +from pipecat.utils.tracing.service_decorators import traced_gemini_live, traced_stt + +from .file_api import GeminiFileAPI + +try: + from google.genai import Client + from google.genai.live import AsyncSession + from google.genai.types import ( + AudioTranscriptionConfig, + AutomaticActivityDetection, + Blob, + Content, + ContextWindowCompressionConfig, + EndSensitivity, + FileData, + FunctionResponse, + GenerationConfig, + GroundingMetadata, + HttpOptions, + LiveConnectConfig, + LiveServerMessage, + MediaResolution, + Modality, + Part, + ProactivityConfig, + RealtimeInputConfig, + SessionResumptionConfig, + SlidingWindow, + SpeechConfig, + StartSensitivity, + ThinkingConfig, + VoiceConfig, + ) +except ModuleNotFoundError as e: + logger.error(f"Exception: {e}") + logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") + raise Exception(f"Missing module: {e}") + + +# Connection management constants +MAX_CONSECUTIVE_FAILURES = 3 +CONNECTION_ESTABLISHED_THRESHOLD = 10.0 # seconds + + +def language_to_gemini_language(language: Language) -> Optional[str]: + """Maps a Language enum value to a Gemini Live supported language code. + + Source: + https://ai.google.dev/api/generate-content#MediaResolution + + Args: + language: The language enum value to convert. + + Returns: + The Gemini language code string, or None if the language is not supported. + """ + language_map = { + # Arabic + Language.AR: "ar-XA", + # Bengali + Language.BN_IN: "bn-IN", + # Chinese (Mandarin) + Language.CMN: "cmn-CN", + Language.CMN_CN: "cmn-CN", + Language.ZH: "cmn-CN", # Map general Chinese to Mandarin for Gemini + Language.ZH_CN: "cmn-CN", # Map Simplified Chinese to Mandarin for Gemini + # German + Language.DE: "de-DE", + Language.DE_DE: "de-DE", + # English + Language.EN: "en-US", # Default to US English (though not explicitly listed in supported codes) + Language.EN_US: "en-US", + Language.EN_AU: "en-AU", + Language.EN_GB: "en-GB", + Language.EN_IN: "en-IN", + # Spanish + Language.ES: "es-ES", # Default to Spain Spanish + Language.ES_ES: "es-ES", + Language.ES_US: "es-US", + # French + Language.FR: "fr-FR", # Default to France French + Language.FR_FR: "fr-FR", + Language.FR_CA: "fr-CA", + # Gujarati + Language.GU: "gu-IN", + Language.GU_IN: "gu-IN", + # Hindi + Language.HI: "hi-IN", + Language.HI_IN: "hi-IN", + # Indonesian + Language.ID: "id-ID", + Language.ID_ID: "id-ID", + # Italian + Language.IT: "it-IT", + Language.IT_IT: "it-IT", + # Japanese + Language.JA: "ja-JP", + Language.JA_JP: "ja-JP", + # Kannada + Language.KN: "kn-IN", + Language.KN_IN: "kn-IN", + # Korean + Language.KO: "ko-KR", + Language.KO_KR: "ko-KR", + # Malayalam + Language.ML: "ml-IN", + Language.ML_IN: "ml-IN", + # Marathi + Language.MR: "mr-IN", + Language.MR_IN: "mr-IN", + # Dutch + Language.NL: "nl-NL", + Language.NL_NL: "nl-NL", + # Polish + Language.PL: "pl-PL", + Language.PL_PL: "pl-PL", + # Portuguese (Brazil) + Language.PT_BR: "pt-BR", + # Russian + Language.RU: "ru-RU", + Language.RU_RU: "ru-RU", + # Tamil + Language.TA: "ta-IN", + Language.TA_IN: "ta-IN", + # Telugu + Language.TE: "te-IN", + Language.TE_IN: "te-IN", + # Thai + Language.TH: "th-TH", + Language.TH_TH: "th-TH", + # Turkish + Language.TR: "tr-TR", + Language.TR_TR: "tr-TR", + # Vietnamese + Language.VI: "vi-VN", + Language.VI_VN: "vi-VN", + } + return language_map.get(language) + + +class GeminiLiveContext(OpenAILLMContext): + """Extended OpenAI context for Gemini Live API. + + Provides Gemini-specific context management including system instruction + extraction and message format conversion for the Live API. + """ + + @staticmethod + def upgrade(obj: OpenAILLMContext) -> "GeminiLiveContext": + """Upgrade an OpenAI context to Gemini context. + + Args: + obj: The OpenAI context to upgrade. + + Returns: + The upgraded Gemini context instance. + """ + if isinstance(obj, OpenAILLMContext) and not isinstance(obj, GeminiLiveContext): + logger.debug(f"Upgrading to Gemini Live Context: {obj}") + obj.__class__ = GeminiLiveContext + obj._restructure_from_openai_messages() + return obj + + def _restructure_from_openai_messages(self): + pass + + def extract_system_instructions(self): + """Extract system instructions from context messages. + + Returns: + Combined system instruction text from all system messages. + """ + system_instruction = "" + for item in self.messages: + if item.get("role") == "system": + content = item.get("content", "") + if content: + if system_instruction and not system_instruction.endswith("\n"): + system_instruction += "\n" + system_instruction += str(content) + return system_instruction + + def add_file_reference(self, file_uri: str, mime_type: str, text: Optional[str] = None): + """Add a file reference to the context. + + This adds a user message with a file reference that will be sent during context initialization. + + Args: + file_uri: URI of the uploaded file + mime_type: MIME type of the file + text: Optional text prompt to accompany the file + """ + # Create parts list with file reference + parts = [] + if text: + parts.append({"type": "text", "text": text}) + + # Add file reference part + parts.append( + {"type": "file_data", "file_data": {"mime_type": mime_type, "file_uri": file_uri}} + ) + + # Add to messages + message = {"role": "user", "content": parts} + self.messages.append(message) + logger.info(f"Added file reference to context: {file_uri}") + + def get_messages_for_initializing_history(self) -> List[Content]: + """Get messages formatted for Gemini history initialization. + + Returns: + List of messages in Gemini format for conversation history. + """ + messages: List[Content] = [] + for item in self.messages: + role = item.get("role") + + if role == "system": + continue + + elif role == "assistant": + role = "model" + + content = item.get("content") + parts: List[Part] = [] + if isinstance(content, str): + parts = [Part(text=content)] + elif isinstance(content, list): + for part in content: + if part.get("type") == "text": + parts.append(Part(text=part.get("text"))) + elif part.get("type") == "file_data": + file_data = part.get("file_data", {}) + parts.append( + Part( + file_data=FileData( + mime_type=file_data.get("mime_type"), + file_uri=file_data.get("file_uri"), + ) + ) + ) + else: + logger.warning(f"Unsupported content type: {str(part)[:80]}") + else: + logger.warning(f"Unsupported content type: {str(content)[:80]}") + messages.append(Content(role=role, parts=parts)) + return messages + + +class GeminiLiveUserContextAggregator(OpenAIUserContextAggregator): + """User context aggregator for Gemini Live. + + Extends OpenAI user aggregator to handle Gemini-specific message passing + while maintaining compatibility with the standard aggregation pipeline. + """ + + async def process_frame(self, frame, direction): + """Process incoming frames for user context aggregation. + + Args: + frame: The frame to process. + direction: The frame processing direction. + """ + await super().process_frame(frame, direction) + # kind of a hack just to pass the LLMMessagesAppendFrame through, but it's fine for now + if isinstance(frame, LLMMessagesAppendFrame): + await self.push_frame(frame, direction) + + +class GeminiLiveAssistantContextAggregator(OpenAIAssistantContextAggregator): + """Assistant context aggregator for Gemini Live. + + Handles assistant response aggregation while filtering out LLMTextFrames + to prevent duplicate context entries, as Gemini Live pushes both + LLMTextFrames and TTSTextFrames. + """ + + async def process_frame(self, frame: Frame, direction: FrameDirection): + """Process incoming frames for assistant context aggregation. + + Args: + frame: The frame to process. + direction: The frame processing direction. + """ + # The LLMAssistantContextAggregator uses TextFrames to aggregate the LLM output, + # but the GeminiLiveAssistantContextAggregator pushes LLMTextFrames and TTSTextFrames. We + # need to override this proces_frame for LLMTextFrame, so that only the TTSTextFrames + # are process. This ensures that the context gets only one set of messages. + if not isinstance(frame, LLMTextFrame): + await super().process_frame(frame, direction) + + async def handle_user_image_frame(self, frame: UserImageRawFrame): + """Handle user image frames. + + Args: + frame: The user image frame to handle. + """ + # We don't want to store any images in the context. Revisit this later + # when the API evolves. + pass + + +@dataclass +class GeminiLiveContextAggregatorPair: + """Pair of user and assistant context aggregators for Gemini Live. + + Parameters: + _user: The user context aggregator instance. + _assistant: The assistant context aggregator instance. + """ + + _user: GeminiLiveUserContextAggregator + _assistant: GeminiLiveAssistantContextAggregator + + def user(self) -> GeminiLiveUserContextAggregator: + """Get the user context aggregator. + + Returns: + The user context aggregator instance. + """ + return self._user + + def assistant(self) -> GeminiLiveAssistantContextAggregator: + """Get the assistant context aggregator. + + Returns: + The assistant context aggregator instance. + """ + return self._assistant + + +class GeminiModalities(Enum): + """Supported modalities for Gemini Live. + + Parameters: + TEXT: Text responses. + AUDIO: Audio responses. + """ + + TEXT = "TEXT" + AUDIO = "AUDIO" + + +class GeminiMediaResolution(str, Enum): + """Media resolution options for Gemini Live. + + Parameters: + UNSPECIFIED: Use default resolution setting. + LOW: Low resolution with 64 tokens. + MEDIUM: Medium resolution with 256 tokens. + HIGH: High resolution with zoomed reframing and 256 tokens. + """ + + UNSPECIFIED = "MEDIA_RESOLUTION_UNSPECIFIED" # Use default + LOW = "MEDIA_RESOLUTION_LOW" # 64 tokens + MEDIUM = "MEDIA_RESOLUTION_MEDIUM" # 256 tokens + HIGH = "MEDIA_RESOLUTION_HIGH" # Zoomed reframing with 256 tokens + + +class GeminiVADParams(BaseModel): + """Voice Activity Detection parameters for Gemini Live. + + Parameters: + disabled: Whether to disable VAD. Defaults to None. + start_sensitivity: Sensitivity for speech start detection. Defaults to None. + end_sensitivity: Sensitivity for speech end detection. Defaults to None. + prefix_padding_ms: Prefix padding in milliseconds. Defaults to None. + silence_duration_ms: Silence duration threshold in milliseconds. Defaults to None. + """ + + disabled: Optional[bool] = Field(default=None) + start_sensitivity: Optional[StartSensitivity] = Field(default=None) + end_sensitivity: Optional[EndSensitivity] = Field(default=None) + prefix_padding_ms: Optional[int] = Field(default=None) + silence_duration_ms: Optional[int] = Field(default=None) + + +class ContextWindowCompressionParams(BaseModel): + """Parameters for context window compression in Gemini Live. + + Parameters: + enabled: Whether compression is enabled. Defaults to False. + trigger_tokens: Token count to trigger compression. None uses 80% of context window. + """ + + enabled: bool = Field(default=False) + trigger_tokens: Optional[int] = Field( + default=None + ) # None = use default (80% of context window) + + +class InputParams(BaseModel): + """Input parameters for Gemini Live generation. + + Parameters: + frequency_penalty: Frequency penalty for generation (0.0-2.0). Defaults to None. + max_tokens: Maximum tokens to generate. Must be >= 1. Defaults to 4096. + presence_penalty: Presence penalty for generation (0.0-2.0). Defaults to None. + temperature: Sampling temperature (0.0-2.0). Defaults to None. + top_k: Top-k sampling parameter. Must be >= 0. Defaults to None. + top_p: Top-p sampling parameter (0.0-1.0). Defaults to None. + modalities: Response modalities. Defaults to AUDIO. + language: Language for generation. Defaults to EN_US. + media_resolution: Media resolution setting. Defaults to UNSPECIFIED. + vad: Voice activity detection parameters. Defaults to None. + context_window_compression: Context compression settings. Defaults to None. + thinking: Thinking settings. Defaults to None. + Note that these settings may require specifying a model that + supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". + enable_affective_dialog: Enable affective dialog, which allows Gemini + to adapt to expression and tone. Defaults to None. + Note that these settings may require specifying a model that + supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". + Also note that this setting may require specifying an API version that + supports it, e.g. HttpOptions(api_version="v1alpha"). + proactivity: Proactivity settings, which allows Gemini to proactively + decide how to behave, such as whether to avoid responding to + content that is not relevant. Defaults to None. + Note that these settings may require specifying a model that + supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". + Also note that this setting may require specifying an API version that + supports it, e.g. HttpOptions(api_version="v1alpha"). + extra: Additional parameters. Defaults to empty dict. + """ + + frequency_penalty: Optional[float] = Field(default=None, ge=0.0, le=2.0) + max_tokens: Optional[int] = Field(default=4096, ge=1) + presence_penalty: Optional[float] = Field(default=None, ge=0.0, le=2.0) + temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0) + top_k: Optional[int] = Field(default=None, ge=0) + top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0) + modalities: Optional[GeminiModalities] = Field(default=GeminiModalities.AUDIO) + language: Optional[Language] = Field(default=Language.EN_US) + media_resolution: Optional[GeminiMediaResolution] = Field( + default=GeminiMediaResolution.UNSPECIFIED + ) + vad: Optional[GeminiVADParams] = Field(default=None) + context_window_compression: Optional[ContextWindowCompressionParams] = Field(default=None) + thinking: Optional[ThinkingConfig] = Field(default=None) + enable_affective_dialog: Optional[bool] = Field(default=None) + proactivity: Optional[ProactivityConfig] = Field(default=None) + extra: Optional[Dict[str, Any]] = Field(default_factory=dict) + + +class GeminiLiveLLMService(LLMService): + """Provides access to Google's Gemini Live API. + + This service enables real-time conversations with Gemini, supporting both + text and audio modalities. It handles voice transcription, streaming audio + responses, and tool usage. + """ + + # Overriding the default adapter to use the Gemini one. + adapter_class = GeminiLLMAdapter + + def __init__( + self, + *, + api_key: str, + base_url: Optional[str] = None, + model="models/gemini-2.0-flash-live-001", + voice_id: str = "Charon", + start_audio_paused: bool = False, + start_video_paused: bool = False, + system_instruction: Optional[str] = None, + tools: Optional[Union[List[dict], ToolsSchema]] = None, + params: Optional[InputParams] = None, + inference_on_context_initialization: bool = True, + file_api_base_url: str = "https://generativelanguage.googleapis.com/v1beta/files", + http_options: Optional[HttpOptions] = None, + **kwargs, + ): + """Initialize the Gemini Live LLM service. + + Args: + api_key: Google AI API key for authentication. + base_url: API endpoint base URL. Defaults to the official Gemini Live endpoint. + + .. deprecated:: 0.0.90 + This parameter is deprecated and no longer has any effect. + Please use `http_options` to customize requests made by the + API client. + + model: Model identifier to use. Defaults to "models/gemini-2.0-flash-live-001". + voice_id: TTS voice identifier. Defaults to "Charon". + start_audio_paused: Whether to start with audio input paused. Defaults to False. + start_video_paused: Whether to start with video input paused. Defaults to False. + system_instruction: System prompt for the model. Defaults to None. + tools: Tools/functions available to the model. Defaults to None. + params: Configuration parameters for the model. Defaults to InputParams(). + inference_on_context_initialization: Whether to generate a response when context + is first set. Defaults to True. + file_api_base_url: Base URL for the Gemini File API. Defaults to the official endpoint. + http_options: HTTP options for the client. + **kwargs: Additional arguments passed to parent LLMService. + """ + # Check for deprecated parameter usage + if base_url is not None: + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Parameter 'base_url' is deprecated and no longer has any effect. Please use 'http_options' to customize requests made by the API client.", + DeprecationWarning, + stacklevel=2, + ) + + super().__init__(base_url=base_url, **kwargs) + + params = params or InputParams() + + self._last_sent_time = 0 + self._base_url = base_url + self.set_model_name(model) + self._voice_id = voice_id + self._language_code = params.language + + self._system_instruction = system_instruction + self._tools = tools + self._inference_on_context_initialization = inference_on_context_initialization + self._needs_turn_complete_message = False + + self._audio_input_paused = start_audio_paused + self._video_input_paused = start_video_paused + self._context = None + self._api_key = api_key + self._http_options = http_options + self._session: AsyncSession = None + self._connection_task = None + + self._disconnecting = False + self._run_llm_when_session_ready = False + + self._user_is_speaking = False + self._bot_is_speaking = False + self._user_audio_buffer = bytearray() + self._user_transcription_buffer = "" + self._last_transcription_sent = "" + self._bot_audio_buffer = bytearray() + self._bot_text_buffer = "" + self._llm_output_buffer = "" + + self._sample_rate = 24000 + + self._language = params.language + self._language_code = ( + language_to_gemini_language(params.language) if params.language else "en-US" + ) + self._vad_params = params.vad + + # Reconnection tracking + self._consecutive_failures = 0 + self._connection_start_time = None + + self._settings = { + "frequency_penalty": params.frequency_penalty, + "max_tokens": params.max_tokens, + "presence_penalty": params.presence_penalty, + "temperature": params.temperature, + "top_k": params.top_k, + "top_p": params.top_p, + "modalities": params.modalities, + "language": self._language_code, + "media_resolution": params.media_resolution, + "vad": params.vad, + "context_window_compression": params.context_window_compression.model_dump() + if params.context_window_compression + else {}, + "thinking": params.thinking or {}, + "enable_affective_dialog": params.enable_affective_dialog or False, + "proactivity": params.proactivity or {}, + "extra": params.extra if isinstance(params.extra, dict) else {}, + } + + self._file_api_base_url = file_api_base_url + self._file_api: Optional[GeminiFileAPI] = None + + # Grounding metadata tracking + self._search_result_buffer = "" + self._accumulated_grounding_metadata = None + + # Session resumption + self._session_resumption_handle: Optional[str] = None + + # Bookkeeping for ending gracefully (i.e. after the bot is finished) + self._end_frame_pending_bot_turn_finished: Optional[EndFrame] = None + + # Initialize the API client. Subclasses can override this if needed. + self.create_client() + + def create_client(self): + """Create the Gemini API client instance. Subclasses can override this.""" + self._client = Client(api_key=self._api_key, http_options=self._http_options) + + @property + def file_api(self) -> GeminiFileAPI: + """Get the Gemini File API client instance. Subclasses can override this. + + Returns: + The Gemini File API client. + """ + if not self._file_api: + self._file_api = GeminiFileAPI(api_key=self._api_key, base_url=self._file_api_base_url) + return self._file_api + + def can_generate_metrics(self) -> bool: + """Check if the service can generate usage metrics. + + Returns: + True as Gemini Live supports token usage metrics. + """ + return True + + def needs_mcp_alternate_schema(self) -> bool: + """Check if this LLM service requires alternate MCP schema. + + Google/Gemini has stricter JSON schema validation and requires + certain properties to be removed or modified for compatibility. + + Returns: + True for Google/Gemini services. + """ + return True + + def set_audio_input_paused(self, paused: bool): + """Set the audio input pause state. + + Args: + paused: Whether to pause audio input. + """ + self._audio_input_paused = paused + + def set_video_input_paused(self, paused: bool): + """Set the video input pause state. + + Args: + paused: Whether to pause video input. + """ + self._video_input_paused = paused + + def set_model_modalities(self, modalities: GeminiModalities): + """Set the model response modalities. + + Args: + modalities: The modalities to use for responses. + """ + self._settings["modalities"] = modalities + + def set_language(self, language: Language): + """Set the language for generation. + + Args: + language: The language to use for generation. + """ + self._language = language + self._language_code = language_to_gemini_language(language) or "en-US" + self._settings["language"] = self._language_code + logger.info(f"Set Gemini language to: {self._language_code}") + + async def set_context(self, context: OpenAILLMContext): + """Set the context explicitly from outside the pipeline. + + This is useful when initializing a conversation because in server-side VAD mode we might not have a + way to trigger the pipeline. This sends the history to the server. The `inference_on_context_initialization` + flag controls whether to set the turnComplete flag when we do this. Without that flag, the model will + not respond. This is often what we want when setting the context at the beginning of a conversation. + + Args: + context: The OpenAI LLM context to set. + """ + if self._context: + logger.error("Context already set. Can only set up Gemini Live context once.") + return + self._context = GeminiLiveContext.upgrade(context) + await self._create_initial_response() + + # + # standard AIService frame handling + # + + async def start(self, frame: StartFrame): + """Start the service and establish connection. + + Args: + frame: The start frame. + """ + await super().start(frame) + await self._connect() + + async def stop(self, frame: EndFrame): + """Stop the service and close connections. + + Args: + frame: The end frame. + """ + await super().stop(frame) + await self._disconnect() + + async def cancel(self, frame: CancelFrame): + """Cancel the service and close connections. + + Args: + frame: The cancel frame. + """ + await super().cancel(frame) + await self._disconnect() + + # + # speech and interruption handling + # + + async def _handle_interruption(self): + await self._set_bot_is_speaking(False) + await self.push_frame(TTSStoppedFrame()) + await self.push_frame(LLMFullResponseEndFrame()) + + async def _handle_user_started_speaking(self, frame): + self._user_is_speaking = True + pass + + async def _handle_user_stopped_speaking(self, frame): + self._user_is_speaking = False + self._user_audio_buffer = bytearray() + await self.start_ttfb_metrics() + if self._needs_turn_complete_message: + self._needs_turn_complete_message = False + # NOTE: without this, the model ignores the context it's been + # seeded with before the user started speaking + await self._session.send_client_content(turn_complete=True) + + # + # frame processing + # + # StartFrame, StopFrame, CancelFrame implemented in base class + # + + async def process_frame(self, frame: Frame, direction: FrameDirection): + """Process incoming frames for the Gemini Live service. + + Args: + frame: The frame to process. + direction: The frame processing direction. + """ + # Defer EndFrame handling until after the bot turn is finished + if isinstance(frame, EndFrame): + if self._bot_is_speaking: + logger.debug("Deferring handling EndFrame until bot turn is finished") + self._end_frame_pending_bot_turn_finished = frame + return + + await super().process_frame(frame, direction) + + if isinstance(frame, TranscriptionFrame): + await self.push_frame(frame, direction) + elif isinstance(frame, OpenAILLMContextFrame): + context: GeminiLiveContext = GeminiLiveContext.upgrade(frame.context) + # For now, we'll only trigger inference here when either: + # 1. We have not seen a context frame before + # 2. The last message is a tool call result + if not self._context: + self._context = context + if frame.context.tools: + self._tools = frame.context.tools + await self._create_initial_response() + elif context.messages and context.messages[-1].get("role") == "tool": + # Support just one tool call per context frame for now + tool_result_message = context.messages[-1] + await self._tool_result(tool_result_message) + elif isinstance(frame, LLMContextFrame): + raise NotImplementedError("Universal LLMContext is not yet supported for Gemini Live.") + elif isinstance(frame, InputTextRawFrame): + await self._send_user_text(frame.text) + await self.push_frame(frame, direction) + elif isinstance(frame, InputAudioRawFrame): + await self._send_user_audio(frame) + await self.push_frame(frame, direction) + elif isinstance(frame, InputImageRawFrame): + await self._send_user_video(frame) + await self.push_frame(frame, direction) + elif isinstance(frame, InterruptionFrame): + await self._handle_interruption() + await self.push_frame(frame, direction) + elif isinstance(frame, UserStartedSpeakingFrame): + await self._handle_user_started_speaking(frame) + await self.push_frame(frame, direction) + elif isinstance(frame, UserStoppedSpeakingFrame): + await self._handle_user_stopped_speaking(frame) + await self.push_frame(frame, direction) + elif isinstance(frame, BotStartedSpeakingFrame): + # Ignore this frame. Use the serverContent API message instead + await self.push_frame(frame, direction) + elif isinstance(frame, BotStoppedSpeakingFrame): + # ignore this frame. Use the serverContent.turnComplete API message + await self.push_frame(frame, direction) + elif isinstance(frame, LLMMessagesAppendFrame): + # NOTE: handling LLMMessagesAppendFrame here in the LLMService is + # unusual - typically this would be handled in the user context + # aggregator. Leaving this handling here so that user code that + # uses this frame *without* a user context aggregator still works + # (we have an example that does just that, actually). + await self._create_single_response(frame.messages) + elif isinstance(frame, LLMUpdateSettingsFrame): + await self._update_settings(frame.settings) + elif isinstance(frame, LLMSetToolsFrame): + await self._update_settings() + else: + await self.push_frame(frame, direction) + + async def _set_bot_is_speaking(self, speaking: bool): + if self._bot_is_speaking == speaking: + return + + self._bot_is_speaking = speaking + + if not self._bot_is_speaking and self._end_frame_pending_bot_turn_finished: + await self.queue_frame(self._end_frame_pending_bot_turn_finished) + self._end_frame_pending_bot_turn_finished = None + + async def _connect(self, session_resumption_handle: Optional[str] = None): + """Establish client connection to Gemini Live API.""" + if self._session: + # Here we assume that if we have a client, we are connected. We + # handle disconnections in the send/recv code paths. + return + + if session_resumption_handle: + logger.info( + f"Connecting to Gemini service with session_resumption_handle: {session_resumption_handle}" + ) + else: + logger.info("Connecting to Gemini service") + try: + # Assemble basic configuration + config = LiveConnectConfig( + generation_config=GenerationConfig( + frequency_penalty=self._settings["frequency_penalty"], + max_output_tokens=self._settings["max_tokens"], + presence_penalty=self._settings["presence_penalty"], + temperature=self._settings["temperature"], + top_k=self._settings["top_k"], + top_p=self._settings["top_p"], + response_modalities=[Modality(self._settings["modalities"].value)], + speech_config=SpeechConfig( + voice_config=VoiceConfig( + prebuilt_voice_config={"voice_name": self._voice_id} + ), + language_code=self._settings["language"], + ), + media_resolution=MediaResolution(self._settings["media_resolution"].value), + ), + input_audio_transcription=AudioTranscriptionConfig(), + output_audio_transcription=AudioTranscriptionConfig(), + session_resumption=SessionResumptionConfig(handle=session_resumption_handle), + ) + + # Add context window compression to configuration, if enabled + if self._settings.get("context_window_compression", {}).get("enabled", False): + compression_config = ContextWindowCompressionConfig() + + # Add sliding window (always true if compression is enabled) + compression_config.sliding_window = SlidingWindow() + + # Add trigger_tokens if specified + trigger_tokens = self._settings.get("context_window_compression", {}).get( + "trigger_tokens" + ) + if trigger_tokens is not None: + compression_config.trigger_tokens = trigger_tokens + + config.context_window_compression = compression_config + + # Add thinking configuration to configuration, if provided + if self._settings.get("thinking"): + config.thinking_config = self._settings["thinking"] + + # Add affective dialog setting, if provided + if self._settings.get("enable_affective_dialog", False): + config.enable_affective_dialog = self._settings["enable_affective_dialog"] + + # Add proactivity configuration to configuration, if provided + if self._settings.get("proactivity"): + config.proactivity = self._settings["proactivity"] + + # Add VAD configuration to configuration, if provided + if self._settings.get("vad"): + vad_config = AutomaticActivityDetection() + vad_params = self._settings["vad"] + has_vad_settings = False + + # Only add parameters that are explicitly set + if vad_params.disabled is not None: + vad_config.disabled = vad_params.disabled + has_vad_settings = True + + if vad_params.start_sensitivity: + vad_config.start_of_speech_sensitivity = vad_params.start_sensitivity + has_vad_settings = True + + if vad_params.end_sensitivity: + vad_config.end_of_speech_sensitivity = vad_params.end_sensitivity + has_vad_settings = True + + if vad_params.prefix_padding_ms is not None: + vad_config.prefix_padding_ms = vad_params.prefix_padding_ms + has_vad_settings = True + + if vad_params.silence_duration_ms is not None: + vad_config.silence_duration_ms = vad_params.silence_duration_ms + has_vad_settings = True + + # Only add automatic_activity_detection if we have VAD settings + if has_vad_settings: + config.realtime_input_config = RealtimeInputConfig( + automatic_activity_detection=vad_config + ) + + # Add system instruction to configuration, if provided + system_instruction = self._system_instruction or "" + if self._context and hasattr(self._context, "extract_system_instructions"): + system_instruction += "\n" + self._context.extract_system_instructions() + if system_instruction: + logger.debug(f"Setting system instruction: {system_instruction}") + config.system_instruction = system_instruction + + # Add tools to configuration, if provided + if self._tools: + logger.debug(f"Setting tools: {self._tools}") + config.tools = self.get_llm_adapter().from_standard_tools(self._tools) + + # Start the connection + self._connection_task = self.create_task(self._connection_task_handler(config=config)) + + except Exception as e: + await self.push_error(ErrorFrame(error=f"{self} Initialization error: {e}", fatal=True)) + + async def _connection_task_handler(self, config: LiveConnectConfig): + async with self._client.aio.live.connect(model=self._model_name, config=config) as session: + logger.info("Connected to Gemini service") + + # Mark connection start time + self._connection_start_time = time.time() + + await self._handle_session_ready(session) + + while True: + try: + turn = self._session.receive() + async for message in turn: + # Reset failure counter if connection has been stable + self._check_and_reset_failure_counter() + + if message.server_content and message.server_content.model_turn: + await self._handle_msg_model_turn(message) + elif ( + message.server_content + and message.server_content.turn_complete + and message.usage_metadata + ): + await self._handle_msg_turn_complete(message) + await self._handle_msg_usage_metadata(message) + elif message.server_content and message.server_content.input_transcription: + await self._handle_msg_input_transcription(message) + elif message.server_content and message.server_content.output_transcription: + await self._handle_msg_output_transcription(message) + elif message.server_content and message.server_content.grounding_metadata: + await self._handle_msg_grounding_metadata(message) + elif message.tool_call: + await self._handle_msg_tool_call(message) + elif message.session_resumption_update: + self._handle_msg_resumption_update(message) + except Exception as e: + if not self._disconnecting: + should_reconnect = await self._handle_connection_error(e) + if should_reconnect: + await self._reconnect() + return # Exit this connection handler, _reconnect will start a new one + break + + def _check_and_reset_failure_counter(self): + """Check if connection has been stable long enough to reset the failure counter. + + If the connection has been active for longer than the established threshold + and there are accumulated failures, reset the counter to 0. + """ + if ( + self._connection_start_time + and self._consecutive_failures > 0 + and time.time() - self._connection_start_time >= CONNECTION_ESTABLISHED_THRESHOLD + ): + logger.info( + f"Connection stable for {CONNECTION_ESTABLISHED_THRESHOLD}s, " + f"resetting failure counter from {self._consecutive_failures} to 0" + ) + self._consecutive_failures = 0 + + async def _handle_connection_error(self, error: Exception) -> bool: + """Handle a connection error and determine if reconnection should be attempted. + + Args: + error: The exception that caused the connection error. + + Returns: + True if reconnection should be attempted, False if a fatal error should be pushed. + """ + self._consecutive_failures += 1 + logger.warning( + f"Connection error (failure {self._consecutive_failures}/{MAX_CONSECUTIVE_FAILURES}): {error}" + ) + + if self._consecutive_failures >= MAX_CONSECUTIVE_FAILURES: + logger.error( + f"Max consecutive failures ({MAX_CONSECUTIVE_FAILURES}) reached, " + "treating as fatal error" + ) + await self.push_error( + ErrorFrame(error=f"{self} Error in receive loop: {error}", fatal=True) + ) + return False + else: + logger.info( + f"Attempting reconnection ({self._consecutive_failures}/{MAX_CONSECUTIVE_FAILURES})" + ) + return True + + async def _reconnect(self): + """Reconnect to Gemini Live API.""" + await self._disconnect() + await self._connect(session_resumption_handle=self._session_resumption_handle) + + async def _disconnect(self): + """Disconnect from Gemini Live API and clean up resources.""" + logger.info("Disconnecting from Gemini service") + try: + self._disconnecting = True + await self.stop_all_metrics() + if self._connection_task: + await self.cancel_task(self._connection_task, timeout=1.0) + self._connection_task = None + if self._session: + await self._session.close() + self._session = None + self._disconnecting = False + except Exception as e: + logger.error(f"{self} error disconnecting: {e}") + + async def _send_user_audio(self, frame): + """Send user audio frame to Gemini Live API.""" + if self._audio_input_paused or self._disconnecting or not self._session: + return + + # Send all audio to Gemini + try: + await self._session.send_realtime_input( + audio=Blob(data=frame.audio, mime_type=f"audio/pcm;rate={frame.sample_rate}") + ) + except Exception as e: + await self._handle_send_error(e) + + # Manage a buffer of audio to use for transcription + audio = frame.audio + if self._user_is_speaking: + self._user_audio_buffer.extend(audio) + else: + # Keep 1/2 second of audio in the buffer even when not speaking. + self._user_audio_buffer.extend(audio) + length = int((frame.sample_rate * frame.num_channels * 2) * 0.5) + self._user_audio_buffer = self._user_audio_buffer[-length:] + + async def _send_user_text(self, text: str): + """Send user text via Gemini Live API's realtime input stream. + + This method sends text through the realtimeInput stream (via TextInputMessage) + rather than the clientContent stream. This ensures text input is synchronized + with audio and video inputs, preventing temporal misalignment that can occur + when different modalities are processed through separate API pathways. + + For realtimeInput, turn completion is automatically inferred by the API based + on user activity, so no explicit turnComplete signal is needed. + + Args: + text: The text to send as user input. + """ + if self._disconnecting or not self._session: + return + + try: + await self._session.send_realtime_input(text=text) + except Exception as e: + await self._handle_send_error(e) + + async def _send_user_video(self, frame): + """Send user video frame to Gemini Live API.""" + if self._video_input_paused or self._disconnecting or not self._session: + return + + now = time.time() + if now - self._last_sent_time < 1: + return # Ignore if less than 1 second has passed + + self._last_sent_time = now # Update last sent time + logger.debug(f"Sending video frame to Gemini: {frame}") + + buffer = io.BytesIO() + Image.frombytes(frame.format, frame.size, frame.image).save(buffer, format="JPEG") + data = base64.b64encode(buffer.getvalue()).decode("utf-8") + + try: + await self._session.send_realtime_input(video=Blob(data=data, mime_type="image/jpeg")) + except Exception as e: + await self._handle_send_error(e) + + async def _create_initial_response(self): + """Create initial response based on context history.""" + if self._disconnecting: + return + + if not self._session: + self._run_llm_when_session_ready = True + return + + messages = self._context.get_messages_for_initializing_history() + if not messages: + return + + logger.debug(f"Creating initial response: {messages}") + + await self.start_ttfb_metrics() + + try: + await self._session.send_client_content( + turns=messages, turn_complete=self._inference_on_context_initialization + ) + except Exception as e: + await self._handle_send_error(e) + + # If we're generating a response right away upon initializing + # conversation history, set a flag saying that we need a turn complete + # message when the user stops speaking. + if not self._inference_on_context_initialization: + self._needs_turn_complete_message = True + + async def _create_single_response(self, messages_list): + """Create a single response from a list of messages.""" + if self._disconnecting or not self._session: + return + + # Create a throwaway context just for the purpose of getting messages + # in the right format + context = GeminiLiveContext.upgrade(OpenAILLMContext(messages=messages_list)) + messages = context.get_messages_for_initializing_history() + + if not messages: + return + + logger.debug(f"Creating response: {messages}") + + await self.start_ttfb_metrics() + + try: + await self._session.send_client_content(turns=messages, turn_complete=True) + except Exception as e: + await self._handle_send_error(e) + + @traced_gemini_live(operation="llm_tool_result") + async def _tool_result(self, tool_result_message): + """Send tool result back to the API.""" + if self._disconnecting or not self._session: + return + + # For now we're shoving the name into the tool_call_id field, so this + # will work until we revisit that. + id = tool_result_message.get("tool_call_id") + name = tool_result_message.get("tool_call_name") + result = json.loads(tool_result_message.get("content") or "") + response = FunctionResponse(name=name, id=id, response=result) + + try: + await self._session.send_tool_response(function_responses=response) + except Exception as e: + await self._handle_send_error(e) + + @traced_gemini_live(operation="llm_setup") + async def _handle_session_ready(self, session: AsyncSession): + """Handle the session being ready.""" + self._session = session + # If we were just waititng for the session to be ready to run the LLM, + # do that now. + if self._run_llm_when_session_ready: + self._run_llm_when_session_ready = False + await self._create_initial_response() + + async def _handle_msg_model_turn(self, msg: LiveServerMessage): + """Handle the model turn message.""" + part = msg.server_content.model_turn.parts[0] + if not part: + return + + await self.stop_ttfb_metrics() + + # part.text is added when `modalities` is set to TEXT; otherwise, it's None + text = part.text + if text: + if not self._bot_text_buffer: + await self.push_frame(LLMFullResponseStartFrame()) + + self._bot_text_buffer += text + self._search_result_buffer += text # Also accumulate for grounding + await self.push_frame(LLMTextFrame(text=text)) + + # Check for grounding metadata in server content + if msg.server_content and msg.server_content.grounding_metadata: + self._accumulated_grounding_metadata = msg.server_content.grounding_metadata + + inline_data = part.inline_data + if not inline_data: + return + + # Check if mime type matches expected format + expected_mime_type = f"audio/pcm;rate={self._sample_rate}" + if inline_data.mime_type == expected_mime_type: + # Perfect match, continue processing + pass + elif inline_data.mime_type == "audio/pcm": + # Sample rate not provided in mime type, assume default + if not hasattr(self, "_sample_rate_warning_logged"): + logger.warning( + f"Sample rate not provided in mime type '{inline_data.mime_type}', assuming rate of {self._sample_rate}" + ) + self._sample_rate_warning_logged = True + else: + # Unrecognized format + logger.warning(f"Unrecognized server_content format {inline_data.mime_type}") + return + + audio = inline_data.data + if not audio: + return + + if not self._bot_is_speaking: + await self._set_bot_is_speaking(True) + await self.push_frame(TTSStartedFrame()) + await self.push_frame(LLMFullResponseStartFrame()) + + self._bot_audio_buffer.extend(audio) + frame = TTSAudioRawFrame( + audio=audio, + sample_rate=self._sample_rate, + num_channels=1, + ) + await self.push_frame(frame) + + @traced_gemini_live(operation="llm_tool_call") + async def _handle_msg_tool_call(self, message: LiveServerMessage): + """Handle tool call messages.""" + function_calls = message.tool_call.function_calls + if not function_calls: + return + if not self._context: + logger.error("Function calls are not supported without a context object.") + + function_calls_llm = [ + FunctionCallFromLLM( + context=self._context, + tool_call_id=( + # NOTE: when using Vertex AI we don't get server-provided + # tool call IDs here + f.id or str(uuid.uuid4()) + ), + function_name=f.name, + arguments=f.args, + ) + for f in function_calls + ] + + await self.run_function_calls(function_calls_llm) + + @traced_gemini_live(operation="llm_response") + async def _handle_msg_turn_complete(self, message: LiveServerMessage): + """Handle the turn complete message.""" + await self._set_bot_is_speaking(False) + text = self._bot_text_buffer + + # Trace the complete LLM response (this will be handled by the decorator) + # The decorator will extract the output text and usage metadata from the message + + self._bot_text_buffer = "" + self._llm_output_buffer = "" + + # Process grounding metadata if we have accumulated any + if self._accumulated_grounding_metadata: + await self._process_grounding_metadata( + self._accumulated_grounding_metadata, self._search_result_buffer + ) + + # Reset grounding tracking for next response + self._search_result_buffer = "" + self._accumulated_grounding_metadata = None + + # Only push the TTSStoppedFrame if the bot is outputting audio + # when text is found, modalities is set to TEXT and no audio + # is produced. + if not text: + await self.push_frame(TTSStoppedFrame()) + + await self.push_frame(LLMFullResponseEndFrame()) + + @traced_stt + async def _handle_user_transcription( + self, transcript: str, is_final: bool, language: Optional[Language] = None + ): + """Handle a transcription result with tracing.""" + pass + + async def _handle_msg_input_transcription(self, message: LiveServerMessage): + """Handle the input transcription message. + + Gemini Live sends user transcriptions in either single words or multi-word + phrases. As a result, we have to aggregate the input transcription. This handler + aggregates into sentences, splitting on the end of sentence markers. + """ + if not message.server_content.input_transcription: + return + + text = message.server_content.input_transcription.text + + if not text: + return + + # Strip leading space from sentence starts if buffer is empty + if text.startswith(" ") and not self._user_transcription_buffer: + text = text.lstrip() + + # Accumulate text in the buffer + self._user_transcription_buffer += text + + # Check for complete sentences + while True: + eos_end_marker = match_endofsentence(self._user_transcription_buffer) + if not eos_end_marker: + break + + # Extract the complete sentence + complete_sentence = self._user_transcription_buffer[:eos_end_marker] + # Keep the remainder for the next chunk + self._user_transcription_buffer = self._user_transcription_buffer[eos_end_marker:] + + # Send a TranscriptionFrame with the complete sentence + logger.debug(f"[Transcription:user] [{complete_sentence}]") + await self._handle_user_transcription( + complete_sentence, True, self._settings["language"] + ) + await self.push_frame( + TranscriptionFrame( + text=complete_sentence, + user_id="", + timestamp=time_now_iso8601(), + result=message, + ), + FrameDirection.UPSTREAM, + ) + + async def _handle_msg_output_transcription(self, message: LiveServerMessage): + """Handle the output transcription message.""" + if not message.server_content.output_transcription: + return + + # This is the output transcription text when modalities is set to AUDIO. + # In this case, we push LLMTextFrame and TTSTextFrame to be handled by the + # downstream assistant context aggregator. + text = message.server_content.output_transcription.text + + if not text: + return + + # Accumulate text for grounding as well + self._search_result_buffer += text + + # Check for grounding metadata in server content + if message.server_content and message.server_content.grounding_metadata: + self._accumulated_grounding_metadata = message.server_content.grounding_metadata + # Collect text for tracing + self._llm_output_buffer += text + + await self.push_frame(LLMTextFrame(text=text)) + await self.push_frame(TTSTextFrame(text=text)) + + async def _handle_msg_grounding_metadata(self, message: LiveServerMessage): + """Handle dedicated grounding metadata messages.""" + if message.server_content and message.server_content.grounding_metadata: + grounding_metadata = message.server_content.grounding_metadata + # Process the grounding metadata immediately + await self._process_grounding_metadata(grounding_metadata, self._search_result_buffer) + + async def _process_grounding_metadata( + self, grounding_metadata: GroundingMetadata, search_result: str = "" + ): + """Process grounding metadata and emit LLMSearchResponseFrame.""" + if not grounding_metadata: + return + + # Extract rendered content for search suggestions + rendered_content = None + if ( + grounding_metadata.search_entry_point + and grounding_metadata.search_entry_point.rendered_content + ): + rendered_content = grounding_metadata.search_entry_point.rendered_content + + # Convert grounding chunks and supports to LLMSearchOrigin format + origins = [] + + if grounding_metadata.grounding_chunks and grounding_metadata.grounding_supports: + # Create a mapping of chunk indices to origins + chunk_to_origin: Dict[int, LLMSearchOrigin] = {} + + for index, chunk in enumerate(grounding_metadata.grounding_chunks): + if chunk.web: + origin = LLMSearchOrigin( + site_uri=chunk.web.uri, site_title=chunk.web.title, results=[] + ) + chunk_to_origin[index] = origin + origins.append(origin) + + # Add grounding support results to the appropriate origins + for support in grounding_metadata.grounding_supports: + if support.segment and support.grounding_chunk_indices: + text = support.segment.text or "" + confidence_scores = support.confidence_scores or [] + + # Add this result to all origins referenced by this support + for chunk_index in support.grounding_chunk_indices: + if chunk_index in chunk_to_origin: + result = LLMSearchResult(text=text, confidence=confidence_scores) + chunk_to_origin[chunk_index].results.append(result) + + # Create and push the search response frame + search_frame = LLMSearchResponseFrame( + search_result=search_result, origins=origins, rendered_content=rendered_content + ) + + await self.push_frame(search_frame) + + async def _handle_msg_usage_metadata(self, message: LiveServerMessage): + """Handle the usage metadata message.""" + if not message.usage_metadata: + return + + usage = message.usage_metadata + + # Ensure we have valid integers for all token counts + prompt_tokens = usage.prompt_token_count or 0 + completion_tokens = usage.response_token_count or 0 + total_tokens = usage.total_token_count or (prompt_tokens + completion_tokens) + + tokens = LLMTokenUsage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=total_tokens, + ) + + await self.start_llm_usage_metrics(tokens) + + def _handle_msg_resumption_update(self, message: LiveServerMessage): + update = message.session_resumption_update + if update.resumable and update.new_handle: + self._session_resumption_handle = update.new_handle + + async def _handle_send_error(self, error: Exception): + # In server-to-server contexts, a WebSocket error should be quite rare. + # Given how hard it is to recover from a send-side error with proper + # state management, and that exponential backoff for retries can have + # cost/stability implications for a service cluster, let's just treat a + # send-side error as fatal. + if not self._disconnecting: + await self.push_error(ErrorFrame(error=f"{self} Send error: {error}", fatal=True)) + + def create_context_aggregator( + self, + context: OpenAILLMContext, + *, + user_params: LLMUserAggregatorParams = LLMUserAggregatorParams(), + assistant_params: LLMAssistantAggregatorParams = LLMAssistantAggregatorParams(), + ) -> GeminiLiveContextAggregatorPair: + """Create an instance of GeminiLiveContextAggregatorPair from an OpenAILLMContext. + + Constructor keyword arguments for both the user and assistant aggregators can be provided. + + Args: + context: The LLM context to use. + user_params: User aggregator parameters. Defaults to LLMUserAggregatorParams(). + assistant_params: Assistant aggregator parameters. Defaults to LLMAssistantAggregatorParams(). + + Returns: + GeminiLiveContextAggregatorPair: A pair of context + aggregators, one for the user and one for the assistant, + encapsulated in an GeminiLiveContextAggregatorPair. + """ + context.set_llm_adapter(self.get_llm_adapter()) + + GeminiLiveContext.upgrade(context) + user = GeminiLiveUserContextAggregator(context, params=user_params) + + assistant_params.expect_stripped_words = False + assistant = GeminiLiveAssistantContextAggregator(context, params=assistant_params) + return GeminiLiveContextAggregatorPair(_user=user, _assistant=assistant) diff --git a/src/pipecat/services/gemini_multimodal_live/vertex.py b/src/pipecat/services/gemini_live/vertex.py similarity index 89% rename from src/pipecat/services/gemini_multimodal_live/vertex.py rename to src/pipecat/services/gemini_live/vertex.py index e07eb474f..c03f589b8 100644 --- a/src/pipecat/services/gemini_multimodal_live/vertex.py +++ b/src/pipecat/services/gemini_live/vertex.py @@ -4,9 +4,9 @@ # SPDX-License-Identifier: BSD 2-Clause License # -"""Google Vertex AI Gemini Multimodal Live service. +"""Service for accessing Gemini Live via Google Vertex AI. -This module provides integration with Google's Gemini Multimodal Live model via +This module provides integration with Google's Gemini Live model via Vertex AI, supporting both text and audio modalities with voice transcription, streaming responses, and tool usage. """ @@ -18,8 +18,8 @@ from typing import List, Optional, Union from loguru import logger from pipecat.adapters.schemas.tools_schema import ToolsSchema -from pipecat.services.gemini_multimodal_live.gemini import ( - GeminiMultimodalLiveLLMService, +from pipecat.services.gemini_live.gemini import ( + GeminiLiveLLMService, HttpOptions, InputParams, ) @@ -39,12 +39,12 @@ except ModuleNotFoundError as e: raise Exception(f"Missing module: {e}") -class GeminiVertexMultimodalLiveLLMService(GeminiMultimodalLiveLLMService): - """Google Vertex AI Gemini Multimodal Live service. +class GeminiLiveVertexLLMService(GeminiLiveLLMService): + """Provides access to Google's Gemini Live model via Vertex AI. - Provides access to Google's Gemini Multimodal Live model via Vertex AI, - supporting both text and audio modalities. It handles voice transcription, - streaming audio responses, and tool usage. + This service enables real-time conversations with Gemini, supporting both + text and audio modalities. It handles voice transcription, streaming audio + responses, and tool usage. """ def __init__( @@ -66,7 +66,7 @@ class GeminiVertexMultimodalLiveLLMService(GeminiMultimodalLiveLLMService): http_options: Optional[HttpOptions] = None, **kwargs, ): - """Initialize the Google Vertex AI Gemini Multimodal Live service. + """Initialize the service for accessing Gemini Live via Google Vertex AI. Args: credentials: JSON string of service account credentials. @@ -85,13 +85,13 @@ class GeminiVertexMultimodalLiveLLMService(GeminiMultimodalLiveLLMService): is first set. Defaults to True. file_api_base_url: Base URL for the Gemini File API. Defaults to the official endpoint. http_options: HTTP options for the client. - **kwargs: Additional arguments passed to parent GeminiMultimodalLiveLLMService. + **kwargs: Additional arguments passed to parent GeminiLiveLLMService. """ # Check if user incorrectly passed api_key, which is used by parent # class but not here. if "api_key" in kwargs: logger.error( - "GeminiVertexMultimodalLiveLLMService does not accept 'api_key' parameter. " + "GeminiLiveVertexLLMService does not accept 'api_key' parameter. " "Use 'credentials' or 'credentials_path' instead for Vertex AI authentication." ) raise ValueError( diff --git a/src/pipecat/services/gemini_multimodal_live/events.py b/src/pipecat/services/gemini_multimodal_live/events.py index 9705cf9f2..be69033aa 100644 --- a/src/pipecat/services/gemini_multimodal_live/events.py +++ b/src/pipecat/services/gemini_multimodal_live/events.py @@ -30,12 +30,15 @@ except ModuleNotFoundError as e: # These aliases are just here for backward compatibility, since we used to # define public-facing StartSensitivity and EndSensitivity enums in this # module. -warnings.warn( - "Importing StartSensitivity and EndSensitivity from " - "pipecat.services.gemini_multimodal_live.events is deprecated. " - "Please import them directly from google.genai.types instead.", - DeprecationWarning, - stacklevel=2, -) +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Importing StartSensitivity and EndSensitivity from " + "pipecat.services.gemini_multimodal_live.events is deprecated. " + "Please import them directly from google.genai.types instead.", + DeprecationWarning, + stacklevel=2, + ) + StartSensitivity = _StartSensitivity EndSensitivity = _EndSensitivity diff --git a/src/pipecat/services/gemini_multimodal_live/file_api.py b/src/pipecat/services/gemini_multimodal_live/file_api.py index 5ae7fdbb7..53f8442fc 100644 --- a/src/pipecat/services/gemini_multimodal_live/file_api.py +++ b/src/pipecat/services/gemini_multimodal_live/file_api.py @@ -9,181 +9,31 @@ This module provides a client for Google's Gemini File API, enabling file uploads, metadata retrieval, listing, and deletion. Files uploaded through this API can be referenced in Gemini generative model calls. + +.. deprecated:: 0.0.90 + Importing GeminiFileAPI from this module is deprecated. + Import it from pipecat.services.gemini_live.file_api instead. """ -import mimetypes -from typing import Any, Dict, Optional +import warnings -import aiohttp from loguru import logger +try: + from pipecat.services.gemini_live.file_api import GeminiFileAPI as _GeminiFileAPI +except ModuleNotFoundError as e: + logger.error(f"Exception: {e}") + logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") + raise Exception(f"Missing module: {e}") -class GeminiFileAPI: - """Client for the Gemini File API. - - This class provides methods for uploading, fetching, listing, and deleting files - through Google's Gemini File API. - - Files uploaded through this API remain available for 48 hours and can be referenced - in calls to the Gemini generative models. Maximum file size is 2GB, with total - project storage limited to 20GB. - """ - - def __init__( - self, api_key: str, base_url: str = "https://generativelanguage.googleapis.com/v1beta/files" - ): - """Initialize the Gemini File API client. - - Args: - api_key: Google AI API key - base_url: Base URL for the Gemini File API (default is the v1beta endpoint) - """ - self._api_key = api_key - self._base_url = base_url - # Upload URL uses the /upload/ path - self.upload_base_url = "https://generativelanguage.googleapis.com/upload/v1beta/files" - - async def upload_file( - self, file_path: str, display_name: Optional[str] = None - ) -> Dict[str, Any]: - """Upload a file to the Gemini File API using the correct resumable upload protocol. - - Args: - file_path: Path to the file to upload - display_name: Optional display name for the file - - Returns: - File metadata including uri, name, and display_name - """ - logger.info(f"Uploading file: {file_path}") - - async with aiohttp.ClientSession() as session: - # Determine the file's MIME type - mime_type, _ = mimetypes.guess_type(file_path) - if not mime_type: - mime_type = "application/octet-stream" - - # Read the file - with open(file_path, "rb") as f: - file_data = f.read() - - # Create the metadata payload - metadata = {} - if display_name: - metadata = {"file": {"display_name": display_name}} - - # Step 1: Initial resumable request to get upload URL - headers = { - "X-Goog-Upload-Protocol": "resumable", - "X-Goog-Upload-Command": "start", - "X-Goog-Upload-Header-Content-Length": str(len(file_data)), - "X-Goog-Upload-Header-Content-Type": mime_type, - "Content-Type": "application/json", - } - - logger.debug(f"Step 1: Getting upload URL from {self.upload_base_url}") - async with session.post( - f"{self.upload_base_url}?key={self._api_key}", headers=headers, json=metadata - ) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"Error initiating file upload: {error_text}") - raise Exception(f"Failed to initiate upload: {response.status} - {error_text}") - - # Get the upload URL from the response header - upload_url = response.headers.get("X-Goog-Upload-URL") - if not upload_url: - logger.error(f"Response headers: {dict(response.headers)}") - raise Exception("No upload URL in response headers") - - logger.debug(f"Got upload URL: {upload_url}") - - # Step 2: Upload the actual file data - upload_headers = { - "Content-Length": str(len(file_data)), - "X-Goog-Upload-Offset": "0", - "X-Goog-Upload-Command": "upload, finalize", - } - - logger.debug(f"Step 2: Uploading file data to {upload_url}") - async with session.post(upload_url, headers=upload_headers, data=file_data) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"Error uploading file data: {error_text}") - raise Exception(f"Failed to upload file: {response.status} - {error_text}") - - file_info = await response.json() - logger.info(f"File uploaded successfully: {file_info.get('file', {}).get('name')}") - return file_info - - async def get_file(self, name: str) -> Dict[str, Any]: - """Get metadata for a file. - - Args: - name: File name (or full path) - - Returns: - File metadata - """ - # Extract just the name part if a full path is provided - if "/" in name: - name = name.split("/")[-1] - - async with aiohttp.ClientSession() as session: - async with session.get(f"{self._base_url}/{name}?key={self._api_key}") as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"Error getting file metadata: {error_text}") - raise Exception(f"Failed to get file metadata: {response.status}") - - file_info = await response.json() - return file_info - - async def list_files( - self, page_size: int = 10, page_token: Optional[str] = None - ) -> Dict[str, Any]: - """List uploaded files. - - Args: - page_size: Number of files to return per page - page_token: Token for pagination - - Returns: - List of files and next page token if available - """ - params = {"key": self._api_key, "pageSize": page_size} - - if page_token: - params["pageToken"] = page_token - - async with aiohttp.ClientSession() as session: - async with session.get(self._base_url, params=params) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"Error listing files: {error_text}") - raise Exception(f"Failed to list files: {response.status}") - - result = await response.json() - return result - - async def delete_file(self, name: str) -> bool: - """Delete a file. - - Args: - name: File name (or full path) - - Returns: - True if deleted successfully - """ - # Extract just the name part if a full path is provided - if "/" in name: - name = name.split("/")[-1] - - async with aiohttp.ClientSession() as session: - async with session.delete(f"{self._base_url}/{name}?key={self._api_key}") as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"Error deleting file: {error_text}") - raise Exception(f"Failed to delete file: {response.status}") - - return True +# These aliases are just here for backward compatibility, since we used to +# define public-facing StartSensitivity and EndSensitivity enums in this +# module. +warnings.warn( + "Importing GeminiFileAPI from " + "pipecat.services.gemini_multimodal_live.file_api is deprecated. " + "Please import it from pipecat.services.gemini_live.file_api instead.", + DeprecationWarning, + stacklevel=2, +) +GeminiFileAPI = _GeminiFileAPI diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 8ab836173..4ac51e02e 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -4,1587 +4,54 @@ # SPDX-License-Identifier: BSD 2-Clause License # -"""Google Gemini Multimodal Live API service implementation. +"""Google Gemini Live API service implementation. This module provides real-time conversational AI capabilities using Google's -Gemini Multimodal Live API, supporting both text and audio modalities with +Gemini Live API, supporting both text and audio modalities with voice transcription, streaming responses, and tool usage. + +.. deprecated:: 0.0.90 + This module is deprecated. Please use the equivalent types from + pipecat.services.gemini_live.gemini instead. Note that the new type names + do not include 'Multimodal'. """ -import base64 -import io -import json -import random -import time -import uuid -from dataclasses import dataclass -from enum import Enum -from typing import Any, Dict, List, Optional, Union +import warnings -from loguru import logger -from PIL import Image -from pydantic import BaseModel, Field +from pipecat.services.gemini_live.gemini import ( + ContextWindowCompressionParams as _ContextWindowCompressionParams, +) +from pipecat.services.gemini_live.gemini import ( + GeminiLiveAssistantContextAggregator, + GeminiLiveContext, + GeminiLiveContextAggregatorPair, + GeminiLiveLLMService, + GeminiLiveUserContextAggregator, + GeminiModalities, +) +from pipecat.services.gemini_live.gemini import GeminiMediaResolution as _GeminiMediaResolution +from pipecat.services.gemini_live.gemini import GeminiVADParams as _GeminiVADParams +from pipecat.services.gemini_live.gemini import InputParams as _InputParams -from pipecat.adapters.schemas.tools_schema import ToolsSchema -from pipecat.adapters.services.gemini_adapter import GeminiLLMAdapter -from pipecat.frames.frames import ( - BotStartedSpeakingFrame, - BotStoppedSpeakingFrame, - CancelFrame, - EndFrame, - ErrorFrame, - Frame, - InputAudioRawFrame, - InputImageRawFrame, - InputTextRawFrame, - InterruptionFrame, - LLMContextFrame, - LLMFullResponseEndFrame, - LLMFullResponseStartFrame, - LLMMessagesAppendFrame, - LLMSetToolsFrame, - LLMTextFrame, - LLMUpdateSettingsFrame, - StartFrame, - TranscriptionFrame, - TTSAudioRawFrame, - TTSStartedFrame, - TTSStoppedFrame, - TTSTextFrame, - UserImageRawFrame, - UserStartedSpeakingFrame, - UserStoppedSpeakingFrame, -) -from pipecat.metrics.metrics import LLMTokenUsage -from pipecat.processors.aggregators.llm_response import ( - LLMAssistantAggregatorParams, - LLMUserAggregatorParams, -) -from pipecat.processors.aggregators.openai_llm_context import ( - OpenAILLMContext, - OpenAILLMContextFrame, -) -from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.google.frames import LLMSearchOrigin, LLMSearchResponseFrame, LLMSearchResult -from pipecat.services.llm_service import FunctionCallFromLLM, LLMService -from pipecat.services.openai.llm import ( - OpenAIAssistantContextAggregator, - OpenAIUserContextAggregator, -) -from pipecat.transcriptions.language import Language -from pipecat.utils.string import match_endofsentence -from pipecat.utils.time import time_now_iso8601 -from pipecat.utils.tracing.service_decorators import traced_gemini_live, traced_stt - -from .file_api import GeminiFileAPI - -try: - from google.genai import Client - from google.genai.live import AsyncSession - from google.genai.types import ( - AudioTranscriptionConfig, - AutomaticActivityDetection, - Blob, - Content, - ContextWindowCompressionConfig, - EndSensitivity, - FileData, - FunctionResponse, - GenerationConfig, - GroundingMetadata, - HttpOptions, - LiveConnectConfig, - LiveServerMessage, - MediaResolution, - Modality, - Part, - ProactivityConfig, - RealtimeInputConfig, - SessionResumptionConfig, - SlidingWindow, - SpeechConfig, - StartSensitivity, - ThinkingConfig, - VoiceConfig, +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.gemini_multimodal_live.gemini are deprecated. " + "Please use the equivalent types from " + "pipecat.services.gemini_live.gemini instead. Note that the new type " + "names do not include 'Multimodal' " + "(e.g. `GeminiMultimodalLiveLLMService` is now `GeminiLiveLLMService`).", + DeprecationWarning, + stacklevel=2, ) -except ModuleNotFoundError as e: - logger.error(f"Exception: {e}") - logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") - raise Exception(f"Missing module: {e}") - -# Connection management constants -MAX_CONSECUTIVE_FAILURES = 3 -CONNECTION_ESTABLISHED_THRESHOLD = 10.0 # seconds - - -def language_to_gemini_language(language: Language) -> Optional[str]: - """Maps a Language enum value to a Gemini Live supported language code. - - Source: - https://ai.google.dev/api/generate-content#MediaResolution - - Args: - language: The language enum value to convert. - - Returns: - The Gemini language code string, or None if the language is not supported. - """ - language_map = { - # Arabic - Language.AR: "ar-XA", - # Bengali - Language.BN_IN: "bn-IN", - # Chinese (Mandarin) - Language.CMN: "cmn-CN", - Language.CMN_CN: "cmn-CN", - Language.ZH: "cmn-CN", # Map general Chinese to Mandarin for Gemini - Language.ZH_CN: "cmn-CN", # Map Simplified Chinese to Mandarin for Gemini - # German - Language.DE: "de-DE", - Language.DE_DE: "de-DE", - # English - Language.EN: "en-US", # Default to US English (though not explicitly listed in supported codes) - Language.EN_US: "en-US", - Language.EN_AU: "en-AU", - Language.EN_GB: "en-GB", - Language.EN_IN: "en-IN", - # Spanish - Language.ES: "es-ES", # Default to Spain Spanish - Language.ES_ES: "es-ES", - Language.ES_US: "es-US", - # French - Language.FR: "fr-FR", # Default to France French - Language.FR_FR: "fr-FR", - Language.FR_CA: "fr-CA", - # Gujarati - Language.GU: "gu-IN", - Language.GU_IN: "gu-IN", - # Hindi - Language.HI: "hi-IN", - Language.HI_IN: "hi-IN", - # Indonesian - Language.ID: "id-ID", - Language.ID_ID: "id-ID", - # Italian - Language.IT: "it-IT", - Language.IT_IT: "it-IT", - # Japanese - Language.JA: "ja-JP", - Language.JA_JP: "ja-JP", - # Kannada - Language.KN: "kn-IN", - Language.KN_IN: "kn-IN", - # Korean - Language.KO: "ko-KR", - Language.KO_KR: "ko-KR", - # Malayalam - Language.ML: "ml-IN", - Language.ML_IN: "ml-IN", - # Marathi - Language.MR: "mr-IN", - Language.MR_IN: "mr-IN", - # Dutch - Language.NL: "nl-NL", - Language.NL_NL: "nl-NL", - # Polish - Language.PL: "pl-PL", - Language.PL_PL: "pl-PL", - # Portuguese (Brazil) - Language.PT_BR: "pt-BR", - # Russian - Language.RU: "ru-RU", - Language.RU_RU: "ru-RU", - # Tamil - Language.TA: "ta-IN", - Language.TA_IN: "ta-IN", - # Telugu - Language.TE: "te-IN", - Language.TE_IN: "te-IN", - # Thai - Language.TH: "th-TH", - Language.TH_TH: "th-TH", - # Turkish - Language.TR: "tr-TR", - Language.TR_TR: "tr-TR", - # Vietnamese - Language.VI: "vi-VN", - Language.VI_VN: "vi-VN", - } - return language_map.get(language) - - -class GeminiMultimodalLiveContext(OpenAILLMContext): - """Extended OpenAI context for Gemini Multimodal Live API. - - Provides Gemini-specific context management including system instruction - extraction and message format conversion for the Live API. - """ - - @staticmethod - def upgrade(obj: OpenAILLMContext) -> "GeminiMultimodalLiveContext": - """Upgrade an OpenAI context to Gemini context. - - Args: - obj: The OpenAI context to upgrade. - - Returns: - The upgraded Gemini context instance. - """ - if isinstance(obj, OpenAILLMContext) and not isinstance(obj, GeminiMultimodalLiveContext): - logger.debug(f"Upgrading to Gemini Multimodal Live Context: {obj}") - obj.__class__ = GeminiMultimodalLiveContext - obj._restructure_from_openai_messages() - return obj - - def _restructure_from_openai_messages(self): - pass - - def extract_system_instructions(self): - """Extract system instructions from context messages. - - Returns: - Combined system instruction text from all system messages. - """ - system_instruction = "" - for item in self.messages: - if item.get("role") == "system": - content = item.get("content", "") - if content: - if system_instruction and not system_instruction.endswith("\n"): - system_instruction += "\n" - system_instruction += str(content) - return system_instruction - - def add_file_reference(self, file_uri: str, mime_type: str, text: Optional[str] = None): - """Add a file reference to the context. - - This adds a user message with a file reference that will be sent during context initialization. - - Args: - file_uri: URI of the uploaded file - mime_type: MIME type of the file - text: Optional text prompt to accompany the file - """ - # Create parts list with file reference - parts = [] - if text: - parts.append({"type": "text", "text": text}) - - # Add file reference part - parts.append( - {"type": "file_data", "file_data": {"mime_type": mime_type, "file_uri": file_uri}} - ) - - # Add to messages - message = {"role": "user", "content": parts} - self.messages.append(message) - logger.info(f"Added file reference to context: {file_uri}") - - def get_messages_for_initializing_history(self) -> List[Content]: - """Get messages formatted for Gemini history initialization. - - Returns: - List of messages in Gemini format for conversation history. - """ - messages: List[Content] = [] - for item in self.messages: - role = item.get("role") - - if role == "system": - continue - - elif role == "assistant": - role = "model" - - content = item.get("content") - parts: List[Part] = [] - if isinstance(content, str): - parts = [Part(text=content)] - elif isinstance(content, list): - for part in content: - if part.get("type") == "text": - parts.append(Part(text=part.get("text"))) - elif part.get("type") == "file_data": - file_data = part.get("file_data", {}) - parts.append( - Part( - file_data=FileData( - mime_type=file_data.get("mime_type"), - file_uri=file_data.get("file_uri"), - ) - ) - ) - else: - logger.warning(f"Unsupported content type: {str(part)[:80]}") - else: - logger.warning(f"Unsupported content type: {str(content)[:80]}") - messages.append(Content(role=role, parts=parts)) - return messages - - -class GeminiMultimodalLiveUserContextAggregator(OpenAIUserContextAggregator): - """User context aggregator for Gemini Multimodal Live. - - Extends OpenAI user aggregator to handle Gemini-specific message passing - while maintaining compatibility with the standard aggregation pipeline. - """ - - async def process_frame(self, frame, direction): - """Process incoming frames for user context aggregation. - - Args: - frame: The frame to process. - direction: The frame processing direction. - """ - await super().process_frame(frame, direction) - # kind of a hack just to pass the LLMMessagesAppendFrame through, but it's fine for now - if isinstance(frame, LLMMessagesAppendFrame): - await self.push_frame(frame, direction) - - -class GeminiMultimodalLiveAssistantContextAggregator(OpenAIAssistantContextAggregator): - """Assistant context aggregator for Gemini Multimodal Live. - - Handles assistant response aggregation while filtering out LLMTextFrames - to prevent duplicate context entries, as Gemini Live pushes both - LLMTextFrames and TTSTextFrames. - """ - - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process incoming frames for assistant context aggregation. - - Args: - frame: The frame to process. - direction: The frame processing direction. - """ - # The LLMAssistantContextAggregator uses TextFrames to aggregate the LLM output, - # but the GeminiMultimodalLiveAssistantContextAggregator pushes LLMTextFrames and TTSTextFrames. We - # need to override this proces_frame for LLMTextFrame, so that only the TTSTextFrames - # are process. This ensures that the context gets only one set of messages. - if not isinstance(frame, LLMTextFrame): - await super().process_frame(frame, direction) - - async def handle_user_image_frame(self, frame: UserImageRawFrame): - """Handle user image frames. - - Args: - frame: The user image frame to handle. - """ - # We don't want to store any images in the context. Revisit this later - # when the API evolves. - pass - - -@dataclass -class GeminiMultimodalLiveContextAggregatorPair: - """Pair of user and assistant context aggregators for Gemini Multimodal Live. - - Parameters: - _user: The user context aggregator instance. - _assistant: The assistant context aggregator instance. - """ - - _user: GeminiMultimodalLiveUserContextAggregator - _assistant: GeminiMultimodalLiveAssistantContextAggregator - - def user(self) -> GeminiMultimodalLiveUserContextAggregator: - """Get the user context aggregator. - - Returns: - The user context aggregator instance. - """ - return self._user - - def assistant(self) -> GeminiMultimodalLiveAssistantContextAggregator: - """Get the assistant context aggregator. - - Returns: - The assistant context aggregator instance. - """ - return self._assistant - - -class GeminiMultimodalModalities(Enum): - """Supported modalities for Gemini Multimodal Live. - - Parameters: - TEXT: Text responses. - AUDIO: Audio responses. - """ - - TEXT = "TEXT" - AUDIO = "AUDIO" - - -class GeminiMediaResolution(str, Enum): - """Media resolution options for Gemini Multimodal Live. - - Parameters: - UNSPECIFIED: Use default resolution setting. - LOW: Low resolution with 64 tokens. - MEDIUM: Medium resolution with 256 tokens. - HIGH: High resolution with zoomed reframing and 256 tokens. - """ - - UNSPECIFIED = "MEDIA_RESOLUTION_UNSPECIFIED" # Use default - LOW = "MEDIA_RESOLUTION_LOW" # 64 tokens - MEDIUM = "MEDIA_RESOLUTION_MEDIUM" # 256 tokens - HIGH = "MEDIA_RESOLUTION_HIGH" # Zoomed reframing with 256 tokens - - -class GeminiVADParams(BaseModel): - """Voice Activity Detection parameters for Gemini Live. - - Parameters: - disabled: Whether to disable VAD. Defaults to None. - start_sensitivity: Sensitivity for speech start detection. Defaults to None. - end_sensitivity: Sensitivity for speech end detection. Defaults to None. - prefix_padding_ms: Prefix padding in milliseconds. Defaults to None. - silence_duration_ms: Silence duration threshold in milliseconds. Defaults to None. - """ - - disabled: Optional[bool] = Field(default=None) - start_sensitivity: Optional[StartSensitivity] = Field(default=None) - end_sensitivity: Optional[EndSensitivity] = Field(default=None) - prefix_padding_ms: Optional[int] = Field(default=None) - silence_duration_ms: Optional[int] = Field(default=None) - - -class ContextWindowCompressionParams(BaseModel): - """Parameters for context window compression in Gemini Live. - - Parameters: - enabled: Whether compression is enabled. Defaults to False. - trigger_tokens: Token count to trigger compression. None uses 80% of context window. - """ - - enabled: bool = Field(default=False) - trigger_tokens: Optional[int] = Field( - default=None - ) # None = use default (80% of context window) - - -class InputParams(BaseModel): - """Input parameters for Gemini Multimodal Live generation. - - Parameters: - frequency_penalty: Frequency penalty for generation (0.0-2.0). Defaults to None. - max_tokens: Maximum tokens to generate. Must be >= 1. Defaults to 4096. - presence_penalty: Presence penalty for generation (0.0-2.0). Defaults to None. - temperature: Sampling temperature (0.0-2.0). Defaults to None. - top_k: Top-k sampling parameter. Must be >= 0. Defaults to None. - top_p: Top-p sampling parameter (0.0-1.0). Defaults to None. - modalities: Response modalities. Defaults to AUDIO. - language: Language for generation. Defaults to EN_US. - media_resolution: Media resolution setting. Defaults to UNSPECIFIED. - vad: Voice activity detection parameters. Defaults to None. - context_window_compression: Context compression settings. Defaults to None. - thinking: Thinking settings. Defaults to None. - Note that these settings may require specifying a model that - supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". - enable_affective_dialog: Enable affective dialog, which allows Gemini - to adapt to expression and tone. Defaults to None. - Note that these settings may require specifying a model that - supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". - Also note that this setting may require specifying an API version that - supports it, e.g. HttpOptions(api_version="v1alpha"). - proactivity: Proactivity settings, which allows Gemini to proactively - decide how to behave, such as whether to avoid responding to - content that is not relevant. Defaults to None. - Note that these settings may require specifying a model that - supports them, e.g. "gemini-2.5-flash-native-audio-preview-09-2025". - Also note that this setting may require specifying an API version that - supports it, e.g. HttpOptions(api_version="v1alpha"). - extra: Additional parameters. Defaults to empty dict. - """ - - frequency_penalty: Optional[float] = Field(default=None, ge=0.0, le=2.0) - max_tokens: Optional[int] = Field(default=4096, ge=1) - presence_penalty: Optional[float] = Field(default=None, ge=0.0, le=2.0) - temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0) - top_k: Optional[int] = Field(default=None, ge=0) - top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0) - modalities: Optional[GeminiMultimodalModalities] = Field( - default=GeminiMultimodalModalities.AUDIO - ) - language: Optional[Language] = Field(default=Language.EN_US) - media_resolution: Optional[GeminiMediaResolution] = Field( - default=GeminiMediaResolution.UNSPECIFIED - ) - vad: Optional[GeminiVADParams] = Field(default=None) - context_window_compression: Optional[ContextWindowCompressionParams] = Field(default=None) - thinking: Optional[ThinkingConfig] = Field(default=None) - enable_affective_dialog: Optional[bool] = Field(default=None) - proactivity: Optional[ProactivityConfig] = Field(default=None) - extra: Optional[Dict[str, Any]] = Field(default_factory=dict) - - -class GeminiMultimodalLiveLLMService(LLMService): - """Provides access to Google's Gemini Multimodal Live API. - - This service enables real-time conversations with Gemini, supporting both - text and audio modalities. It handles voice transcription, streaming audio - responses, and tool usage. - """ - - # Overriding the default adapter to use the Gemini one. - adapter_class = GeminiLLMAdapter - - def __init__( - self, - *, - api_key: str, - base_url: Optional[str] = None, - model="models/gemini-2.0-flash-live-001", - voice_id: str = "Charon", - start_audio_paused: bool = False, - start_video_paused: bool = False, - system_instruction: Optional[str] = None, - tools: Optional[Union[List[dict], ToolsSchema]] = None, - params: Optional[InputParams] = None, - inference_on_context_initialization: bool = True, - file_api_base_url: str = "https://generativelanguage.googleapis.com/v1beta/files", - http_options: Optional[HttpOptions] = None, - **kwargs, - ): - """Initialize the Gemini Multimodal Live LLM service. - - Args: - api_key: Google AI API key for authentication. - base_url: API endpoint base URL. Defaults to the official Gemini Live endpoint. - - .. deprecated:: 0.0.90 - This parameter is deprecated and no longer has any effect. - Please use `http_options` to customize requests made by the - API client. - - model: Model identifier to use. Defaults to "models/gemini-2.0-flash-live-001". - voice_id: TTS voice identifier. Defaults to "Charon". - start_audio_paused: Whether to start with audio input paused. Defaults to False. - start_video_paused: Whether to start with video input paused. Defaults to False. - system_instruction: System prompt for the model. Defaults to None. - tools: Tools/functions available to the model. Defaults to None. - params: Configuration parameters for the model. Defaults to InputParams(). - inference_on_context_initialization: Whether to generate a response when context - is first set. Defaults to True. - file_api_base_url: Base URL for the Gemini File API. Defaults to the official endpoint. - http_options: HTTP options for the client. - **kwargs: Additional arguments passed to parent LLMService. - """ - # Check for deprecated parameter usage - if base_url is not None: - import warnings - - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - "Parameter 'base_url' is deprecated and no longer has any effect. Please use 'http_options' to customize requests made by the API client.", - DeprecationWarning, - stacklevel=2, - ) - - super().__init__(base_url=base_url, **kwargs) - - params = params or InputParams() - - self._last_sent_time = 0 - self._base_url = base_url - self.set_model_name(model) - self._voice_id = voice_id - self._language_code = params.language - - self._system_instruction = system_instruction - self._tools = tools - self._inference_on_context_initialization = inference_on_context_initialization - self._needs_turn_complete_message = False - - self._audio_input_paused = start_audio_paused - self._video_input_paused = start_video_paused - self._context = None - self._api_key = api_key - self._http_options = http_options - self._session: AsyncSession = None - self._connection_task = None - - self._disconnecting = False - self._run_llm_when_session_ready = False - - self._user_is_speaking = False - self._bot_is_speaking = False - self._user_audio_buffer = bytearray() - self._user_transcription_buffer = "" - self._last_transcription_sent = "" - self._bot_audio_buffer = bytearray() - self._bot_text_buffer = "" - self._llm_output_buffer = "" - - self._sample_rate = 24000 - - self._language = params.language - self._language_code = ( - language_to_gemini_language(params.language) if params.language else "en-US" - ) - self._vad_params = params.vad - - # Reconnection tracking - self._consecutive_failures = 0 - self._connection_start_time = None - - self._settings = { - "frequency_penalty": params.frequency_penalty, - "max_tokens": params.max_tokens, - "presence_penalty": params.presence_penalty, - "temperature": params.temperature, - "top_k": params.top_k, - "top_p": params.top_p, - "modalities": params.modalities, - "language": self._language_code, - "media_resolution": params.media_resolution, - "vad": params.vad, - "context_window_compression": params.context_window_compression.model_dump() - if params.context_window_compression - else {}, - "thinking": params.thinking or {}, - "enable_affective_dialog": params.enable_affective_dialog or False, - "proactivity": params.proactivity or {}, - "extra": params.extra if isinstance(params.extra, dict) else {}, - } - - self._file_api_base_url = file_api_base_url - self._file_api: Optional[GeminiFileAPI] = None - - # Grounding metadata tracking - self._search_result_buffer = "" - self._accumulated_grounding_metadata = None - - # Session resumption - self._session_resumption_handle: Optional[str] = None - - # Bookkeeping for ending gracefully (i.e. after the bot is finished) - self._end_frame_pending_bot_turn_finished: Optional[EndFrame] = None - - # Initialize the API client. Subclasses can override this if needed. - self.create_client() - - def create_client(self): - """Create the Gemini API client instance. Subclasses can override this.""" - self._client = Client(api_key=self._api_key, http_options=self._http_options) - - @property - def file_api(self) -> GeminiFileAPI: - """Get the Gemini File API client instance. Subclasses can override this. - - Returns: - The Gemini File API client. - """ - if not self._file_api: - self._file_api = GeminiFileAPI(api_key=self._api_key, base_url=self._file_api_base_url) - return self._file_api - - def can_generate_metrics(self) -> bool: - """Check if the service can generate usage metrics. - - Returns: - True as Gemini Live supports token usage metrics. - """ - return True - - def needs_mcp_alternate_schema(self) -> bool: - """Check if this LLM service requires alternate MCP schema. - - Google/Gemini has stricter JSON schema validation and requires - certain properties to be removed or modified for compatibility. - - Returns: - True for Google/Gemini services. - """ - return True - - def set_audio_input_paused(self, paused: bool): - """Set the audio input pause state. - - Args: - paused: Whether to pause audio input. - """ - self._audio_input_paused = paused - - def set_video_input_paused(self, paused: bool): - """Set the video input pause state. - - Args: - paused: Whether to pause video input. - """ - self._video_input_paused = paused - - def set_model_modalities(self, modalities: GeminiMultimodalModalities): - """Set the model response modalities. - - Args: - modalities: The modalities to use for responses. - """ - self._settings["modalities"] = modalities - - def set_language(self, language: Language): - """Set the language for generation. - - Args: - language: The language to use for generation. - """ - self._language = language - self._language_code = language_to_gemini_language(language) or "en-US" - self._settings["language"] = self._language_code - logger.info(f"Set Gemini language to: {self._language_code}") - - async def set_context(self, context: OpenAILLMContext): - """Set the context explicitly from outside the pipeline. - - This is useful when initializing a conversation because in server-side VAD mode we might not have a - way to trigger the pipeline. This sends the history to the server. The `inference_on_context_initialization` - flag controls whether to set the turnComplete flag when we do this. Without that flag, the model will - not respond. This is often what we want when setting the context at the beginning of a conversation. - - Args: - context: The OpenAI LLM context to set. - """ - if self._context: - logger.error( - "Context already set. Can only set up Gemini Multimodal Live context once." - ) - return - self._context = GeminiMultimodalLiveContext.upgrade(context) - await self._create_initial_response() - - # - # standard AIService frame handling - # - - async def start(self, frame: StartFrame): - """Start the service and establish connection. - - Args: - frame: The start frame. - """ - await super().start(frame) - await self._connect() - - async def stop(self, frame: EndFrame): - """Stop the service and close connections. - - Args: - frame: The end frame. - """ - await super().stop(frame) - await self._disconnect() - - async def cancel(self, frame: CancelFrame): - """Cancel the service and close connections. - - Args: - frame: The cancel frame. - """ - await super().cancel(frame) - await self._disconnect() - - # - # speech and interruption handling - # - - async def _handle_interruption(self): - await self._set_bot_is_speaking(False) - await self.push_frame(TTSStoppedFrame()) - await self.push_frame(LLMFullResponseEndFrame()) - - async def _handle_user_started_speaking(self, frame): - self._user_is_speaking = True - pass - - async def _handle_user_stopped_speaking(self, frame): - self._user_is_speaking = False - self._user_audio_buffer = bytearray() - await self.start_ttfb_metrics() - if self._needs_turn_complete_message: - self._needs_turn_complete_message = False - # NOTE: without this, the model ignores the context it's been - # seeded with before the user started speaking - await self._session.send_client_content(turn_complete=True) - - # - # frame processing - # - # StartFrame, StopFrame, CancelFrame implemented in base class - # - - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process incoming frames for the Gemini Live service. - - Args: - frame: The frame to process. - direction: The frame processing direction. - """ - # Defer EndFrame handling until after the bot turn is finished - if isinstance(frame, EndFrame): - if self._bot_is_speaking: - logger.debug("Deferring handling EndFrame until bot turn is finished") - self._end_frame_pending_bot_turn_finished = frame - return - - await super().process_frame(frame, direction) - - if isinstance(frame, TranscriptionFrame): - await self.push_frame(frame, direction) - elif isinstance(frame, OpenAILLMContextFrame): - context: GeminiMultimodalLiveContext = GeminiMultimodalLiveContext.upgrade( - frame.context - ) - # For now, we'll only trigger inference here when either: - # 1. We have not seen a context frame before - # 2. The last message is a tool call result - if not self._context: - self._context = context - if frame.context.tools: - self._tools = frame.context.tools - await self._create_initial_response() - elif context.messages and context.messages[-1].get("role") == "tool": - # Support just one tool call per context frame for now - tool_result_message = context.messages[-1] - await self._tool_result(tool_result_message) - elif isinstance(frame, LLMContextFrame): - raise NotImplementedError( - "Universal LLMContext is not yet supported for Gemini Multimodal Live." - ) - elif isinstance(frame, InputTextRawFrame): - await self._send_user_text(frame.text) - await self.push_frame(frame, direction) - elif isinstance(frame, InputAudioRawFrame): - await self._send_user_audio(frame) - await self.push_frame(frame, direction) - elif isinstance(frame, InputImageRawFrame): - await self._send_user_video(frame) - await self.push_frame(frame, direction) - elif isinstance(frame, InterruptionFrame): - await self._handle_interruption() - await self.push_frame(frame, direction) - elif isinstance(frame, UserStartedSpeakingFrame): - await self._handle_user_started_speaking(frame) - await self.push_frame(frame, direction) - elif isinstance(frame, UserStoppedSpeakingFrame): - await self._handle_user_stopped_speaking(frame) - await self.push_frame(frame, direction) - elif isinstance(frame, BotStartedSpeakingFrame): - # Ignore this frame. Use the serverContent API message instead - await self.push_frame(frame, direction) - elif isinstance(frame, BotStoppedSpeakingFrame): - # ignore this frame. Use the serverContent.turnComplete API message - await self.push_frame(frame, direction) - elif isinstance(frame, LLMMessagesAppendFrame): - # NOTE: handling LLMMessagesAppendFrame here in the LLMService is - # unusual - typically this would be handled in the user context - # aggregator. Leaving this handling here so that user code that - # uses this frame *without* a user context aggregator still works - # (we have an example that does just that, actually). - await self._create_single_response(frame.messages) - elif isinstance(frame, LLMUpdateSettingsFrame): - await self._update_settings(frame.settings) - elif isinstance(frame, LLMSetToolsFrame): - await self._update_settings() - else: - await self.push_frame(frame, direction) - - async def _set_bot_is_speaking(self, speaking: bool): - if self._bot_is_speaking == speaking: - return - - self._bot_is_speaking = speaking - - if not self._bot_is_speaking and self._end_frame_pending_bot_turn_finished: - await self.queue_frame(self._end_frame_pending_bot_turn_finished) - self._end_frame_pending_bot_turn_finished = None - - async def _connect(self, session_resumption_handle: Optional[str] = None): - """Establish client connection to Gemini Live API.""" - if self._session: - # Here we assume that if we have a client, we are connected. We - # handle disconnections in the send/recv code paths. - return - - if session_resumption_handle: - logger.info( - f"Connecting to Gemini service with session_resumption_handle: {session_resumption_handle}" - ) - else: - logger.info("Connecting to Gemini service") - try: - # Assemble basic configuration - config = LiveConnectConfig( - generation_config=GenerationConfig( - frequency_penalty=self._settings["frequency_penalty"], - max_output_tokens=self._settings["max_tokens"], - presence_penalty=self._settings["presence_penalty"], - temperature=self._settings["temperature"], - top_k=self._settings["top_k"], - top_p=self._settings["top_p"], - response_modalities=[Modality(self._settings["modalities"].value)], - speech_config=SpeechConfig( - voice_config=VoiceConfig( - prebuilt_voice_config={"voice_name": self._voice_id} - ), - language_code=self._settings["language"], - ), - media_resolution=MediaResolution(self._settings["media_resolution"].value), - ), - input_audio_transcription=AudioTranscriptionConfig(), - output_audio_transcription=AudioTranscriptionConfig(), - session_resumption=SessionResumptionConfig(handle=session_resumption_handle), - ) - - # Add context window compression to configuration, if enabled - if self._settings.get("context_window_compression", {}).get("enabled", False): - compression_config = ContextWindowCompressionConfig() - - # Add sliding window (always true if compression is enabled) - compression_config.sliding_window = SlidingWindow() - - # Add trigger_tokens if specified - trigger_tokens = self._settings.get("context_window_compression", {}).get( - "trigger_tokens" - ) - if trigger_tokens is not None: - compression_config.trigger_tokens = trigger_tokens - - config.context_window_compression = compression_config - - # Add thinking configuration to configuration, if provided - if self._settings.get("thinking"): - config.thinking_config = self._settings["thinking"] - - # Add affective dialog setting, if provided - if self._settings.get("enable_affective_dialog", False): - config.enable_affective_dialog = self._settings["enable_affective_dialog"] - - # Add proactivity configuration to configuration, if provided - if self._settings.get("proactivity"): - config.proactivity = self._settings["proactivity"] - - # Add VAD configuration to configuration, if provided - if self._settings.get("vad"): - vad_config = AutomaticActivityDetection() - vad_params = self._settings["vad"] - has_vad_settings = False - - # Only add parameters that are explicitly set - if vad_params.disabled is not None: - vad_config.disabled = vad_params.disabled - has_vad_settings = True - - if vad_params.start_sensitivity: - vad_config.start_of_speech_sensitivity = vad_params.start_sensitivity - has_vad_settings = True - - if vad_params.end_sensitivity: - vad_config.end_of_speech_sensitivity = vad_params.end_sensitivity - has_vad_settings = True - - if vad_params.prefix_padding_ms is not None: - vad_config.prefix_padding_ms = vad_params.prefix_padding_ms - has_vad_settings = True - - if vad_params.silence_duration_ms is not None: - vad_config.silence_duration_ms = vad_params.silence_duration_ms - has_vad_settings = True - - # Only add automatic_activity_detection if we have VAD settings - if has_vad_settings: - config.realtime_input_config = RealtimeInputConfig( - automatic_activity_detection=vad_config - ) - - # Add system instruction to configuration, if provided - system_instruction = self._system_instruction or "" - if self._context and hasattr(self._context, "extract_system_instructions"): - system_instruction += "\n" + self._context.extract_system_instructions() - if system_instruction: - logger.debug(f"Setting system instruction: {system_instruction}") - config.system_instruction = system_instruction - - # Add tools to configuration, if provided - if self._tools: - logger.debug(f"Setting tools: {self._tools}") - config.tools = self.get_llm_adapter().from_standard_tools(self._tools) - - # Start the connection - self._connection_task = self.create_task(self._connection_task_handler(config=config)) - - except Exception as e: - await self.push_error(ErrorFrame(error=f"{self} Initialization error: {e}", fatal=True)) - - async def _connection_task_handler(self, config: LiveConnectConfig): - async with self._client.aio.live.connect(model=self._model_name, config=config) as session: - logger.info("Connected to Gemini service") - - # Mark connection start time - self._connection_start_time = time.time() - - await self._handle_session_ready(session) - - while True: - try: - turn = self._session.receive() - async for message in turn: - # Reset failure counter if connection has been stable - self._check_and_reset_failure_counter() - - if message.server_content and message.server_content.model_turn: - await self._handle_msg_model_turn(message) - elif ( - message.server_content - and message.server_content.turn_complete - and message.usage_metadata - ): - await self._handle_msg_turn_complete(message) - await self._handle_msg_usage_metadata(message) - elif message.server_content and message.server_content.input_transcription: - await self._handle_msg_input_transcription(message) - elif message.server_content and message.server_content.output_transcription: - await self._handle_msg_output_transcription(message) - elif message.server_content and message.server_content.grounding_metadata: - await self._handle_msg_grounding_metadata(message) - elif message.tool_call: - await self._handle_msg_tool_call(message) - elif message.session_resumption_update: - self._handle_msg_resumption_update(message) - except Exception as e: - if not self._disconnecting: - should_reconnect = await self._handle_connection_error(e) - if should_reconnect: - await self._reconnect() - return # Exit this connection handler, _reconnect will start a new one - break - - def _check_and_reset_failure_counter(self): - """Check if connection has been stable long enough to reset the failure counter. - - If the connection has been active for longer than the established threshold - and there are accumulated failures, reset the counter to 0. - """ - if ( - self._connection_start_time - and self._consecutive_failures > 0 - and time.time() - self._connection_start_time >= CONNECTION_ESTABLISHED_THRESHOLD - ): - logger.info( - f"Connection stable for {CONNECTION_ESTABLISHED_THRESHOLD}s, " - f"resetting failure counter from {self._consecutive_failures} to 0" - ) - self._consecutive_failures = 0 - - async def _handle_connection_error(self, error: Exception) -> bool: - """Handle a connection error and determine if reconnection should be attempted. - - Args: - error: The exception that caused the connection error. - - Returns: - True if reconnection should be attempted, False if a fatal error should be pushed. - """ - self._consecutive_failures += 1 - logger.warning( - f"Connection error (failure {self._consecutive_failures}/{MAX_CONSECUTIVE_FAILURES}): {error}" - ) - - if self._consecutive_failures >= MAX_CONSECUTIVE_FAILURES: - logger.error( - f"Max consecutive failures ({MAX_CONSECUTIVE_FAILURES}) reached, " - "treating as fatal error" - ) - await self.push_error( - ErrorFrame(error=f"{self} Error in receive loop: {error}", fatal=True) - ) - return False - else: - logger.info( - f"Attempting reconnection ({self._consecutive_failures}/{MAX_CONSECUTIVE_FAILURES})" - ) - return True - - async def _reconnect(self): - """Reconnect to Gemini Live API.""" - await self._disconnect() - await self._connect(session_resumption_handle=self._session_resumption_handle) - - async def _disconnect(self): - """Disconnect from Gemini Live API and clean up resources.""" - logger.info("Disconnecting from Gemini service") - try: - self._disconnecting = True - await self.stop_all_metrics() - if self._connection_task: - await self.cancel_task(self._connection_task, timeout=1.0) - self._connection_task = None - if self._session: - await self._session.close() - self._session = None - self._disconnecting = False - except Exception as e: - logger.error(f"{self} error disconnecting: {e}") - - async def _send_user_audio(self, frame): - """Send user audio frame to Gemini Live API.""" - if self._audio_input_paused or self._disconnecting or not self._session: - return - - # Send all audio to Gemini - try: - await self._session.send_realtime_input( - audio=Blob(data=frame.audio, mime_type=f"audio/pcm;rate={frame.sample_rate}") - ) - except Exception as e: - await self._handle_send_error(e) - - # Manage a buffer of audio to use for transcription - audio = frame.audio - if self._user_is_speaking: - self._user_audio_buffer.extend(audio) - else: - # Keep 1/2 second of audio in the buffer even when not speaking. - self._user_audio_buffer.extend(audio) - length = int((frame.sample_rate * frame.num_channels * 2) * 0.5) - self._user_audio_buffer = self._user_audio_buffer[-length:] - - async def _send_user_text(self, text: str): - """Send user text via Gemini Live API's realtime input stream. - - This method sends text through the realtimeInput stream (via TextInputMessage) - rather than the clientContent stream. This ensures text input is synchronized - with audio and video inputs, preventing temporal misalignment that can occur - when different modalities are processed through separate API pathways. - - For realtimeInput, turn completion is automatically inferred by the API based - on user activity, so no explicit turnComplete signal is needed. - - Args: - text: The text to send as user input. - """ - if self._disconnecting or not self._session: - return - - try: - await self._session.send_realtime_input(text=text) - except Exception as e: - await self._handle_send_error(e) - - async def _send_user_video(self, frame): - """Send user video frame to Gemini Live API.""" - if self._video_input_paused or self._disconnecting or not self._session: - return - - now = time.time() - if now - self._last_sent_time < 1: - return # Ignore if less than 1 second has passed - - self._last_sent_time = now # Update last sent time - logger.debug(f"Sending video frame to Gemini: {frame}") - - buffer = io.BytesIO() - Image.frombytes(frame.format, frame.size, frame.image).save(buffer, format="JPEG") - data = base64.b64encode(buffer.getvalue()).decode("utf-8") - - try: - await self._session.send_realtime_input(video=Blob(data=data, mime_type="image/jpeg")) - except Exception as e: - await self._handle_send_error(e) - - async def _create_initial_response(self): - """Create initial response based on context history.""" - if self._disconnecting: - return - - if not self._session: - self._run_llm_when_session_ready = True - return - - messages = self._context.get_messages_for_initializing_history() - if not messages: - return - - logger.debug(f"Creating initial response: {messages}") - - await self.start_ttfb_metrics() - - try: - await self._session.send_client_content( - turns=messages, turn_complete=self._inference_on_context_initialization - ) - except Exception as e: - await self._handle_send_error(e) - - # If we're generating a response right away upon initializing - # conversation history, set a flag saying that we need a turn complete - # message when the user stops speaking. - if not self._inference_on_context_initialization: - self._needs_turn_complete_message = True - - async def _create_single_response(self, messages_list): - """Create a single response from a list of messages.""" - if self._disconnecting or not self._session: - return - - # Create a throwaway context just for the purpose of getting messages - # in the right format - context = GeminiMultimodalLiveContext.upgrade(OpenAILLMContext(messages=messages_list)) - messages = context.get_messages_for_initializing_history() - - if not messages: - return - - logger.debug(f"Creating response: {messages}") - - await self.start_ttfb_metrics() - - try: - await self._session.send_client_content(turns=messages, turn_complete=True) - except Exception as e: - await self._handle_send_error(e) - - @traced_gemini_live(operation="llm_tool_result") - async def _tool_result(self, tool_result_message): - """Send tool result back to the API.""" - if self._disconnecting or not self._session: - return - - # For now we're shoving the name into the tool_call_id field, so this - # will work until we revisit that. - id = tool_result_message.get("tool_call_id") - name = tool_result_message.get("tool_call_name") - result = json.loads(tool_result_message.get("content") or "") - response = FunctionResponse(name=name, id=id, response=result) - - try: - await self._session.send_tool_response(function_responses=response) - except Exception as e: - await self._handle_send_error(e) - - @traced_gemini_live(operation="llm_setup") - async def _handle_session_ready(self, session: AsyncSession): - """Handle the session being ready.""" - self._session = session - # If we were just waititng for the session to be ready to run the LLM, - # do that now. - if self._run_llm_when_session_ready: - self._run_llm_when_session_ready = False - await self._create_initial_response() - - async def _handle_msg_model_turn(self, msg: LiveServerMessage): - """Handle the model turn message.""" - part = msg.server_content.model_turn.parts[0] - if not part: - return - - await self.stop_ttfb_metrics() - - # part.text is added when `modalities` is set to TEXT; otherwise, it's None - text = part.text - if text: - if not self._bot_text_buffer: - await self.push_frame(LLMFullResponseStartFrame()) - - self._bot_text_buffer += text - self._search_result_buffer += text # Also accumulate for grounding - await self.push_frame(LLMTextFrame(text=text)) - - # Check for grounding metadata in server content - if msg.server_content and msg.server_content.grounding_metadata: - self._accumulated_grounding_metadata = msg.server_content.grounding_metadata - - inline_data = part.inline_data - if not inline_data: - return - - # Check if mime type matches expected format - expected_mime_type = f"audio/pcm;rate={self._sample_rate}" - if inline_data.mime_type == expected_mime_type: - # Perfect match, continue processing - pass - elif inline_data.mime_type == "audio/pcm": - # Sample rate not provided in mime type, assume default - if not hasattr(self, "_sample_rate_warning_logged"): - logger.warning( - f"Sample rate not provided in mime type '{inline_data.mime_type}', assuming rate of {self._sample_rate}" - ) - self._sample_rate_warning_logged = True - else: - # Unrecognized format - logger.warning(f"Unrecognized server_content format {inline_data.mime_type}") - return - - audio = inline_data.data - if not audio: - return - - if not self._bot_is_speaking: - await self._set_bot_is_speaking(True) - await self.push_frame(TTSStartedFrame()) - await self.push_frame(LLMFullResponseStartFrame()) - - self._bot_audio_buffer.extend(audio) - frame = TTSAudioRawFrame( - audio=audio, - sample_rate=self._sample_rate, - num_channels=1, - ) - await self.push_frame(frame) - - @traced_gemini_live(operation="llm_tool_call") - async def _handle_msg_tool_call(self, message: LiveServerMessage): - """Handle tool call messages.""" - function_calls = message.tool_call.function_calls - if not function_calls: - return - if not self._context: - logger.error("Function calls are not supported without a context object.") - - function_calls_llm = [ - FunctionCallFromLLM( - context=self._context, - tool_call_id=( - # NOTE: when using Vertex AI we don't get server-provided - # tool call IDs here - f.id or str(uuid.uuid4()) - ), - function_name=f.name, - arguments=f.args, - ) - for f in function_calls - ] - - await self.run_function_calls(function_calls_llm) - - @traced_gemini_live(operation="llm_response") - async def _handle_msg_turn_complete(self, message: LiveServerMessage): - """Handle the turn complete message.""" - await self._set_bot_is_speaking(False) - text = self._bot_text_buffer - - # Trace the complete LLM response (this will be handled by the decorator) - # The decorator will extract the output text and usage metadata from the message - - self._bot_text_buffer = "" - self._llm_output_buffer = "" - - # Process grounding metadata if we have accumulated any - if self._accumulated_grounding_metadata: - await self._process_grounding_metadata( - self._accumulated_grounding_metadata, self._search_result_buffer - ) - - # Reset grounding tracking for next response - self._search_result_buffer = "" - self._accumulated_grounding_metadata = None - - # Only push the TTSStoppedFrame if the bot is outputting audio - # when text is found, modalities is set to TEXT and no audio - # is produced. - if not text: - await self.push_frame(TTSStoppedFrame()) - - await self.push_frame(LLMFullResponseEndFrame()) - - @traced_stt - async def _handle_user_transcription( - self, transcript: str, is_final: bool, language: Optional[Language] = None - ): - """Handle a transcription result with tracing.""" - pass - - async def _handle_msg_input_transcription(self, message: LiveServerMessage): - """Handle the input transcription message. - - Gemini Live sends user transcriptions in either single words or multi-word - phrases. As a result, we have to aggregate the input transcription. This handler - aggregates into sentences, splitting on the end of sentence markers. - """ - if not message.server_content.input_transcription: - return - - text = message.server_content.input_transcription.text - - if not text: - return - - # Strip leading space from sentence starts if buffer is empty - if text.startswith(" ") and not self._user_transcription_buffer: - text = text.lstrip() - - # Accumulate text in the buffer - self._user_transcription_buffer += text - - # Check for complete sentences - while True: - eos_end_marker = match_endofsentence(self._user_transcription_buffer) - if not eos_end_marker: - break - - # Extract the complete sentence - complete_sentence = self._user_transcription_buffer[:eos_end_marker] - # Keep the remainder for the next chunk - self._user_transcription_buffer = self._user_transcription_buffer[eos_end_marker:] - - # Send a TranscriptionFrame with the complete sentence - logger.debug(f"[Transcription:user] [{complete_sentence}]") - await self._handle_user_transcription( - complete_sentence, True, self._settings["language"] - ) - await self.push_frame( - TranscriptionFrame( - text=complete_sentence, - user_id="", - timestamp=time_now_iso8601(), - result=message, - ), - FrameDirection.UPSTREAM, - ) - - async def _handle_msg_output_transcription(self, message: LiveServerMessage): - """Handle the output transcription message.""" - if not message.server_content.output_transcription: - return - - # This is the output transcription text when modalities is set to AUDIO. - # In this case, we push LLMTextFrame and TTSTextFrame to be handled by the - # downstream assistant context aggregator. - text = message.server_content.output_transcription.text - - if not text: - return - - # Accumulate text for grounding as well - self._search_result_buffer += text - - # Check for grounding metadata in server content - if message.server_content and message.server_content.grounding_metadata: - self._accumulated_grounding_metadata = message.server_content.grounding_metadata - # Collect text for tracing - self._llm_output_buffer += text - - await self.push_frame(LLMTextFrame(text=text)) - await self.push_frame(TTSTextFrame(text=text)) - - async def _handle_msg_grounding_metadata(self, message: LiveServerMessage): - """Handle dedicated grounding metadata messages.""" - if message.server_content and message.server_content.grounding_metadata: - grounding_metadata = message.server_content.grounding_metadata - # Process the grounding metadata immediately - await self._process_grounding_metadata(grounding_metadata, self._search_result_buffer) - - async def _process_grounding_metadata( - self, grounding_metadata: GroundingMetadata, search_result: str = "" - ): - """Process grounding metadata and emit LLMSearchResponseFrame.""" - if not grounding_metadata: - return - - # Extract rendered content for search suggestions - rendered_content = None - if ( - grounding_metadata.search_entry_point - and grounding_metadata.search_entry_point.rendered_content - ): - rendered_content = grounding_metadata.search_entry_point.rendered_content - - # Convert grounding chunks and supports to LLMSearchOrigin format - origins = [] - - if grounding_metadata.grounding_chunks and grounding_metadata.grounding_supports: - # Create a mapping of chunk indices to origins - chunk_to_origin: Dict[int, LLMSearchOrigin] = {} - - for index, chunk in enumerate(grounding_metadata.grounding_chunks): - if chunk.web: - origin = LLMSearchOrigin( - site_uri=chunk.web.uri, site_title=chunk.web.title, results=[] - ) - chunk_to_origin[index] = origin - origins.append(origin) - - # Add grounding support results to the appropriate origins - for support in grounding_metadata.grounding_supports: - if support.segment and support.grounding_chunk_indices: - text = support.segment.text or "" - confidence_scores = support.confidence_scores or [] - - # Add this result to all origins referenced by this support - for chunk_index in support.grounding_chunk_indices: - if chunk_index in chunk_to_origin: - result = LLMSearchResult(text=text, confidence=confidence_scores) - chunk_to_origin[chunk_index].results.append(result) - - # Create and push the search response frame - search_frame = LLMSearchResponseFrame( - search_result=search_result, origins=origins, rendered_content=rendered_content - ) - - await self.push_frame(search_frame) - - async def _handle_msg_usage_metadata(self, message: LiveServerMessage): - """Handle the usage metadata message.""" - if not message.usage_metadata: - return - - usage = message.usage_metadata - - # Ensure we have valid integers for all token counts - prompt_tokens = usage.prompt_token_count or 0 - completion_tokens = usage.response_token_count or 0 - total_tokens = usage.total_token_count or (prompt_tokens + completion_tokens) - - tokens = LLMTokenUsage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=total_tokens, - ) - - await self.start_llm_usage_metrics(tokens) - - def _handle_msg_resumption_update(self, message: LiveServerMessage): - update = message.session_resumption_update - if update.resumable and update.new_handle: - self._session_resumption_handle = update.new_handle - - async def _handle_send_error(self, error: Exception): - # In server-to-server contexts, a WebSocket error should be quite rare. - # Given how hard it is to recover from a send-side error with proper - # state management, and that exponential backoff for retries can have - # cost/stability implications for a service cluster, let's just treat a - # send-side error as fatal. - if not self._disconnecting: - await self.push_error(ErrorFrame(error=f"{self} Send error: {error}", fatal=True)) - - def create_context_aggregator( - self, - context: OpenAILLMContext, - *, - user_params: LLMUserAggregatorParams = LLMUserAggregatorParams(), - assistant_params: LLMAssistantAggregatorParams = LLMAssistantAggregatorParams(), - ) -> GeminiMultimodalLiveContextAggregatorPair: - """Create an instance of GeminiMultimodalLiveContextAggregatorPair from an OpenAILLMContext. - - Constructor keyword arguments for both the user and assistant aggregators can be provided. - - Args: - context: The LLM context to use. - user_params: User aggregator parameters. Defaults to LLMUserAggregatorParams(). - assistant_params: Assistant aggregator parameters. Defaults to LLMAssistantAggregatorParams(). - - Returns: - GeminiMultimodalLiveContextAggregatorPair: A pair of context - aggregators, one for the user and one for the assistant, - encapsulated in an GeminiMultimodalLiveContextAggregatorPair. - """ - context.set_llm_adapter(self.get_llm_adapter()) - - GeminiMultimodalLiveContext.upgrade(context) - user = GeminiMultimodalLiveUserContextAggregator(context, params=user_params) - - assistant_params.expect_stripped_words = False - assistant = GeminiMultimodalLiveAssistantContextAggregator(context, params=assistant_params) - return GeminiMultimodalLiveContextAggregatorPair(_user=user, _assistant=assistant) +GeminiMultimodalLiveContext = GeminiLiveContext +GeminiMultimodalLiveUserContextAggregator = GeminiLiveUserContextAggregator +GeminiMultimodalLiveAssistantContextAggregator = GeminiLiveAssistantContextAggregator +GeminiMultimodalLiveContextAggregatorPair = GeminiLiveContextAggregatorPair +GeminiMultimodalModalities = GeminiModalities +GeminiMediaResolution = _GeminiMediaResolution +GeminiVADParams = _GeminiVADParams +ContextWindowCompressionParams = _ContextWindowCompressionParams +InputParams = _InputParams +GeminiMultimodalLiveLLMService = GeminiLiveLLMService From 10069719e4f3018463e6e93049803daf2ad2f099 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 9 Oct 2025 15:48:59 -0400 Subject: [PATCH 048/158] Make `pause_processing_frames()` and `pause_processing_system_frames()` more robust in `FrameProcessor`. To understand this fix, let's look exclusively at `pause_processing_frames()` (`pause_processing_system_frames()` works the same way). `pause_processing_frames()` works by setting a `__should_block_frames` flag, which is then read each time through the loop in the long-running `__process_frame_task_handler`. if `__should_block_frames` is `True`, it pauses processing frames until it's resumed. Prior to this fix, the check for `__should_block_frames` was before `await self.__process_queue.get()`. The problem is that a lot of the time spent in the loop is waiting for a frame from the process queue. So if `pause_processing_frames()` is set at any time other than within `process_frame()` itself, it actually won't have an effect by the next frame, only on the frame *after* the next, which is later than intended. Because thus far in the Pipecat codebase we've only ever called `pause_processing_frames()` and `pause_processing_system_frames()` from within `process_frame()`, this change should have no behavioral effect. But it will be helpful if we ever need to call it from anywhere else. I noticed this issue while developing a feature that did exactly that (though I later abandoned that code). --- src/pipecat/processors/frame_processor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pipecat/processors/frame_processor.py b/src/pipecat/processors/frame_processor.py index 0b55082aa..1ca3333b5 100644 --- a/src/pipecat/processors/frame_processor.py +++ b/src/pipecat/processors/frame_processor.py @@ -877,6 +877,8 @@ class FrameProcessor(BaseObject): """ while True: + (frame, direction, callback) = await self.__input_queue.get() + if self.__should_block_system_frames and self.__input_event: logger.trace(f"{self}: system frame processing paused") await self.__input_event.wait() @@ -884,8 +886,6 @@ class FrameProcessor(BaseObject): self.__should_block_system_frames = False logger.trace(f"{self}: system frame processing resumed") - (frame, direction, callback) = await self.__input_queue.get() - if isinstance(frame, SystemFrame): await self.__process_frame(frame, direction, callback) elif self.__process_queue: @@ -900,6 +900,8 @@ class FrameProcessor(BaseObject): async def __process_frame_task_handler(self): """Handle non-system frames from the process queue.""" while True: + (frame, direction, callback) = await self.__process_queue.get() + if self.__should_block_frames and self.__process_event: logger.trace(f"{self}: frame processing paused") await self.__process_event.wait() @@ -907,8 +909,6 @@ class FrameProcessor(BaseObject): self.__should_block_frames = False logger.trace(f"{self}: frame processing resumed") - (frame, direction, callback) = await self.__process_queue.get() - await self.__process_frame(frame, direction, callback) self.__process_queue.task_done() From 2994640f475c8a65d671d970d48460715f345669 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 9 Oct 2025 16:45:47 -0400 Subject: [PATCH 049/158] Move `location` and `project_id` out of `InputParams` in `GoogleVertexLLMService`, making them top-level init args instead. We do this for two reasons: - Conceptually, these args comprise project-level setup, akin to credentials, whereas everything in `InputParams` is concerned with model configuration - Providing a `project_id` when initializing `GoogleVertexLLMService` should not be optional, but prior to the change in this commit it was (erroneously) treated as optional by dint of `InputParams` being optional This improvement was discussed [in this comment](https://github.com/pipecat-ai/pipecat/pull/2795#discussion_r2408279142). --- src/pipecat/services/google/llm_vertex.py | 67 ++++++++++++++++++++--- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/src/pipecat/services/google/llm_vertex.py b/src/pipecat/services/google/llm_vertex.py index ee66c1f20..3a3bf9ea9 100644 --- a/src/pipecat/services/google/llm_vertex.py +++ b/src/pipecat/services/google/llm_vertex.py @@ -51,6 +51,10 @@ class GoogleVertexLLMService(OpenAILLMService): class InputParams(OpenAILLMService.InputParams): """Input parameters specific to Vertex AI. + .. deprecated:: 0.0.90 + Use `OpenAILLMService.InputParams` instead and provide `location` and + `project_id` as direct arguments to `GoogleVertexLLMService.__init__()`. + Parameters: location: GCP region for Vertex AI endpoint (e.g., "us-east4"). project_id: Google Cloud project ID. @@ -60,12 +64,29 @@ class GoogleVertexLLMService(OpenAILLMService): location: str = "us-east4" project_id: str + def __init__(self, **kwargs): + """Initializes the InputParams.""" + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + warnings.warn( + "GoogleVertexLLMService.InputParams is deprecated. " + "Please use OpenAILLMService.InputParams instead and provide " + "'location' and 'project_id' as direct arguments to __init__.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(**kwargs) + def __init__( self, *, credentials: Optional[str] = None, credentials_path: Optional[str] = None, model: str = "google/gemini-2.0-flash-001", + location: Optional[str] = None, + project_id: Optional[str] = None, params: Optional[InputParams] = None, **kwargs, ): @@ -75,11 +96,42 @@ class GoogleVertexLLMService(OpenAILLMService): credentials: JSON string of service account credentials. credentials_path: Path to the service account JSON file. model: Model identifier (e.g., "google/gemini-2.0-flash-001"). + location: GCP region for Vertex AI endpoint (e.g., "us-east4"). + project_id: Google Cloud project ID. params: Vertex AI input parameters including location and project. + + .. deprecated:: 0.0.90 + Use `OpenAILLMService.InputParams` instead and provide `location` + and `project_id` as direct arguments to `__init__()`. + **kwargs: Additional arguments passed to OpenAILLMService. """ - params = params or OpenAILLMService.InputParams() - base_url = self._get_base_url(params) + # Handle deprecated InputParams + if params is not None and isinstance(params, GoogleVertexLLMService.InputParams): + # Extract location and project_id from params if not provided directly + if project_id is None: + project_id = params.project_id + if location is None: + location = params.location + # Convert to base InputParams + params = OpenAILLMService.InputParams( + **params.model_dump(exclude={"location", "project_id"}, exclude_unset=True) + ) + else: + params = params or OpenAILLMService.InputParams() + + # Validate parameters + # NOTE: once we remove deprecated InputParams, we can update __init__() + # signature with the following: + # - location: str = "us-east4", + # - project_id: str, + # For now, we need them as-is to maintain backward compatibility. + if project_id is None: + raise ValueError("project_id is required") + if location is None: + location = "us-east4" # Default location if not provided + + base_url = self._get_base_url(location, project_id) self._api_key = self._get_api_token(credentials, credentials_path) super().__init__( @@ -91,17 +143,14 @@ class GoogleVertexLLMService(OpenAILLMService): ) @staticmethod - def _get_base_url(params: InputParams) -> str: + def _get_base_url(location: str, project_id: str) -> str: """Construct the base URL for Vertex AI API.""" # Determine the correct API host based on location - if params.location == "global": + if location == "global": api_host = "aiplatform.googleapis.com" else: - api_host = f"{params.location}-aiplatform.googleapis.com" - return ( - f"https://{api_host}/v1/" - f"projects/{params.project_id}/locations/{params.location}/endpoints/openapi" - ) + api_host = f"{location}-aiplatform.googleapis.com" + return f"https://{api_host}/v1/projects/{project_id}/locations/{location}/endpoints/openapi" @staticmethod def _get_api_token(credentials: Optional[str], credentials_path: Optional[str]) -> str: From aeace9b9be1005b85fa9147412f8045dc3447c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 16:02:52 -0700 Subject: [PATCH 050/158] google: move gemini_live inside google service --- CHANGELOG.md | 23 +++++++++++-------- .../foundational/26-gemini-multimodal-live.py | 2 +- ...6a-gemini-multimodal-live-transcription.py | 2 +- ...gemini-multimodal-live-function-calling.py | 2 +- .../26c-gemini-multimodal-live-video.py | 2 +- .../26d-gemini-multimodal-live-text.py | 2 +- .../26e-gemini-multimodal-google-search.py | 2 +- .../26f-gemini-multimodal-live-files-api.py | 2 +- ...emini-multimodal-live-groundingMetadata.py | 2 +- .../26h-gemini-multimodal-live-vertex.py | 4 ++-- ...26i-gemini-multimodal-live-graceful-end.py | 4 +--- examples/foundational/46-video-processing.py | 2 +- .../gemini_multimodal_live/file_api.py | 2 +- .../services/gemini_multimodal_live/gemini.py | 12 ++++++---- .../{ => google}/gemini_live/__init__.py | 0 .../{ => google}/gemini_live/file_api.py | 0 .../{ => google}/gemini_live/gemini.py | 0 .../{ => google}/gemini_live/vertex.py | 2 +- 18 files changed, 35 insertions(+), 30 deletions(-) rename src/pipecat/services/{ => google}/gemini_live/__init__.py (100%) rename src/pipecat/services/{ => google}/gemini_live/file_api.py (100%) rename src/pipecat/services/{ => google}/gemini_live/gemini.py (100%) rename src/pipecat/services/{ => google}/gemini_live/vertex.py (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6999bd37c..10c749dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added `GeminiVertexMultimodalLiveLLMService`, for accessing Gemini Live via - Google Vertex AI. +- Added `GeminiLiveVertexLLMService`, for accessing Gemini Live via Google + Vertex AI. -- Added some new configuration options to `GeminiMultimodalLiveLLMService`: +- Added some new configuration options to `GeminiLiveLLMService`: - `thinking` - `enable_affective_dialog` @@ -37,16 +37,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Updated `GeminiMultimodalLiveLLMService` to use the `google-genai` library - rather than use WebSockets directly. +- Updated `GeminiLiveLLMService` to use the `google-genai` library rather than + use WebSockets directly. + +### Deprecated + +- `GeminiMultimodalLiveLLMService` is now deprecated, use + `GeminiLiveLLMService`. ### Fixed -- `GeminiMultimodalLiveLLMService` will now end gracefully (i.e. after the bot - has finished) upon receiving an `EndFrame`. +- `GeminiLiveLLMService` will now end gracefully (i.e. after the bot has + finished) upon receiving an `EndFrame`. -- `GeminiMultimodalLiveLLMService` will try to seamlessly reconnect when it - loses its connection. +- `GeminiLiveLLMService` will try to seamlessly reconnect when it loses its + connection. ## [0.0.89] - 2025-10-07 diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index f063c247f..fc7ad9d35 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -17,7 +17,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index 3c0eb19ce..cf8660edf 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -20,7 +20,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index a86822a20..a08f5a352 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -22,7 +22,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index 7eac0bc0b..1ac4eb923 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -24,7 +24,7 @@ from pipecat.runner.utils import ( maybe_capture_participant_camera, maybe_capture_participant_screen, ) -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index 625ca17d0..68fd79c54 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -20,7 +20,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.services.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.gemini import ( GeminiLiveLLMService, GeminiModalities, InputParams, diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index 1bcf02db9..87c754b75 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -19,7 +19,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26f-gemini-multimodal-live-files-api.py b/examples/foundational/26f-gemini-multimodal-live-files-api.py index 7ce4a483d..51dad712d 100644 --- a/examples/foundational/26f-gemini-multimodal-live-files-api.py +++ b/examples/foundational/26f-gemini-multimodal-live-files-api.py @@ -19,7 +19,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.gemini import ( GeminiLiveLLMService, ) from pipecat.transports.base_transport import BaseTransport, TransportParams diff --git a/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py b/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py index 9b8b25315..2b424eb18 100644 --- a/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py +++ b/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py @@ -14,8 +14,8 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService from pipecat.services.google.frames import LLMSearchResponseFrame +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26h-gemini-multimodal-live-vertex.py b/examples/foundational/26h-gemini-multimodal-live-vertex.py index 4210dc0e2..f8dfc8c52 100644 --- a/examples/foundational/26h-gemini-multimodal-live-vertex.py +++ b/examples/foundational/26h-gemini-multimodal-live-vertex.py @@ -17,8 +17,8 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService -from pipecat.services.gemini_live.vertex import GeminiLiveVertexLLMService +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.vertex import GeminiLiveVertexLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py index 9451d0ef0..3527e89e3 100644 --- a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py +++ b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py @@ -4,8 +4,6 @@ # SPDX-License-Identifier: BSD 2-Clause License # - -import asyncio import os from datetime import datetime @@ -24,7 +22,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/46-video-processing.py b/examples/foundational/46-video-processing.py index 7f7363538..0152f3211 100644 --- a/examples/foundational/46-video-processing.py +++ b/examples/foundational/46-video-processing.py @@ -20,7 +20,7 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.processors.frameworks.rtvi import RTVIObserver, RTVIProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.gemini_live import GeminiLiveLLMService +from pipecat.services.google.gemini_live import GeminiLiveLLMService from pipecat.transports.base_transport import TransportParams from pipecat.transports.daily.transport import DailyParams, DailyTransport diff --git a/src/pipecat/services/gemini_multimodal_live/file_api.py b/src/pipecat/services/gemini_multimodal_live/file_api.py index 53f8442fc..ed4d6c522 100644 --- a/src/pipecat/services/gemini_multimodal_live/file_api.py +++ b/src/pipecat/services/gemini_multimodal_live/file_api.py @@ -20,7 +20,7 @@ import warnings from loguru import logger try: - from pipecat.services.gemini_live.file_api import GeminiFileAPI as _GeminiFileAPI + from pipecat.services.google.gemini_live.file_api import GeminiFileAPI as _GeminiFileAPI except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error("In order to use Google AI, you need to `pip install pipecat-ai[google]`.") diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 4ac51e02e..a3547551a 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -18,10 +18,10 @@ voice transcription, streaming responses, and tool usage. import warnings -from pipecat.services.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.gemini import ( ContextWindowCompressionParams as _ContextWindowCompressionParams, ) -from pipecat.services.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.gemini import ( GeminiLiveAssistantContextAggregator, GeminiLiveContext, GeminiLiveContextAggregatorPair, @@ -29,9 +29,11 @@ from pipecat.services.gemini_live.gemini import ( GeminiLiveUserContextAggregator, GeminiModalities, ) -from pipecat.services.gemini_live.gemini import GeminiMediaResolution as _GeminiMediaResolution -from pipecat.services.gemini_live.gemini import GeminiVADParams as _GeminiVADParams -from pipecat.services.gemini_live.gemini import InputParams as _InputParams +from pipecat.services.google.gemini_live.gemini import ( + GeminiMediaResolution as _GeminiMediaResolution, +) +from pipecat.services.google.gemini_live.gemini import GeminiVADParams as _GeminiVADParams +from pipecat.services.google.gemini_live.gemini import InputParams as _InputParams with warnings.catch_warnings(): warnings.simplefilter("always") diff --git a/src/pipecat/services/gemini_live/__init__.py b/src/pipecat/services/google/gemini_live/__init__.py similarity index 100% rename from src/pipecat/services/gemini_live/__init__.py rename to src/pipecat/services/google/gemini_live/__init__.py diff --git a/src/pipecat/services/gemini_live/file_api.py b/src/pipecat/services/google/gemini_live/file_api.py similarity index 100% rename from src/pipecat/services/gemini_live/file_api.py rename to src/pipecat/services/google/gemini_live/file_api.py diff --git a/src/pipecat/services/gemini_live/gemini.py b/src/pipecat/services/google/gemini_live/gemini.py similarity index 100% rename from src/pipecat/services/gemini_live/gemini.py rename to src/pipecat/services/google/gemini_live/gemini.py diff --git a/src/pipecat/services/gemini_live/vertex.py b/src/pipecat/services/google/gemini_live/vertex.py similarity index 99% rename from src/pipecat/services/gemini_live/vertex.py rename to src/pipecat/services/google/gemini_live/vertex.py index c03f589b8..29ff63a0b 100644 --- a/src/pipecat/services/gemini_live/vertex.py +++ b/src/pipecat/services/google/gemini_live/vertex.py @@ -18,7 +18,7 @@ from typing import List, Optional, Union from loguru import logger from pipecat.adapters.schemas.tools_schema import ToolsSchema -from pipecat.services.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.gemini import ( GeminiLiveLLMService, HttpOptions, InputParams, From 59217eae38ccf4a12039a3c85c78b2beda552f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 8 Oct 2025 19:10:47 -0700 Subject: [PATCH 051/158] runner: add --folder argument to allow file downloads --- CHANGELOG.md | 3 ++ src/pipecat/runner/run.py | 83 ++++++++++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10c749dbc..829991a85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `--folder` argument to the runner, allowing files saved in that folder + to be downloaded from `http://HOST:PORT/file/FILE`. + - Added `GeminiLiveVertexLLMService`, for accessing Gemini Live via Google Vertex AI. diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index 1ab923314..fd1a8a952 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -67,12 +67,15 @@ To run locally: import argparse import asyncio +import mimetypes import os import sys from contextlib import asynccontextmanager +from pathlib import Path from typing import Optional import aiohttp +from fastapi.responses import FileResponse from loguru import logger from pipecat.runner.types import ( @@ -98,6 +101,12 @@ except ImportError as e: load_dotenv(override=True) os.environ["ENV"] = "local" +TELEPHONY_TRANSPORTS = ["twilio", "telnyx", "plivo", "exotel"] + +RUNNER_DOWNLOADS_FOLDER: Optional[str] = None +RUNNER_HOST: str = "localhost" +RUNNER_PORT: int = 7860 + def _get_bot_module(): """Get the bot module from the calling script.""" @@ -152,7 +161,12 @@ async def _run_telephony_bot(websocket: WebSocket): def _create_server_app( - transport_type: str, host: str = "localhost", proxy: str = None, esp32_mode: bool = False + *, + transport_type: str, + host: str = "localhost", + proxy: str, + esp32_mode: bool = False, + folder: Optional[str] = None, ): """Create FastAPI app with transport-specific routes.""" app = FastAPI() @@ -167,19 +181,21 @@ def _create_server_app( # Set up transport-specific routes if transport_type == "webrtc": - _setup_webrtc_routes(app, esp32_mode=esp32_mode, host=host) + _setup_webrtc_routes(app, esp32_mode=esp32_mode, host=host, folder=folder) _setup_whatsapp_routes(app) elif transport_type == "daily": _setup_daily_routes(app) - elif transport_type in ["twilio", "telnyx", "plivo", "exotel"]: - _setup_telephony_routes(app, transport_type, proxy) + elif transport_type in TELEPHONY_TRANSPORTS: + _setup_telephony_routes(app, transport_type=transport_type, proxy=proxy) else: logger.warning(f"Unknown transport type: {transport_type}") return app -def _setup_webrtc_routes(app: FastAPI, esp32_mode: bool = False, host: str = "localhost"): +def _setup_webrtc_routes( + app: FastAPI, *, esp32_mode: bool = False, host: str = "localhost", folder: Optional[str] = None +): """Set up WebRTC-specific routes.""" try: from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI @@ -201,6 +217,21 @@ def _setup_webrtc_routes(app: FastAPI, esp32_mode: bool = False, host: str = "lo """Redirect root requests to client interface.""" return RedirectResponse(url="/client/") + @app.get("/files/{filename}") + async def download_file(filename: str): + """Handle file downloads.""" + if not folder: + logger.warning(f"Attempting to dowload {filename}, but downloads folder not setup.") + return + + file_path = Path(folder) / filename + if not os.path.exists(file_path): + raise HTTPException(404) + + media_type, _ = mimetypes.guess_type(file_path) + + return FileResponse(path=file_path, media_type=media_type, filename=filename) + # Initialize the SmallWebRTC request handler small_webrtc_handler: SmallWebRTCRequestHandler = SmallWebRTCRequestHandler( esp32_mode=esp32_mode, host=host @@ -489,7 +520,7 @@ def _setup_daily_routes(app: FastAPI): return await _handle_rtvi_request(request) -def _setup_telephony_routes(app: FastAPI, transport_type: str, proxy: str): +def _setup_telephony_routes(app: FastAPI, *, transport_type: str, proxy: str): """Set up telephony-specific routes.""" # XML response templates (Exotel doesn't use XML webhooks) XML_TEMPLATES = { @@ -592,6 +623,21 @@ def _validate_and_clean_proxy(proxy: str) -> str: return proxy +def runner_downloads_folder() -> Optional[str]: + """Returns the folder where files are stored for later download.""" + return RUNNER_DOWNLOADS_FOLDER + + +def runner_host() -> str: + """Returns the host name of this runner.""" + return RUNNER_HOST + + +def runner_port() -> int: + """Returns the port of this runner.""" + return RUNNER_PORT + + def main(): """Start the Pipecat development runner. @@ -612,14 +658,16 @@ def main(): The bot file must contain a `bot(runner_args)` function as the entry point. """ + global RUNNER_DOWNLOADS_FOLDER, RUNNER_HOST, RUNNER_PORT + parser = argparse.ArgumentParser(description="Pipecat Development Runner") - parser.add_argument("--host", type=str, default="localhost", help="Host address") - parser.add_argument("--port", type=int, default=7860, help="Port number") + parser.add_argument("--host", type=str, default=RUNNER_HOST, help="Host address") + parser.add_argument("--port", type=int, default=RUNNER_PORT, help="Port number") parser.add_argument( "-t", "--transport", type=str, - choices=["daily", "webrtc", "twilio", "telnyx", "plivo", "exotel"], + choices=["daily", "webrtc", *TELEPHONY_TRANSPORTS], default="webrtc", help="Transport type", ) @@ -637,6 +685,7 @@ def main(): default=False, help="Connect directly to Daily room (automatically sets transport to daily)", ) + parser.add_argument("-f", "--folder", type=str, help="Path to downloads folder") parser.add_argument( "--verbose", "-v", action="count", default=0, help="Increase logging verbosity" ) @@ -659,6 +708,10 @@ def main(): logger.error("For ESP32, you need to specify `--host IP` so we can do SDP munging.") return + if args.transport in TELEPHONY_TRANSPORTS and not args.proxy: + logger.error(f"For telephony transports, you need to specify `--proxy PROXY`.") + return + # Log level logger.remove() logger.add(sys.stderr, level="TRACE" if args.verbose else "DEBUG") @@ -689,8 +742,18 @@ def main(): print(f" β†’ Open http://{args.host}:{args.port} in your browser to start a session") print() + RUNNER_DOWNLOADS_FOLDER = args.folder + RUNNER_HOST = args.host + RUNNER_PORT = args.port + # Create the app with transport-specific setup - app = _create_server_app(args.transport, args.host, args.proxy, args.esp32) + app = _create_server_app( + transport_type=args.transport, + host=args.host, + proxy=args.proxy, + esp32_mode=args.esp32, + folder=args.folder, + ) # Run the server uvicorn.run(app, host=args.host, port=args.port) From cdc86db8cebb064c1fa01d29e0ad44672303d226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 16:58:22 -0700 Subject: [PATCH 052/158] update CHANGELOG with GoogleVertexLLMService token fix --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 829991a85..28da49e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed a `GoogleVertexLLMService` issue that would generate an error if no + token information was returned. + - `GeminiLiveLLMService` will now end gracefully (i.e. after the bot has finished) upon receiving an `EndFrame`. From 15bf5b15339b8af6fe2a955b471c5b673da9cf33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 17:28:16 -0700 Subject: [PATCH 053/158] aws: move aws_nova_sonic to aws.nova_sonic --- .../20e-persistent-context-aws-nova-sonic.py | 2 +- examples/foundational/40-aws-nova-sonic.py | 2 +- src/pipecat/services/aws/__init__.py | 1 + src/pipecat/services/aws/nova_sonic/__init__.py | 0 .../{aws_nova_sonic => aws/nova_sonic}/context.py | 2 +- .../{aws_nova_sonic => aws/nova_sonic}/frames.py | 0 .../aws.py => aws/nova_sonic/llm.py} | 6 +++--- .../{aws_nova_sonic => aws/nova_sonic}/ready.wav | Bin src/pipecat/services/aws_nova_sonic/__init__.py | 1 - 9 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 src/pipecat/services/aws/nova_sonic/__init__.py rename src/pipecat/services/{aws_nova_sonic => aws/nova_sonic}/context.py (99%) rename src/pipecat/services/{aws_nova_sonic => aws/nova_sonic}/frames.py (100%) rename src/pipecat/services/{aws_nova_sonic/aws.py => aws/nova_sonic/llm.py} (99%) rename src/pipecat/services/{aws_nova_sonic => aws/nova_sonic}/ready.wav (100%) delete mode 100644 src/pipecat/services/aws_nova_sonic/__init__.py diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index bd3d9d545..2161aff8f 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -23,7 +23,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.aws_nova_sonic.aws import AWSNovaSonicLLMService +from pipecat.services.aws.nova_sonic.llm import AWSNovaSonicLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py index de7bbf638..d9bd2257d 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -21,7 +21,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.aws_nova_sonic import AWSNovaSonicLLMService +from pipecat.services.aws.nova_sonic.llm import AWSNovaSonicLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/src/pipecat/services/aws/__init__.py b/src/pipecat/services/aws/__init__.py index b1f157bd3..3cdd4cc5a 100644 --- a/src/pipecat/services/aws/__init__.py +++ b/src/pipecat/services/aws/__init__.py @@ -9,6 +9,7 @@ import sys from pipecat.services import DeprecatedModuleProxy from .llm import * +from .nova_sonic import * from .stt import * from .tts import * diff --git a/src/pipecat/services/aws/nova_sonic/__init__.py b/src/pipecat/services/aws/nova_sonic/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/pipecat/services/aws_nova_sonic/context.py b/src/pipecat/services/aws/nova_sonic/context.py similarity index 99% rename from src/pipecat/services/aws_nova_sonic/context.py rename to src/pipecat/services/aws/nova_sonic/context.py index 0ce5ce033..86aa0f0b5 100644 --- a/src/pipecat/services/aws_nova_sonic/context.py +++ b/src/pipecat/services/aws/nova_sonic/context.py @@ -33,7 +33,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.aws_nova_sonic.frames import AWSNovaSonicFunctionCallResultFrame +from pipecat.services.aws.nova_sonic.frames import AWSNovaSonicFunctionCallResultFrame from pipecat.services.openai.llm import ( OpenAIAssistantContextAggregator, OpenAIUserContextAggregator, diff --git a/src/pipecat/services/aws_nova_sonic/frames.py b/src/pipecat/services/aws/nova_sonic/frames.py similarity index 100% rename from src/pipecat/services/aws_nova_sonic/frames.py rename to src/pipecat/services/aws/nova_sonic/frames.py diff --git a/src/pipecat/services/aws_nova_sonic/aws.py b/src/pipecat/services/aws/nova_sonic/llm.py similarity index 99% rename from src/pipecat/services/aws_nova_sonic/aws.py rename to src/pipecat/services/aws/nova_sonic/llm.py index 66b020055..2801de688 100644 --- a/src/pipecat/services/aws_nova_sonic/aws.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -54,14 +54,14 @@ from pipecat.processors.aggregators.openai_llm_context import ( OpenAILLMContextFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.aws_nova_sonic.context import ( +from pipecat.services.aws.nova_sonic.context import ( AWSNovaSonicAssistantContextAggregator, AWSNovaSonicContextAggregatorPair, AWSNovaSonicLLMContext, AWSNovaSonicUserContextAggregator, Role, ) -from pipecat.services.aws_nova_sonic.frames import AWSNovaSonicFunctionCallResultFrame +from pipecat.services.aws.nova_sonic.frames import AWSNovaSonicFunctionCallResultFrame from pipecat.services.llm_service import LLMService from pipecat.utils.time import time_now_iso8601 @@ -251,7 +251,7 @@ class AWSNovaSonicLLMService(LLMService): self._connected_time: Optional[float] = None self._wants_connection = False - file_path = files("pipecat.services.aws_nova_sonic").joinpath("ready.wav") + file_path = files("pipecat.services.aws.nova_sonic").joinpath("ready.wav") with wave.open(file_path.open("rb"), "rb") as wav_file: self._assistant_response_trigger_audio = wav_file.readframes(wav_file.getnframes()) diff --git a/src/pipecat/services/aws_nova_sonic/ready.wav b/src/pipecat/services/aws/nova_sonic/ready.wav similarity index 100% rename from src/pipecat/services/aws_nova_sonic/ready.wav rename to src/pipecat/services/aws/nova_sonic/ready.wav diff --git a/src/pipecat/services/aws_nova_sonic/__init__.py b/src/pipecat/services/aws_nova_sonic/__init__.py deleted file mode 100644 index 4da394cf6..000000000 --- a/src/pipecat/services/aws_nova_sonic/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .aws import AWSNovaSonicLLMService, Params From 2a79b2c85392479e93922519ad2f92d67b728e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 17:29:02 -0700 Subject: [PATCH 054/158] aws: deprecate aws_nova_sonic --- .../services/aws_nova_sonic/__init__.py | 19 ++++++++++++++ src/pipecat/services/aws_nova_sonic/aws.py | 25 +++++++++++++++++++ .../services/aws_nova_sonic/context.py | 25 +++++++++++++++++++ src/pipecat/services/aws_nova_sonic/frames.py | 21 ++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 src/pipecat/services/aws_nova_sonic/__init__.py create mode 100644 src/pipecat/services/aws_nova_sonic/aws.py create mode 100644 src/pipecat/services/aws_nova_sonic/context.py create mode 100644 src/pipecat/services/aws_nova_sonic/frames.py diff --git a/src/pipecat/services/aws_nova_sonic/__init__.py b/src/pipecat/services/aws_nova_sonic/__init__.py new file mode 100644 index 000000000..e1cb912b6 --- /dev/null +++ b/src/pipecat/services/aws_nova_sonic/__init__.py @@ -0,0 +1,19 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import warnings + +from pipecat.services.aws.nova_sonic.llm import AWSNovaSonicLLMService, Params + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.aws_nova_sonic are deprecated. " + "Please use the equivalent types from " + "pipecat.services.aws.nova_sonic.llm instead.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/aws_nova_sonic/aws.py b/src/pipecat/services/aws_nova_sonic/aws.py new file mode 100644 index 000000000..0524829df --- /dev/null +++ b/src/pipecat/services/aws_nova_sonic/aws.py @@ -0,0 +1,25 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""AWS Nova Sonic LLM service implementation for Pipecat AI framework. + +This module provides a speech-to-speech LLM service using AWS Nova Sonic, which supports +bidirectional audio streaming, text generation, and function calling capabilities. +""" + +import warnings + +from pipecat.services.aws.nova_sonic.llm import * + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.aws_nova_sonic.aws are deprecated. " + "Please use the equivalent types from " + "pipecat.services.aws.nova_sonic.llm instead.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/aws_nova_sonic/context.py b/src/pipecat/services/aws_nova_sonic/context.py new file mode 100644 index 000000000..05a24f337 --- /dev/null +++ b/src/pipecat/services/aws_nova_sonic/context.py @@ -0,0 +1,25 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Context management for AWS Nova Sonic LLM service. + +This module provides specialized context aggregators and message handling for AWS Nova Sonic, +including conversation history management and role-specific message processing. +""" + +import warnings + +from pipecat.services.aws.nova_sonic.context import * + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.aws_nova_sonic.context are deprecated. " + "Please use the equivalent types from " + "pipecat.services.aws.nova_sonic.context instead.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/aws_nova_sonic/frames.py b/src/pipecat/services/aws_nova_sonic/frames.py new file mode 100644 index 000000000..def5f26c4 --- /dev/null +++ b/src/pipecat/services/aws_nova_sonic/frames.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Custom frames for AWS Nova Sonic LLM service.""" + +import warnings + +from pipecat.services.aws.nova_sonic.frames import * + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.aws_nova_sonic.frames are deprecated. " + "Please use the equivalent types from " + "pipecat.services.aws.nova_sonic.frames instead.", + DeprecationWarning, + stacklevel=2, + ) From aac4ce2d12e310d8cb652ef5d942c90e9dc68050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 17:29:42 -0700 Subject: [PATCH 055/158] update CHANGELOG with aws_nova_sonic deprecation --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28da49e20..9493c9d06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated +- `pipecat.service.aws_nova_sonic` is now deprecated, use + `pipecat.services.aws.nova_sonic` instead. + - `GeminiMultimodalLiveLLMService` is now deprecated, use `GeminiLiveLLMService`. From aa6e81648a7cac657771c97abd42aaf2551455ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 19:00:54 -0700 Subject: [PATCH 056/158] move openai_realtime to openai.realtime --- examples/foundational/19-openai-realtime.py | 7 ++++--- examples/foundational/19b-openai-realtime-text.py | 7 ++++--- .../20b-persistent-context-openai-realtime.py | 7 ++++--- src/pipecat/services/openai/__init__.py | 1 + src/pipecat/services/openai/realtime/__init__.py | 0 .../{openai_realtime => openai/realtime}/azure.py | 0 .../{openai_realtime => openai/realtime}/context.py | 0 .../{openai_realtime => openai/realtime}/events.py | 0 .../{openai_realtime => openai/realtime}/frames.py | 2 +- .../{openai_realtime => openai/realtime}/openai.py | 0 src/pipecat/services/openai_realtime/__init__.py | 9 --------- 11 files changed, 14 insertions(+), 19 deletions(-) create mode 100644 src/pipecat/services/openai/realtime/__init__.py rename src/pipecat/services/{openai_realtime => openai/realtime}/azure.py (100%) rename src/pipecat/services/{openai_realtime => openai/realtime}/context.py (100%) rename src/pipecat/services/{openai_realtime => openai/realtime}/events.py (100%) rename src/pipecat/services/{openai_realtime => openai/realtime}/frames.py (93%) rename src/pipecat/services/{openai_realtime => openai/realtime}/openai.py (100%) delete mode 100644 src/pipecat/services/openai_realtime/__init__.py diff --git a/examples/foundational/19-openai-realtime.py b/examples/foundational/19-openai-realtime.py index 0f9309f3d..aff4e15d7 100644 --- a/examples/foundational/19-openai-realtime.py +++ b/examples/foundational/19-openai-realtime.py @@ -24,14 +24,15 @@ from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.llm_service import FunctionCallParams -from pipecat.services.openai_realtime import ( +from pipecat.services.openai.realtime.events import ( + AudioConfiguration, + AudioInput, InputAudioNoiseReduction, InputAudioTranscription, - OpenAIRealtimeLLMService, SemanticTurnDetection, SessionProperties, ) -from pipecat.services.openai_realtime.events import AudioConfiguration, AudioInput +from pipecat.services.openai.realtime.openai import OpenAIRealtimeLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/19b-openai-realtime-text.py b/examples/foundational/19b-openai-realtime-text.py index b5d1d73e1..8ac916b23 100644 --- a/examples/foundational/19b-openai-realtime-text.py +++ b/examples/foundational/19b-openai-realtime-text.py @@ -24,14 +24,15 @@ from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia import CartesiaTTSService from pipecat.services.llm_service import FunctionCallParams -from pipecat.services.openai_realtime import ( +from pipecat.services.openai.realtime.events import ( + AudioConfiguration, + AudioInput, InputAudioNoiseReduction, InputAudioTranscription, - OpenAIRealtimeLLMService, SemanticTurnDetection, SessionProperties, ) -from pipecat.services.openai_realtime.events import AudioConfiguration, AudioInput +from pipecat.services.openai.realtime.openai import OpenAIRealtimeLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 52c7d8f49..100109de3 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -25,13 +25,14 @@ from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams -from pipecat.services.openai_realtime import ( +from pipecat.services.openai.realtime.events import ( + AudioConfiguration, + AudioInput, InputAudioTranscription, - OpenAIRealtimeLLMService, SessionProperties, TurnDetection, ) -from pipecat.services.openai_realtime.events import AudioConfiguration, AudioInput +from pipecat.services.openai.realtime.openai import OpenAIRealtimeLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/src/pipecat/services/openai/__init__.py b/src/pipecat/services/openai/__init__.py index 4decac126..d913c5ee0 100644 --- a/src/pipecat/services/openai/__init__.py +++ b/src/pipecat/services/openai/__init__.py @@ -10,6 +10,7 @@ from pipecat.services import DeprecatedModuleProxy from .image import * from .llm import * +from .realtime import * from .stt import * from .tts import * diff --git a/src/pipecat/services/openai/realtime/__init__.py b/src/pipecat/services/openai/realtime/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/pipecat/services/openai_realtime/azure.py b/src/pipecat/services/openai/realtime/azure.py similarity index 100% rename from src/pipecat/services/openai_realtime/azure.py rename to src/pipecat/services/openai/realtime/azure.py diff --git a/src/pipecat/services/openai_realtime/context.py b/src/pipecat/services/openai/realtime/context.py similarity index 100% rename from src/pipecat/services/openai_realtime/context.py rename to src/pipecat/services/openai/realtime/context.py diff --git a/src/pipecat/services/openai_realtime/events.py b/src/pipecat/services/openai/realtime/events.py similarity index 100% rename from src/pipecat/services/openai_realtime/events.py rename to src/pipecat/services/openai/realtime/events.py diff --git a/src/pipecat/services/openai_realtime/frames.py b/src/pipecat/services/openai/realtime/frames.py similarity index 93% rename from src/pipecat/services/openai_realtime/frames.py rename to src/pipecat/services/openai/realtime/frames.py index 290e025f9..8617c6efd 100644 --- a/src/pipecat/services/openai_realtime/frames.py +++ b/src/pipecat/services/openai/realtime/frames.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING from pipecat.frames.frames import DataFrame, FunctionCallResultFrame if TYPE_CHECKING: - from pipecat.services.openai_realtime.context import OpenAIRealtimeLLMContext + from pipecat.services.openai.realtime.context import OpenAIRealtimeLLMContext @dataclass diff --git a/src/pipecat/services/openai_realtime/openai.py b/src/pipecat/services/openai/realtime/openai.py similarity index 100% rename from src/pipecat/services/openai_realtime/openai.py rename to src/pipecat/services/openai/realtime/openai.py diff --git a/src/pipecat/services/openai_realtime/__init__.py b/src/pipecat/services/openai_realtime/__init__.py deleted file mode 100644 index 6f3154f1c..000000000 --- a/src/pipecat/services/openai_realtime/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .azure import AzureRealtimeLLMService -from .events import ( - InputAudioNoiseReduction, - InputAudioTranscription, - SemanticTurnDetection, - SessionProperties, - TurnDetection, -) -from .openai import OpenAIRealtimeLLMService From 303dd2ec355e4a1e2804ad071d85c44342d62595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 19:02:40 -0700 Subject: [PATCH 057/158] move openai.realtime.openai to openai.realtime.llm --- examples/foundational/19-openai-realtime.py | 2 +- examples/foundational/19b-openai-realtime-text.py | 2 +- examples/foundational/20b-persistent-context-openai-realtime.py | 2 +- src/pipecat/services/openai/realtime/{openai.py => llm.py} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/pipecat/services/openai/realtime/{openai.py => llm.py} (100%) diff --git a/examples/foundational/19-openai-realtime.py b/examples/foundational/19-openai-realtime.py index aff4e15d7..f182d7c8c 100644 --- a/examples/foundational/19-openai-realtime.py +++ b/examples/foundational/19-openai-realtime.py @@ -32,7 +32,7 @@ from pipecat.services.openai.realtime.events import ( SemanticTurnDetection, SessionProperties, ) -from pipecat.services.openai.realtime.openai import OpenAIRealtimeLLMService +from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/19b-openai-realtime-text.py b/examples/foundational/19b-openai-realtime-text.py index 8ac916b23..33f3a1cb2 100644 --- a/examples/foundational/19b-openai-realtime-text.py +++ b/examples/foundational/19b-openai-realtime-text.py @@ -32,7 +32,7 @@ from pipecat.services.openai.realtime.events import ( SemanticTurnDetection, SessionProperties, ) -from pipecat.services.openai.realtime.openai import OpenAIRealtimeLLMService +from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 100109de3..629a17c67 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -32,7 +32,7 @@ from pipecat.services.openai.realtime.events import ( SessionProperties, TurnDetection, ) -from pipecat.services.openai.realtime.openai import OpenAIRealtimeLLMService +from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/src/pipecat/services/openai/realtime/openai.py b/src/pipecat/services/openai/realtime/llm.py similarity index 100% rename from src/pipecat/services/openai/realtime/openai.py rename to src/pipecat/services/openai/realtime/llm.py From 7b05c9283b2f4f0db086daa5a373f794df527319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 19:06:09 -0700 Subject: [PATCH 058/158] move openai.realtime.azure to azure.realtime.llm --- examples/foundational/19a-azure-realtime.py | 7 ++++--- src/pipecat/services/azure/realtime/__init__.py | 0 .../{openai/realtime/azure.py => azure/realtime/llm.py} | 6 ++---- 3 files changed, 6 insertions(+), 7 deletions(-) create mode 100644 src/pipecat/services/azure/realtime/__init__.py rename src/pipecat/services/{openai/realtime/azure.py => azure/realtime/llm.py} (91%) diff --git a/examples/foundational/19a-azure-realtime.py b/examples/foundational/19a-azure-realtime.py index a39826b81..c4b0fc02a 100644 --- a/examples/foundational/19a-azure-realtime.py +++ b/examples/foundational/19a-azure-realtime.py @@ -21,13 +21,14 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport +from pipecat.services.azure.realtime.llm import AzureRealtimeLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.services.openai_realtime import ( - AzureRealtimeLLMService, +from pipecat.services.openai.realtime.events import ( + AudioConfiguration, + AudioInput, InputAudioTranscription, SessionProperties, ) -from pipecat.services.openai_realtime.events import AudioConfiguration, AudioInput from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/src/pipecat/services/azure/realtime/__init__.py b/src/pipecat/services/azure/realtime/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/pipecat/services/openai/realtime/azure.py b/src/pipecat/services/azure/realtime/llm.py similarity index 91% rename from src/pipecat/services/openai/realtime/azure.py rename to src/pipecat/services/azure/realtime/llm.py index aed53b94c..fb420e10b 100644 --- a/src/pipecat/services/openai/realtime/azure.py +++ b/src/pipecat/services/azure/realtime/llm.py @@ -8,15 +8,13 @@ from loguru import logger -from .openai import OpenAIRealtimeLLMService +from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService try: from websockets.asyncio.client import connect as websocket_connect except ModuleNotFoundError as e: logger.error(f"Exception: {e}") - logger.error( - "In order to use OpenAI, you need to `pip install pipecat-ai[openai]`. Also, set `OPENAI_API_KEY` environment variable." - ) + logger.error("In order to use Azure Realtime, you need to `pip install pipecat-ai[openai]`.") raise Exception(f"Missing module: {e}") From 9698b008da9363675b7c07cce49e252ce9e56e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 19:13:25 -0700 Subject: [PATCH 059/158] deprecate openai_realtime --- .../services/openai_realtime/__init__.py | 27 +++++++++++++++++++ src/pipecat/services/openai_realtime/azure.py | 21 +++++++++++++++ .../services/openai_realtime/context.py | 21 +++++++++++++++ .../services/openai_realtime/events.py | 21 +++++++++++++++ .../services/openai_realtime/frames.py | 21 +++++++++++++++ 5 files changed, 111 insertions(+) create mode 100644 src/pipecat/services/openai_realtime/__init__.py create mode 100644 src/pipecat/services/openai_realtime/azure.py create mode 100644 src/pipecat/services/openai_realtime/context.py create mode 100644 src/pipecat/services/openai_realtime/events.py create mode 100644 src/pipecat/services/openai_realtime/frames.py diff --git a/src/pipecat/services/openai_realtime/__init__.py b/src/pipecat/services/openai_realtime/__init__.py new file mode 100644 index 000000000..f1fbefeda --- /dev/null +++ b/src/pipecat/services/openai_realtime/__init__.py @@ -0,0 +1,27 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import warnings + +from pipecat.services.azure.realtime.llm import AzureRealtimeLLMService +from pipecat.services.openai.realtime.events import ( + InputAudioNoiseReduction, + InputAudioTranscription, + SemanticTurnDetection, + SessionProperties, + TurnDetection, +) +from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.openai_realtime are deprecated. " + "Please use the equivalent types from " + "pipecat.services.openai.realtime instead.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/openai_realtime/azure.py b/src/pipecat/services/openai_realtime/azure.py new file mode 100644 index 000000000..dae6ef496 --- /dev/null +++ b/src/pipecat/services/openai_realtime/azure.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Azure OpenAI Realtime LLM service implementation.""" + +import warnings + +from pipecat.services.azure.realtime.llm import * + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.openai_realtime.azure are deprecated. " + "Please use the equivalent types from " + "pipecat.services.azure.realtime.llm instead.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/openai_realtime/context.py b/src/pipecat/services/openai_realtime/context.py new file mode 100644 index 000000000..58f1cfe75 --- /dev/null +++ b/src/pipecat/services/openai_realtime/context.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""OpenAI Realtime LLM context and aggregator implementations.""" + +import warnings + +from pipecat.services.openai.realtime.context import * + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.openai_realtime.context are deprecated. " + "Please use the equivalent types from " + "pipecat.services.openai.realtime.context instead.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/openai_realtime/events.py b/src/pipecat/services/openai_realtime/events.py new file mode 100644 index 000000000..53b4b0dff --- /dev/null +++ b/src/pipecat/services/openai_realtime/events.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Event models and data structures for OpenAI Realtime API communication.""" + +import warnings + +from pipecat.services.openai.realtime.events import * + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.openai_realtime.events are deprecated. " + "Please use the equivalent types from " + "pipecat.services.openai.realtime.events instead.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/openai_realtime/frames.py b/src/pipecat/services/openai_realtime/frames.py new file mode 100644 index 000000000..e7e4d7d9f --- /dev/null +++ b/src/pipecat/services/openai_realtime/frames.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Custom frame types for OpenAI Realtime API integration.""" + +import warnings + +from pipecat.services.openai.realtime.frames import * + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.openai_realtime.frames are deprecated. " + "Please use the equivalent types from " + "pipecat.services.openai.realtime.frames instead.", + DeprecationWarning, + stacklevel=2, + ) From d81b0f63689d0b3c106379dee5b12924f8b6a2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 19:14:43 -0700 Subject: [PATCH 060/158] update CHANGELOG with openai_realtime deprecation --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9493c9d06..7079f21f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated +- `pipecat.service.openai_realtime` is now deprecated, use + `pipecat.services.openai.realtime` instead or + `pipecat.services.azure.realtime` for Azure Realtime. + - `pipecat.service.aws_nova_sonic` is now deprecated, use `pipecat.services.aws.nova_sonic` instead. From 5679dde70fa259c7070e554a37ce9ec44dc8e9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 22:09:11 -0700 Subject: [PATCH 061/158] ai_service: use openai.realtime.events instead of openai_realtime_beta.events --- src/pipecat/services/ai_service.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pipecat/services/ai_service.py b/src/pipecat/services/ai_service.py index eebdbfb0b..759efcf00 100644 --- a/src/pipecat/services/ai_service.py +++ b/src/pipecat/services/ai_service.py @@ -97,9 +97,7 @@ class AIService(FrameProcessor): pass async def _update_settings(self, settings: Mapping[str, Any]): - from pipecat.services.openai_realtime_beta.events import ( - SessionProperties, - ) + from pipecat.services.openai.realtime.events import SessionProperties for key, value in settings.items(): logger.debug("Update request for:", key, value) @@ -111,9 +109,7 @@ class AIService(FrameProcessor): logger.debug("Attempting to update", key, value) try: - from pipecat.services.openai_realtime_beta.events import ( - TurnDetection, - ) + from pipecat.services.openai.realtime.events import TurnDetection if isinstance(self._session_properties, SessionProperties): current_properties = self._session_properties From 9935a68018c43b5388fa07e41553eb9aca229112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 22:14:52 -0700 Subject: [PATCH 062/158] examples(19b): fix deprecations --- examples/foundational/19b-openai-realtime-text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/foundational/19b-openai-realtime-text.py b/examples/foundational/19b-openai-realtime-text.py index 33f3a1cb2..bb63a4814 100644 --- a/examples/foundational/19b-openai-realtime-text.py +++ b/examples/foundational/19b-openai-realtime-text.py @@ -22,7 +22,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.cartesia import CartesiaTTSService +from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.realtime.events import ( AudioConfiguration, From 7cec013666d5620fbfae62c175d6576435463728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 9 Oct 2025 21:56:54 -0700 Subject: [PATCH 063/158] google: rename google.gemini_live.gemini to google.gemini_live.llm --- examples/foundational/26-gemini-multimodal-live.py | 2 +- .../26a-gemini-multimodal-live-transcription.py | 2 +- .../26b-gemini-multimodal-live-function-calling.py | 2 +- .../foundational/26c-gemini-multimodal-live-video.py | 2 +- .../foundational/26d-gemini-multimodal-live-text.py | 2 +- .../26e-gemini-multimodal-google-search.py | 2 +- .../26f-gemini-multimodal-live-files-api.py | 4 +--- .../26g-gemini-multimodal-live-groundingMetadata.py | 4 ++-- .../26h-gemini-multimodal-live-vertex.py | 1 - .../26i-gemini-multimodal-live-graceful-end.py | 2 +- examples/foundational/46-video-processing.py | 2 +- .../services/gemini_multimodal_live/gemini.py | 12 +++++------- src/pipecat/services/google/__init__.py | 1 + src/pipecat/services/google/gemini_live/__init__.py | 2 +- .../google/gemini_live/{gemini.py => llm.py} | 0 src/pipecat/services/google/gemini_live/vertex.py | 7 ++----- 16 files changed, 20 insertions(+), 27 deletions(-) rename src/pipecat/services/google/gemini_live/{gemini.py => llm.py} (100%) diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index fc7ad9d35..dd5dfcffc 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -17,7 +17,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index cf8660edf..de277156b 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -20,7 +20,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index a08f5a352..8fecf0de6 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -22,7 +22,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index 1ac4eb923..7b765075e 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -24,7 +24,7 @@ from pipecat.runner.utils import ( maybe_capture_participant_camera, maybe_capture_participant_screen, ) -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index 68fd79c54..05b792c00 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -20,7 +20,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.services.google.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.llm import ( GeminiLiveLLMService, GeminiModalities, InputParams, diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index 87c754b75..178fdd282 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -19,7 +19,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26f-gemini-multimodal-live-files-api.py b/examples/foundational/26f-gemini-multimodal-live-files-api.py index 51dad712d..eeda16f52 100644 --- a/examples/foundational/26f-gemini-multimodal-live-files-api.py +++ b/examples/foundational/26f-gemini-multimodal-live-files-api.py @@ -19,9 +19,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.gemini import ( - GeminiLiveLLMService, -) +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py b/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py index 2b424eb18..bea1756b2 100644 --- a/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py +++ b/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py @@ -9,13 +9,13 @@ from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.frames.frames import Frame, LLMRunFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.pipeline.task import PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.google.frames import LLMSearchResponseFrame -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/examples/foundational/26h-gemini-multimodal-live-vertex.py b/examples/foundational/26h-gemini-multimodal-live-vertex.py index f8dfc8c52..97ebbbbde 100644 --- a/examples/foundational/26h-gemini-multimodal-live-vertex.py +++ b/examples/foundational/26h-gemini-multimodal-live-vertex.py @@ -17,7 +17,6 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService from pipecat.services.google.gemini_live.vertex import GeminiLiveVertexLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py index 3527e89e3..e51bbb032 100644 --- a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py +++ b/examples/foundational/26i-gemini-multimodal-live-graceful-end.py @@ -22,7 +22,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.gemini import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/46-video-processing.py b/examples/foundational/46-video-processing.py index 0152f3211..62ea5debe 100644 --- a/examples/foundational/46-video-processing.py +++ b/examples/foundational/46-video-processing.py @@ -20,7 +20,7 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.processors.frameworks.rtvi import RTVIObserver, RTVIProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService from pipecat.transports.base_transport import TransportParams from pipecat.transports.daily.transport import DailyParams, DailyTransport diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index a3547551a..8d6847425 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -18,10 +18,10 @@ voice transcription, streaming responses, and tool usage. import warnings -from pipecat.services.google.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.llm import ( ContextWindowCompressionParams as _ContextWindowCompressionParams, ) -from pipecat.services.google.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.llm import ( GeminiLiveAssistantContextAggregator, GeminiLiveContext, GeminiLiveContextAggregatorPair, @@ -29,11 +29,9 @@ from pipecat.services.google.gemini_live.gemini import ( GeminiLiveUserContextAggregator, GeminiModalities, ) -from pipecat.services.google.gemini_live.gemini import ( - GeminiMediaResolution as _GeminiMediaResolution, -) -from pipecat.services.google.gemini_live.gemini import GeminiVADParams as _GeminiVADParams -from pipecat.services.google.gemini_live.gemini import InputParams as _InputParams +from pipecat.services.google.gemini_live.llm import GeminiMediaResolution as _GeminiMediaResolution +from pipecat.services.google.gemini_live.llm import GeminiVADParams as _GeminiVADParams +from pipecat.services.google.gemini_live.llm import InputParams as _InputParams with warnings.catch_warnings(): warnings.simplefilter("always") diff --git a/src/pipecat/services/google/__init__.py b/src/pipecat/services/google/__init__.py index ec187000f..08e295b9e 100644 --- a/src/pipecat/services/google/__init__.py +++ b/src/pipecat/services/google/__init__.py @@ -9,6 +9,7 @@ import sys from pipecat.services import DeprecatedModuleProxy from .frames import * +from .gemini_live import * from .image import * from .llm import * from .llm_openai import * diff --git a/src/pipecat/services/google/gemini_live/__init__.py b/src/pipecat/services/google/gemini_live/__init__.py index 6a2d33bd8..1ab28050e 100644 --- a/src/pipecat/services/google/gemini_live/__init__.py +++ b/src/pipecat/services/google/gemini_live/__init__.py @@ -1,3 +1,3 @@ from .file_api import GeminiFileAPI -from .gemini import GeminiLiveLLMService +from .llm import GeminiLiveLLMService from .vertex import GeminiLiveVertexLLMService diff --git a/src/pipecat/services/google/gemini_live/gemini.py b/src/pipecat/services/google/gemini_live/llm.py similarity index 100% rename from src/pipecat/services/google/gemini_live/gemini.py rename to src/pipecat/services/google/gemini_live/llm.py diff --git a/src/pipecat/services/google/gemini_live/vertex.py b/src/pipecat/services/google/gemini_live/vertex.py index 29ff63a0b..4f1b57f4c 100644 --- a/src/pipecat/services/google/gemini_live/vertex.py +++ b/src/pipecat/services/google/gemini_live/vertex.py @@ -12,13 +12,12 @@ streaming responses, and tool usage. """ import json -import os from typing import List, Optional, Union from loguru import logger from pipecat.adapters.schemas.tools_schema import ToolsSchema -from pipecat.services.google.gemini_live.gemini import ( +from pipecat.services.google.gemini_live.llm import ( GeminiLiveLLMService, HttpOptions, InputParams, @@ -33,9 +32,7 @@ try: except ModuleNotFoundError as e: logger.error(f"Exception: {e}") - logger.error( - "In order to use Google Vertex AI, you need to `pip install pipecat-ai[google]`. Also, set up your Google credentials properly." - ) + logger.error("In order to use Google Vertex AI, you need to `pip install pipecat-ai[google]`.") raise Exception(f"Missing module: {e}") From 1b3afb551196ef8d0a1db459da7c40e809a264ae Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Fri, 10 Oct 2025 09:44:47 -0300 Subject: [PATCH 064/158] Added audio filter KrispVivaFilter using the Krisp VIVA SDK --- CHANGELOG.md | 2 + env.example | 3 + .../07p-interruptible-krisp-viva.py | 129 ++++++++++++ .../audio/filters/krisp_viva_filter.py | 193 ++++++++++++++++++ 4 files changed, 327 insertions(+) create mode 100644 examples/foundational/07p-interruptible-krisp-viva.py create mode 100644 src/pipecat/audio/filters/krisp_viva_filter.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 7079f21f5..02b31d93f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added audio filter `KrispVivaFilter` using the Krisp VIVA SDK. + - Added `--folder` argument to the runner, allowing files saved in that folder to be downloaded from `http://HOST:PORT/file/FILE`. diff --git a/env.example b/env.example index 22768494b..f707f49c9 100644 --- a/env.example +++ b/env.example @@ -90,6 +90,9 @@ SIMLI_FACE_ID=... # Krisp KRISP_MODEL_PATH=... +# Krisp Viva +KRISP_VIVA_MODEL_PATH=... + # DeepSeek DEEPSEEK_API_KEY=... diff --git a/examples/foundational/07p-interruptible-krisp-viva.py b/examples/foundational/07p-interruptible-krisp-viva.py new file mode 100644 index 000000000..c7ca15b40 --- /dev/null +++ b/examples/foundational/07p-interruptible-krisp-viva.py @@ -0,0 +1,129 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + + +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.filters.krisp_viva_filter import KrispVivaFilter +from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams +from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import LLMRunFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.deepgram.tts import DeepgramTTSService +from pipecat.services.openai.llm import OpenAILLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + audio_in_filter=KrispVivaFilter(), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + audio_in_filter=KrispVivaFilter(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + audio_in_filter=KrispVivaFilter(), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en") + + llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = LLMContext(messages) + context_aggregator = LLMContextAggregatorPair(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, # STT + context_aggregator.user(), # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + messages.append({"role": "system", "content": "Please introduce yourself to the user."}) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/src/pipecat/audio/filters/krisp_viva_filter.py b/src/pipecat/audio/filters/krisp_viva_filter.py new file mode 100644 index 000000000..ddb489168 --- /dev/null +++ b/src/pipecat/audio/filters/krisp_viva_filter.py @@ -0,0 +1,193 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Krisp noise reduction audio filter for Pipecat. + +This module provides an audio filter implementation using Krisp VIVA SDK. +""" + +import os + +import numpy as np +from loguru import logger + +from pipecat.audio.filters.base_audio_filter import BaseAudioFilter +from pipecat.frames.frames import FilterControlFrame, FilterEnableFrame + +try: + import krisp_audio +except ModuleNotFoundError as e: + logger.error(f"Exception: {e}") + logger.error("In order to use the Krisp filter, you need to install krisp_audio.") + raise Exception(f"Missing module: {e}") + + +def _log_callback(log_message, log_level): + logger.info(f"[{log_level}] {log_message}") + + +class KrispVivaFilter(BaseAudioFilter): + """Audio filter using the Krisp VIVA SDK. + + Provides real-time noise reduction for audio streams using Krisp's + proprietary noise suppression algorithms. This filter requires a + valid Krisp model file to operate. + + Supported sample rates: + - 8000 Hz + - 16000 Hz + - 24000 Hz + - 32000 Hz + - 44100 Hz + - 48000 Hz + """ + + # Initialize Krisp Audio SDK globally + krisp_audio.globalInit("", _log_callback, krisp_audio.LogLevel.Off) + SDK_VERSION = krisp_audio.getVersion() + logger.debug( + f"Krisp Audio Python SDK Version: {SDK_VERSION.major}." + f"{SDK_VERSION.minor}.{SDK_VERSION.patch}" + ) + + SAMPLE_RATES = { + 8000: krisp_audio.SamplingRate.Sr8000Hz, + 16000: krisp_audio.SamplingRate.Sr16000Hz, + 24000: krisp_audio.SamplingRate.Sr24000Hz, + 32000: krisp_audio.SamplingRate.Sr32000Hz, + 44100: krisp_audio.SamplingRate.Sr44100Hz, + 48000: krisp_audio.SamplingRate.Sr48000Hz, + } + + FRAME_SIZE_MS = 10 # Krisp requires audio frames of 10ms duration for processing. + + def __init__(self, model_path: str = None, noise_suppression_level: int = 100) -> None: + """Initialize the Krisp noise reduction filter. + + Args: + model_path: Path to the Krisp model file (.kef extension). + If None, uses KRISP_VIVA_MODEL_PATH environment variable. + noise_suppression_level: Noise suppression level. + + Raises: + ValueError: If model_path is not provided and KRISP_VIVA_MODEL_PATH is not set. + Exception: If model file doesn't have .kef extension. + FileNotFoundError: If model file doesn't exist. + """ + super().__init__() + + # Set model path, checking environment if not specified + self._model_path = model_path or os.getenv("KRISP_VIVA_MODEL_PATH") + if not self._model_path: + logger.error("Model path is not provided and KRISP_VIVA_MODEL_PATH is not set.") + raise ValueError("Model path for KrispAudioProcessor must be provided.") + + if not self._model_path.endswith(".kef"): + raise Exception("Model is expected with .kef extension") + + if not os.path.isfile(self._model_path): + raise FileNotFoundError(f"Model file not found: {self._model_path}") + + self._filtering = True + self._session = None + self._samples_per_frame = None + self._noise_suppression_level = noise_suppression_level + + # Audio buffer to accumulate samples for complete frames + self._audio_buffer = bytearray() + + def _int_to_sample_rate(self, sample_rate): + """Convert integer sample rate to krisp_audio SamplingRate enum. + + Args: + sample_rate: Sample rate as integer + + Returns: + krisp_audio.SamplingRate enum value + + Raises: + ValueError: If sample rate is not supported + """ + if sample_rate not in self.SAMPLE_RATES: + raise ValueError("Unsupported sample rate") + return self.SAMPLE_RATES[sample_rate] + + async def start(self, sample_rate: int): + """Initialize the Krisp processor with the transport's sample rate. + + Args: + sample_rate: The sample rate of the input transport in Hz. + """ + model_info = krisp_audio.ModelInfo() + model_info.path = self._model_path + + nc_cfg = krisp_audio.NcSessionConfig() + nc_cfg.inputSampleRate = self._int_to_sample_rate(sample_rate) + nc_cfg.inputFrameDuration = krisp_audio.FrameDuration.Fd10ms + nc_cfg.outputSampleRate = nc_cfg.inputSampleRate + nc_cfg.modelInfo = model_info + + self._samples_per_frame = int((sample_rate * self.FRAME_SIZE_MS) / 1000) + self._session = krisp_audio.NcInt16.create(nc_cfg) + + async def stop(self): + """Clean up the Krisp processor when stopping.""" + self._session = None + + async def process_frame(self, frame: FilterControlFrame): + """Process control frames to enable/disable filtering. + + Args: + frame: The control frame containing filter commands. + """ + if isinstance(frame, FilterEnableFrame): + self._filtering = frame.enable + + async def filter(self, audio: bytes) -> bytes: + """Apply Krisp noise reduction to audio data. + + Args: + audio: Raw audio data as bytes to be filtered. + + Returns: + Noise-reduced audio data as bytes. + """ + if not self._filtering: + return audio + + # Add incoming audio to our buffer + self._audio_buffer.extend(audio) + + # Calculate how many complete frames we can process + total_samples = len(self._audio_buffer) // 2 # 2 bytes per int16 sample + num_complete_frames = total_samples // self._samples_per_frame + + if num_complete_frames == 0: + # Not enough samples for a complete frame yet, return empty + return b"" + + # Calculate how many bytes we need for complete frames + complete_samples_count = num_complete_frames * self._samples_per_frame + bytes_to_process = complete_samples_count * 2 # 2 bytes per sample + + # Extract the bytes we can process + audio_to_process = bytes(self._audio_buffer[:bytes_to_process]) + + # Remove processed bytes from buffer, keep the remainder + self._audio_buffer = self._audio_buffer[bytes_to_process:] + + # Process the complete frames + samples = np.frombuffer(audio_to_process, dtype=np.int16) + frames = samples.reshape(-1, self._samples_per_frame) + processed_samples = np.empty_like(samples) + + for i, frame in enumerate(frames): + cleaned_frame = self._session.process(frame, self._noise_suppression_level) + processed_samples[i * self._samples_per_frame : (i + 1) * self._samples_per_frame] = ( + cleaned_frame + ) + + return processed_samples.tobytes() From 8ee28b37cdd31811294ed077c0947a081004fcbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Fri, 10 Oct 2025 06:41:19 -0700 Subject: [PATCH 065/158] google: rename google.gemini_live.vertext to google.gemini_live.llm_vertex --- examples/foundational/26h-gemini-multimodal-live-vertex.py | 2 +- src/pipecat/services/google/gemini_live/__init__.py | 2 +- .../services/google/gemini_live/{vertex.py => llm_vertex.py} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/pipecat/services/google/gemini_live/{vertex.py => llm_vertex.py} (100%) diff --git a/examples/foundational/26h-gemini-multimodal-live-vertex.py b/examples/foundational/26h-gemini-multimodal-live-vertex.py index 97ebbbbde..79bfac531 100644 --- a/examples/foundational/26h-gemini-multimodal-live-vertex.py +++ b/examples/foundational/26h-gemini-multimodal-live-vertex.py @@ -17,7 +17,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.vertex import GeminiLiveVertexLLMService +from pipecat.services.google.gemini_live.llm_vertex import GeminiLiveVertexLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/src/pipecat/services/google/gemini_live/__init__.py b/src/pipecat/services/google/gemini_live/__init__.py index 1ab28050e..142ca2a83 100644 --- a/src/pipecat/services/google/gemini_live/__init__.py +++ b/src/pipecat/services/google/gemini_live/__init__.py @@ -1,3 +1,3 @@ from .file_api import GeminiFileAPI from .llm import GeminiLiveLLMService -from .vertex import GeminiLiveVertexLLMService +from .llm_vertex import GeminiLiveVertexLLMService diff --git a/src/pipecat/services/google/gemini_live/vertex.py b/src/pipecat/services/google/gemini_live/llm_vertex.py similarity index 100% rename from src/pipecat/services/google/gemini_live/vertex.py rename to src/pipecat/services/google/gemini_live/llm_vertex.py From 3894d2a4b9fd8c339882720b357656f79ba92545 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 9 Oct 2025 22:03:06 -0400 Subject: [PATCH 066/158] Deprecate LivekitFrameSerializer --- CHANGELOG.md | 2 ++ src/pipecat/serializers/livekit.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b31d93f..9763f78f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated +- `LivekitFrameSerializer` is now deprecated. Use `LiveKitTransport` instead. + - `pipecat.service.openai_realtime` is now deprecated, use `pipecat.services.openai.realtime` instead or `pipecat.services.azure.realtime` for Azure Realtime. diff --git a/src/pipecat/serializers/livekit.py b/src/pipecat/serializers/livekit.py index 3d4188960..f3a34c434 100644 --- a/src/pipecat/serializers/livekit.py +++ b/src/pipecat/serializers/livekit.py @@ -25,11 +25,31 @@ except ModuleNotFoundError as e: class LivekitFrameSerializer(FrameSerializer): """Serializer for converting between Pipecat frames and LiveKit audio frames. + .. deprecated:: 0.0.90 + + This class is deprecated and will be removed in a future version. + Please use LiveKitTransport instead, which handles audio streaming + and frame conversion natively. + This serializer handles the conversion of Pipecat's OutputAudioRawFrame objects to LiveKit AudioFrame objects for transmission, and the reverse conversion for received audio data. """ + def __init__(self): + """Initialize the LiveKit frame serializer.""" + super().__init__() + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "LivekitFrameSerializer is deprecated and will be removed in a future version. " + "Please use LiveKitTransport instead, which handles audio streaming natively.", + DeprecationWarning, + stacklevel=2, + ) + @property def type(self) -> FrameSerializerType: """Get the serializer type. From 8b62a96878da6863148ed923b2dcf1cb9d246940 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 10 Oct 2025 10:12:00 -0400 Subject: [PATCH 067/158] Improve how we're deprecating `location` and `project_id` in `GoogleVertexLLMService`, allowing user code to (correctly) continue referring to `GoogleVertexLLMService.InputParams`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fix the slightly wrong (but so far harmless) pattern of initializing `OpenAILLMService.InputParams()` in the `GoogleVertexLLMService` if `params` wasn't providedβ€”we should be letting the superclass decide what to do if the argument isn't specified. --- src/pipecat/services/google/llm_vertex.py | 69 +++++++++++++---------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/pipecat/services/google/llm_vertex.py b/src/pipecat/services/google/llm_vertex.py index 3a3bf9ea9..cc0b8654e 100644 --- a/src/pipecat/services/google/llm_vertex.py +++ b/src/pipecat/services/google/llm_vertex.py @@ -51,32 +51,45 @@ class GoogleVertexLLMService(OpenAILLMService): class InputParams(OpenAILLMService.InputParams): """Input parameters specific to Vertex AI. - .. deprecated:: 0.0.90 - Use `OpenAILLMService.InputParams` instead and provide `location` and - `project_id` as direct arguments to `GoogleVertexLLMService.__init__()`. - Parameters: location: GCP region for Vertex AI endpoint (e.g., "us-east4"). + + .. deprecated:: 0.0.90 + Use `location` as a direct argument to + `GoogleVertexLLMService.__init__()` instead. + project_id: Google Cloud project ID. + + .. deprecated:: 0.0.90 + Use `project_id` as a direct argument to + `GoogleVertexLLMService.__init__()` instead. """ # https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations - location: str = "us-east4" - project_id: str + location: Optional[str] = None + project_id: Optional[str] = None def __init__(self, **kwargs): """Initializes the InputParams.""" import warnings with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - warnings.warn( - "GoogleVertexLLMService.InputParams is deprecated. " - "Please use OpenAILLMService.InputParams instead and provide " - "'location' and 'project_id' as direct arguments to __init__.", - DeprecationWarning, - stacklevel=2, - ) + warnings.simplefilter("always") + if "location" in kwargs and kwargs["location"] is not None: + warnings.warn( + "GoogleVertexLLMService.InputParams.location is deprecated. " + "Please provide 'location' as a direct argument to GoogleVertexLLMService.__init__() instead.", + DeprecationWarning, + stacklevel=2, + ) + + if "project_id" in kwargs and kwargs["project_id"] is not None: + warnings.warn( + "GoogleVertexLLMService.InputParams.project_id is deprecated. " + "Please provide 'project_id' as a direct argument to GoogleVertexLLMService.__init__() instead.", + DeprecationWarning, + stacklevel=2, + ) super().__init__(**kwargs) def __init__( @@ -87,7 +100,6 @@ class GoogleVertexLLMService(OpenAILLMService): model: str = "google/gemini-2.0-flash-001", location: Optional[str] = None, project_id: Optional[str] = None, - params: Optional[InputParams] = None, **kwargs, ): """Initializes the VertexLLMService. @@ -98,17 +110,13 @@ class GoogleVertexLLMService(OpenAILLMService): model: Model identifier (e.g., "google/gemini-2.0-flash-001"). location: GCP region for Vertex AI endpoint (e.g., "us-east4"). project_id: Google Cloud project ID. - params: Vertex AI input parameters including location and project. - - .. deprecated:: 0.0.90 - Use `OpenAILLMService.InputParams` instead and provide `location` - and `project_id` as direct arguments to `__init__()`. - **kwargs: Additional arguments passed to OpenAILLMService. """ - # Handle deprecated InputParams - if params is not None and isinstance(params, GoogleVertexLLMService.InputParams): - # Extract location and project_id from params if not provided directly + # Handle deprecated InputParams fields + if "params" in kwargs and isinstance(kwargs["params"], GoogleVertexLLMService.InputParams): + params = kwargs["params"] + # Extract location and project_id from params if not provided + # directly, for backward compatibility if project_id is None: project_id = params.project_id if location is None: @@ -117,15 +125,15 @@ class GoogleVertexLLMService(OpenAILLMService): params = OpenAILLMService.InputParams( **params.model_dump(exclude={"location", "project_id"}, exclude_unset=True) ) - else: - params = params or OpenAILLMService.InputParams() + kwargs["params"] = params - # Validate parameters - # NOTE: once we remove deprecated InputParams, we can update __init__() - # signature with the following: + # Validate project_id and location parameters + # NOTE: once we remove Vertex-spcific InputParams class, we can update + # __init__() signature as follows: # - location: str = "us-east4", # - project_id: str, - # For now, we need them as-is to maintain backward compatibility. + # But for now, we need them as-is to maintain proper backward + # compatibility. if project_id is None: raise ValueError("project_id is required") if location is None: @@ -138,7 +146,6 @@ class GoogleVertexLLMService(OpenAILLMService): api_key=self._api_key, base_url=base_url, model=model, - params=params, **kwargs, ) From 523e890c8cd0fb5872baa9ab981193082b7096af Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 10 Oct 2025 10:30:38 -0400 Subject: [PATCH 068/158] Fix deprecation messages pointing users to the new import paths for Gemini Live --- src/pipecat/services/gemini_multimodal_live/file_api.py | 4 ++-- src/pipecat/services/gemini_multimodal_live/gemini.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pipecat/services/gemini_multimodal_live/file_api.py b/src/pipecat/services/gemini_multimodal_live/file_api.py index ed4d6c522..367d6797a 100644 --- a/src/pipecat/services/gemini_multimodal_live/file_api.py +++ b/src/pipecat/services/gemini_multimodal_live/file_api.py @@ -12,7 +12,7 @@ this API can be referenced in Gemini generative model calls. .. deprecated:: 0.0.90 Importing GeminiFileAPI from this module is deprecated. - Import it from pipecat.services.gemini_live.file_api instead. + Import it from pipecat.services.google.gemini_live.file_api instead. """ import warnings @@ -32,7 +32,7 @@ except ModuleNotFoundError as e: warnings.warn( "Importing GeminiFileAPI from " "pipecat.services.gemini_multimodal_live.file_api is deprecated. " - "Please import it from pipecat.services.gemini_live.file_api instead.", + "Please import it from pipecat.services.google.gemini_live.file_api instead.", DeprecationWarning, stacklevel=2, ) diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 8d6847425..e56e34e9d 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -12,7 +12,7 @@ voice transcription, streaming responses, and tool usage. .. deprecated:: 0.0.90 This module is deprecated. Please use the equivalent types from - pipecat.services.gemini_live.gemini instead. Note that the new type names + pipecat.services.google.gemini_live.llm instead. Note that the new type names do not include 'Multimodal'. """ @@ -38,7 +38,7 @@ with warnings.catch_warnings(): warnings.warn( "Types in pipecat.services.gemini_multimodal_live.gemini are deprecated. " "Please use the equivalent types from " - "pipecat.services.gemini_live.gemini instead. Note that the new type " + "pipecat.services.google.gemini_live.llm instead. Note that the new type " "names do not include 'Multimodal' " "(e.g. `GeminiMultimodalLiveLLMService` is now `GeminiLiveLLMService`).", DeprecationWarning, From 4b415721e26d039db8e8736b5678b57b0b7764f8 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Oct 2025 12:25:23 -0400 Subject: [PATCH 069/158] Update release evals for OpenAI Realtime, Gemini Live Vertex; shorten 26 foundational names --- CHANGELOG.md | 10 +- ...i-multimodal-live.py => 26-gemini-live.py} | 0 ...on.py => 26a-gemini-live-transcription.py} | 0 ...py => 26b-gemini-live-function-calling.py} | 2 +- ...live-video.py => 26c-gemini-live-video.py} | 0 ...l-live-text.py => 26d-gemini-live-text.py} | 2 +- ...ch.py => 26e-gemini-live-google-search.py} | 0 ...es-api.py => 26f-gemini-live-files-api.py} | 0 ...y => 26g-gemini-live-groundingMetadata.py} | 0 ...26h-gemini-live-vertex-function-calling.py | 191 ++++++++++++++++++ .../26h-gemini-multimodal-live-vertex.py | 133 ------------ ...end.py => 26i-gemini-live-graceful-end.py} | 0 examples/foundational/README.md | 2 +- scripts/evals/run-release-evals.py | 17 +- 14 files changed, 212 insertions(+), 145 deletions(-) rename examples/foundational/{26-gemini-multimodal-live.py => 26-gemini-live.py} (100%) rename examples/foundational/{26a-gemini-multimodal-live-transcription.py => 26a-gemini-live-transcription.py} (100%) rename examples/foundational/{26b-gemini-multimodal-live-function-calling.py => 26b-gemini-live-function-calling.py} (98%) rename examples/foundational/{26c-gemini-multimodal-live-video.py => 26c-gemini-live-video.py} (100%) rename examples/foundational/{26d-gemini-multimodal-live-text.py => 26d-gemini-live-text.py} (98%) rename examples/foundational/{26e-gemini-multimodal-google-search.py => 26e-gemini-live-google-search.py} (100%) rename examples/foundational/{26f-gemini-multimodal-live-files-api.py => 26f-gemini-live-files-api.py} (100%) rename examples/foundational/{26g-gemini-multimodal-live-groundingMetadata.py => 26g-gemini-live-groundingMetadata.py} (100%) create mode 100644 examples/foundational/26h-gemini-live-vertex-function-calling.py delete mode 100644 examples/foundational/26h-gemini-multimodal-live-vertex.py rename examples/foundational/{26i-gemini-multimodal-live-graceful-end.py => 26i-gemini-live-graceful-end.py} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9763f78f2..70324c65e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1487,7 +1487,7 @@ quality and critical bugs impacting `ParallelPipelines` functionality.** - Added `session_token` parameter to `AWSNovaSonicLLMService`. - Added Gemini Multimodal Live File API for uploading, fetching, listing, and - deleting files. See `26f-gemini-multimodal-live-files-api.py` for example usage. + deleting files. See `26f-gemini-live-files-api.py` for example usage. ### Changed @@ -3493,7 +3493,7 @@ stt = DeepgramSTTService(..., live_options=LiveOptions(model="nova-2-general")) - Added the new modalities option and helper function to set Gemini output modalities. -- Added `examples/foundational/26d-gemini-multimodal-live-text.py` which is +- Added `examples/foundational/26d-gemini-live-text.py` which is using Gemini as TEXT modality and using another TTS provider for TTS process. ### Changed @@ -3680,9 +3680,9 @@ stt = DeepgramSTTService(..., live_options=LiveOptions(model="nova-2-general")) - Added new foundational examples for `GeminiMultimodalLiveLLMService`: - `26-gemini-multimodal-live.py` - - `26a-gemini-multimodal-live-transcription.py` - - `26b-gemini-multimodal-live-video.py` - - `26c-gemini-multimodal-live-video.py` + - `26a-gemini-live-transcription.py` + - `26b-gemini-live-video.py` + - `26c-gemini-live-video.py` - Added `SimliVideoService`. This is an integration for Simli AI avatars. (see https://www.simli.com) diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-live.py similarity index 100% rename from examples/foundational/26-gemini-multimodal-live.py rename to examples/foundational/26-gemini-live.py diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-live-transcription.py similarity index 100% rename from examples/foundational/26a-gemini-multimodal-live-transcription.py rename to examples/foundational/26a-gemini-live-transcription.py diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-live-function-calling.py similarity index 98% rename from examples/foundational/26b-gemini-multimodal-live-function-calling.py rename to examples/foundational/26b-gemini-live-function-calling.py index 8fecf0de6..65d159bb0 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-live-function-calling.py @@ -122,7 +122,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): required=["location"], ) search_tool = {"google_search": {}} - # KNOWN ISSUE: If using GeminiVertexMultimodalLiveLLMService, it appears + # KNOWN ISSUE: If using GeminiVertexLiveLLMService, it appears # you cannot use the "google_search" tool alongside other tools. # See https://github.com/googleapis/python-genai/issues/941. tools = ToolsSchema( diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-live-video.py similarity index 100% rename from examples/foundational/26c-gemini-multimodal-live-video.py rename to examples/foundational/26c-gemini-live-video.py diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-live-text.py similarity index 98% rename from examples/foundational/26d-gemini-multimodal-live-text.py rename to examples/foundational/26d-gemini-live-text.py index 05b792c00..42387f76d 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-live-text.py @@ -80,7 +80,7 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - # KNOWN ISSUE: If using GeminiVertexMultimodalLiveLLMService, it appears + # KNOWN ISSUE: If using GeminiVertexLiveLLMService, it appears # you cannot specify a modality other than AUDIO. llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-live-google-search.py similarity index 100% rename from examples/foundational/26e-gemini-multimodal-google-search.py rename to examples/foundational/26e-gemini-live-google-search.py diff --git a/examples/foundational/26f-gemini-multimodal-live-files-api.py b/examples/foundational/26f-gemini-live-files-api.py similarity index 100% rename from examples/foundational/26f-gemini-multimodal-live-files-api.py rename to examples/foundational/26f-gemini-live-files-api.py diff --git a/examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py b/examples/foundational/26g-gemini-live-groundingMetadata.py similarity index 100% rename from examples/foundational/26g-gemini-multimodal-live-groundingMetadata.py rename to examples/foundational/26g-gemini-live-groundingMetadata.py diff --git a/examples/foundational/26h-gemini-live-vertex-function-calling.py b/examples/foundational/26h-gemini-live-vertex-function-calling.py new file mode 100644 index 000000000..c0344a052 --- /dev/null +++ b/examples/foundational/26h-gemini-live-vertex-function-calling.py @@ -0,0 +1,191 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + + +import os +from datetime import datetime + +from dotenv import load_dotenv +from google.genai.types import HttpOptions +from loguru import logger + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import AdapterType, ToolsSchema +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import LLMRunFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService +from pipecat.services.google.gemini_live.llm_vertex import GeminiLiveVertexLLMService +from pipecat.services.llm_service import FunctionCallParams +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + + +async def fetch_weather_from_api(params: FunctionCallParams): + temperature = 75 if params.arguments["format"] == "fahrenheit" else 24 + await params.result_callback( + { + "conditions": "nice", + "temperature": temperature, + "format": params.arguments["format"], + "timestamp": datetime.now().strftime("%Y%m%d_%H%M%S"), + } + ) + + +async def fetch_restaurant_recommendation(params: FunctionCallParams): + await params.result_callback({"name": "The Golden Dragon"}) + + +system_instruction = """ +You are a helpful assistant who can answer questions and use tools. + +You have three tools available to you: +1. get_current_weather: Use this tool to get the current weather in a specific location. +2. get_restaurant_recommendation: Use this tool to get a restaurant recommendation in a specific location. +""" + + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + weather_function = FunctionSchema( + name="get_current_weather", + description="Get the current weather", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "format": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "The temperature unit to use. Infer this from the user's location.", + }, + }, + required=["location", "format"], + ) + restaurant_function = FunctionSchema( + name="get_restaurant_recommendation", + description="Get a restaurant recommendation", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + }, + required=["location"], + ) + # KNOWN ISSUE: If using GeminiVertexLiveLLMService, it appears + # you cannot use the "google_search" tool alongside other tools. + # See https://github.com/googleapis/python-genai/issues/941. + tools = ToolsSchema(standard_tools=[weather_function, restaurant_function]) + + llm = GeminiLiveVertexLLMService( + credentials=os.getenv("GOOGLE_VERTEX_TEST_CREDENTIALS"), + project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), + location=os.getenv("GOOGLE_CLOUD_LOCATION"), + system_instruction=system_instruction, + voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck + tools=tools, + ) + + llm.register_function("get_current_weather", fetch_weather_from_api) + llm.register_function("get_restaurant_recommendation", fetch_restaurant_recommendation) + + context = OpenAILLMContext( + [{"role": "user", "content": "Say hello."}], + ) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), + context_aggregator.user(), + llm, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/examples/foundational/26h-gemini-multimodal-live-vertex.py b/examples/foundational/26h-gemini-multimodal-live-vertex.py deleted file mode 100644 index 79bfac531..000000000 --- a/examples/foundational/26h-gemini-multimodal-live-vertex.py +++ /dev/null @@ -1,133 +0,0 @@ -# -# Copyright (c) 2024–2025, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -import os - -from dotenv import load_dotenv -from loguru import logger - -from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams -from pipecat.frames.frames import LLMMessagesAppendFrame -from pipecat.pipeline.pipeline import Pipeline -from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.runner.types import RunnerArguments -from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.llm_vertex import GeminiLiveVertexLLMService -from pipecat.transports.base_transport import BaseTransport, TransportParams -from pipecat.transports.daily.transport import DailyParams -from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams - -# Load environment variables -load_dotenv(override=True) - - -# We store functions so objects (e.g. SileroVADAnalyzer) don't get -# instantiated. The function will be called when the desired transport gets -# selected. -transport_params = { - "daily": lambda: DailyParams( - audio_in_enabled=True, - audio_out_enabled=True, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), - "twilio": lambda: FastAPIWebsocketParams( - audio_in_enabled=True, - audio_out_enabled=True, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), - "webrtc": lambda: TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), -} - - -async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): - logger.info(f"Starting bot") - - # Create the Gemini Vertex Multimodal Live LLM service - system_instruction = f""" - You are a helpful AI assistant. - Your goal is to demonstrate your capabilities in a helpful and engaging way. - Your output will be converted to audio so don't include special characters in your answers. - Respond to what the user said in a creative and helpful way. - """ - - llm = GeminiLiveVertexLLMService( - credentials=os.getenv("GOOGLE_VERTEX_TEST_CREDENTIALS"), - project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), - location=os.getenv("GOOGLE_CLOUD_LOCATION"), - system_instruction=system_instruction, - voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck - ) - - # Build the pipeline - pipeline = Pipeline( - [ - transport.input(), - llm, - transport.output(), - ] - ) - - # Configure the pipeline task - task = PipelineTask( - pipeline, - params=PipelineParams( - enable_metrics=True, - enable_usage_metrics=True, - ), - idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, - ) - - # Handle client connection event - @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected") - # Kick off the conversation. - await task.queue_frames( - [ - LLMMessagesAppendFrame( - messages=[ - { - "role": "user", - "content": f"Greet the user and introduce yourself.", - } - ] - ) - ] - ) - - # Handle client disconnection events - @transport.event_handler("on_client_disconnected") - async def on_client_disconnected(transport, client): - logger.info(f"Client disconnected") - await task.cancel() - - # Run the pipeline - runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) - await runner.run(task) - - -async def bot(runner_args: RunnerArguments): - """Main bot entry point compatible with Pipecat Cloud.""" - transport = await create_transport(runner_args, transport_params) - await run_bot(transport, runner_args) - - -if __name__ == "__main__": - from pipecat.runner.run import main - - main() diff --git a/examples/foundational/26i-gemini-multimodal-live-graceful-end.py b/examples/foundational/26i-gemini-live-graceful-end.py similarity index 100% rename from examples/foundational/26i-gemini-multimodal-live-graceful-end.py rename to examples/foundational/26i-gemini-live-graceful-end.py diff --git a/examples/foundational/README.md b/examples/foundational/README.md index 9a6c26005..c1ead3ece 100644 --- a/examples/foundational/README.md +++ b/examples/foundational/README.md @@ -105,7 +105,7 @@ uv run 07-interruptible.py -t twilio -x NGROK_HOST_NAME ### Vision & Multimodal - **[12a-describe-video-gemini-flash.py](./12a-describe-video-gemini-flash.py)**: Bot describes user's video (Video input, Multimodal LLMs) -- **[26c-gemini-multimodal-live-video.py](./26c-gemini-multimodal-live-video.py)**: Gemini with video input (Streaming video, Function calls) +- **[26c-gemini-live-video.py](./26c-gemini-live-video.py)**: Gemini with video input (Streaming video, Function calls) ### Voice & Language diff --git a/scripts/evals/run-release-evals.py b/scripts/evals/run-release-evals.py index 206cf848e..44cb69aed 100644 --- a/scripts/evals/run-release-evals.py +++ b/scripts/evals/run-release-evals.py @@ -147,7 +147,10 @@ TESTS_15 = [ ] TESTS_19 = [ + ("19-openai-realtime.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), ("19-openai-realtime-beta.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), + # OpenAI Realtime not released on Azure yet + # ("19a-azure-realtime.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), ("19a-azure-realtime-beta.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), ("19b-openai-realtime-text.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), ("19b-openai-realtime-beta-text.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), @@ -160,18 +163,18 @@ TESTS_21 = [ TESTS_26 = [ ("26-gemini-multimodal-live.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), ( - "26a-gemini-multimodal-live-transcription.py", + "26a-gemini-live-transcription.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST, ), ( - "26b-gemini-multimodal-live-function-calling.py", + "26b-gemini-live-function-calling.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST, ), - ("26c-gemini-multimodal-live-video.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), + ("26c-gemini-live-video.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), ( "26e-gemini-multimodal-google-search.py", PROMPT_ONLINE_SEARCH, @@ -179,7 +182,13 @@ TESTS_26 = [ BOT_SPEAKS_FIRST, ), # Currently not working. - # ("26d-gemini-multimodal-live-text.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), + # ("26d-gemini-live-text.py", PROMPT_SIMPLE_MATH, EVAL_SIMPLE_MATH, BOT_SPEAKS_FIRST), + ( + "26h-gemini-live-vertex-function-calling.py", + PROMPT_WEATHER, + EVAL_WEATHER, + BOT_SPEAKS_FIRST, + ), ] TESTS_27 = [ From 21d610cd30b298c473f793ded5fc76af2bef4511 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Oct 2025 12:43:31 -0400 Subject: [PATCH 070/158] Docs fixes for 0.0.90 release --- docs/api/conf.py | 1 + src/pipecat/services/hume/tts.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/api/conf.py b/docs/api/conf.py index a3062b612..8946a34d3 100644 --- a/docs/api/conf.py +++ b/docs/api/conf.py @@ -50,6 +50,7 @@ autodoc_mock_imports = [ # Krisp - has build issues on some platforms "pipecat_ai_krisp", "krisp", + "krisp_audio", # System-specific GUI libraries "_tkinter", "tkinter", diff --git a/src/pipecat/services/hume/tts.py b/src/pipecat/services/hume/tts.py index 0629a8909..2701c5d05 100644 --- a/src/pipecat/services/hume/tts.py +++ b/src/pipecat/services/hume/tts.py @@ -42,7 +42,7 @@ class HumeTTSService(TTSService): """Hume Octave Text-to-Speech service. Streams PCM audio via Hume's HTTP output streaming (JSON chunks) endpoint - using the Python SDK and emits `TTSAudioRawFrame`s suitable for Pipecat transports. + using the Python SDK and emits ``TTSAudioRawFrame`` frames suitable for Pipecat transports. Supported features: @@ -78,7 +78,7 @@ class HumeTTSService(TTSService): Args: api_key: Hume API key. If omitted, reads the ``HUME_API_KEY`` environment variable. - voice_id: ID of the voice to use (ID-only; names are not supported here). + voice_id: ID of the voice to use. Only voice IDs are supported; voice names are not. params: Optional synthesis controls (acting instructions, speed, trailing silence). sample_rate: Output sample rate for emitted PCM frames. Defaults to 48_000 (Hume). **kwargs: Additional arguments passed to the parent class. From 1c25b6fb72c079fae5ecfb186516a3ab157dbf0a Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 10 Oct 2025 12:56:37 -0400 Subject: [PATCH 071/158] `location` should not be optional when using Google Vertex. Also, update `GoogleVertexLLMService` initialization pattern in the example file. --- .../foundational/14p-function-calling-gemini-vertex-ai.py | 5 ++--- src/pipecat/services/google/gemini_live/llm_vertex.py | 2 +- src/pipecat/services/google/llm_vertex.py | 4 ++++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py index f9ad264eb..cba5eee60 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -76,9 +76,8 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleVertexLLMService( credentials=os.getenv("GOOGLE_VERTEX_TEST_CREDENTIALS"), - params=GoogleVertexLLMService.InputParams( - project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), - ), + project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), + location=os.getenv("GOOGLE_CLOUD_LOCATION"), ) # You can aslo register a function_name of None to get all functions # sent to the same callback with an additional function_name parameter. diff --git a/src/pipecat/services/google/gemini_live/llm_vertex.py b/src/pipecat/services/google/gemini_live/llm_vertex.py index 4f1b57f4c..a38154755 100644 --- a/src/pipecat/services/google/gemini_live/llm_vertex.py +++ b/src/pipecat/services/google/gemini_live/llm_vertex.py @@ -49,7 +49,7 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): *, credentials: Optional[str] = None, credentials_path: Optional[str] = None, - location: str = "us-east4", + location: str, project_id: str, model="google/gemini-2.0-flash-live-preview-04-09", voice_id: str = "Charon", diff --git a/src/pipecat/services/google/llm_vertex.py b/src/pipecat/services/google/llm_vertex.py index cc0b8654e..49adb2e9b 100644 --- a/src/pipecat/services/google/llm_vertex.py +++ b/src/pipecat/services/google/llm_vertex.py @@ -137,6 +137,10 @@ class GoogleVertexLLMService(OpenAILLMService): if project_id is None: raise ValueError("project_id is required") if location is None: + # If location is not provided, default to "us-east4". + # Note: this is legacy behavior; ideally location would be + # required. + logger.warning("location is not provided. Defaulting to 'us-east4'.") location = "us-east4" # Default location if not provided base_url = self._get_base_url(location, project_id) From 55014bdd771fe703efa112ccddc3f833f713b856 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 10 Oct 2025 13:18:03 -0400 Subject: [PATCH 072/158] Update a Google Vertex disclaimer for accuracy --- examples/foundational/26d-gemini-live-text.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/foundational/26d-gemini-live-text.py b/examples/foundational/26d-gemini-live-text.py index 42387f76d..062c0231b 100644 --- a/examples/foundational/26d-gemini-live-text.py +++ b/examples/foundational/26d-gemini-live-text.py @@ -80,8 +80,10 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - # KNOWN ISSUE: If using GeminiVertexLiveLLMService, it appears - # you cannot specify a modality other than AUDIO. + # KNOWN ISSUE: If using GeminiLiveVertexLLMService, you cannot specify a + # modality other than AUDIO (at least not if using the service's default + # model, which is a native audio model: + # https://cloud.google.com/vertex-ai/generative-ai/docs/live-api/tools#native-audio). llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), system_instruction=SYSTEM_INSTRUCTION, From 502e7e42a7b2cc5af93bc29402435153121fa755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Fri, 10 Oct 2025 08:33:21 -0700 Subject: [PATCH 073/158] update CHANGELOG for 0.0.90 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70324c65e..e92361569 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.0.90] - 2025-10-10 ### Added From 0473556992f7142fc864b4c44a6455122b59d609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Fri, 10 Oct 2025 12:09:20 -0700 Subject: [PATCH 074/158] tts: fix RimeHttpTTSService/PiperTTSService 16-bit audio frames alignment --- CHANGELOG.md | 8 ++++++ src/pipecat/services/piper/tts.py | 16 +++++------ src/pipecat/services/rime/tts.py | 14 +++++----- src/pipecat/services/tts_service.py | 42 ++++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e92361569..797ca06ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed + +- Fixed an issue where `RimeHttpTTSService` and `PiperTTSService` could generate + incorrectly 16-bit aligned audio frames, potentially leading to internal + errors or static audio. + ## [0.0.90] - 2025-10-10 ### Added diff --git a/src/pipecat/services/piper/tts.py b/src/pipecat/services/piper/tts.py index d5b663c77..fa43a720c 100644 --- a/src/pipecat/services/piper/tts.py +++ b/src/pipecat/services/piper/tts.py @@ -14,7 +14,6 @@ from loguru import logger from pipecat.frames.frames import ( ErrorFrame, Frame, - TTSAudioRawFrame, TTSStartedFrame, TTSStoppedFrame, ) @@ -99,16 +98,15 @@ class PiperTTSService(TTSService): await self.start_tts_usage_metrics(text) + yield TTSStartedFrame() + CHUNK_SIZE = self.chunk_size - yield TTSStartedFrame() - async for chunk in response.content.iter_chunked(CHUNK_SIZE): - # remove wav header if present - if chunk.startswith(b"RIFF"): - chunk = chunk[44:] - if len(chunk) > 0: - await self.stop_ttfb_metrics() - yield TTSAudioRawFrame(chunk, self.sample_rate, 1) + async for frame in self._stream_audio_frames_from_iterator( + response.content.iter_chunked(CHUNK_SIZE), strip_wav_header=True + ): + await self.stop_ttfb_metrics() + yield frame except Exception as e: logger.error(f"Error in run_tts: {e}") yield ErrorFrame(error=str(e)) diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index 917716545..1ac829ebd 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -553,15 +553,13 @@ class RimeHttpTTSService(TTSService): CHUNK_SIZE = self.chunk_size - async for chunk in response.content.iter_chunked(CHUNK_SIZE): - if need_to_strip_wav_header and chunk.startswith(b"RIFF"): - chunk = chunk[44:] - need_to_strip_wav_header = False + async for frame in self._stream_audio_frames_from_iterator( + response.content.iter_chunked(CHUNK_SIZE), + strip_wav_header=need_to_strip_wav_header, + ): + await self.stop_ttfb_metrics() + yield frame - if len(chunk) > 0: - await self.stop_ttfb_metrics() - frame = TTSAudioRawFrame(chunk, self.sample_rate, 1) - yield frame except Exception as e: logger.exception(f"Error generating TTS: {e}") yield ErrorFrame(error=f"Rime TTS error: {str(e)}") diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index 02b80b609..a60b50818 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -8,7 +8,17 @@ import asyncio from abc import abstractmethod -from typing import Any, AsyncGenerator, Dict, List, Mapping, Optional, Sequence, Tuple +from typing import ( + Any, + AsyncGenerator, + AsyncIterator, + Dict, + List, + Mapping, + Optional, + Sequence, + Tuple, +) from loguru import logger @@ -374,6 +384,36 @@ class TTSService(AIService): ): await self._stop_frame_queue.put(frame) + async def _stream_audio_frames_from_iterator( + self, iterator: AsyncIterator[bytes], *, strip_wav_header: bool + ) -> AsyncGenerator[Frame, None]: + buffer = bytearray() + need_to_strip_wav_header = strip_wav_header + async for chunk in iterator: + if need_to_strip_wav_header and chunk.startswith(b"RIFF"): + chunk = chunk[44:] + need_to_strip_wav_header = False + + # Append to current buffer. + buffer.extend(chunk) + + # Round to nearest even number. + aligned_length = len(buffer) & ~1 # 111111111...11110 + if aligned_length > 0: + aligned_chunk = buffer[:aligned_length] + buffer = buffer[aligned_length:] # keep any leftover byte + + if len(aligned_chunk) > 0: + frame = TTSAudioRawFrame(bytes(aligned_chunk), self.sample_rate, 1) + yield frame + + if len(buffer) > 0: + # Make sure we don't need an extra padding byte. + if len(buffer) % 2 == 1: + buffer.extend(b"\x00") + frame = TTSAudioRawFrame(bytes(buffer), self.sample_rate, 1) + yield frame + async def _handle_interruption(self, frame: InterruptionFrame, direction: FrameDirection): self._processing_text = False await self._text_aggregator.handle_interruption() From 16e9093d5a25a435eda743e542771a4475c1a1d5 Mon Sep 17 00:00:00 2001 From: makosst <191634637+makosst@users.noreply.github.com> Date: Sat, 11 Oct 2025 09:20:17 -0700 Subject: [PATCH 075/158] Added Manta Graph to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3eb9bf346..50cc72791 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![PyPI](https://img.shields.io/pypi/v/pipecat-ai)](https://pypi.org/project/pipecat-ai) ![Tests](https://github.com/pipecat-ai/pipecat/actions/workflows/tests.yaml/badge.svg) [![codecov](https://codecov.io/gh/pipecat-ai/pipecat/graph/badge.svg?token=LNVUIVO4Y9)](https://codecov.io/gh/pipecat-ai/pipecat) [![Docs](https://img.shields.io/badge/Documentation-blue)](https://docs.pipecat.ai) [![Discord](https://img.shields.io/discord/1239284677165056021)](https://discord.gg/pipecat) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/pipecat-ai/pipecat) +[![](https://getmanta.ai/api/badges?text=Manta%20Graph&link=manta)](https://getmanta.ai/pipecat) # πŸŽ™οΈ Pipecat: Real-Time Voice & Multimodal AI Agents From 21d8d148b8adebfaaf5986fc7058a85aa4b8b4ec Mon Sep 17 00:00:00 2001 From: Pyae Sone Myo <66440980+Rickaym@users.noreply.github.com> Date: Sun, 12 Oct 2025 22:10:11 +0630 Subject: [PATCH 076/158] fix: handle partial words across alignment chunks gracefully --- src/pipecat/services/elevenlabs/tts.py | 60 ++++++++++++++++---------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 3d6d5bd4c..4c91c4f38 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -168,16 +168,24 @@ def build_elevenlabs_voice_settings( def calculate_word_times( - alignment_info: Mapping[str, Any], cumulative_time: float -) -> List[Tuple[str, float]]: + alignment_info: Mapping[str, Any], + cumulative_time: float, + partial_word: str = "", + partial_word_start_time: float = 0.0, +) -> tuple[List[Tuple[str, float]], str, float]: """Calculate word timestamps from character alignment information. Args: alignment_info: Character alignment data from ElevenLabs API. cumulative_time: Base time offset for this chunk. + partial_word: Partial word carried over from previous chunk. + partial_word_start_time: Start time of the partial word. Returns: - List of (word, timestamp) tuples. + Tuple of (word_times, new_partial_word, new_partial_word_start_time): + - word_times: List of (word, timestamp) tuples for complete words + - new_partial_word: Incomplete word at end of chunk (empty if chunk ends with space) + - new_partial_word_start_time: Start time of the incomplete word """ chars = alignment_info["chars"] char_start_times_ms = alignment_info["charStartTimesMs"] @@ -186,41 +194,37 @@ def calculate_word_times( logger.error( f"calculate_word_times: length mismatch - chars={len(chars)}, times={len(char_start_times_ms)}" ) - return [] + return ([], partial_word, partial_word_start_time) # Build words and track their start positions words = [] - word_start_indices = [] - current_word = "" - word_start_index = None + word_start_times = [] + current_word = partial_word # Start with any partial word from previous chunk + word_start_time = partial_word_start_time if partial_word else None for i, char in enumerate(chars): if char == " ": # End of current word if current_word: # Only add non-empty words words.append(current_word) - word_start_indices.append(word_start_index) + word_start_times.append(word_start_time) current_word = "" - word_start_index = None + word_start_time = None else: # Building a word - if word_start_index is None: # First character of new word - word_start_index = i + if word_start_time is None: # First character of new word + # Convert from milliseconds to seconds and add cumulative offset + word_start_time = cumulative_time + (char_start_times_ms[i] / 1000.0) current_word += char - # Handle the last word if there's no trailing space - if current_word and word_start_index is not None: - words.append(current_word) - word_start_indices.append(word_start_index) + # Build result for complete words + word_times = list(zip(words, word_start_times)) - # Calculate timestamps for each word - word_times = [] - for word, start_idx in zip(words, word_start_indices): - # Convert from milliseconds to seconds and add cumulative offset - start_time_seconds = cumulative_time + (char_start_times_ms[start_idx] / 1000.0) - word_times.append((word, start_time_seconds)) + # Return any incomplete word at the end of this chunk + new_partial_word = current_word if current_word else "" + new_partial_word_start_time = word_start_time if word_start_time is not None else 0.0 - return word_times + return (word_times, new_partial_word, new_partial_word_start_time) class ElevenLabsTTSService(AudioContextWordTTSService): @@ -332,6 +336,9 @@ class ElevenLabsTTSService(AudioContextWordTTSService): # there's an interruption or TTSStoppedFrame. self._started = False self._cumulative_time = 0 + # Track partial words that span across alignment chunks + self._partial_word = "" + self._partial_word_start_time = 0.0 # Context management for v1 multi API self._context_id = None @@ -609,7 +616,12 @@ class ElevenLabsTTSService(AudioContextWordTTSService): if msg.get("alignment"): alignment = msg["alignment"] - word_times = calculate_word_times(alignment, self._cumulative_time) + word_times, self._partial_word, self._partial_word_start_time = calculate_word_times( + alignment, + self._cumulative_time, + self._partial_word, + self._partial_word_start_time, + ) if word_times: await self.add_word_timestamps(word_times) @@ -683,6 +695,8 @@ class ElevenLabsTTSService(AudioContextWordTTSService): yield TTSStartedFrame() self._started = True self._cumulative_time = 0 + self._partial_word = "" + self._partial_word_start_time = 0.0 # If a context ID does not exist, create a new one and # register it. If an ID exists, that means the Pipeline is # configured for allow_interruptions=False, so continue From 9babfe9fd9cbded098d32fc787644db5089653fc Mon Sep 17 00:00:00 2001 From: shreyas-sarvam Date: Mon, 13 Oct 2025 08:54:29 +0530 Subject: [PATCH 077/158] refactor: Improve code reability and replace deprecated interruption frames --- src/pipecat/services/sarvam/tts.py | 126 ++++++++++++++--------------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index 5bacffdf0..ae48e1ee5 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -20,9 +20,9 @@ from pipecat.frames.frames import ( EndFrame, ErrorFrame, Frame, + InterruptionFrame, LLMFullResponseEndFrame, StartFrame, - StartInterruptionFrame, TTSAudioRawFrame, TTSStartedFrame, TTSStoppedFrame, @@ -121,6 +121,9 @@ class SarvamHttpTTSService(TTSService): default=0.6, ge=0.01, le=1.0, + description="Controls the randomness of the output for bulbul v3 beta. " + "Lower values make the output more focused and deterministic, while " + "higher values make it more random. Range: 0.01 to 1.0. Default: 0.6.", ) def __init__( @@ -155,31 +158,31 @@ class SarvamHttpTTSService(TTSService): self._base_url = base_url self._session = aiohttp_session - if model == "bulbul:v3-beta" or model == "bulbul:v3": - # For bulbul v3 beta, exclude pace and loudness parameters - self._settings = { - "language": ( - self.language_to_service_language(params.language) - if params.language - else "en-IN" - ), - "enable_preprocessing": params.enable_preprocessing, - "temperature": getattr(params, "temperature", 0.6), - "model": model, # Include model in settings for v3 beta - } + # Build base settings common to all models + self._settings = { + "language": ( + self.language_to_service_language(params.language) if params.language else "en-IN" + ), + "enable_preprocessing": params.enable_preprocessing, + } + + # Add model-specific parameters + if model in ("bulbul:v3-beta", "bulbul:v3"): + self._settings.update( + { + "temperature": getattr(params, "temperature", 0.6), + "model": model, + } + ) else: - # For bulbul v2, include all parameters including pace and loudness - self._settings = { - "language": ( - self.language_to_service_language(params.language) - if params.language - else "en-IN" - ), - "pitch": params.pitch, - "pace": params.pace, - "loudness": params.loudness, - "enable_preprocessing": params.enable_preprocessing, - } + self._settings.update( + { + "pitch": params.pitch, + "pace": params.pace, + "loudness": params.loudness, + "model": model, + } + ) self.set_model_name(model) self.set_voice(voice_id) @@ -310,7 +313,7 @@ class SarvamTTSService(InterruptibleTTSService): ) # For bulbul v3 beta with any speaker and temperature: - # Note: pace and loudness are not supported for bulbul v3 beta + # Note: pace and loudness are not supported for bulbul v3 and bulbul v3 beta tts_v3 = SarvamTTSService( api_key="your-api-key", voice_id="speaker_name", @@ -386,8 +389,10 @@ class SarvamTTSService(InterruptibleTTSService): voice_id: Voice identifier for synthesis (default "anushka"). url: WebSocket URL for connecting to the TTS backend (default production URL). aiohttp_session: Optional shared aiohttp session. To maintain backward compatibility. + .. deprecated:: 0.0.81 aiohttp_session is no longer used. This parameter will be removed in a future version. + aggregate_sentences: Whether to merge multiple sentences into one audio chunk (default True). sample_rate: Desired sample rate for the output audio in Hz (overrides default if set). params: Optional input parameters to override global configuration. @@ -423,44 +428,37 @@ class SarvamTTSService(InterruptibleTTSService): self._api_key = api_key self.set_model_name(model) self.set_voice(voice_id) - # Configuration parameters - if model == "bulbul:v3-beta" or model == "bulbul:v3": - # For bulbul v3 beta, exclude pace and loudness parameters - self._settings = { - "target_language_code": ( - self.language_to_service_language(params.language) - if params.language - else "en-IN" - ), - "speaker": voice_id, - "speech_sample_rate": 0, - "enable_preprocessing": params.enable_preprocessing, - "min_buffer_size": params.min_buffer_size, - "max_chunk_length": params.max_chunk_length, - "output_audio_codec": params.output_audio_codec, - "output_audio_bitrate": params.output_audio_bitrate, - "temperature": getattr(params, "temperature", 0.6), - "model": model, # Include model in settings for v3 beta - } + # Build base settings common to all models + self._settings = { + "target_language_code": ( + self.language_to_service_language(params.language) if params.language else "en-IN" + ), + "speaker": voice_id, + "speech_sample_rate": 0, + "enable_preprocessing": params.enable_preprocessing, + "min_buffer_size": params.min_buffer_size, + "max_chunk_length": params.max_chunk_length, + "output_audio_codec": params.output_audio_codec, + "output_audio_bitrate": params.output_audio_bitrate, + } + + # Add model-specific parameters + if model in ("bulbul:v3-beta", "bulbul:v3"): + self._settings.update( + { + "temperature": getattr(params, "temperature", 0.6), + "model": model, + } + ) else: - # For bulbul v2, include all parameters including pace and loudness - self._settings = { - "target_language_code": ( - self.language_to_service_language(params.language) - if params.language - else "en-IN" - ), - "pitch": params.pitch, - "pace": params.pace, - "speaker": voice_id, - "loudness": params.loudness, - "speech_sample_rate": 0, - "enable_preprocessing": params.enable_preprocessing, - "min_buffer_size": params.min_buffer_size, - "max_chunk_length": params.max_chunk_length, - "output_audio_codec": params.output_audio_codec, - "output_audio_bitrate": params.output_audio_bitrate, - } + self._settings.update( + { + "pitch": params.pitch, + "pace": params.pace, + "loudness": params.loudness, + "model": model, + } + ) self._started = False self._receive_task = None @@ -529,7 +527,7 @@ class SarvamTTSService(InterruptibleTTSService): direction: The direction to push the frame. """ await super().push_frame(frame, direction) - if isinstance(frame, (TTSStoppedFrame, StartInterruptionFrame)): + if isinstance(frame, (TTSStoppedFrame, InterruptionFrame)): self._started = False async def process_frame(self, frame: Frame, direction: FrameDirection): From 9ddec0f8b491cdbe4513a494449eaaa9734dde57 Mon Sep 17 00:00:00 2001 From: nbyers-altira Date: Mon, 13 Oct 2025 10:44:25 -0400 Subject: [PATCH 078/158] is_final is not part of the segmented _handle_transcription function signature --- src/pipecat/services/riva/stt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/riva/stt.py b/src/pipecat/services/riva/stt.py index d00eb4f42..f077355f4 100644 --- a/src/pipecat/services/riva/stt.py +++ b/src/pipecat/services/riva/stt.py @@ -647,7 +647,7 @@ class RivaSegmentedSTTService(SegmentedSTTService): ) transcription_found = True - await self._handle_transcription(text, True, self._language_enum) + await self._handle_transcription(text, self._language_enum) if not transcription_found: logger.debug("No transcription results found in Riva response") From cc66ac14f1699357bc2e39be9adac376721bbcac Mon Sep 17 00:00:00 2001 From: nbyers-altira Date: Mon, 13 Oct 2025 10:48:41 -0400 Subject: [PATCH 079/158] add is_final to segmented func. sig. instead so tracing is consistent --- src/pipecat/services/riva/stt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/riva/stt.py b/src/pipecat/services/riva/stt.py index f077355f4..ccd166536 100644 --- a/src/pipecat/services/riva/stt.py +++ b/src/pipecat/services/riva/stt.py @@ -583,7 +583,7 @@ class RivaSegmentedSTTService(SegmentedSTTService): self._config.language_code = self._language @traced_stt - async def _handle_transcription(self, transcript: str, language: Optional[Language] = None): + async def _handle_transcription(self, transcript: str, is_final: bool, language: Optional[Language] = None): """Handle a transcription result with tracing.""" pass @@ -647,7 +647,7 @@ class RivaSegmentedSTTService(SegmentedSTTService): ) transcription_found = True - await self._handle_transcription(text, self._language_enum) + await self._handle_transcription(text, True, self._language_enum) if not transcription_found: logger.debug("No transcription results found in Riva response") From d9580f72a92601fd09e9b4a8f9b9e507a5b40f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 13 Oct 2025 18:29:19 -0700 Subject: [PATCH 080/158] runner: allow subdirectories in --folder --- CHANGELOG.md | 5 +++++ src/pipecat/runner/run.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 797ca06ab..424d6a420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- The runner `--folder` argument now supports downloading files from + subdirectories. + ### Fixed - Fixed an issue where `RimeHttpTTSService` and `PiperTTSService` could generate diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index fd1a8a952..53b39f4e3 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -217,7 +217,7 @@ def _setup_webrtc_routes( """Redirect root requests to client interface.""" return RedirectResponse(url="/client/") - @app.get("/files/{filename}") + @app.get("/files/{filename:path}") async def download_file(filename: str): """Handle file downloads.""" if not folder: From 3b751322d3d18832b4fca299624eae710c7ed2af Mon Sep 17 00:00:00 2001 From: Pyae Sone Myo <66440980+Rickaym@users.noreply.github.com> Date: Tue, 14 Oct 2025 23:04:09 +0630 Subject: [PATCH 081/158] fix: add interruption reset for partial word states --- src/pipecat/services/elevenlabs/tts.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 4c91c4f38..9e4154e4d 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -577,6 +577,8 @@ class ElevenLabsTTSService(AudioContextWordTTSService): logger.error(f"Error closing context on interruption: {e}") self._context_id = None self._started = False + self._partial_word = "" + self._partial_word_start_time = 0.0 async def _receive_messages(self): """Handle incoming WebSocket messages from ElevenLabs.""" @@ -616,11 +618,13 @@ class ElevenLabsTTSService(AudioContextWordTTSService): if msg.get("alignment"): alignment = msg["alignment"] - word_times, self._partial_word, self._partial_word_start_time = calculate_word_times( - alignment, - self._cumulative_time, - self._partial_word, - self._partial_word_start_time, + word_times, self._partial_word, self._partial_word_start_time = ( + calculate_word_times( + alignment, + self._cumulative_time, + self._partial_word, + self._partial_word_start_time, + ) ) if word_times: From b6b09975534b9f645b35a809f602aa6f1358a15b Mon Sep 17 00:00:00 2001 From: Pyae Sone Myo <66440980+Rickaym@users.noreply.github.com> Date: Tue, 14 Oct 2025 23:06:13 +0630 Subject: [PATCH 082/158] fix: add support for partial words --- src/pipecat/services/elevenlabs/tts.py | 36 +++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 9e4154e4d..1544e94be 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -827,6 +827,10 @@ class ElevenLabsHttpTTSService(WordTTSService): # Store previous text for context within a turn self._previous_text = "" + # Track partial words that span across alignment chunks + self._partial_word = "" + self._partial_word_start_time = 0.0 + def language_to_service_language(self, language: Language) -> Optional[str]: """Convert pipecat Language to ElevenLabs language code. @@ -854,6 +858,8 @@ class ElevenLabsHttpTTSService(WordTTSService): self._cumulative_time = 0 self._started = False self._previous_text = "" + self._partial_word = "" + self._partial_word_start_time = 0.0 logger.debug(f"{self}: Reset internal state") async def start(self, frame: StartFrame): @@ -888,11 +894,13 @@ class ElevenLabsHttpTTSService(WordTTSService): def calculate_word_times(self, alignment_info: Mapping[str, Any]) -> List[Tuple[str, float]]: """Calculate word timing from character alignment data. + This method handles partial words that may span across multiple alignment chunks. + Args: alignment_info: Character timing data from ElevenLabs. Returns: - List of (word, timestamp) pairs. + List of (word, timestamp) pairs for complete words in this chunk. Example input data:: @@ -918,30 +926,28 @@ class ElevenLabsHttpTTSService(WordTTSService): # Build the words and find their start times words = [] word_start_times = [] - current_word = "" - first_char_idx = -1 + # Start with any partial word from previous chunk + current_word = self._partial_word + word_start_time = self._partial_word_start_time if self._partial_word else None for i, char in enumerate(chars): if char == " ": if current_word: # Only add non-empty words words.append(current_word) - # Use time of the first character of the word, offset by cumulative time - word_start_times.append( - self._cumulative_time + char_start_times[first_char_idx] - ) + word_start_times.append(word_start_time) current_word = "" - first_char_idx = -1 + word_start_time = None else: - if not current_word: # This is the first character of a new word - first_char_idx = i + if word_start_time is None: # First character of a new word + # Use time of the first character of the word, offset by cumulative time + word_start_time = self._cumulative_time + char_start_times[i] current_word += char - # Don't forget the last word if there's no trailing space - if current_word and first_char_idx >= 0: - words.append(current_word) - word_start_times.append(self._cumulative_time + char_start_times[first_char_idx]) + # Store any incomplete word at the end of this chunk + self._partial_word = current_word if current_word else "" + self._partial_word_start_time = word_start_time if word_start_time is not None else 0.0 - # Create word-time pairs + # Create word-time pairs for complete words only word_times = list(zip(words, word_start_times)) return word_times From be2858bfbb3df8b814bd4d73ed7316e1f35e4aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 14 Oct 2025 14:09:45 -0700 Subject: [PATCH 083/158] CartesiaSTTService: inherit from WebsocketSTTService --- CHANGELOG.md | 4 + src/pipecat/services/cartesia/stt.py | 145 ++++++++++++++------------- src/pipecat/services/cartesia/tts.py | 2 +- 3 files changed, 80 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 424d6a420..dd3bfa3c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The runner `--folder` argument now supports downloading files from subdirectories. +### Changed + +- `CartesiaSTTService` now inherits from `WebsocketSTTService`. + ### Fixed - Fixed an issue where `RimeHttpTTSService` and `PiperTTSService` could generate diff --git a/src/pipecat/services/cartesia/stt.py b/src/pipecat/services/cartesia/stt.py index 5412c422c..97a4f7127 100644 --- a/src/pipecat/services/cartesia/stt.py +++ b/src/pipecat/services/cartesia/stt.py @@ -28,13 +28,12 @@ from pipecat.frames.frames import ( UserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.stt_service import STTService +from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language from pipecat.utils.time import time_now_iso8601 from pipecat.utils.tracing.service_decorators import traced_stt try: - import websockets from websockets.asyncio.client import connect as websocket_connect from websockets.protocol import State except ModuleNotFoundError as e: @@ -124,7 +123,7 @@ class CartesiaLiveOptions: return cls(**json.loads(json_str)) -class CartesiaSTTService(STTService): +class CartesiaSTTService(WebsocketSTTService): """Speech-to-text service using Cartesia Live API. Provides real-time speech transcription through WebSocket connection @@ -176,8 +175,7 @@ class CartesiaSTTService(STTService): self.set_model_name(merged_options.model) self._api_key = api_key self._base_url = base_url or "api.cartesia.ai" - self._connection = None - self._receiver_task = None + self._receive_task = None def can_generate_metrics(self) -> bool: """Check if the service can generate processing metrics. @@ -214,6 +212,27 @@ class CartesiaSTTService(STTService): await super().cancel(frame) await self._disconnect() + async def start_metrics(self): + """Start performance metrics collection for transcription processing.""" + await self.start_ttfb_metrics() + await self.start_processing_metrics() + + async def process_frame(self, frame: Frame, direction: FrameDirection): + """Process incoming frames and handle speech events. + + Args: + frame: The frame to process. + direction: Direction of frame flow in the pipeline. + """ + await super().process_frame(frame, direction) + + if isinstance(frame, UserStartedSpeakingFrame): + await self.start_metrics() + elif isinstance(frame, UserStoppedSpeakingFrame): + # Send finalize command to flush the transcription session + if self._websocket and self._websocket.state is State.OPEN: + await self._websocket.send("finalize") + async def run_stt(self, audio: bytes) -> AsyncGenerator[Frame, None]: """Process audio data for speech-to-text transcription. @@ -224,45 +243,69 @@ class CartesiaSTTService(STTService): None - transcription results are handled via WebSocket responses. """ # If the connection is closed, due to timeout, we need to reconnect when the user starts speaking again - if not self._connection or self._connection.state is State.CLOSED: + if not self._websocket or self._websocket.state is State.CLOSED: await self._connect() - await self._connection.send(audio) + await self._websocket.send(audio) yield None async def _connect(self): - params = self._settings.to_dict() - ws_url = f"wss://{self._base_url}/stt/websocket?{urllib.parse.urlencode(params)}" - logger.debug(f"Connecting to Cartesia: {ws_url}") - headers = {"Cartesia-Version": "2025-04-16", "X-API-Key": self._api_key} + await self._connect_websocket() + if self._websocket and not self._receive_task: + self._receive_task = asyncio.create_task(self._receive_task_handler(self._report_error)) + + async def _disconnect(self): + if self._receive_task: + await self.cancel_task(self._receive_task) + self._receive_task = None + + await self._disconnect_websocket() + + async def _connect_websocket(self): try: - self._connection = await websocket_connect(ws_url, additional_headers=headers) - # Setup the receiver task to handle the incoming messages from the Cartesia server - if self._receiver_task is None or self._receiver_task.done(): - self._receiver_task = asyncio.create_task(self._receive_messages()) - logger.debug(f"Connected to Cartesia") + if self._websocket and self._websocket.state is State.OPEN: + return + logger.debug("Connecting to Cartesia STT") + + params = self._settings.to_dict() + ws_url = f"wss://{self._base_url}/stt/websocket?{urllib.parse.urlencode(params)}" + headers = {"Cartesia-Version": "2025-04-16", "X-API-Key": self._api_key} + + self._websocket = await websocket_connect(ws_url, additional_headers=headers) except Exception as e: logger.error(f"{self}: unable to connect to Cartesia: {e}") - async def _receive_messages(self): + async def _disconnect_websocket(self): try: - while True: - if not self._connection or self._connection.state is State.CLOSED: - break - - message = await self._connection.recv() - try: - data = json.loads(message) - await self._process_response(data) - except json.JSONDecodeError: - logger.warning(f"Received non-JSON message: {message}") - except asyncio.CancelledError: - pass - except websockets.exceptions.ConnectionClosed as e: - logger.debug(f"WebSocket connection closed: {e}") + if self._websocket and self._websocket.state is State.OPEN: + logger.debug("Disconnecting from Cartesia STT") + await self._websocket.close() except Exception as e: - logger.error(f"Error in message receiver: {e}") + logger.error(f"{self} error closing websocket: {e}") + finally: + self._websocket = None + + def _get_websocket(self): + if self._websocket: + return self._websocket + raise Exception("Websocket not connected") + + async def _process_messages(self): + async for message in self._get_websocket(): + try: + data = json.loads(message) + await self._process_response(data) + except json.JSONDecodeError: + logger.warning(f"Received non-JSON message: {message}") + + async def _receive_messages(self): + while True: + await self._process_messages() + # Cartesia times out after 5 minutes of innactivity (no keepalive + # mechanism is available). So, we try to reconnect. + logger.debug(f"{self} Cartesia connection was disconnected (timeout?), reconnecting") + await self._connect_websocket() async def _process_response(self, data): if "type" in data: @@ -316,41 +359,3 @@ class CartesiaSTTService(STTService): language, ) ) - - async def _disconnect(self): - if self._receiver_task: - self._receiver_task.cancel() - try: - await self._receiver_task - except asyncio.CancelledError: - pass - except Exception as e: - logger.exception(f"Unexpected exception while cancelling task: {e}") - self._receiver_task = None - - if self._connection and self._connection.state is State.OPEN: - logger.debug("Disconnecting from Cartesia") - - await self._connection.close() - self._connection = None - - async def start_metrics(self): - """Start performance metrics collection for transcription processing.""" - await self.start_ttfb_metrics() - await self.start_processing_metrics() - - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process incoming frames and handle speech events. - - Args: - frame: The frame to process. - direction: Direction of frame flow in the pipeline. - """ - await super().process_frame(frame, direction) - - if isinstance(frame, UserStartedSpeakingFrame): - await self.start_metrics() - elif isinstance(frame, UserStoppedSpeakingFrame): - # Send finalize command to flush the transcription session - if self._connection and self._connection.state is State.OPEN: - await self._connection.send("finalize") diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index 3b81da5d4..9b2475cb6 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -344,7 +344,7 @@ class CartesiaTTSService(AudioContextWordTTSService): try: if self._websocket and self._websocket.state is State.OPEN: return - logger.debug("Connecting to Cartesia") + logger.debug("Connecting to Cartesia TTS") self._websocket = await websocket_connect( f"{self._url}?api_key={self._api_key}&cartesia_version={self._cartesia_version}" ) From d16c36c56de1e3289594c4b2081b65c3425c0d5a Mon Sep 17 00:00:00 2001 From: dan-ince-aai Date: Wed, 15 Oct 2025 14:27:52 +0100 Subject: [PATCH 084/158] feat: add keyterms_prompt to AssemblyAI service --- src/pipecat/services/assemblyai/models.py | 2 ++ src/pipecat/services/assemblyai/stt.py | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/pipecat/services/assemblyai/models.py b/src/pipecat/services/assemblyai/models.py index b34ec554d..d263d113d 100644 --- a/src/pipecat/services/assemblyai/models.py +++ b/src/pipecat/services/assemblyai/models.py @@ -108,6 +108,7 @@ class AssemblyAIConnectionParams(BaseModel): end_of_turn_confidence_threshold: Confidence threshold for end-of-turn detection. min_end_of_turn_silence_when_confident: Minimum silence duration when confident about end-of-turn. max_turn_silence: Maximum silence duration before forcing end-of-turn. + keyterms_prompt: List of key terms to guide transcription. Will be JSON serialized before sending. """ sample_rate: int = 16000 @@ -117,3 +118,4 @@ class AssemblyAIConnectionParams(BaseModel): end_of_turn_confidence_threshold: Optional[float] = None min_end_of_turn_silence_when_confident: Optional[int] = None max_turn_silence: Optional[int] = None + keyterms_prompt: Optional[List[str]] = None diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index aa2fc36bc..d87261f86 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -174,11 +174,16 @@ class AssemblyAISTTService(STTService): def _build_ws_url(self) -> str: """Build WebSocket URL with query parameters using urllib.parse.urlencode.""" - params = { - k: str(v).lower() if isinstance(v, bool) else v - for k, v in self._connection_params.model_dump().items() - if v is not None - } + params = {} + for k, v in self._connection_params.model_dump().items(): + if v is not None: + if k == "keyterms_prompt": + params[k] = json.dumps(v) + elif isinstance(v, bool): + params[k] = str(v).lower() + else: + params[k] = v + if params: query_string = urlencode(params) return f"{self._api_endpoint_base_url}?{query_string}" From 0c31b5ef19d21a190b6600d0e2366bb14f9713a9 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 15 Oct 2025 11:49:26 -0400 Subject: [PATCH 085/158] Add aggregate_sentences arg to ElevenLabsHttpTTSService --- CHANGELOG.md | 3 +++ src/pipecat/services/elevenlabs/tts.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 424d6a420..73a42dd22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added an `aggregate_sentences` arg in `ElevenLabsHttpTTSService`, where the + default value is True. + - The runner `--folder` argument now supports downloading files from subdirectories. diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 1544e94be..a9bf65376 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -774,6 +774,7 @@ class ElevenLabsHttpTTSService(WordTTSService): base_url: str = "https://api.elevenlabs.io", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, + aggregate_sentences: Optional[bool] = True, **kwargs, ): """Initialize the ElevenLabs HTTP TTS service. @@ -786,10 +787,11 @@ class ElevenLabsHttpTTSService(WordTTSService): base_url: Base URL for ElevenLabs HTTP API. sample_rate: Audio sample rate. If None, uses default. params: Additional input parameters for voice customization. + aggregate_sentences: Whether to aggregate sentences within the TTSService. **kwargs: Additional arguments passed to the parent service. """ super().__init__( - aggregate_sentences=True, + aggregate_sentences=aggregate_sentences, push_text_frames=False, push_stop_frames=True, sample_rate=sample_rate, From 83b0dc39f715f350e2509171cabff910d7805885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 15 Oct 2025 09:22:48 -0700 Subject: [PATCH 086/158] BaseInputTransport: stop audio filter on cancel --- CHANGELOG.md | 3 +++ src/pipecat/transports/base_input.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 424d6a420..aaaf0b54c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue where audio filters' `stop()` would not be called when using + `CancelFrame`. + - Fixed an issue where `RimeHttpTTSService` and `PiperTTSService` could generate incorrectly 16-bit aligned audio frames, potentially leading to internal errors or static audio. diff --git a/src/pipecat/transports/base_input.py b/src/pipecat/transports/base_input.py index 25cc56f68..0f247c6dc 100644 --- a/src/pipecat/transports/base_input.py +++ b/src/pipecat/transports/base_input.py @@ -232,6 +232,9 @@ class BaseInputTransport(FrameProcessor): """ # Cancel and wait for the audio input task to finish. await self._cancel_audio_task() + # Stop audio filter. + if self._params.audio_in_filter: + await self._params.audio_in_filter.stop() async def set_transport_ready(self, frame: StartFrame): """Called when the transport is ready to stream. From fa5d4ecf869621d96d038f1379030cb04a32c0fc Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 15 Oct 2025 12:45:07 -0400 Subject: [PATCH 087/158] Add foundation 47-sentry-metrics.py --- CHANGELOG.md | 5 + examples/foundational/47-sentry-metrics.py | 142 +++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 examples/foundational/47-sentry-metrics.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 424d6a420..3e37b4ef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 incorrectly 16-bit aligned audio frames, potentially leading to internal errors or static audio. +### Other + +- Added foundational example `47-sentry-metrics.py`, demonstrating how to use the + `SentryMetrics` processor. + ## [0.0.90] - 2025-10-10 ### Added diff --git a/examples/foundational/47-sentry-metrics.py b/examples/foundational/47-sentry-metrics.py new file mode 100644 index 000000000..ae7b7a59d --- /dev/null +++ b/examples/foundational/47-sentry-metrics.py @@ -0,0 +1,142 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import os + +import sentry_sdk +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams +from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import LLMRunFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.metrics.sentry import SentryMetrics +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.openai.llm import OpenAILLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + # Initialize Sentry + sentry_sdk.init( + dsn=os.getenv("SENTRY_DSN"), + traces_sample_rate=1.0, + ) + + stt = DeepgramSTTService( + api_key=os.getenv("DEEPGRAM_API_KEY"), + metrics=SentryMetrics(), + ) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + metrics=SentryMetrics(), + ) + + llm = OpenAILLMService( + api_key=os.getenv("OPENAI_API_KEY"), + metrics=SentryMetrics(), + ) + + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = LLMContext(messages) + context_aggregator = LLMContextAggregatorPair(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, + context_aggregator.user(), # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + messages.append({"role": "system", "content": "Please introduce yourself to the user."}) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() From abc569b3d231e4ff1eccf835773e3aa9226a5acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 14 Oct 2025 14:10:12 -0700 Subject: [PATCH 088/158] examples(foundational/07): use CartesiaSTTService --- examples/foundational/07-interruptible.py | 4 ++-- examples/foundational/13f-cartesia-transcription.py | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 81ba692c7..1e7bd5718 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -21,8 +21,8 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.stt import CartesiaSTTService from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -58,7 +58,7 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + stt = CartesiaSTTService(api_key=os.getenv("CARTESIA_API_KEY")) tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), diff --git a/examples/foundational/13f-cartesia-transcription.py b/examples/foundational/13f-cartesia-transcription.py index 913dce797..d685d0ba8 100644 --- a/examples/foundational/13f-cartesia-transcription.py +++ b/examples/foundational/13f-cartesia-transcription.py @@ -48,10 +48,7 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - stt = CartesiaSTTService( - api_key=os.getenv("CARTESIA_API_KEY"), - base_url=os.getenv("CARTESIA_BASE_URL"), - ) + stt = CartesiaSTTService(api_key=os.getenv("CARTESIA_API_KEY")) tl = TranscriptionLogger() From 73877218e948efe5ba3a3ff4c6221d6aae7358f8 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 15 Oct 2025 10:28:03 -0400 Subject: [PATCH 089/158] Add room_properties to the Daily runner configure() method --- CHANGELOG.md | 3 ++ src/pipecat/runner/daily.py | 75 +++++++++++++++++++++++++++---------- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c35a7de..5a51132af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added an `aggregate_sentences` arg in `ElevenLabsHttpTTSService`, where the default value is True. +- Added a `room_properties` arg to the Daily runner's `configure()` method, + allowing `DailyRoomProperties` to be provided. + - The runner `--folder` argument now supports downloading files from subdirectories. diff --git a/src/pipecat/runner/daily.py b/src/pipecat/runner/daily.py index f52ccbc2b..5bff7d060 100644 --- a/src/pipecat/runner/daily.py +++ b/src/pipecat/runner/daily.py @@ -82,6 +82,7 @@ async def configure( sip_enable_video: Optional[bool] = False, sip_num_endpoints: Optional[int] = 1, sip_codecs: Optional[Dict[str, List[str]]] = None, + room_properties: Optional[DailyRoomProperties] = None, ) -> DailyRoomConfig: """Configure Daily room URL and token with optional SIP capabilities. @@ -99,6 +100,10 @@ async def configure( sip_num_endpoints: Number of allowed SIP endpoints. sip_codecs: Codecs to support for audio and video. If None, uses Daily defaults. Example: {"audio": ["OPUS"], "video": ["H264"]} + room_properties: Optional DailyRoomProperties to use instead of building from + individual parameters. When provided, this overrides room_exp_duration and + SIP-related parameters. If not provided, properties are built from the + individual parameters as before. Returns: DailyRoomConfig: Object with room_url, token, and optional sip_endpoint. @@ -115,6 +120,13 @@ async def configure( # SIP-enabled room sip_config = await configure(session, sip_caller_phone="+15551234567") print(f"SIP endpoint: {sip_config.sip_endpoint}") + + # Custom room properties with recording enabled + custom_props = DailyRoomProperties( + enable_recording="cloud", + max_participants=2, + ) + config = await configure(session, room_properties=custom_props) """ # Check for required API key api_key = os.getenv("DAILY_API_KEY") @@ -124,9 +136,32 @@ async def configure( "Get your API key from https://dashboard.daily.co/developers" ) + # Warn if both room_properties and individual parameters are provided + if room_properties is not None: + individual_params_provided = any( + [ + room_exp_duration != 2.0, + token_exp_duration != 2.0, + sip_caller_phone is not None, + sip_enable_video is not False, + sip_num_endpoints != 1, + sip_codecs is not None, + ] + ) + if individual_params_provided: + logger.warning( + "Both room_properties and individual parameters (room_exp_duration, token_exp_duration, " + "sip_*) were provided. The room_properties will be used and individual parameters " + "will be ignored." + ) + # Determine if SIP mode is enabled sip_enabled = sip_caller_phone is not None + # If room_properties is provided, check if it has SIP configuration + if room_properties and room_properties.sip: + sip_enabled = True + daily_rest_helper = DailyRESTHelper( daily_api_key=api_key, daily_api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"), @@ -150,27 +185,29 @@ async def configure( room_name = f"{room_prefix}-{uuid.uuid4().hex[:8]}" logger.info(f"Creating new Daily room: {room_name}") - # Calculate expiration time - expiration_time = time.time() + (room_exp_duration * 60 * 60) + # Use provided room_properties or build from parameters + if room_properties is None: + # Calculate expiration time + expiration_time = time.time() + (room_exp_duration * 60 * 60) - # Create room properties - room_properties = DailyRoomProperties( - exp=expiration_time, - eject_at_room_exp=True, - ) - - # Add SIP configuration if enabled - if sip_enabled: - sip_params = DailyRoomSipParams( - display_name=sip_caller_phone, - video=sip_enable_video, - sip_mode="dial-in", - num_endpoints=sip_num_endpoints, - codecs=sip_codecs, + # Create room properties + room_properties = DailyRoomProperties( + exp=expiration_time, + eject_at_room_exp=True, ) - room_properties.sip = sip_params - room_properties.enable_dialout = True # Enable outbound calls if needed - room_properties.start_video_off = not sip_enable_video # Voice-only by default + + # Add SIP configuration if enabled + if sip_enabled: + sip_params = DailyRoomSipParams( + display_name=sip_caller_phone, + video=sip_enable_video, + sip_mode="dial-in", + num_endpoints=sip_num_endpoints, + codecs=sip_codecs, + ) + room_properties.sip = sip_params + room_properties.enable_dialout = True # Enable outbound calls if needed + room_properties.start_video_off = not sip_enable_video # Voice-only by default # Create room parameters room_params = DailyRoomParams(name=room_name, properties=room_properties) From 9d78402a3326eb9185d05bd7ccaf8c098d741fb4 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 15 Oct 2025 11:32:07 -0400 Subject: [PATCH 090/158] fix: set apply_text_normalization as request parameter in ElevenLabsHttpTTSService --- CHANGELOG.md | 4 ++++ src/pipecat/services/elevenlabs/tts.py | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c35a7de..4524d8296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue where audio filters' `stop()` would not be called when using `CancelFrame`. +- Fixed an issue in `ElevenLabsHttpTTSService`, where + `apply_text_normalization` was incorrectly set as a query parameter. It's now + being added as a request parameter. + - Fixed an issue where `RimeHttpTTSService` and `PiperTTSService` could generate incorrectly 16-bit aligned audio frames, potentially leading to internal errors or static audio. diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index a9bf65376..080f54cd6 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -985,6 +985,9 @@ class ElevenLabsHttpTTSService(WordTTSService): if self._voice_settings: payload["voice_settings"] = self._voice_settings + if self._settings["apply_text_normalization"] is not None: + payload["apply_text_normalization"] = self._settings["apply_text_normalization"] + language = self._settings["language"] if self._model_name in ELEVENLABS_MULTILINGUAL_MODELS and language: payload["language_code"] = language @@ -1005,8 +1008,6 @@ class ElevenLabsHttpTTSService(WordTTSService): } if self._settings["optimize_streaming_latency"] is not None: params["optimize_streaming_latency"] = self._settings["optimize_streaming_latency"] - if self._settings["apply_text_normalization"] is not None: - params["apply_text_normalization"] = self._settings["apply_text_normalization"] try: await self.start_ttfb_metrics() From e11ede475b643502c1a90e840cdbdc8f6e1abceb Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 15 Oct 2025 13:22:18 -0400 Subject: [PATCH 091/158] Update moondream chatbot README link --- README.md | 26 ++++++++++----------- examples/foundational/assets/moondream.png | Bin 0 -> 1101056 bytes 2 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 examples/foundational/assets/moondream.png diff --git a/README.md b/README.md index 50cc72791..6b00832f3 100644 --- a/README.md +++ b/README.md @@ -63,24 +63,24 @@ Catch new features, interviews, and how-tos on our [Pipecat TV](https://www.yout
  - +

## 🧩 Available services -| Category | Services | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/server/services/stt/assemblyai), [AWS](https://docs.pipecat.ai/server/services/stt/aws), [Azure](https://docs.pipecat.ai/server/services/stt/azure), [Cartesia](https://docs.pipecat.ai/server/services/stt/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/stt/deepgram), [ElevenLabs](https://docs.pipecat.ai/server/services/stt/elevenlabs), [Fal Wizper](https://docs.pipecat.ai/server/services/stt/fal), [Gladia](https://docs.pipecat.ai/server/services/stt/gladia), [Google](https://docs.pipecat.ai/server/services/stt/google), [Groq (Whisper)](https://docs.pipecat.ai/server/services/stt/groq), [NVIDIA Riva](https://docs.pipecat.ai/server/services/stt/riva), [OpenAI (Whisper)](https://docs.pipecat.ai/server/services/stt/openai), [SambaNova (Whisper)](https://docs.pipecat.ai/server/services/stt/sambanova), [Soniox](https://docs.pipecat.ai/server/services/stt/soniox), [Speechmatics](https://docs.pipecat.ai/server/services/stt/speechmatics), [Ultravox](https://docs.pipecat.ai/server/services/stt/ultravox), [Whisper](https://docs.pipecat.ai/server/services/stt/whisper) | -| LLMs | [Anthropic](https://docs.pipecat.ai/server/services/llm/anthropic), [AWS](https://docs.pipecat.ai/server/services/llm/aws), [Azure](https://docs.pipecat.ai/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/server/services/llm/cerebras), [DeepSeek](https://docs.pipecat.ai/server/services/llm/deepseek), [Fireworks AI](https://docs.pipecat.ai/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/server/services/llm/grok), [Groq](https://docs.pipecat.ai/server/services/llm/groq), [Mistral](https://docs.pipecat.ai/server/services/llm/mistral), [NVIDIA NIM](https://docs.pipecat.ai/server/services/llm/nim), [Ollama](https://docs.pipecat.ai/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/server/services/llm/openai), [OpenRouter](https://docs.pipecat.ai/server/services/llm/openrouter), [Perplexity](https://docs.pipecat.ai/server/services/llm/perplexity), [Qwen](https://docs.pipecat.ai/server/services/llm/qwen), [SambaNova](https://docs.pipecat.ai/server/services/llm/sambanova) [Together AI](https://docs.pipecat.ai/server/services/llm/together) | +| Category | Services | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/server/services/stt/assemblyai), [AWS](https://docs.pipecat.ai/server/services/stt/aws), [Azure](https://docs.pipecat.ai/server/services/stt/azure), [Cartesia](https://docs.pipecat.ai/server/services/stt/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/stt/deepgram), [ElevenLabs](https://docs.pipecat.ai/server/services/stt/elevenlabs), [Fal Wizper](https://docs.pipecat.ai/server/services/stt/fal), [Gladia](https://docs.pipecat.ai/server/services/stt/gladia), [Google](https://docs.pipecat.ai/server/services/stt/google), [Groq (Whisper)](https://docs.pipecat.ai/server/services/stt/groq), [NVIDIA Riva](https://docs.pipecat.ai/server/services/stt/riva), [OpenAI (Whisper)](https://docs.pipecat.ai/server/services/stt/openai), [SambaNova (Whisper)](https://docs.pipecat.ai/server/services/stt/sambanova), [Soniox](https://docs.pipecat.ai/server/services/stt/soniox), [Speechmatics](https://docs.pipecat.ai/server/services/stt/speechmatics), [Ultravox](https://docs.pipecat.ai/server/services/stt/ultravox), [Whisper](https://docs.pipecat.ai/server/services/stt/whisper) | +| LLMs | [Anthropic](https://docs.pipecat.ai/server/services/llm/anthropic), [AWS](https://docs.pipecat.ai/server/services/llm/aws), [Azure](https://docs.pipecat.ai/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/server/services/llm/cerebras), [DeepSeek](https://docs.pipecat.ai/server/services/llm/deepseek), [Fireworks AI](https://docs.pipecat.ai/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/server/services/llm/grok), [Groq](https://docs.pipecat.ai/server/services/llm/groq), [Mistral](https://docs.pipecat.ai/server/services/llm/mistral), [NVIDIA NIM](https://docs.pipecat.ai/server/services/llm/nim), [Ollama](https://docs.pipecat.ai/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/server/services/llm/openai), [OpenRouter](https://docs.pipecat.ai/server/services/llm/openrouter), [Perplexity](https://docs.pipecat.ai/server/services/llm/perplexity), [Qwen](https://docs.pipecat.ai/server/services/llm/qwen), [SambaNova](https://docs.pipecat.ai/server/services/llm/sambanova) [Together AI](https://docs.pipecat.ai/server/services/llm/together) | | Text-to-Speech | [Async](https://docs.pipecat.ai/server/services/tts/asyncai), [AWS](https://docs.pipecat.ai/server/services/tts/aws), [Azure](https://docs.pipecat.ai/server/services/tts/azure), [Cartesia](https://docs.pipecat.ai/server/services/tts/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/tts/deepgram), [ElevenLabs](https://docs.pipecat.ai/server/services/tts/elevenlabs), [Fish](https://docs.pipecat.ai/server/services/tts/fish), [Google](https://docs.pipecat.ai/server/services/tts/google), [Groq](https://docs.pipecat.ai/server/services/tts/groq), [Hume](https://docs.pipecat.ai/server/services/tts/hume), [Inworld](https://docs.pipecat.ai/server/services/tts/inworld), [LMNT](https://docs.pipecat.ai/server/services/tts/lmnt), [MiniMax](https://docs.pipecat.ai/server/services/tts/minimax), [Neuphonic](https://docs.pipecat.ai/server/services/tts/neuphonic), [NVIDIA Riva](https://docs.pipecat.ai/server/services/tts/riva), [OpenAI](https://docs.pipecat.ai/server/services/tts/openai), [Piper](https://docs.pipecat.ai/server/services/tts/piper), [PlayHT](https://docs.pipecat.ai/server/services/tts/playht), [Rime](https://docs.pipecat.ai/server/services/tts/rime), [Sarvam](https://docs.pipecat.ai/server/services/tts/sarvam), [XTTS](https://docs.pipecat.ai/server/services/tts/xtts) | -| Speech-to-Speech | [AWS Nova Sonic](https://docs.pipecat.ai/server/services/s2s/aws), [Gemini Multimodal Live](https://docs.pipecat.ai/server/services/s2s/gemini), [OpenAI Realtime](https://docs.pipecat.ai/server/services/s2s/openai) | -| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/server/services/transport/daily), [FastAPI Websocket](https://docs.pipecat.ai/server/services/transport/fastapi-websocket), [SmallWebRTCTransport](https://docs.pipecat.ai/server/services/transport/small-webrtc), [WebSocket Server](https://docs.pipecat.ai/server/services/transport/websocket-server), Local | -| Serializers | [Plivo](https://docs.pipecat.ai/server/utilities/serializers/plivo), [Twilio](https://docs.pipecat.ai/server/utilities/serializers/twilio), [Telnyx](https://docs.pipecat.ai/server/utilities/serializers/telnyx) | -| Video | [HeyGen](https://docs.pipecat.ai/server/services/video/heygen), [Tavus](https://docs.pipecat.ai/server/services/video/tavus), [Simli](https://docs.pipecat.ai/server/services/video/simli) | -| Memory | [mem0](https://docs.pipecat.ai/server/services/memory/mem0) | -| Vision & Image | [fal](https://docs.pipecat.ai/server/services/image-generation/fal), [Google Imagen](https://docs.pipecat.ai/server/services/image-generation/fal), [Moondream](https://docs.pipecat.ai/server/services/vision/moondream) | -| Audio Processing | [Silero VAD](https://docs.pipecat.ai/server/utilities/audio/silero-vad-analyzer), [Krisp](https://docs.pipecat.ai/server/utilities/audio/krisp-filter), [Koala](https://docs.pipecat.ai/server/utilities/audio/koala-filter), [ai-coustics](https://docs.pipecat.ai/server/utilities/audio/aic-filter) | -| Analytics & Metrics | [OpenTelemetry](https://docs.pipecat.ai/server/utilities/opentelemetry), [Sentry](https://docs.pipecat.ai/server/services/analytics/sentry) | +| Speech-to-Speech | [AWS Nova Sonic](https://docs.pipecat.ai/server/services/s2s/aws), [Gemini Multimodal Live](https://docs.pipecat.ai/server/services/s2s/gemini), [OpenAI Realtime](https://docs.pipecat.ai/server/services/s2s/openai) | +| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/server/services/transport/daily), [FastAPI Websocket](https://docs.pipecat.ai/server/services/transport/fastapi-websocket), [SmallWebRTCTransport](https://docs.pipecat.ai/server/services/transport/small-webrtc), [WebSocket Server](https://docs.pipecat.ai/server/services/transport/websocket-server), Local | +| Serializers | [Plivo](https://docs.pipecat.ai/server/utilities/serializers/plivo), [Twilio](https://docs.pipecat.ai/server/utilities/serializers/twilio), [Telnyx](https://docs.pipecat.ai/server/utilities/serializers/telnyx) | +| Video | [HeyGen](https://docs.pipecat.ai/server/services/video/heygen), [Tavus](https://docs.pipecat.ai/server/services/video/tavus), [Simli](https://docs.pipecat.ai/server/services/video/simli) | +| Memory | [mem0](https://docs.pipecat.ai/server/services/memory/mem0) | +| Vision & Image | [fal](https://docs.pipecat.ai/server/services/image-generation/fal), [Google Imagen](https://docs.pipecat.ai/server/services/image-generation/fal), [Moondream](https://docs.pipecat.ai/server/services/vision/moondream) | +| Audio Processing | [Silero VAD](https://docs.pipecat.ai/server/utilities/audio/silero-vad-analyzer), [Krisp](https://docs.pipecat.ai/server/utilities/audio/krisp-filter), [Koala](https://docs.pipecat.ai/server/utilities/audio/koala-filter), [ai-coustics](https://docs.pipecat.ai/server/utilities/audio/aic-filter) | +| Analytics & Metrics | [OpenTelemetry](https://docs.pipecat.ai/server/utilities/opentelemetry), [Sentry](https://docs.pipecat.ai/server/services/analytics/sentry) | πŸ“š [View full services documentation β†’](https://docs.pipecat.ai/server/services/supported-services) diff --git a/examples/foundational/assets/moondream.png b/examples/foundational/assets/moondream.png new file mode 100644 index 0000000000000000000000000000000000000000..17ba5401fb2cdfb4d1a5a2e6f9738a09bbf00d28 GIT binary patch literal 1101056 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

@cx;)oKgu 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�r>mtkzp2(Z+9a?NH24mhxT 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

=)Jq;?B4wU;FAKJtg&3xd7V^qrA)mh` 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πeg3W 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∾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

Us>Ws^8xx9*y4o2m8uAP?DcobHlX6G$nt>& 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¯`@&V=CxFSvMKb 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

$V?{U5q2$GOkYej_}yl8_& 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 jGg>xB?Y|3N!}E+LFf)-l+d(Gax|+;IRJ(ztSp} literal 0 HcmV?d00001 From 5ea7cc9d32b42f86bf18bcdc6de2e27a4fe34e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 14 Oct 2025 13:16:52 -0700 Subject: [PATCH 092/158] services: added on_connected/on_disconnected events --- CHANGELOG.md | 3 ++ src/pipecat/services/assemblyai/stt.py | 3 ++ src/pipecat/services/asyncai/tts.py | 3 ++ src/pipecat/services/aws/stt.py | 2 ++ src/pipecat/services/cartesia/stt.py | 2 ++ src/pipecat/services/cartesia/tts.py | 2 ++ src/pipecat/services/deepgram/flux/stt.py | 4 +++ src/pipecat/services/elevenlabs/tts.py | 2 ++ src/pipecat/services/fish/tts.py | 3 ++ src/pipecat/services/google/stt.py | 4 +++ src/pipecat/services/lmnt/tts.py | 2 ++ src/pipecat/services/neuphonic/tts.py | 3 ++ src/pipecat/services/playht/tts.py | 3 ++ src/pipecat/services/rime/tts.py | 3 ++ src/pipecat/services/sarvam/tts.py | 5 ++++ src/pipecat/services/speechmatics/stt.py | 2 ++ src/pipecat/services/stt_service.py | 33 +++++++++++++++------- src/pipecat/services/tts_service.py | 34 +++++++++++++++-------- 18 files changed, 92 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 433555890..acd680d5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `on_connected` and `on_disconnected` events to TTS and STT + websocket-based services. + - Added an `aggregate_sentences` arg in `ElevenLabsHttpTTSService`, where the default value is True. diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index aa2fc36bc..381d60506 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -197,6 +197,8 @@ class AssemblyAISTTService(STTService): ) self._connected = True self._receive_task = self.create_task(self._receive_task_handler()) + + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"Failed to connect to AssemblyAI: {e}") self._connected = False @@ -238,6 +240,7 @@ class AssemblyAISTTService(STTService): self._websocket = None self._connected = False self._receive_task = None + await self._call_event_handler("on_disconnected") async def _receive_task_handler(self): """Handle incoming WebSocket messages.""" diff --git a/src/pipecat/services/asyncai/tts.py b/src/pipecat/services/asyncai/tts.py index b0ddf9275..3e4ff33cc 100644 --- a/src/pipecat/services/asyncai/tts.py +++ b/src/pipecat/services/asyncai/tts.py @@ -235,6 +235,8 @@ class AsyncAITTSService(InterruptibleTTSService): } await self._get_websocket().send(json.dumps(init_msg)) + + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -252,6 +254,7 @@ class AsyncAITTSService(InterruptibleTTSService): finally: self._websocket = None self._started = False + await self._call_event_handler("on_disconnected") def _get_websocket(self): if self._websocket: diff --git a/src/pipecat/services/aws/stt.py b/src/pipecat/services/aws/stt.py index 59cda5865..b019fc058 100644 --- a/src/pipecat/services/aws/stt.py +++ b/src/pipecat/services/aws/stt.py @@ -286,6 +286,7 @@ class AWSTranscribeSTTService(STTService): logger.info(f"{self} Successfully connected to AWS Transcribe") + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} Failed to connect to AWS Transcribe: {e}") await self._disconnect() @@ -310,6 +311,7 @@ class AWSTranscribeSTTService(STTService): logger.warning(f"{self} Error closing WebSocket connection: {e}") finally: self._ws_client = None + await self._call_event_handler("on_disconnected") def language_to_service_language(self, language: Language) -> str | None: """Convert internal language enum to AWS Transcribe language code. diff --git a/src/pipecat/services/cartesia/stt.py b/src/pipecat/services/cartesia/stt.py index 97a4f7127..b4e232c4a 100644 --- a/src/pipecat/services/cartesia/stt.py +++ b/src/pipecat/services/cartesia/stt.py @@ -273,6 +273,7 @@ class CartesiaSTTService(WebsocketSTTService): headers = {"Cartesia-Version": "2025-04-16", "X-API-Key": self._api_key} self._websocket = await websocket_connect(ws_url, additional_headers=headers) + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self}: unable to connect to Cartesia: {e}") @@ -285,6 +286,7 @@ class CartesiaSTTService(WebsocketSTTService): logger.error(f"{self} error closing websocket: {e}") finally: self._websocket = None + await self._call_event_handler("on_disconnected") def _get_websocket(self): if self._websocket: diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index 9b2475cb6..90f0ac3b4 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -348,6 +348,7 @@ class CartesiaTTSService(AudioContextWordTTSService): self._websocket = await websocket_connect( f"{self._url}?api_key={self._api_key}&cartesia_version={self._cartesia_version}" ) + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -365,6 +366,7 @@ class CartesiaTTSService(AudioContextWordTTSService): finally: self._context_id = None self._websocket = None + await self._call_event_handler("on_disconnected") def _get_websocket(self): if self._websocket: diff --git a/src/pipecat/services/deepgram/flux/stt.py b/src/pipecat/services/deepgram/flux/stt.py index 493bece80..f0b1a5baa 100644 --- a/src/pipecat/services/deepgram/flux/stt.py +++ b/src/pipecat/services/deepgram/flux/stt.py @@ -205,6 +205,7 @@ class DeepgramFluxSTTService(WebsocketSTTService): additional_headers={"Authorization": f"Token {self._api_key}"}, ) logger.debug("Connected to Deepgram Flux Websocket") + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -225,6 +226,9 @@ class DeepgramFluxSTTService(WebsocketSTTService): await self._websocket.close() except Exception as e: logger.error(f"{self} error closing websocket: {e}") + finally: + self._websocket = None + await self._call_event_handler("on_disconnected") async def _send_close_stream(self) -> None: """Sends a CloseStream control message to the Deepgram Flux WebSocket API. diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 080f54cd6..641f50aa1 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -528,6 +528,7 @@ class ElevenLabsTTSService(AudioContextWordTTSService): url, max_size=16 * 1024 * 1024, additional_headers={"xi-api-key": self._api_key} ) + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -550,6 +551,7 @@ class ElevenLabsTTSService(AudioContextWordTTSService): self._started = False self._context_id = None self._websocket = None + await self._call_event_handler("on_disconnected") def _get_websocket(self): if self._websocket: diff --git a/src/pipecat/services/fish/tts.py b/src/pipecat/services/fish/tts.py index b39b775e5..669d2ce97 100644 --- a/src/pipecat/services/fish/tts.py +++ b/src/pipecat/services/fish/tts.py @@ -225,6 +225,8 @@ class FishAudioTTSService(InterruptibleTTSService): start_message = {"event": "start", "request": {"text": "", **self._settings}} await self._websocket.send(ormsgpack.packb(start_message)) logger.debug("Sent start message to Fish Audio") + + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"Fish Audio initialization error: {e}") self._websocket = None @@ -245,6 +247,7 @@ class FishAudioTTSService(InterruptibleTTSService): self._request_id = None self._started = False self._websocket = None + await self._call_event_handler("on_disconnected") async def flush_audio(self): """Flush any buffered audio by sending a flush event to Fish Audio.""" diff --git a/src/pipecat/services/google/stt.py b/src/pipecat/services/google/stt.py index 31ae597f7..b9e56f55b 100644 --- a/src/pipecat/services/google/stt.py +++ b/src/pipecat/services/google/stt.py @@ -730,6 +730,8 @@ class GoogleSTTService(STTService): self._request_queue = asyncio.Queue() self._streaming_task = self.create_task(self._stream_audio()) + await self._call_event_handler("on_connected") + async def _disconnect(self): """Clean up streaming recognition resources.""" if self._streaming_task: @@ -737,6 +739,8 @@ class GoogleSTTService(STTService): await self.cancel_task(self._streaming_task) self._streaming_task = None + await self._call_event_handler("on_disconnected") + async def _request_generator(self): """Generates requests for the streaming recognize method.""" recognizer_path = f"projects/{self._project_id}/locations/{self._location}/recognizers/_" diff --git a/src/pipecat/services/lmnt/tts.py b/src/pipecat/services/lmnt/tts.py index a602789fd..9f9fef5fc 100644 --- a/src/pipecat/services/lmnt/tts.py +++ b/src/pipecat/services/lmnt/tts.py @@ -222,6 +222,7 @@ class LmntTTSService(InterruptibleTTSService): # Send initialization message await self._websocket.send(json.dumps(init_msg)) + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -243,6 +244,7 @@ class LmntTTSService(InterruptibleTTSService): finally: self._started = False self._websocket = None + await self._call_event_handler("on_disconnected") def _get_websocket(self): """Get the WebSocket connection if available.""" diff --git a/src/pipecat/services/neuphonic/tts.py b/src/pipecat/services/neuphonic/tts.py index 46d805086..6ccdfe17f 100644 --- a/src/pipecat/services/neuphonic/tts.py +++ b/src/pipecat/services/neuphonic/tts.py @@ -293,6 +293,8 @@ class NeuphonicTTSService(InterruptibleTTSService): headers = {"x-api-key": self._api_key} self._websocket = await websocket_connect(url, additional_headers=headers) + + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -311,6 +313,7 @@ class NeuphonicTTSService(InterruptibleTTSService): finally: self._started = False self._websocket = None + await self._call_event_handler("on_disconnected") async def _receive_messages(self): """Receive and process messages from Neuphonic WebSocket.""" diff --git a/src/pipecat/services/playht/tts.py b/src/pipecat/services/playht/tts.py index 9288ebd59..925480794 100644 --- a/src/pipecat/services/playht/tts.py +++ b/src/pipecat/services/playht/tts.py @@ -269,6 +269,8 @@ class PlayHTTTSService(InterruptibleTTSService): raise ValueError("WebSocket URL is not a string") self._websocket = await websocket_connect(self._websocket_url) + + await self._call_event_handler("on_connected") except ValueError as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -291,6 +293,7 @@ class PlayHTTTSService(InterruptibleTTSService): finally: self._request_id = None self._websocket = None + await self._call_event_handler("on_disconnected") async def _get_websocket_url(self): """Retrieve WebSocket URL from PlayHT API.""" diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index 1ac829ebd..fa3fa447d 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -255,6 +255,8 @@ class RimeTTSService(AudioContextWordTTSService): url = f"{self._url}?{params}" headers = {"Authorization": f"Bearer {self._api_key}"} self._websocket = await websocket_connect(url, additional_headers=headers) + + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -272,6 +274,7 @@ class RimeTTSService(AudioContextWordTTSService): finally: self._context_id = None self._websocket = None + await self._call_event_handler("on_disconnected") def _get_websocket(self): """Get active websocket connection or raise exception.""" diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index a9fedcc58..75e6de125 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -525,6 +525,7 @@ class SarvamTTSService(InterruptibleTTSService): logger.debug("Connected to Sarvam TTS Websocket") await self._send_config() + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} initialization error: {e}") self._websocket = None @@ -556,6 +557,10 @@ class SarvamTTSService(InterruptibleTTSService): await self._websocket.close() except Exception as e: logger.error(f"{self} error closing websocket: {e}") + finally: + self._started = False + self._websocket = None + await self._call_event_handler("on_disconnected") def _get_websocket(self): if self._websocket: diff --git a/src/pipecat/services/speechmatics/stt.py b/src/pipecat/services/speechmatics/stt.py index 4028dd248..2c1db2a15 100644 --- a/src/pipecat/services/speechmatics/stt.py +++ b/src/pipecat/services/speechmatics/stt.py @@ -577,6 +577,7 @@ class SpeechmaticsSTTService(STTService): ), ) logger.debug(f"{self} Connected to Speechmatics STT service") + await self._call_event_handler("on_connected") except Exception as e: logger.error(f"{self} Error connecting to Speechmatics: {e}") self._client = None @@ -595,6 +596,7 @@ class SpeechmaticsSTTService(STTService): logger.error(f"{self} Error closing Speechmatics client: {e}") finally: self._client = None + await self._call_event_handler("on_disconnected") def _process_config(self) -> None: """Create a formatted STT transcription config. diff --git a/src/pipecat/services/stt_service.py b/src/pipecat/services/stt_service.py index a02619e44..6fb96c571 100644 --- a/src/pipecat/services/stt_service.py +++ b/src/pipecat/services/stt_service.py @@ -35,6 +35,25 @@ class STTService(AIService): Provides common functionality for STT services including audio passthrough, muting, settings management, and audio processing. Subclasses must implement the run_stt method to provide actual speech recognition. + + Event handlers: + on_connected: Called when connected to the STT service. + on_connected: Called when disconnected from the STT service. + on_connection_error: Called when a connection to the STT service error occurs. + + Example:: + + @stt.event_handler("on_connected") + async def on_connected(stt: STTService): + logger.debug(f"STT connected") + + @stt.event_handler("on_disconnected") + async def on_disconnected(stt: STTService): + logger.debug(f"STT disconnected") + + @stt.event_handler("on_connection_error") + async def on_connection_error(stt: STTService, error: str): + logger.error(f"STT connection error: {error}") """ def __init__( @@ -62,6 +81,10 @@ class STTService(AIService): self._muted: bool = False self._user_id: str = "" + self._register_event_handler("on_connected") + self._register_event_handler("on_disconnected") + self._register_event_handler("on_connection_error") + @property def is_muted(self) -> bool: """Check if the STT service is currently muted. @@ -292,15 +315,6 @@ class WebsocketSTTService(STTService, WebsocketService): Combines STT functionality with websocket connectivity, providing automatic error handling and reconnection capabilities. - - Event handlers: - on_connection_error: Called when a websocket connection error occurs. - - Example:: - - @stt.event_handler("on_connection_error") - async def on_connection_error(stt: STTService, error: str): - logger.error(f"STT connection error: {error}") """ def __init__(self, *, reconnect_on_error: bool = True, **kwargs): @@ -312,7 +326,6 @@ class WebsocketSTTService(STTService, WebsocketService): """ STTService.__init__(self, **kwargs) WebsocketService.__init__(self, reconnect_on_error=reconnect_on_error, **kwargs) - self._register_event_handler("on_connection_error") async def _report_error(self, error: ErrorFrame): await self._call_event_handler("on_connection_error", error.error) diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index a60b50818..b356c7244 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -59,6 +59,25 @@ class TTSService(AIService): Provides common functionality for TTS services including text aggregation, filtering, audio generation, and frame management. Supports configurable sentence aggregation, silence insertion, and frame processing control. + + Event handlers: + on_connected: Called when connected to the STT service. + on_connected: Called when disconnected from the STT service. + on_connection_error: Called when a connection to the STT service error occurs. + + Example:: + + @tts.event_handler("on_connected") + async def on_connected(tts: TTSService): + logger.debug(f"TTS connected") + + @tts.event_handler("on_disconnected") + async def on_disconnected(tts: TTSService): + logger.debug(f"TTS disconnected") + + @tts.event_handler("on_connection_error") + async def on_connection_error(stt: TTSService, error: str): + logger.error(f"TTS connection error: {error}") """ def __init__( @@ -143,6 +162,10 @@ class TTSService(AIService): self._processing_text: bool = False + self._register_event_handler("on_connected") + self._register_event_handler("on_disconnected") + self._register_event_handler("on_connection_error") + @property def sample_rate(self) -> int: """Get the current sample rate for audio output. @@ -626,7 +649,6 @@ class WebsocketTTSService(TTSService, WebsocketService): """ TTSService.__init__(self, **kwargs) WebsocketService.__init__(self, reconnect_on_error=reconnect_on_error, **kwargs) - self._register_event_handler("on_connection_error") async def _report_error(self, error: ErrorFrame): await self._call_event_handler("on_connection_error", error.error) @@ -678,15 +700,6 @@ class WebsocketWordTTSService(WordTTSService, WebsocketService): """Base class for websocket-based TTS services that support word timestamps. Combines word timestamp functionality with websocket connectivity. - - Event handlers: - on_connection_error: Called when a websocket connection error occurs. - - Example:: - - @tts.event_handler("on_connection_error") - async def on_connection_error(tts: TTSService, error: str): - logger.error(f"TTS connection error: {error}") """ def __init__(self, *, reconnect_on_error: bool = True, **kwargs): @@ -698,7 +711,6 @@ class WebsocketWordTTSService(WordTTSService, WebsocketService): """ WordTTSService.__init__(self, **kwargs) WebsocketService.__init__(self, reconnect_on_error=reconnect_on_error, **kwargs) - self._register_event_handler("on_connection_error") async def _report_error(self, error: ErrorFrame): await self._call_event_handler("on_connection_error", error.error) From 129ab5ea0e1cde26f720cddfb0d71f605f7471c3 Mon Sep 17 00:00:00 2001 From: vipyne Date: Wed, 15 Oct 2025 10:54:54 -0500 Subject: [PATCH 093/158] WhatsApp transport debug log -> trace log --- src/pipecat/runner/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index 53b39f4e3..a7af52e18 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -315,7 +315,7 @@ def _setup_whatsapp_routes(app: FastAPI): WHATSAPP_WEBHOOK_VERIFICATION_TOKEN, ] ): - logger.debug( + logger.trace( "Missing required environment variables for WhatsApp transport. Keeping it disabled." ) return From 22557fa66866e67f351ed67c5d4975469b90dfed Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 15 Oct 2025 09:05:40 -0400 Subject: [PATCH 094/158] Fix an issue in ElevenLabsHttpTTSService where the last word is not emitted --- CHANGELOG.md | 4 ++++ src/pipecat/services/elevenlabs/tts.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index acd680d5a..1832b9a0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue in `ElevenLabsTTSService` and `ElevenLabsHttpTTSService` where + the Flash models would split words, resulting in a space being inserted + between words. + - Fixed an issue where audio filters' `stop()` would not be called when using `CancelFrame`. diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 641f50aa1..460b23d18 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -1070,6 +1070,14 @@ class ElevenLabsHttpTTSService(WordTTSService): logger.error(f"Error processing response: {e}", exc_info=True) continue + # After processing all chunks, emit any remaining partial word + # since this is the end of the utterance + if self._partial_word: + final_word_time = [(self._partial_word, self._partial_word_start_time)] + await self.add_word_timestamps(final_word_time) + self._partial_word = "" + self._partial_word_start_time = 0.0 + # After processing all chunks, add the total utterance duration # to the cumulative time to ensure next utterance starts after this one if utterance_duration > 0: From 8902f1dc94eb496d223dab7be8cb92bc9063d190 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 15 Oct 2025 10:49:13 -0400 Subject: [PATCH 095/158] Bump openai, openpipe versions, add 14x foundational example --- CHANGELOG.md | 6 + .../14x-function-calling-openpipe.py | 182 ++++++++++ pyproject.toml | 4 +- scripts/evals/run-release-evals.py | 1 + uv.lock | 337 +++++++----------- 5 files changed, 322 insertions(+), 208 deletions(-) create mode 100644 examples/foundational/14x-function-calling-openpipe.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c173d4a8..3cd715776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CartesiaSTTService` now inherits from `WebsocketSTTService`. +- Package upgrades: + - `openai` upgraded to support up to 2.x.x. + - `openpipe` upgraded to support up to 5.x.x. + ### Fixed - Fixed an issue in `ElevenLabsTTSService` and `ElevenLabsHttpTTSService` where @@ -47,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added foundational example `47-sentry-metrics.py`, demonstrating how to use the `SentryMetrics` processor. +- Added foundational example `14x-function-calling-openpipe.py`. + ## [0.0.90] - 2025-10-10 ### Added diff --git a/examples/foundational/14x-function-calling-openpipe.py b/examples/foundational/14x-function-calling-openpipe.py new file mode 100644 index 000000000..3f2537bb7 --- /dev/null +++ b/examples/foundational/14x-function-calling-openpipe.py @@ -0,0 +1,182 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import os +import time + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams +from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import LLMRunFrame, TTSSpeakFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.llm_service import FunctionCallParams +from pipecat.services.openpipe.llm import OpenPipeLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + + +async def fetch_weather_from_api(params: FunctionCallParams): + await params.result_callback({"conditions": "nice", "temperature": "75"}) + + +async def fetch_restaurant_recommendation(params: FunctionCallParams): + await params.result_callback({"name": "The Golden Dragon"}) + + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ) + + timestamp = int(time.time()) + llm = OpenPipeLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + openpipe_api_key=os.getenv("OPENPIPE_API_KEY"), + tags={"conversation_id": f"pipecat-{timestamp}"}, + ) + + # You can also register a function_name of None to get all functions + # sent to the same callback with an additional function_name parameter. + llm.register_function("get_current_weather", fetch_weather_from_api) + llm.register_function("get_restaurant_recommendation", fetch_restaurant_recommendation) + + @llm.event_handler("on_function_calls_started") + async def on_function_calls_started(service, function_calls): + await tts.queue_frame(TTSSpeakFrame("Let me check on that.")) + + weather_function = FunctionSchema( + name="get_current_weather", + description="Get the current weather", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "format": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "The temperature unit to use. Infer this from the user's location.", + }, + }, + required=["location", "format"], + ) + restaurant_function = FunctionSchema( + name="get_restaurant_recommendation", + description="Get a restaurant recommendation", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + }, + required=["location"], + ) + tools = ToolsSchema(standard_tools=[weather_function, restaurant_function]) + + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = LLMContext(messages, tools) + context_aggregator = LLMContextAggregatorPair(context) + + pipeline = Pipeline( + [ + transport.input(), + stt, + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/pyproject.toml b/pyproject.toml index c04dd2433..0d94d0fa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ "pyloudnorm~=0.1.1", "resampy~=0.4.3", "soxr~=0.5.0", - "openai>=1.74.0,<=1.99.1", + "openai>=1.74.0,<3", # Pinning numba to resolve package dependencies "numba==0.61.2", "wait_for2>=0.4.1; python_version<'3.12'", @@ -84,7 +84,7 @@ nim = [] neuphonic = [ "pipecat-ai[websockets-base]" ] noisereduce = [ "noisereduce~=3.0.3" ] openai = [ "pipecat-ai[websockets-base]" ] -openpipe = [ "openpipe~=4.50.0" ] +openpipe = [ "openpipe>=4.50.0,<6" ] openrouter = [] perplexity = [] playht = [ "pipecat-ai[websockets-base]" ] diff --git a/scripts/evals/run-release-evals.py b/scripts/evals/run-release-evals.py index 44cb69aed..14f9dee52 100644 --- a/scripts/evals/run-release-evals.py +++ b/scripts/evals/run-release-evals.py @@ -136,6 +136,7 @@ TESTS_14 = [ ("14r-function-calling-aws.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), ("14v-function-calling-openai.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), ("14w-function-calling-mistral.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), + ("14x-function-calling-openpipe.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), # Currently not working. # ("14c-function-calling-together.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), # ("14l-function-calling-deepseek.py", PROMPT_WEATHER, EVAL_WEATHER, BOT_SPEAKS_FIRST), diff --git a/uv.lock b/uv.lock index d7f8aebee..dec765912 100644 --- a/uv.lock +++ b/uv.lock @@ -569,6 +569,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/54/db7a801933dd2537f5376fb8a9e28caff488ef5c2d61f3a8fced55fe6336/blake3-1.0.7-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d9046bb1e22a8607e1d0d7c3ff47e56e0a197c988502df4bf4d78563f3e9fe2c", size = 553411, upload-time = "2025-09-29T16:40:45.667Z" }, { url = "https://files.pythonhosted.org/packages/2c/08/949cf68d16d1f731d502968bb1486e1a4bf7ef032c38fbc2ef26a2353494/blake3-1.0.7-cp313-cp313t-win32.whl", hash = "sha256:bd2f638bcc00fc09ce985ea3c642d45940e1eda198ab1f4b90cfdecbebbc9315", size = 227049, upload-time = "2025-09-29T16:40:47.446Z" }, { url = "https://files.pythonhosted.org/packages/f2/ae/6783a5ca6235024e00a1e92ab6ca2cd855f4c61c763cf8d6d643846d110c/blake3-1.0.7-cp313-cp313t-win_amd64.whl", hash = "sha256:cb3aa1db14231c2ef0ec5acd805505ce128c39ffa510deb3384eed96fe4addcb", size = 214101, upload-time = "2025-09-29T16:40:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/aa/99b4b6c22972b9a854f77d97846a717448a77d079e4bd38e46a3f8ecea76/blake3-1.0.7-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:f7db997205aa420d59fb5639346e40beafb9c09252e2ec6efedca8f230f7520c", size = 346664, upload-time = "2025-10-11T18:02:54.609Z" }, + { url = "https://files.pythonhosted.org/packages/f9/44/e98bc5450be415a335a191b154e299e335046d11fe9514d93961902b7aed/blake3-1.0.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:19afec6e276f3bc154541248d92b1ecb198af2ee920025f7ce521028f9a69d8b", size = 324576, upload-time = "2025-10-11T18:02:57.062Z" }, + { url = "https://files.pythonhosted.org/packages/74/25/23a39913c8424ac3df705ed71a00efe34cc1cdbd4588ed6eaf458ea9d7ef/blake3-1.0.7-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:006a11bbba65a95e88ddc069cca751c8812fd144d582715eeea512452fdbe80d", size = 370545, upload-time = "2025-10-11T18:02:59.824Z" }, + { url = "https://files.pythonhosted.org/packages/db/83/9f53a86de9a5999b043febfd84765d240014da42055aeac06d1005b20b07/blake3-1.0.7-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7febeffdc8412fed105ca517cee641ac521fb9cfb750bf7e27a5cdf3ddf74a08", size = 374370, upload-time = "2025-10-11T18:03:01.412Z" }, + { url = "https://files.pythonhosted.org/packages/c4/4c/3290aa4fb7483975a7b3322a73692aa3cf491a77ce7ac61c216c71c6f834/blake3-1.0.7-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c032ce7c52b71015651c0abe9fe599aa2669e6be578aa17d5f993dc93373401", size = 447808, upload-time = "2025-10-11T18:03:02.893Z" }, + { url = "https://files.pythonhosted.org/packages/66/26/92b6e15552865416aae1aedad8b9b4d8b47ca9b73d25373622b1798c05a9/blake3-1.0.7-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b81455f7d24b58fe26be037cc3854c28ea6eb3671ceab3b1ec0b1239aeb6fef", size = 506118, upload-time = "2025-10-11T18:03:04.51Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ef/f158fc43a03fd366bc428a52a845bd0f884e518deda901c9216bd469867e/blake3-1.0.7-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:41b0127b0e7c8610054c421959dbe7140a81ac2c88fa9e099994fbaa529af3c1", size = 393239, upload-time = "2025-10-11T18:03:07.102Z" }, + { url = "https://files.pythonhosted.org/packages/10/49/2a56ce897ec7ed0e25953b3873da271ea60cc107ae02ecc6655252e554c7/blake3-1.0.7-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4755ca95b4114b629d8f3570bc661916d211d52d47f57ff70e9687377ab39cb9", size = 386267, upload-time = "2025-10-11T18:03:08.904Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c4/ee4c03ea419198b91c889ef173015b5d637a390d3f7d63cb70033a7201d6/blake3-1.0.7-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:8abe929cfd27b375e02e3dd7a690192fa4efecc52ef510df91ef01651ef08dc7", size = 549641, upload-time = "2025-10-11T18:03:10.64Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cc/a918d6649b56fe705133e06d9958d90978aad30063d42cca4dfe23db16e9/blake3-1.0.7-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:dd607eb5ad5a9b44ff62243759aa0af4085f6f43c9b01f503561a70da63e3b94", size = 553691, upload-time = "2025-10-11T18:03:12.108Z" }, + { url = "https://files.pythonhosted.org/packages/fd/9f/568546f555fd1555d4867c497e9413f67bf769d076e773b9ca9e07a0b6f6/blake3-1.0.7-cp314-cp314-win32.whl", hash = "sha256:a51684d1f346e7680f7c244c25b0e279e3b297f1938126e4ea8e32425ea269f5", size = 227552, upload-time = "2025-10-11T18:03:13.468Z" }, + { url = "https://files.pythonhosted.org/packages/97/2b/d4ef7365d9f601c8a127b5993f2662d45d2cb6d430bf3dbbb7a6f0b33639/blake3-1.0.7-cp314-cp314-win_amd64.whl", hash = "sha256:a6a481719e28e2c61aafd4273d32663365d97613341b72fcdf2f6afbd426319b", size = 214719, upload-time = "2025-10-11T18:03:14.835Z" }, + { url = "https://files.pythonhosted.org/packages/2f/53/f697cc34e382a225d163ea0c6a35c7eb4cfd1011e85db6610adfac98e522/blake3-1.0.7-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:daa8933cd7db19143bd6b59f7ac4c7c7446767d7b2c3a748a4559aa483275fa2", size = 347071, upload-time = "2025-10-11T18:03:16.637Z" }, + { url = "https://files.pythonhosted.org/packages/4c/85/836dcb5c5709c2331f02ce065f7ebfaae710a6c1768cdc47ee3197645f98/blake3-1.0.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:24074adfffffe0fa7a7dd930cc608d6e965e70306e2c1e14d412e29ec94fa360", size = 324341, upload-time = "2025-10-11T18:03:18.073Z" }, + { url = "https://files.pythonhosted.org/packages/6d/48/36b2c25007933619ce60e24b9f360baaa77d08939284045476c8e157fe62/blake3-1.0.7-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dce6e6f03de2674f9860cf330d8a4fcdb63a60659435e5e31d72d174fc102d8e", size = 370140, upload-time = "2025-10-11T18:03:19.582Z" }, + { url = "https://files.pythonhosted.org/packages/70/82/8a8977e5d56b9fb719033940c8ce34afc733190d34ab868a647a9af7b584/blake3-1.0.7-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e783f33d53a2de8d2ab845235dd53393d521b5e4a76c23d03e77e472266359d3", size = 373022, upload-time = "2025-10-11T18:03:21.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c4/44017ba40804a528568b35a36c05187786830c4d891c5540d59a121a7cec/blake3-1.0.7-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:782784aef18eb61f4ce8bf2b9506b7d90f0d183176b453345b221837a18041b7", size = 447243, upload-time = "2025-10-11T18:03:22.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/c1/4fa20e68624784082734d31b8c9c80ad226658c024e61b9f9b6751ba0a4a/blake3-1.0.7-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6062122e77f40e3733cac2ef3f25e0fc7f555e352fe6f513f8404ad11dc69974", size = 506149, upload-time = "2025-10-11T18:03:24.424Z" }, + { url = "https://files.pythonhosted.org/packages/8e/63/af65466e27e7b92800a068afaee11b2fa071e34a7f5900f8e13832f18185/blake3-1.0.7-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c2614bc9d69fd6067571f3bb37b3b07a6b86a56167553ad4784a3c508771f39", size = 393243, upload-time = "2025-10-11T18:03:25.872Z" }, + { url = "https://files.pythonhosted.org/packages/f3/82/54a4807a3243d0e094ada9d65687aeb40059587e374b3beb9c89f6552c9b/blake3-1.0.7-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6df2bd56c43bdeb6699d4af0a0dd0d77537d95cb4a5dde4b39ed6e54cc725d6", size = 386318, upload-time = "2025-10-11T18:03:27.338Z" }, + { url = "https://files.pythonhosted.org/packages/42/e8/32b56531b5d9da67e476735ceaec7c3bf89310629abeeafb03c724145c88/blake3-1.0.7-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:8b635cf4350caf459ecb335b32be622068423245bda457d5bc159106eb20f912", size = 548945, upload-time = "2025-10-11T18:03:28.779Z" }, + { url = "https://files.pythonhosted.org/packages/ad/50/33b1aca708be629e285a537f1adf34dfcabc4c30b28c436361323d11f593/blake3-1.0.7-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:f96a685775f87ddf75ff495dc9698703268c66c170caca977347427ef8d52324", size = 553564, upload-time = "2025-10-11T18:03:30.247Z" }, + { url = "https://files.pythonhosted.org/packages/fe/07/8b17cbf40ccd9afeed6ae9f55018181786b30ff4e079ac8bf4ca4799e47b/blake3-1.0.7-cp314-cp314t-win32.whl", hash = "sha256:0633b7d9bad87dc7fce545042353f2e056604d993f71d1dce666a9f5edc13e05", size = 227345, upload-time = "2025-10-11T18:03:31.933Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8a/ab9de8a73616350759356a483f440212bc2a22fc9aaa77cabbf06c3483db/blake3-1.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:5e356daa0089968dc1ff1d0d112e7cc1700533441d8f30ae99f835a94dc8b0f3", size = 213964, upload-time = "2025-10-11T18:03:33.919Z" }, ] [[package]] @@ -867,16 +891,16 @@ wheels = [ [[package]] name = "compressed-tensors" -version = "0.10.2" +version = "0.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "torch" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/86/d43d369abc81ec63ec7b8f6f27fc8b113ea0fd18a4116ae12063387b8b34/compressed_tensors-0.10.2.tar.gz", hash = "sha256:6de13ac535d7ffdd8890fad3d229444c33076170acaa8fab6bab8ecfa96c1d8f", size = 173459, upload-time = "2025-06-23T13:19:06.135Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/eb/2229523a539e8074b238c225d168f734f6f056ab4ea2278eefe752f4a6f3/compressed_tensors-0.10.1.tar.gz", hash = "sha256:f99ce620ddcf8a657eaa7995daf5faa8e988d4b4cadc595bf2c4ff9346c2c19a", size = 126778, upload-time = "2025-06-06T18:25:16.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ac/56bb4b6b3150783119479e2f05e32ebfc39ca6ff8e6fcd45eb178743b39e/compressed_tensors-0.10.2-py3-none-any.whl", hash = "sha256:e1b4d9bc2006e3fd3a938e59085f318fdb280c5af64688a4792bf1bc263e579d", size = 169030, upload-time = "2025-06-23T13:19:03.487Z" }, + { url = "https://files.pythonhosted.org/packages/5b/07/e70a0b9efc24a32740396c404e7213c62b8aeb4a577ed5a3f191f8d7806b/compressed_tensors-0.10.1-py3-none-any.whl", hash = "sha256:b8890735522c119900e8d4192cced0b0f70a98440ae070448cb699165c404659", size = 116998, upload-time = "2025-06-06T18:25:14.54Z" }, ] [[package]] @@ -2726,7 +2750,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.77" +version = "0.3.79" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -2737,23 +2761,23 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/cc/786184e5f6a921a2aa4d2ac51d3adf0cd037289f3becff39644bee9654ee/langchain_core-0.3.77.tar.gz", hash = "sha256:1d6f2ad6bb98dd806c6c66a822fa93808d821e9f0348b28af0814b3a149830e7", size = 580255, upload-time = "2025-10-01T14:34:37.368Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/99/f926495f467e0f43289f12e951655d267d1eddc1136c3cf4dd907794a9a7/langchain_core-0.3.79.tar.gz", hash = "sha256:024ba54a346dd9b13fb8b2342e0c83d0111e7f26fa01f545ada23ad772b55a60", size = 580895, upload-time = "2025-10-09T21:59:08.359Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/18/e7462ae0ce57caa9f6d5d975dca861e9a751e5ca253d60a809e0d833eac3/langchain_core-0.3.77-py3-none-any.whl", hash = "sha256:9966dfe3d8365847c5fb85f97dd20e3e21b1904ae87cfd9d362b7196fb516637", size = 449525, upload-time = "2025-10-01T14:34:35.672Z" }, + { url = "https://files.pythonhosted.org/packages/fc/71/46b0efaf3fc6ad2c2bd600aef500f1cb2b7038a4042f58905805630dd29d/langchain_core-0.3.79-py3-none-any.whl", hash = "sha256:92045bfda3e741f8018e1356f83be203ec601561c6a7becfefe85be5ddc58fdb", size = 449779, upload-time = "2025-10-09T21:59:06.493Z" }, ] [[package]] name = "langchain-openai" -version = "0.3.23" +version = "0.3.29" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "openai" }, { name = "tiktoken" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/f1/575120e829430f9bdcfc2c5c4121f04b1b5a143d96e572ff32399b787ef2/langchain_openai-0.3.23.tar.gz", hash = "sha256:73411c06e04bc145db7146a6fcf33dd0f1a85130499dcae988829a4441ddaa66", size = 647923, upload-time = "2025-06-13T14:24:31.388Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/56/2e2010d15118ac52760f92ebf6ce75b3508e7a1023107ea04233fd6263e0/langchain_openai-0.3.29.tar.gz", hash = "sha256:83a0455f8ce874aa1806131ca3b4db08e482be037b7457a9b3ca21a213d2ab47", size = 766499, upload-time = "2025-08-08T15:12:32.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/65/88060305d5d627841bc8da7e9fb31fb603e5b103b4e5ec5b4d1a7edfbc3b/langchain_openai-0.3.23-py3-none-any.whl", hash = "sha256:624794394482c0923823f0aac44979968d77fdcfa810e42d4b0abd8096199a40", size = 65392, upload-time = "2025-06-13T14:24:30.263Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f2/a6a73beec15e90605e6a24c4498a8592d79a72c8e81c18ed0f5e9b7308e9/langchain_openai-0.3.29-py3-none-any.whl", hash = "sha256:71ae6791b3e017ec892a8062f993edc882c6665fd8385aa66e9dc3bff8205996", size = 74316, upload-time = "2025-08-08T15:12:30.794Z" }, ] [[package]] @@ -3209,23 +3233,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4b/b4/b61eeb92c424947675492dec3a411bdbeae307dfd78162d65ab47e8c3b4f/mlx-0.29.2-cp313-cp313-manylinux_2_35_x86_64.whl", hash = "sha256:c3b9a9aee13f346d060966472954eebe99d9f1b295c9a237c9a000f1ef9adf2c", size = 648709, upload-time = "2025-09-26T22:26:03.452Z" }, ] -[[package]] -name = "mlx-lm" -version = "0.28.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "mlx" }, - { name = "numpy" }, - { name = "protobuf" }, - { name = "pyyaml" }, - { name = "transformers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/d7/fdde445c7bd443a2ed23badda6064f1477c4051543922106f365e94082cd/mlx_lm-0.28.2.tar.gz", hash = "sha256:d28752635ed5c89ff2b41361916c928e6b16f765c07b2908044e1dcaf921ed9b", size = 209374, upload-time = "2025-10-02T14:23:57.497Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/1c/89e0f60d45e364de8507065f73aeb8d2fd810d6cb95a9a512880b09399d5/mlx_lm-0.28.2-py3-none-any.whl", hash = "sha256:1501529e625d0d648216f7bb543b8b449d5fd17bd598f635536dbc1fbde6d1d6", size = 284600, upload-time = "2025-10-02T14:23:56.395Z" }, -] - [[package]] name = "mlx-metal" version = "0.29.2" @@ -3851,7 +3858,7 @@ wheels = [ [[package]] name = "openai" -version = "1.74.0" +version = "1.97.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3863,9 +3870,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/86/c605a6e84da0248f2cebfcd864b5a6076ecf78849245af5e11d2a5ec7977/openai-1.74.0.tar.gz", hash = "sha256:592c25b8747a7cad33a841958f5eb859a785caea9ee22b9e4f4a2ec062236526", size = 427571, upload-time = "2025-04-14T16:45:25.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/57/1c471f6b3efb879d26686d31582997615e969f3bb4458111c9705e56332e/openai-1.97.1.tar.gz", hash = "sha256:a744b27ae624e3d4135225da9b1c89c107a2a7e5bc4c93e5b7b5214772ce7a4e", size = 494267, upload-time = "2025-07-22T13:10:12.607Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/91/8c150f16a96367e14bd7d20e86e0bbbec3080e3eb593e63f21a7f013f8e4/openai-1.74.0-py3-none-any.whl", hash = "sha256:aff3e0f9fb209836382ec112778667027f4fd6ae38bdb2334bc9e173598b092a", size = 644790, upload-time = "2025-04-14T16:45:23.041Z" }, + { url = "https://files.pythonhosted.org/packages/ee/35/412a0e9c3f0d37c94ed764b8ac7adae2d834dbd20e69f6aca582118e0f55/openai-1.97.1-py3-none-any.whl", hash = "sha256:4e96bbdf672ec3d44968c9ea39d2c375891db1acc1794668d8149d5fa6000606", size = 764380, upload-time = "2025-07-22T13:10:10.689Z" }, ] [[package]] @@ -3904,7 +3911,7 @@ wheels = [ [[package]] name = "openpipe" -version = "4.50.0" +version = "5.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anthropic" }, @@ -3913,9 +3920,9 @@ dependencies = [ { name = "openai" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/0b/5ac4afd2253e058463fe46b44ebdf9cf153af343b457f13e9e592943c16d/openpipe-4.50.0.tar.gz", hash = "sha256:a2b1bf7a30a8d4c2cf45b85c749839ea9811e36f9d03916df8ffa343d9193a0e", size = 98954, upload-time = "2025-04-15T18:13:36.935Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/34/b487bc0ff60d3ed634e6f7bc34b5138f04e6ae319cc6578001822df93901/openpipe-5.0.0.tar.gz", hash = "sha256:040acc526fece42ba505fcedd8cd584f42482c9bd01f16b2538c9ea9c82882f4", size = 98910, upload-time = "2025-07-31T01:36:29.482Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/39/04870a3157d4ad6e8b1671f584da3e064750ccd64aa08339c6fc6dbd3a1c/openpipe-4.50.0-py3-none-any.whl", hash = "sha256:2071c3edbba3e08ceb977ad8c12d407f4da86c0c3815447fa33674d918276e5e", size = 440892, upload-time = "2025-04-15T18:13:35.258Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/516010c25a32884a87e1f8303a292f3981fa382cc7570a9ed88fb28681d5/openpipe-5.0.0-py3-none-any.whl", hash = "sha256:c04af7afb4d9bcd52e1250757dd93d0e0ed19c9ff4b524f131dd94aadf4c1a9b", size = 439951, upload-time = "2025-07-31T01:36:28.003Z" }, ] [[package]] @@ -3931,6 +3938,67 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/48/28ed9e55dcf2f453128df738210a980e09f4e468a456fa3c763dbc8be70a/opentelemetry_api-1.37.0-py3-none-any.whl", hash = "sha256:accf2024d3e89faec14302213bc39550ec0f4095d1cf5ca688e1bfb1c8612f47", size = 65732, upload-time = "2025-09-11T10:28:41.826Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/df/47fde1de15a3d5ad410e98710fac60cd3d509df5dc7ec1359b71d6bf7e70/opentelemetry_exporter_otlp-1.37.0.tar.gz", hash = "sha256:f85b1929dd0d750751cc9159376fb05aa88bb7a08b6cdbf84edb0054d93e9f26", size = 6145, upload-time = "2025-09-11T10:29:03.075Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/23/7e35e41111e3834d918e414eca41555d585e8860c9149507298bb3b9b061/opentelemetry_exporter_otlp-1.37.0-py3-none-any.whl", hash = "sha256:bd44592c6bc7fc3e5c0a9b60f2ee813c84c2800c449e59504ab93f356cc450fc", size = 7019, upload-time = "2025-09-11T10:28:44.094Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/6c/10018cbcc1e6fff23aac67d7fd977c3d692dbe5f9ef9bb4db5c1268726cc/opentelemetry_exporter_otlp_proto_common-1.37.0.tar.gz", hash = "sha256:c87a1bdd9f41fdc408d9cc9367bb53f8d2602829659f2b90be9f9d79d0bfe62c", size = 20430, upload-time = "2025-09-11T10:29:03.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/13/b4ef09837409a777f3c0af2a5b4ba9b7af34872bc43609dda0c209e4060d/opentelemetry_exporter_otlp_proto_common-1.37.0-py3-none-any.whl", hash = "sha256:53038428449c559b0c564b8d718df3314da387109c4d36bd1b94c9a641b0292e", size = 18359, upload-time = "2025-09-11T10:28:44.939Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/11/4ad0979d0bb13ae5a845214e97c8d42da43980034c30d6f72d8e0ebe580e/opentelemetry_exporter_otlp_proto_grpc-1.37.0.tar.gz", hash = "sha256:f55bcb9fc848ce05ad3dd954058bc7b126624d22c4d9e958da24d8537763bec5", size = 24465, upload-time = "2025-09-11T10:29:04.172Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/17/46630b74751031a658706bef23ac99cdc2953cd3b2d28ec90590a0766b3e/opentelemetry_exporter_otlp_proto_grpc-1.37.0-py3-none-any.whl", hash = "sha256:aee5104835bf7993b7ddaaf380b6467472abaedb1f1dbfcc54a52a7d781a3890", size = 19305, upload-time = "2025-09-11T10:28:45.776Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/e3/6e320aeb24f951449e73867e53c55542bebbaf24faeee7623ef677d66736/opentelemetry_exporter_otlp_proto_http-1.37.0.tar.gz", hash = "sha256:e52e8600f1720d6de298419a802108a8f5afa63c96809ff83becb03f874e44ac", size = 17281, upload-time = "2025-09-11T10:29:04.844Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/e9/70d74a664d83976556cec395d6bfedd9b85ec1498b778367d5f93e373397/opentelemetry_exporter_otlp_proto_http-1.37.0-py3-none-any.whl", hash = "sha256:54c42b39945a6cc9d9a2a33decb876eabb9547e0dcb49df090122773447f1aef", size = 19576, upload-time = "2025-09-11T10:28:46.726Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.58b0" @@ -3960,6 +4028,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a5/54/add1076cb37980e617723a96e29c84006983e8ad6fc589dde7f69ddc57d4/opentelemetry_instrumentation_threading-0.58b0-py3-none-any.whl", hash = "sha256:eacc072881006aceb5b9b6831bcdce718c67ef6f31ac0b32bd6a23a94d979b4a", size = 9312, upload-time = "2025-09-11T11:41:58.603Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/ea/a75f36b463a36f3c5a10c0b5292c58b31dbdde74f6f905d3d0ab2313987b/opentelemetry_proto-1.37.0.tar.gz", hash = "sha256:30f5c494faf66f77faeaefa35ed4443c5edb3b0aa46dad073ed7210e1a789538", size = 46151, upload-time = "2025-09-11T10:29:11.04Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/25/f89ea66c59bd7687e218361826c969443c4fa15dfe89733f3bf1e2a9e971/opentelemetry_proto-1.37.0-py3-none-any.whl", hash = "sha256:8ed8c066ae8828bbf0c39229979bdf583a126981142378a9cbe9d6fd5701c6e2", size = 72534, upload-time = "2025-09-11T10:28:56.831Z" }, +] + [[package]] name = "opentelemetry-sdk" version = "1.37.0" @@ -3987,6 +4067,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/90/68152b7465f50285d3ce2481b3aec2f82822e3f52e5152eeeaf516bab841/opentelemetry_semantic_conventions-0.58b0-py3-none-any.whl", hash = "sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28", size = 207954, upload-time = "2025-09-11T10:28:59.218Z" }, ] +[[package]] +name = "opentelemetry-semantic-conventions-ai" +version = "0.4.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e6/40b59eda51ac47009fb47afcdf37c6938594a0bd7f3b9fadcbc6058248e3/opentelemetry_semantic_conventions_ai-0.4.13.tar.gz", hash = "sha256:94efa9fb4ffac18c45f54a3a338ffeb7eedb7e1bb4d147786e77202e159f0036", size = 5368, upload-time = "2025-08-22T10:14:17.387Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/b5/cf25da2218910f0d6cdf7f876a06bed118c4969eacaf60a887cbaef44f44/opentelemetry_semantic_conventions_ai-0.4.13-py3-none-any.whl", hash = "sha256:883a30a6bb5deaec0d646912b5f9f6dcbb9f6f72557b73d0f2560bf25d13e2d5", size = 6080, upload-time = "2025-08-22T10:14:16.477Z" }, +] + [[package]] name = "orjson" version = "3.11.3" @@ -4579,9 +4668,9 @@ requires-dist = [ { name = "nvidia-riva-client", marker = "extra == 'riva'", specifier = "~=2.21.1" }, { name = "onnxruntime", marker = "extra == 'local-smart-turn-v3'", specifier = ">=1.20.1,<2" }, { name = "onnxruntime", marker = "extra == 'silero'", specifier = ">=1.20.1,<2" }, - { name = "openai", specifier = ">=1.74.0,<=1.99.1" }, + { name = "openai", specifier = ">=1.74.0,<3" }, { name = "opencv-python", marker = "extra == 'webrtc'", specifier = ">=4.11.0.86,<5" }, - { name = "openpipe", marker = "extra == 'openpipe'", specifier = "~=4.50.0" }, + { name = "openpipe", marker = "extra == 'openpipe'", specifier = ">=4.50.0,<6" }, { name = "opentelemetry-api", marker = "extra == 'tracing'", specifier = ">=1.33.0" }, { name = "opentelemetry-instrumentation", marker = "extra == 'tracing'", specifier = ">=0.54b0" }, { name = "opentelemetry-sdk", marker = "extra == 'tracing'", specifier = ">=1.33.0" }, @@ -4961,172 +5050,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a5/8b/7f9a061c1cc2b230f9ac02a6003fcd14c85ce1828013aecbaf45aa988d20/PyAudio-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:692d8c1446f52ed2662120bcd9ddcb5aa2b71f38bda31e58b19fb4672fffba69", size = 173655, upload-time = "2024-11-20T19:12:13.616Z" }, ] -[[package]] -name = "pybase64" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/14/43297a7b7f0c1bf0c00b596f754ee3ac946128c64d21047ccf9c9bbc5165/pybase64-1.4.2.tar.gz", hash = "sha256:46cdefd283ed9643315d952fe44de80dc9b9a811ce6e3ec97fd1827af97692d0", size = 137246, upload-time = "2025-07-27T13:08:57.808Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6d/0a7159c24ed35c8b9190b148376ad9b96598354f94ede29df74861da9ec6/pybase64-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82b4593b480773b17698fef33c68bae0e1c474ba07663fad74249370c46b46c9", size = 38240, upload-time = "2025-07-27T13:02:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/86/2e/dad4cd832a90a49d98867e824180585e7c928504987d37304bccae11a314/pybase64-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a126f29d29cb4a498db179135dbf955442a0de5b00f374523f5dcceb9074ff58", size = 31658, upload-time = "2025-07-27T13:02:20.823Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d8/30ea35dc2c8c568be93e1379efcaa35092e37efa2ce7f1985ccc63babee7/pybase64-1.4.2-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1eef93c29cc5567480d168f9cc1ebd3fc3107c65787aed2019a8ea68575a33e0", size = 65963, upload-time = "2025-07-27T13:02:22.376Z" }, - { url = "https://files.pythonhosted.org/packages/f6/da/1c22f2a21d6bb9ec2a214d15ae02d5b20a95335de218a0ecbf769c535a5c/pybase64-1.4.2-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:17b871a34aaeb0644145cb6bf28feb163f593abea11aec3dbcc34a006edfc828", size = 68887, upload-time = "2025-07-27T13:02:23.606Z" }, - { url = "https://files.pythonhosted.org/packages/ac/8d/e04d489ba99b444ce94b4d5b232365d00b0f0e8564275d7ba7434dcabe72/pybase64-1.4.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1f734e16293637a35d282ce594eb05a7a90ea3ae2bc84a3496a5df9e6b890725", size = 57503, upload-time = "2025-07-27T13:02:24.83Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b8/5ec9c334f30cf898709a084d596bf4b47aec2e07870f07bac5cf39754eca/pybase64-1.4.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:22bd38db2d990d5545dde83511edeec366630d00679dbd945472315c09041dc6", size = 54517, upload-time = "2025-07-27T13:02:26.006Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5a/6e4424ecca041e53aa7c14525f99edd43d0117c23c5d9cb14e931458a536/pybase64-1.4.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:dc65cee686dda72007b7541b2014f33ee282459c781b9b61305bd8b9cfadc8e1", size = 57167, upload-time = "2025-07-27T13:02:27.47Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d0/13f1a9467cf565eecc21dce89fb0723458d8c563d2ccfb99b96e8318dfd5/pybase64-1.4.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1e79641c420a22e49c67c046895efad05bf5f8b1dbe0dd78b4af3ab3f2923fe2", size = 57718, upload-time = "2025-07-27T13:02:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/3e/34/d80335c36ad9400b18b4f92e9f680cf7646102fe4919f7bce5786a2ccb7b/pybase64-1.4.2-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:12f5e7db522ef780a8b333dab5f7d750d270b23a1684bc2235ba50756c7ba428", size = 53021, upload-time = "2025-07-27T13:02:29.823Z" }, - { url = "https://files.pythonhosted.org/packages/68/57/504ff75f7c78df28be126fe6634083d28d7f84c17e04a74a7dcb50ab2377/pybase64-1.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a618b1e1a63e75dd40c2a397d875935ed0835464dc55cb1b91e8f880113d0444", size = 56306, upload-time = "2025-07-27T13:02:31.314Z" }, - { url = "https://files.pythonhosted.org/packages/bf/bc/2d21cda8b73c8c9f5cd3d7e6e26dd6dfc96491052112f282332a3d5bf1d9/pybase64-1.4.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89b0a51702c7746fa914e75e680ad697b979cdead6b418603f56a6fc9de2f50f", size = 50101, upload-time = "2025-07-27T13:02:32.662Z" }, - { url = "https://files.pythonhosted.org/packages/88/6d/51942e7737bb0711ca3e55db53924fd7f07166d79da5508ab8f5fd5972a8/pybase64-1.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5161b8b82f8ba5dbbc3f76e0270622a2c2fdb9ffaf092d8f774ad7ec468c027", size = 66555, upload-time = "2025-07-27T13:02:34.122Z" }, - { url = "https://files.pythonhosted.org/packages/b6/c8/c46024d196402e7be4d3fad85336863a34816c3436c51fcf9c7c0781bf11/pybase64-1.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2168de920c9b1e57850e9ff681852923a953601f73cc96a0742a42236695c316", size = 55684, upload-time = "2025-07-27T13:02:35.427Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c5/953782c9d599ff5217ee87f19e317c494cd4840afcab4c48f99cb78ca201/pybase64-1.4.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7a1e3dc977562abe40ab43483223013be71b215a5d5f3c78a666e70a5076eeec", size = 52475, upload-time = "2025-07-27T13:02:36.634Z" }, - { url = "https://files.pythonhosted.org/packages/05/fb/57d36173631aab67ca4558cdbde1047fc67a09b77f9c53addd57c7e9fdd4/pybase64-1.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4cf1e8a57449e48137ef4de00a005e24c3f1cffc0aafc488e36ceb5bb2cbb1da", size = 53943, upload-time = "2025-07-27T13:02:37.777Z" }, - { url = "https://files.pythonhosted.org/packages/75/73/23e5bb0bffac0cabe2d11d1c618f6ef73da9f430da03c5249931e3c49b63/pybase64-1.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d8e1a381ba124f26a93d5925efbf6e6c36287fc2c93d74958e8b677c30a53fc0", size = 68411, upload-time = "2025-07-27T13:02:39.302Z" }, - { url = "https://files.pythonhosted.org/packages/ce/e7/0d5c99e5e61ff5e46949a0128b49fc2c47afc0d2b815333459b17aa9d467/pybase64-1.4.2-cp310-cp310-win32.whl", hash = "sha256:8fdd9c5b60ec9a1db854f5f96bba46b80a9520069282dc1d37ff433eb8248b1f", size = 33614, upload-time = "2025-07-27T13:02:40.478Z" }, - { url = "https://files.pythonhosted.org/packages/23/40/879b6de61d7c07a2cbf76b75e9739c4938c3a1f66ac03243f2ff7ec9fb6b/pybase64-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:37a6c73f14c6539c0ad1aebf0cce92138af25c99a6e7aee637d9f9fc634c8a40", size = 35790, upload-time = "2025-07-27T13:02:41.864Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e2/75cec12880ce3f47a79a2b9a0cdc766dc0429a7ce967bb3ab3a4b55a7f6b/pybase64-1.4.2-cp310-cp310-win_arm64.whl", hash = "sha256:b3280d03b7b361622c469d005cc270d763d9e29d0a490c26addb4f82dfe71a79", size = 30900, upload-time = "2025-07-27T13:02:43.022Z" }, - { url = "https://files.pythonhosted.org/packages/da/fb/edaa56bbf04715efc3c36966cc0150e01d7a8336c3da182f850b7fd43d32/pybase64-1.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26284ef64f142067293347bcc9d501d2b5d44b92eab9d941cb10a085fb01c666", size = 38238, upload-time = "2025-07-27T13:02:44.224Z" }, - { url = "https://files.pythonhosted.org/packages/28/a4/ca1538e9adf08f5016b3543b0060c18aea9a6e805dd20712a197c509d90d/pybase64-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52dd32fe5cbfd8af8f3f034a4a65ee61948c72e5c358bf69d59543fc0dbcf950", size = 31659, upload-time = "2025-07-27T13:02:45.445Z" }, - { url = "https://files.pythonhosted.org/packages/0b/8f/f9b49926a60848ba98350dd648227ec524fb78340b47a450c4dbaf24b1bb/pybase64-1.4.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:37f133e8c96427995480bb6d396d9d49e949a3e829591845bb6a5a7f215ca177", size = 68318, upload-time = "2025-07-27T13:02:46.644Z" }, - { url = "https://files.pythonhosted.org/packages/29/9b/6ed2dd2bc8007f33b8316d6366b0901acbdd5665b419c2893b3dd48708de/pybase64-1.4.2-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6ee3874b0abbdd4c903d3989682a3f016fd84188622879f6f95a5dc5718d7e5", size = 71357, upload-time = "2025-07-27T13:02:47.937Z" }, - { url = "https://files.pythonhosted.org/packages/fb/69/be9ac8127da8d8339db7129683bd2975cecb0bf40a82731e1a492577a177/pybase64-1.4.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c69f177b1e404b22b05802127d6979acf4cb57f953c7de9472410f9c3fdece7", size = 59817, upload-time = "2025-07-27T13:02:49.163Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a2/e3e09e000b509609276ee28b71beb0b61462d4a43b3e0db0a44c8652880c/pybase64-1.4.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:80c817e88ef2ca3cc9a285fde267690a1cb821ce0da4848c921c16f0fec56fda", size = 56639, upload-time = "2025-07-27T13:02:50.384Z" }, - { url = "https://files.pythonhosted.org/packages/01/70/ad7eff88aa4f1be06db705812e1f01749606933bf8fe9df553bb04b703e6/pybase64-1.4.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7a4bb6e7e45bfdaea0f2aaf022fc9a013abe6e46ccea31914a77e10f44098688", size = 59368, upload-time = "2025-07-27T13:02:51.883Z" }, - { url = "https://files.pythonhosted.org/packages/9d/82/0cd1b4bcd2a4da7805cfa04587be783bf9583b34ac16cadc29cf119a4fa2/pybase64-1.4.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2710a80d41a2b41293cb0e5b84b5464f54aa3f28f7c43de88784d2d9702b8a1c", size = 59981, upload-time = "2025-07-27T13:02:53.16Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4c/8029a03468307dfaf0f9694d31830487ee43af5f8a73407004907724e8ac/pybase64-1.4.2-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:aa6122c8a81f6597e1c1116511f03ed42cf377c2100fe7debaae7ca62521095a", size = 54908, upload-time = "2025-07-27T13:02:54.363Z" }, - { url = "https://files.pythonhosted.org/packages/a1/8b/70bd0fe659e242efd0f60895a8ce1fe88e3a4084fd1be368974c561138c9/pybase64-1.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7e22b02505d64db308e9feeb6cb52f1d554ede5983de0befa59ac2d2ffb6a5f", size = 58650, upload-time = "2025-07-27T13:02:55.905Z" }, - { url = "https://files.pythonhosted.org/packages/64/ca/9c1d23cbc4b9beac43386a32ad53903c816063cef3f14c10d7c3d6d49a23/pybase64-1.4.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:edfe4a3c8c4007f09591f49b46a89d287ef5e8cd6630339536fe98ff077263c2", size = 52323, upload-time = "2025-07-27T13:02:57.192Z" }, - { url = "https://files.pythonhosted.org/packages/aa/29/a6292e9047248c8616dc53131a49da6c97a61616f80e1e36c73d7ef895fe/pybase64-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b79b4a53dd117ffbd03e96953f2e6bd2827bfe11afeb717ea16d9b0893603077", size = 68979, upload-time = "2025-07-27T13:02:58.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e0/cfec7b948e170395d8e88066e01f50e71195db9837151db10c14965d6222/pybase64-1.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fd9afa7a61d89d170607faf22287290045757e782089f0357b8f801d228d52c3", size = 58037, upload-time = "2025-07-27T13:02:59.753Z" }, - { url = "https://files.pythonhosted.org/packages/74/7e/0ac1850198c9c35ef631174009cee576f4d8afff3bf493ce310582976ab4/pybase64-1.4.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5c17b092e4da677a595178d2db17a5d2fafe5c8e418d46c0c4e4cde5adb8cff3", size = 54416, upload-time = "2025-07-27T13:03:00.978Z" }, - { url = "https://files.pythonhosted.org/packages/1b/45/b0b037f27e86c50e62d927f0bc1bde8b798dd55ab39197b116702e508d05/pybase64-1.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:120799274cf55f3f5bb8489eaa85142f26170564baafa7cf3e85541c46b6ab13", size = 56257, upload-time = "2025-07-27T13:03:02.201Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0d/5034598aac56336d88fd5aaf6f34630330643b51d399336b8c788d798fc5/pybase64-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:522e4e712686acec2d25de9759dda0b0618cb9f6588523528bc74715c0245c7b", size = 70889, upload-time = "2025-07-27T13:03:03.437Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3b/0645f21bb08ecf45635b624958b5f9e569069d31ecbf125dc7e0e5b83f60/pybase64-1.4.2-cp311-cp311-win32.whl", hash = "sha256:bfd828792982db8d787515535948c1e340f1819407c8832f94384c0ebeaf9d74", size = 33631, upload-time = "2025-07-27T13:03:05.194Z" }, - { url = "https://files.pythonhosted.org/packages/8f/08/24f8103c1f19e78761026cdd9f3b3be73239bc19cf5ab6fef0e8042d0bc6/pybase64-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7a9e89d40dbf833af481d1d5f1a44d173c9c4b56a7c8dba98e39a78ee87cfc52", size = 35781, upload-time = "2025-07-27T13:03:06.779Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/832fb035a0ea7eb53d776a5cfa961849e22828f6dfdfcdb9eb43ba3c0166/pybase64-1.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:ce5809fa90619b03eab1cd63fec142e6cf1d361731a9b9feacf27df76c833343", size = 30903, upload-time = "2025-07-27T13:03:07.903Z" }, - { url = "https://files.pythonhosted.org/packages/28/6d/11ede991e800797b9f5ebd528013b34eee5652df93de61ffb24503393fa5/pybase64-1.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2c75d1388855b5a1015b65096d7dbcc708e7de3245dcbedeb872ec05a09326", size = 38326, upload-time = "2025-07-27T13:03:09.065Z" }, - { url = "https://files.pythonhosted.org/packages/fe/84/87f1f565f42e2397e2aaa2477c86419f5173c3699881c42325c090982f0a/pybase64-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b621a972a01841368fdb9dedc55fd3c6e0c7217d0505ba3b1ebe95e7ef1b493", size = 31661, upload-time = "2025-07-27T13:03:10.295Z" }, - { url = "https://files.pythonhosted.org/packages/cb/2a/a24c810e7a61d2cc6f73fe9ee4872a03030887fa8654150901b15f376f65/pybase64-1.4.2-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f48c32ac6a16cbf57a5a96a073fef6ff7e3526f623cd49faa112b7f9980bafba", size = 68192, upload-time = "2025-07-27T13:03:11.467Z" }, - { url = "https://files.pythonhosted.org/packages/ee/87/d9baf98cbfc37b8657290ad4421f3a3c36aa0eafe4872c5859cfb52f3448/pybase64-1.4.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ace8b23093a6bb862477080d9059b784096ab2f97541e8bfc40d42f062875149", size = 71587, upload-time = "2025-07-27T13:03:12.719Z" }, - { url = "https://files.pythonhosted.org/packages/0b/89/3df043cc56ef3b91b7aa0c26ae822a2d7ec8da0b0fd7c309c879b0eb5988/pybase64-1.4.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1772c7532a7fb6301baea3dd3e010148dbf70cd1136a83c2f5f91bdc94822145", size = 59910, upload-time = "2025-07-27T13:03:14.266Z" }, - { url = "https://files.pythonhosted.org/packages/75/4f/6641e9edf37aeb4d4524dc7ba2168eff8d96c90e77f6283c2be3400ab380/pybase64-1.4.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:f86f7faddcba5cbfea475f8ab96567834c28bf09ca6c7c3d66ee445adac80d8f", size = 56701, upload-time = "2025-07-27T13:03:15.6Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7f/20d8ac1046f12420a0954a45a13033e75f98aade36eecd00c64e3549b071/pybase64-1.4.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0b8c8e275b5294089f314814b4a50174ab90af79d6a4850f6ae11261ff6a7372", size = 59288, upload-time = "2025-07-27T13:03:16.823Z" }, - { url = "https://files.pythonhosted.org/packages/17/ea/9c0ca570e3e50b3c6c3442e280c83b321a0464c86a9db1f982a4ff531550/pybase64-1.4.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:864d85a0470c615807ae8b97d724d068b940a2d10ac13a5f1b9e75a3ce441758", size = 60267, upload-time = "2025-07-27T13:03:18.132Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46894929d71ccedebbfb0284173b0fea96bc029cd262654ba8451a7035d6/pybase64-1.4.2-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:47254d97ed2d8351e30ecfdb9e2414547f66ba73f8a09f932c9378ff75cd10c5", size = 54801, upload-time = "2025-07-27T13:03:19.669Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/02c95218ea964f0b2469717c2c69b48e63f4ca9f18af01a5b2a29e4c1216/pybase64-1.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:264b65ecc4f0ee73f3298ab83bbd8008f7f9578361b8df5b448f985d8c63e02a", size = 58599, upload-time = "2025-07-27T13:03:20.951Z" }, - { url = "https://files.pythonhosted.org/packages/15/45/ccc21004930789b8fb439d43e3212a6c260ccddb2bf450c39a20db093f33/pybase64-1.4.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbcc2b30cd740c16c9699f596f22c7a9e643591311ae72b1e776f2d539e9dd9d", size = 52388, upload-time = "2025-07-27T13:03:23.064Z" }, - { url = "https://files.pythonhosted.org/packages/c4/45/22e46e549710c4c237d77785b6fb1bc4c44c288a5c44237ba9daf5c34b82/pybase64-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cda9f79c22d51ee4508f5a43b673565f1d26af4330c99f114e37e3186fdd3607", size = 68802, upload-time = "2025-07-27T13:03:24.673Z" }, - { url = "https://files.pythonhosted.org/packages/55/0c/232c6261b81296e5593549b36e6e7884a5da008776d12665923446322c36/pybase64-1.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0c91c6d2a7232e2a1cd10b3b75a8bb657defacd4295a1e5e80455df2dfc84d4f", size = 57841, upload-time = "2025-07-27T13:03:25.948Z" }, - { url = "https://files.pythonhosted.org/packages/20/8a/b35a615ae6f04550d696bb179c414538b3b477999435fdd4ad75b76139e4/pybase64-1.4.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a370dea7b1cee2a36a4d5445d4e09cc243816c5bc8def61f602db5a6f5438e52", size = 54320, upload-time = "2025-07-27T13:03:27.495Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a9/8bd4f9bcc53689f1b457ecefed1eaa080e4949d65a62c31a38b7253d5226/pybase64-1.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9aa4de83f02e462a6f4e066811c71d6af31b52d7484de635582d0e3ec3d6cc3e", size = 56482, upload-time = "2025-07-27T13:03:28.942Z" }, - { url = "https://files.pythonhosted.org/packages/75/e5/4a7735b54a1191f61c3f5c2952212c85c2d6b06eb5fb3671c7603395f70c/pybase64-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83a1c2f9ed00fee8f064d548c8654a480741131f280e5750bb32475b7ec8ee38", size = 70959, upload-time = "2025-07-27T13:03:30.171Z" }, - { url = "https://files.pythonhosted.org/packages/d3/67/e2b6cb32c782e12304d467418e70da0212567f42bd4d3b5eb1fdf64920ad/pybase64-1.4.2-cp312-cp312-win32.whl", hash = "sha256:a6e5688b18d558e8c6b8701cc8560836c4bbeba61d33c836b4dba56b19423716", size = 33683, upload-time = "2025-07-27T13:03:31.775Z" }, - { url = "https://files.pythonhosted.org/packages/4f/bc/d5c277496063a09707486180f17abbdbdebbf2f5c4441b20b11d3cb7dc7c/pybase64-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:c995d21b8bd08aa179cd7dd4db0695c185486ecc72da1e8f6c37ec86cadb8182", size = 35817, upload-time = "2025-07-27T13:03:32.99Z" }, - { url = "https://files.pythonhosted.org/packages/e6/69/e4be18ae685acff0ae77f75d4586590f29d2cd187bf603290cf1d635cad4/pybase64-1.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:e254b9258c40509c2ea063a7784f6994988f3f26099d6e08704e3c15dfed9a55", size = 30900, upload-time = "2025-07-27T13:03:34.499Z" }, - { url = "https://files.pythonhosted.org/packages/f4/56/5337f27a8b8d2d6693f46f7b36bae47895e5820bfa259b0072574a4e1057/pybase64-1.4.2-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:0f331aa59549de21f690b6ccc79360ffed1155c3cfbc852eb5c097c0b8565a2b", size = 33888, upload-time = "2025-07-27T13:03:35.698Z" }, - { url = "https://files.pythonhosted.org/packages/4c/09/f3f4b11fc9beda7e8625e29fb0f549958fcbb34fea3914e1c1d95116e344/pybase64-1.4.2-cp313-cp313-android_21_x86_64.whl", hash = "sha256:9dad20bf1f3ed9e6fe566c4c9d07d9a6c04f5a280daebd2082ffb8620b0a880d", size = 40796, upload-time = "2025-07-27T13:03:36.927Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/470768f0fe6de0aa302a8cb1bdf2f9f5cffc3f69e60466153be68bc953aa/pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:69d3f0445b0faeef7bb7f93bf8c18d850785e2a77f12835f49e524cc54af04e7", size = 30914, upload-time = "2025-07-27T13:03:38.475Z" }, - { url = "https://files.pythonhosted.org/packages/75/6b/d328736662665e0892409dc410353ebef175b1be5eb6bab1dad579efa6df/pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2372b257b1f4dd512f317fb27e77d313afd137334de64c87de8374027aacd88a", size = 31380, upload-time = "2025-07-27T13:03:39.7Z" }, - { url = "https://files.pythonhosted.org/packages/ca/96/7ff718f87c67f4147c181b73d0928897cefa17dc75d7abc6e37730d5908f/pybase64-1.4.2-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fb794502b4b1ec91c4ca5d283ae71aef65e3de7721057bd9e2b3ec79f7a62d7d", size = 38230, upload-time = "2025-07-27T13:03:41.637Z" }, - { url = "https://files.pythonhosted.org/packages/4d/58/a3307b048d799ff596a3c7c574fcba66f9b6b8c899a3c00a698124ca7ad5/pybase64-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d5c532b03fd14a5040d6cf6571299a05616f925369c72ddf6fe2fb643eb36fed", size = 38319, upload-time = "2025-07-27T13:03:42.847Z" }, - { url = "https://files.pythonhosted.org/packages/08/a7/0bda06341b0a2c830d348c6e1c4d348caaae86c53dc9a046e943467a05e9/pybase64-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f699514dc1d5689ca9cf378139e0214051922732f9adec9404bc680a8bef7c0", size = 31655, upload-time = "2025-07-27T13:03:44.426Z" }, - { url = "https://files.pythonhosted.org/packages/87/df/e1d6e8479e0c5113c2c63c7b44886935ce839c2d99884c7304ca9e86547c/pybase64-1.4.2-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:cd3e8713cbd32c8c6aa935feaf15c7670e2b7e8bfe51c24dc556811ebd293a29", size = 68232, upload-time = "2025-07-27T13:03:45.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/ab/db4dbdfccb9ca874d6ce34a0784761471885d96730de85cee3d300381529/pybase64-1.4.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d377d48acf53abf4b926c2a7a24a19deb092f366a04ffd856bf4b3aa330b025d", size = 71608, upload-time = "2025-07-27T13:03:47.01Z" }, - { url = "https://files.pythonhosted.org/packages/11/e9/508df958563951045d728bbfbd3be77465f9231cf805cb7ccaf6951fc9f1/pybase64-1.4.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d83c076e78d619b9e1dd674e2bf5fb9001aeb3e0b494b80a6c8f6d4120e38cd9", size = 59912, upload-time = "2025-07-27T13:03:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/f2/58/7f2cef1ceccc682088958448d56727369de83fa6b29148478f4d2acd107a/pybase64-1.4.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:ab9cdb6a8176a5cb967f53e6ad60e40c83caaa1ae31c5e1b29e5c8f507f17538", size = 56413, upload-time = "2025-07-27T13:03:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/08/7c/7e0af5c5728fa7e2eb082d88eca7c6bd17429be819d58518e74919d42e66/pybase64-1.4.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:adf0c103ad559dbfb9fe69edfd26a15c65d9c991a5ab0a25b04770f9eb0b9484", size = 59311, upload-time = "2025-07-27T13:03:51.238Z" }, - { url = "https://files.pythonhosted.org/packages/03/8b/09825d0f37e45b9a3f546e5f990b6cf2dd838e54ea74122c2464646e0c77/pybase64-1.4.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:0d03ef2f253d97ce0685d3624bf5e552d716b86cacb8a6c971333ba4b827e1fc", size = 60282, upload-time = "2025-07-27T13:03:52.56Z" }, - { url = "https://files.pythonhosted.org/packages/9c/3f/3711d2413f969bfd5b9cc19bc6b24abae361b7673ff37bcb90c43e199316/pybase64-1.4.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:e565abf906efee76ae4be1aef5df4aed0fda1639bc0d7732a3dafef76cb6fc35", size = 54845, upload-time = "2025-07-27T13:03:54.167Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3c/4c7ce1ae4d828c2bb56d144322f81bffbaaac8597d35407c3d7cbb0ff98f/pybase64-1.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3c6a5f15fd03f232fc6f295cce3684f7bb08da6c6d5b12cc771f81c9f125cc6", size = 58615, upload-time = "2025-07-27T13:03:55.494Z" }, - { url = "https://files.pythonhosted.org/packages/f5/8f/c2fc03bf4ed038358620065c75968a30184d5d3512d09d3ef9cc3bd48592/pybase64-1.4.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bad9e3db16f448728138737bbd1af9dc2398efd593a8bdd73748cc02cd33f9c6", size = 52434, upload-time = "2025-07-27T13:03:56.808Z" }, - { url = "https://files.pythonhosted.org/packages/e2/0a/757d6df0a60327c893cfae903e15419914dd792092dc8cc5c9523d40bc9b/pybase64-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2683ef271328365c31afee0ed8fa29356fb8fb7c10606794656aa9ffb95e92be", size = 68824, upload-time = "2025-07-27T13:03:58.735Z" }, - { url = "https://files.pythonhosted.org/packages/a0/14/84abe2ed8c29014239be1cfab45dfebe5a5ca779b177b8b6f779bd8b69da/pybase64-1.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:265b20089cd470079114c09bb74b101b3bfc3c94ad6b4231706cf9eff877d570", size = 57898, upload-time = "2025-07-27T13:04:00.379Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c6/d193031f90c864f7b59fa6d1d1b5af41f0f5db35439988a8b9f2d1b32a13/pybase64-1.4.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e53173badead10ef8b839aa5506eecf0067c7b75ad16d9bf39bc7144631f8e67", size = 54319, upload-time = "2025-07-27T13:04:01.742Z" }, - { url = "https://files.pythonhosted.org/packages/cb/37/ec0c7a610ff8f994ee6e0c5d5d66b6b6310388b96ebb347b03ae39870fdf/pybase64-1.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5823b8dcf74da7da0f761ed60c961e8928a6524e520411ad05fe7f9f47d55b40", size = 56472, upload-time = "2025-07-27T13:04:03.089Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5a/e585b74f85cedd261d271e4c2ef333c5cfce7e80750771808f56fee66b98/pybase64-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1237f66c54357d325390da60aa5e21c6918fbcd1bf527acb9c1f4188c62cb7d5", size = 70966, upload-time = "2025-07-27T13:04:04.361Z" }, - { url = "https://files.pythonhosted.org/packages/ad/20/1b2fdd98b4ba36008419668c813025758214c543e362c66c49214ecd1127/pybase64-1.4.2-cp313-cp313-win32.whl", hash = "sha256:b0b851eb4f801d16040047f6889cca5e9dfa102b3e33f68934d12511245cef86", size = 33681, upload-time = "2025-07-27T13:04:06.126Z" }, - { url = "https://files.pythonhosted.org/packages/ff/64/3df4067d169c047054889f34b5a946cbe3785bca43404b93c962a5461a41/pybase64-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:19541c6e26d17d9522c02680fe242206ae05df659c82a657aabadf209cd4c6c7", size = 35822, upload-time = "2025-07-27T13:04:07.752Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fd/db505188adf812e60ee923f196f9deddd8a1895b2b29b37f5db94afc3b1c/pybase64-1.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:77a191863d576c0a5dd81f8a568a5ca15597cc980ae809dce62c717c8d42d8aa", size = 30899, upload-time = "2025-07-27T13:04:09.062Z" }, - { url = "https://files.pythonhosted.org/packages/d9/27/5f5fecd206ec1e06e1608a380af18dcb76a6ab08ade6597a3251502dcdb2/pybase64-1.4.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2e194bbabe3fdf9e47ba9f3e157394efe0849eb226df76432126239b3f44992c", size = 38677, upload-time = "2025-07-27T13:04:10.334Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0f/abe4b5a28529ef5f74e8348fa6a9ef27d7d75fbd98103d7664cf485b7d8f/pybase64-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:39aef1dadf4a004f11dd09e703abaf6528a87c8dbd39c448bb8aebdc0a08c1be", size = 32066, upload-time = "2025-07-27T13:04:11.641Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7e/ea0ce6a7155cada5526017ec588b6d6185adea4bf9331565272f4ef583c2/pybase64-1.4.2-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:91cb920c7143e36ec8217031282c8651da3b2206d70343f068fac0e7f073b7f9", size = 72300, upload-time = "2025-07-27T13:04:12.969Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/e64c7a056c9ec48dfe130d1295e47a8c2b19c3984488fc08e5eaa1e86c88/pybase64-1.4.2-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6958631143fb9e71f9842000da042ec2f6686506b6706e2dfda29e97925f6aa0", size = 75520, upload-time = "2025-07-27T13:04:14.374Z" }, - { url = "https://files.pythonhosted.org/packages/43/e0/e5f93b2e1cb0751a22713c4baa6c6eaf5f307385e369180486c8316ed21e/pybase64-1.4.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dc35f14141ef3f1ac70d963950a278a2593af66fe5a1c7a208e185ca6278fa25", size = 65384, upload-time = "2025-07-27T13:04:16.204Z" }, - { url = "https://files.pythonhosted.org/packages/ff/23/8c645a1113ad88a1c6a3d0e825e93ef8b74ad3175148767853a0a4d7626e/pybase64-1.4.2-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:5d949d2d677859c3a8507e1b21432a039d2b995e0bd3fe307052b6ded80f207a", size = 60471, upload-time = "2025-07-27T13:04:17.947Z" }, - { url = "https://files.pythonhosted.org/packages/8b/81/edd0f7d8b0526b91730a0dd4ce6b4c8be2136cd69d424afe36235d2d2a06/pybase64-1.4.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:09caacdd3e15fe7253a67781edd10a6a918befab0052a2a3c215fe5d1f150269", size = 63945, upload-time = "2025-07-27T13:04:19.383Z" }, - { url = "https://files.pythonhosted.org/packages/a5/a5/edc224cd821fd65100b7af7c7e16b8f699916f8c0226c9c97bbae5a75e71/pybase64-1.4.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:e44b0e793b23f28ea0f15a9754bd0c960102a2ac4bccb8fafdedbd4cc4d235c0", size = 64858, upload-time = "2025-07-27T13:04:20.807Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/92853f968f1af7e42b7e54d21bdd319097b367e7dffa2ca20787361df74c/pybase64-1.4.2-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:849f274d0bcb90fc6f642c39274082724d108e41b15f3a17864282bd41fc71d5", size = 58557, upload-time = "2025-07-27T13:04:22.229Z" }, - { url = "https://files.pythonhosted.org/packages/76/09/0ec6bd2b2303b0ea5c6da7535edc9a608092075ef8c0cdd96e3e726cd687/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:528dba7ef1357bd7ce1aea143084501f47f5dd0fff7937d3906a68565aa59cfe", size = 63624, upload-time = "2025-07-27T13:04:23.952Z" }, - { url = "https://files.pythonhosted.org/packages/73/6e/52cb1ced2a517a3118b2e739e9417432049013ac7afa15d790103059e8e4/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:1da54be743d9a68671700cfe56c3ab8c26e8f2f5cc34eface905c55bc3a9af94", size = 56174, upload-time = "2025-07-27T13:04:25.419Z" }, - { url = "https://files.pythonhosted.org/packages/5b/9d/820fe79347467e48af985fe46180e1dd28e698ade7317bebd66de8a143f5/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9b07c0406c3eaa7014499b0aacafb21a6d1146cfaa85d56f0aa02e6d542ee8f3", size = 72640, upload-time = "2025-07-27T13:04:26.824Z" }, - { url = "https://files.pythonhosted.org/packages/53/58/e863e10d08361e694935c815b73faad7e1ab03f99ae154d86c4e2f331896/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:312f2aa4cf5d199a97fbcaee75d2e59ebbaafcd091993eb373b43683498cdacb", size = 62453, upload-time = "2025-07-27T13:04:28.562Z" }, - { url = "https://files.pythonhosted.org/packages/95/f0/c392c4ac8ccb7a34b28377c21faa2395313e3c676d76c382642e19a20703/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad59362fc267bf15498a318c9e076686e4beeb0dfe09b457fabbc2b32468b97a", size = 58103, upload-time = "2025-07-27T13:04:29.996Z" }, - { url = "https://files.pythonhosted.org/packages/32/30/00ab21316e7df8f526aa3e3dc06f74de6711d51c65b020575d0105a025b2/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:01593bd064e7dcd6c86d04e94e44acfe364049500c20ac68ca1e708fbb2ca970", size = 60779, upload-time = "2025-07-27T13:04:31.549Z" }, - { url = "https://files.pythonhosted.org/packages/a6/65/114ca81839b1805ce4a2b7d58bc16e95634734a2059991f6382fc71caf3e/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5b81547ad8ea271c79fdf10da89a1e9313cb15edcba2a17adf8871735e9c02a0", size = 74684, upload-time = "2025-07-27T13:04:32.976Z" }, - { url = "https://files.pythonhosted.org/packages/54/8f/aa9d445b9bb693b8f6bb1456bd6d8576d79b7a63bf6c69af3a539235b15f/pybase64-1.4.2-cp313-cp313t-win32.whl", hash = "sha256:7edbe70b5654545a37e6e6b02de738303b1bbdfcde67f6cfec374cfb5cc4099e", size = 33961, upload-time = "2025-07-27T13:04:34.806Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e5/da37cfb173c646fd4fc7c6aae2bc41d40de2ee49529854af8f4e6f498b45/pybase64-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:385690addf87c25d6366fab5d8ff512eed8a7ecb18da9e8152af1c789162f208", size = 36199, upload-time = "2025-07-27T13:04:36.223Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/1eb68fb7d00f2cec8bd9838e2a30d183d6724ae06e745fd6e65216f170ff/pybase64-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c2070d0aa88580f57fe15ca88b09f162e604d19282915a95a3795b5d3c1c05b5", size = 31221, upload-time = "2025-07-27T13:04:37.704Z" }, - { url = "https://files.pythonhosted.org/packages/99/bf/00a87d951473ce96c8c08af22b6983e681bfabdb78dd2dcf7ee58eac0932/pybase64-1.4.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:4157ad277a32cf4f02a975dffc62a3c67d73dfa4609b2c1978ef47e722b18b8e", size = 30924, upload-time = "2025-07-27T13:04:39.189Z" }, - { url = "https://files.pythonhosted.org/packages/ae/43/dee58c9d60e60e6fb32dc6da722d84592e22f13c277297eb4ce6baf99a99/pybase64-1.4.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e113267dc349cf624eb4f4fbf53fd77835e1aa048ac6877399af426aab435757", size = 31390, upload-time = "2025-07-27T13:04:40.995Z" }, - { url = "https://files.pythonhosted.org/packages/e1/11/b28906fc2e330b8b1ab4bc845a7bef808b8506734e90ed79c6062b095112/pybase64-1.4.2-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:cea5aaf218fd9c5c23afacfe86fd4464dfedc1a0316dd3b5b4075b068cc67df0", size = 38212, upload-time = "2025-07-27T13:04:42.729Z" }, - { url = "https://files.pythonhosted.org/packages/24/9e/868d1e104413d14b19feaf934fc7fad4ef5b18946385f8bb79684af40f24/pybase64-1.4.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:41213497abbd770435c7a9c8123fb02b93709ac4cf60155cd5aefc5f3042b600", size = 38303, upload-time = "2025-07-27T13:04:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/a3/73/f7eac96ca505df0600280d6bfc671a9e2e2f947c2b04b12a70e36412f7eb/pybase64-1.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c8b522df7ee00f2ac1993ccd5e1f6608ae7482de3907668c2ff96a83ef213925", size = 31669, upload-time = "2025-07-27T13:04:45.845Z" }, - { url = "https://files.pythonhosted.org/packages/c6/43/8e18bea4fd455100112d6a73a83702843f067ef9b9272485b6bdfd9ed2f0/pybase64-1.4.2-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:06725022e540c5b098b978a0418ca979773e2cbdbb76f10bd97536f2ad1c5b49", size = 68452, upload-time = "2025-07-27T13:04:47.788Z" }, - { url = "https://files.pythonhosted.org/packages/e4/2e/851eb51284b97354ee5dfa1309624ab90920696e91a33cd85b13d20cc5c1/pybase64-1.4.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a3e54dcf0d0305ec88473c9d0009f698cabf86f88a8a10090efeff2879c421bb", size = 71674, upload-time = "2025-07-27T13:04:49.294Z" }, - { url = "https://files.pythonhosted.org/packages/57/0d/5cf1e5dc64aec8db43e8dee4e4046856d639a72bcb0fb3e716be42ced5f1/pybase64-1.4.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67675cee727a60dc91173d2790206f01aa3c7b3fbccfa84fd5c1e3d883fe6caa", size = 60027, upload-time = "2025-07-27T13:04:50.769Z" }, - { url = "https://files.pythonhosted.org/packages/a4/8e/3479266bc0e65f6cc48b3938d4a83bff045330649869d950a378f2ddece0/pybase64-1.4.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:753da25d4fd20be7bda2746f545935773beea12d5cb5ec56ec2d2960796477b1", size = 56461, upload-time = "2025-07-27T13:04:52.37Z" }, - { url = "https://files.pythonhosted.org/packages/20/b6/f2b6cf59106dd78bae8717302be5b814cec33293504ad409a2eb752ad60c/pybase64-1.4.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a78c768ce4ca550885246d14babdb8923e0f4a848dfaaeb63c38fc99e7ea4052", size = 59446, upload-time = "2025-07-27T13:04:53.967Z" }, - { url = "https://files.pythonhosted.org/packages/16/70/3417797dfccdfdd0a54e4ad17c15b0624f0fc2d6a362210f229f5c4e8fd0/pybase64-1.4.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:51b17f36d890c92f0618fb1c8db2ccc25e6ed07afa505bab616396fc9b0b0492", size = 60350, upload-time = "2025-07-27T13:04:55.881Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c6/6e4269dd98d150ae95d321b311a345eae0f7fd459d97901b4a586d7513bb/pybase64-1.4.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f92218d667049ab4f65d54fa043a88ffdb2f07fff1f868789ef705a5221de7ec", size = 54989, upload-time = "2025-07-27T13:04:57.436Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e8/18c1b0c255f964fafd0412b0d5a163aad588aeccb8f84b9bf9c8611d80f6/pybase64-1.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3547b3d1499919a06491b3f879a19fbe206af2bd1a424ecbb4e601eb2bd11fea", size = 58724, upload-time = "2025-07-27T13:04:59.406Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ad/ddfbd2125fc20b94865fb232b2e9105376fa16eee492e4b7786d42a86cbf/pybase64-1.4.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:958af7b0e09ddeb13e8c2330767c47b556b1ade19c35370f6451d139cde9f2a9", size = 52285, upload-time = "2025-07-27T13:05:01.198Z" }, - { url = "https://files.pythonhosted.org/packages/b6/4c/b9d4ec9224add33c84b925a03d1a53cd4106efb449ea8e0ae7795fed7bf7/pybase64-1.4.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4facc57f6671e2229a385a97a618273e7be36a9ea0a9d1c1b9347f14d19ceba8", size = 69036, upload-time = "2025-07-27T13:05:03.109Z" }, - { url = "https://files.pythonhosted.org/packages/92/38/7b96794da77bed3d9b4fea40f14ae563648fba83a696e7602fabe60c0eb7/pybase64-1.4.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a32fc57d05d73a7c9b0ca95e9e265e21cf734195dc6873829a890058c35f5cfd", size = 57938, upload-time = "2025-07-27T13:05:04.744Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c5/ae8bbce3c322d1b074e79f51f5df95961fe90cb8748df66c6bc97616e974/pybase64-1.4.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3dc853243c81ce89cc7318e6946f860df28ddb7cd2a0648b981652d9ad09ee5a", size = 54474, upload-time = "2025-07-27T13:05:06.662Z" }, - { url = "https://files.pythonhosted.org/packages/15/9a/c09887c4bb1b43c03fc352e2671ef20c6686c6942a99106a45270ee5b840/pybase64-1.4.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0e6d863a86b3e7bc6ac9bd659bebda4501b9da842521111b0b0e54eb51295df5", size = 56533, upload-time = "2025-07-27T13:05:08.368Z" }, - { url = "https://files.pythonhosted.org/packages/4f/0f/d5114d63d35d085639606a880cb06e2322841cd4b213adfc14d545c1186f/pybase64-1.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6579475140ff2067903725d8aca47f5747bcb211597a1edd60b58f6d90ada2bd", size = 71030, upload-time = "2025-07-27T13:05:10.3Z" }, - { url = "https://files.pythonhosted.org/packages/40/0e/fe6f1ed22ea52eb99f490a8441815ba21de288f4351aeef4968d71d20d2d/pybase64-1.4.2-cp314-cp314-win32.whl", hash = "sha256:373897f728d7b4f241a1f803ac732c27b6945d26d86b2741ad9b75c802e4e378", size = 34174, upload-time = "2025-07-27T13:05:12.254Z" }, - { url = "https://files.pythonhosted.org/packages/71/46/0e15bea52ffc63e8ae7935e945accbaf635e0aefa26d3e31fdf9bc9dcd01/pybase64-1.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:1afe3361344617d298c1d08bc657ef56d0f702d6b72cb65d968b2771017935aa", size = 36308, upload-time = "2025-07-27T13:05:13.898Z" }, - { url = "https://files.pythonhosted.org/packages/4f/dc/55849fee2577bda77c1e078da04cc9237e8e474a8c8308deb702a26f2511/pybase64-1.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:f131c9360babe522f3d90f34da3f827cba80318125cf18d66f2ee27e3730e8c4", size = 31341, upload-time = "2025-07-27T13:05:15.553Z" }, - { url = "https://files.pythonhosted.org/packages/39/44/c69d088e28b25e70ac742b6789cde038473815b2a69345c4bae82d5e244d/pybase64-1.4.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2583ac304131c1bd6e3120b0179333610f18816000db77c0a2dd6da1364722a8", size = 38678, upload-time = "2025-07-27T13:05:17.544Z" }, - { url = "https://files.pythonhosted.org/packages/00/93/2860ec067497b9cbb06242f96d44caebbd9eed32174e4eb8c1ffef760f94/pybase64-1.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:75a8116be4ea4cdd30a5c4f1a6f3b038e0d457eb03c8a2685d8ce2aa00ef8f92", size = 32066, upload-time = "2025-07-27T13:05:19.18Z" }, - { url = "https://files.pythonhosted.org/packages/d3/55/1e96249a38759332e8a01b31c370d88c60ceaf44692eb6ba4f0f451ee496/pybase64-1.4.2-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:217ea776a098d7c08668e5526b9764f5048bbfd28cac86834217ddfe76a4e3c4", size = 72465, upload-time = "2025-07-27T13:05:20.866Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ab/0f468605b899f3e35dbb7423fba3ff98aeed1ec16abb02428468494a58f4/pybase64-1.4.2-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ec14683e343c95b14248cdfdfa78c052582be7a3865fd570aa7cffa5ab5cf37", size = 75693, upload-time = "2025-07-27T13:05:22.896Z" }, - { url = "https://files.pythonhosted.org/packages/91/d1/9980a0159b699e2489baba05b71b7c953b29249118ba06fdbb3e9ea1b9b5/pybase64-1.4.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:480ecf21e1e956c5a10d3cf7b3b7e75bce3f9328cf08c101e4aab1925d879f34", size = 65577, upload-time = "2025-07-27T13:05:25Z" }, - { url = "https://files.pythonhosted.org/packages/16/86/b27e7b95f9863d245c0179a7245582eda3d262669d8f822777364d8fd7d5/pybase64-1.4.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:1fe1ebdc55e9447142e2f6658944aadfb5a4fbf03dbd509be34182585515ecc1", size = 60662, upload-time = "2025-07-27T13:05:27.138Z" }, - { url = "https://files.pythonhosted.org/packages/28/87/a7f0dde0abc26bfbee761f1d3558eb4b139f33ddd9fe1f6825ffa7daa22d/pybase64-1.4.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c793a2b06753accdaf5e1a8bbe5d800aab2406919e5008174f989a1ca0081411", size = 64179, upload-time = "2025-07-27T13:05:28.996Z" }, - { url = "https://files.pythonhosted.org/packages/1e/88/5d6fa1c60e1363b4cac4c396978f39e9df4689e75225d7d9c0a5998e3a14/pybase64-1.4.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6acae6e1d1f7ebe40165f08076c7a73692b2bf9046fefe673f350536e007f556", size = 64968, upload-time = "2025-07-27T13:05:30.818Z" }, - { url = "https://files.pythonhosted.org/packages/20/6e/2ed585af5b2211040445d9849326dd2445320c9316268794f5453cfbaf30/pybase64-1.4.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:88b91cd0949358aadcea75f8de5afbcf3c8c5fb9ec82325bd24285b7119cf56e", size = 58738, upload-time = "2025-07-27T13:05:32.629Z" }, - { url = "https://files.pythonhosted.org/packages/ce/94/e2960b56322eabb3fbf303fc5a72e6444594c1b90035f3975c6fe666db5c/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:53316587e1b1f47a11a5ff068d3cbd4a3911c291f2aec14882734973684871b2", size = 63802, upload-time = "2025-07-27T13:05:34.687Z" }, - { url = "https://files.pythonhosted.org/packages/95/47/312139d764c223f534f751528ce3802887c279125eac64f71cd3b4e05abc/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:caa7f20f43d00602cf9043b5ba758d54f5c41707d3709b2a5fac17361579c53c", size = 56341, upload-time = "2025-07-27T13:05:36.554Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d7/aec9a6ed53b128dac32f8768b646ca5730c88eef80934054d7fa7d02f3ef/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2d93817e24fdd79c534ed97705df855af6f1d2535ceb8dfa80da9de75482a8d7", size = 72838, upload-time = "2025-07-27T13:05:38.459Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a8/6ccc54c5f1f7c3450ad7c56da10c0f131d85ebe069ea6952b5b42f2e92d9/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:63cd769b51474d8d08f7f2ce73b30380d9b4078ec92ea6b348ea20ed1e1af88a", size = 62633, upload-time = "2025-07-27T13:05:40.624Z" }, - { url = "https://files.pythonhosted.org/packages/34/22/2b9d89f8ff6f2a01d6d6a88664b20a4817049cfc3f2c62caca040706660c/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cd07e6a9993c392ec8eb03912a43c6a6b21b2deb79ee0d606700fe276e9a576f", size = 58282, upload-time = "2025-07-27T13:05:42.565Z" }, - { url = "https://files.pythonhosted.org/packages/b2/14/dbf6266177532a6a11804ac080ebffcee272f491b92820c39886ee20f201/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:6a8944e8194adff4668350504bc6b7dbde2dab9244c88d99c491657d145b5af5", size = 60948, upload-time = "2025-07-27T13:05:44.48Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7a/b2ae9046a66dd5746cd72836a41386517b1680bea5ce02f2b4f1c9ebc688/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04ab398ec4b6a212af57f6a21a6336d5a1d754ff4ccb215951366ab9080481b2", size = 74854, upload-time = "2025-07-27T13:05:46.416Z" }, - { url = "https://files.pythonhosted.org/packages/ef/7e/9856f6d6c38a7b730e001123d2d9fa816b8b1a45f0cdee1d509d5947b047/pybase64-1.4.2-cp314-cp314t-win32.whl", hash = "sha256:3b9201ecdcb1c3e23be4caebd6393a4e6615bd0722528f5413b58e22e3792dd3", size = 34490, upload-time = "2025-07-27T13:05:48.304Z" }, - { url = "https://files.pythonhosted.org/packages/c7/38/8523a9dc1ec8704dedbe5ccc95192ae9a7585f7eec85cc62946fe3cacd32/pybase64-1.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:36e9b0cad8197136d73904ef5a71d843381d063fd528c5ab203fc4990264f682", size = 36680, upload-time = "2025-07-27T13:05:50.264Z" }, - { url = "https://files.pythonhosted.org/packages/3c/52/5600104ef7b85f89fb8ec54f73504ead3f6f0294027e08d281f3cafb5c1a/pybase64-1.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:f25140496b02db0e7401567cd869fb13b4c8118bf5c2428592ec339987146d8b", size = 31600, upload-time = "2025-07-27T13:05:52.24Z" }, - { url = "https://files.pythonhosted.org/packages/32/34/b67371f4fcedd5e2def29b1cf92a4311a72f590c04850f370c75297b48ce/pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:b4eed40a5f1627ee65613a6ac834a33f8ba24066656f569c852f98eb16f6ab5d", size = 38667, upload-time = "2025-07-27T13:07:25.315Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3e/e57fe09ed1c7e740d21c37023c5f7c8963b4c36380f41d10261cc76f93b4/pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:57885fa521e9add235af4db13e9e048d3a2934cd27d7c5efac1925e1b4d6538d", size = 32094, upload-time = "2025-07-27T13:07:28.235Z" }, - { url = "https://files.pythonhosted.org/packages/51/34/f40d3262c3953814b9bcdcf858436bd5bc1133a698be4bcc7ed2a8c0730d/pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eef9255d926c64e2fca021d3aee98023bacb98e1518e5986d6aab04102411b04", size = 43212, upload-time = "2025-07-27T13:07:31.327Z" }, - { url = "https://files.pythonhosted.org/packages/8c/2a/5e05d25718cb8ffd68bd46553ddfd2b660893d937feda1716b8a3b21fb38/pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89614ea2d2329b6708746c540e0f14d692125df99fb1203ff0de948d9e68dfc9", size = 35789, upload-time = "2025-07-27T13:07:34.026Z" }, - { url = "https://files.pythonhosted.org/packages/d5/9d/f56c3ee6e94faaae2896ecaf666428330cb24096abf7d2427371bb2b403a/pybase64-1.4.2-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:e401cecd2d7ddcd558768b2140fd4430746be4d17fb14c99eec9e40789df136d", size = 35861, upload-time = "2025-07-27T13:07:37.099Z" }, - { url = "https://files.pythonhosted.org/packages/fb/04/bfe2bd0d76385750f3541724b4abfe4ea111b3cc01ff7e83f410054adc30/pybase64-1.4.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4b29c93414ba965777643a9d98443f08f76ac04519ad717aa859113695372a07", size = 38226, upload-time = "2025-07-27T13:07:40.121Z" }, - { url = "https://files.pythonhosted.org/packages/22/13/c717855760b78ded1a9d308984c7e3e99fcf79c6cac5a231ed8c1238218f/pybase64-1.4.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e0c3353c0bf099c5c3f8f750202c486abee8f23a566b49e9e7b1222fbf5f259", size = 31524, upload-time = "2025-07-27T13:07:43.946Z" }, - { url = "https://files.pythonhosted.org/packages/cf/da/2b7e69abfc62abe4d54b10d1e09ec78021a6b9b2d7e6e7b632243a19433e/pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4f98c5c6152d3c01d933fcde04322cd9ddcf65b5346034aac69a04c1a7cbb012", size = 40667, upload-time = "2025-07-27T13:07:46.715Z" }, - { url = "https://files.pythonhosted.org/packages/f1/11/ba738655fb3ba85c7a0605eddd2709fef606e654840c72ee5c5ff7ab29bf/pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9096a4977b7aff7ef250f759fb6a4b6b7b6199d99c84070c7fc862dd3b208b34", size = 41290, upload-time = "2025-07-27T13:07:49.534Z" }, - { url = "https://files.pythonhosted.org/packages/5d/38/2d5502fcaf712297b95c1b6ca924656dd7d17501fd7f9c9e0b3bbf8892ef/pybase64-1.4.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49d8597e2872966399410502310b1e2a5b7e8d8ba96766ee1fe242e00bd80775", size = 35438, upload-time = "2025-07-27T13:07:52.327Z" }, - { url = "https://files.pythonhosted.org/packages/b6/db/e03b8b6daa60a3fbef21741403e0cf18b2aff3beebdf6e3596bb9bab16c7/pybase64-1.4.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ef16366565389a287df82659e055e88bdb6c36e46a3394950903e0a9cb2e5bf", size = 36121, upload-time = "2025-07-27T13:07:55.54Z" }, - { url = "https://files.pythonhosted.org/packages/0e/bf/5ebaa2d9ddb5fc506633bc8b820fc27e64da964937fb30929c0367c47d00/pybase64-1.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0a5393be20b0705870f5a8969749af84d734c077de80dd7e9f5424a247afa85e", size = 38162, upload-time = "2025-07-27T13:07:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/25/41/795c5fd6e5571bb675bf9add8a048166dddf8951c2a903fea8557743886b/pybase64-1.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:448f0259a2f1a17eb086f70fe2ad9b556edba1fc5bc4e62ce6966179368ee9f8", size = 31452, upload-time = "2025-07-27T13:08:01.259Z" }, - { url = "https://files.pythonhosted.org/packages/aa/dd/c819003b59b2832256b72ad23cbeadbd95d083ef0318d07149a58b7a88af/pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1159e70cba8e76c3d8f334bd1f8fd52a1bb7384f4c3533831b23ab2df84a6ef3", size = 40668, upload-time = "2025-07-27T13:08:04.176Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c5/38c6aba28678c4a4db49312a6b8171b93a0ffe9f21362cf4c0f325caa850/pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d943bc5dad8388971494554b97f22ae06a46cc7779ad0de3d4bfdf7d0bbea30", size = 41281, upload-time = "2025-07-27T13:08:07.395Z" }, - { url = "https://files.pythonhosted.org/packages/e5/23/5927bd9e59714e4e8cefd1d21ccd7216048bb1c6c3e7104b1b200afdc63d/pybase64-1.4.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10b99182c561d86422c5de4265fd1f8f172fb38efaed9d72c71fb31e279a7f94", size = 35433, upload-time = "2025-07-27T13:08:10.551Z" }, - { url = "https://files.pythonhosted.org/packages/01/0f/fab7ed5bf4926523c3b39f7621cea3e0da43f539fbc2270e042f1afccb79/pybase64-1.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bb082c1114f046e59fcbc4f2be13edc93b36d7b54b58605820605be948f8fdf6", size = 36131, upload-time = "2025-07-27T13:08:13.777Z" }, -] - [[package]] name = "pycairo" version = "1.28.0" @@ -7528,7 +7451,7 @@ wheels = [ [[package]] name = "vllm" -version = "0.9.2" +version = "0.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -7552,6 +7475,10 @@ dependencies = [ { name = "numpy" }, { name = "openai" }, { name = "opencv-python-headless" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp" }, + { name = "opentelemetry-sdk" }, + { name = "opentelemetry-semantic-conventions-ai" }, { name = "outlines" }, { name = "partial-json-parser" }, { name = "pillow" }, @@ -7560,7 +7487,6 @@ dependencies = [ { name = "protobuf" }, { name = "psutil" }, { name = "py-cpuinfo" }, - { name = "pybase64" }, { name = "pydantic" }, { name = "python-json-logger" }, { name = "pyyaml" }, @@ -7583,11 +7509,11 @@ dependencies = [ { name = "typing-extensions" }, { name = "watchfiles" }, { name = "xformers", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "xgrammar", marker = "platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "xgrammar", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/89/2fbf95d398b5751b44c7256bd80e57c589142f1bfcc15f5dc76438b8853a/vllm-0.9.2.tar.gz", hash = "sha256:6b0d855ea8ba18d76364c9b82ea94bfcaa9c9e724055438b5733e4716ed104e1", size = 8997087, upload-time = "2025-07-08T04:49:01.722Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/5b/5f42b41d045c01821be62162fc6b1cfb14db1674027c7b623adb3a66dccf/vllm-0.9.1.tar.gz", hash = "sha256:c5ad11603f49a1fad05c88debabb8b839780403ce1b51751ec4da4e8a838082c", size = 8670972, upload-time = "2025-06-10T21:46:12.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/72/c14ff1acac64294f45782769b9c8144a1c3e8d4f2228d4648197511b015a/vllm-0.9.2-cp38-abi3-manylinux1_x86_64.whl", hash = "sha256:f3c5da29a286f4933b480a5b4749fab226564f35c96928eeef547f88d385cd34", size = 383350132, upload-time = "2025-07-08T04:48:54.133Z" }, + { url = "https://files.pythonhosted.org/packages/b5/56/ffcf6215a571cf9aa58ded06a9640bff21b4918e27344677cd33290ab9da/vllm-0.9.1-cp38-abi3-manylinux1_x86_64.whl", hash = "sha256:28b99e8df39c7aaeda04f7e5353b18564a1a9d1c579691945523fc4777a1a8c8", size = 394637693, upload-time = "2025-06-10T21:46:01.784Z" }, ] [[package]] @@ -7897,7 +7823,6 @@ name = "xgrammar" version = "0.1.19" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mlx-lm", marker = "platform_machine == 'arm64' and sys_platform == 'darwin'" }, { name = "ninja" }, { name = "pydantic" }, { name = "sentencepiece" }, From 41f817bf04d0c96217f2fce6adb2f0962f9cc4e5 Mon Sep 17 00:00:00 2001 From: vipyne Date: Wed, 15 Oct 2025 13:02:44 -0500 Subject: [PATCH 096/158] only import whatsapp deps if using whatsapp runner --- src/pipecat/runner/run.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index a7af52e18..c5c9dc9db 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -289,19 +289,6 @@ def _add_lifespan_to_app(app: FastAPI, new_lifespan): def _setup_whatsapp_routes(app: FastAPI): """Set up WebRTC-specific routes.""" - try: - from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI - - from pipecat.transports.smallwebrtc.connection import SmallWebRTCConnection - from pipecat.transports.smallwebrtc.request_handler import ( - SmallWebRTCRequest, - SmallWebRTCRequestHandler, - ) - from pipecat.transports.whatsapp.api import WhatsAppWebhookRequest - from pipecat.transports.whatsapp.client import WhatsAppClient - except ImportError as e: - logger.error(f"WebRTC transport dependencies not installed: {e}") - return WHATSAPP_TOKEN = os.getenv("WHATSAPP_TOKEN") WHATSAPP_PHONE_NUMBER_ID = os.getenv("WHATSAPP_PHONE_NUMBER_ID") @@ -320,6 +307,14 @@ def _setup_whatsapp_routes(app: FastAPI): ) return + # only import WhatsApp dependencies if appropriate env vars are present + try: + from pipecat.transports.whatsapp.api import WhatsAppWebhookRequest + from pipecat.transports.whatsapp.client import WhatsAppClient + except ImportError as e: + logger.error(f"WhatsApp transport dependencies not installed: {e}") + return + # Global WhatsApp client instance whatsapp_client: Optional[WhatsAppClient] = None From 0fd5d2610484909b6162bf4ee528ca221bf20ce6 Mon Sep 17 00:00:00 2001 From: vipyne Date: Wed, 15 Oct 2025 13:16:16 -0500 Subject: [PATCH 097/158] add WHATSAPP_APP_SECRET to required whatsapp env vars --- src/pipecat/runner/run.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index c5c9dc9db..4663b115d 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -289,7 +289,6 @@ def _add_lifespan_to_app(app: FastAPI, new_lifespan): def _setup_whatsapp_routes(app: FastAPI): """Set up WebRTC-specific routes.""" - WHATSAPP_TOKEN = os.getenv("WHATSAPP_TOKEN") WHATSAPP_PHONE_NUMBER_ID = os.getenv("WHATSAPP_PHONE_NUMBER_ID") WHATSAPP_WEBHOOK_VERIFICATION_TOKEN = os.getenv("WHATSAPP_WEBHOOK_VERIFICATION_TOKEN") @@ -300,10 +299,17 @@ def _setup_whatsapp_routes(app: FastAPI): WHATSAPP_TOKEN, WHATSAPP_PHONE_NUMBER_ID, WHATSAPP_WEBHOOK_VERIFICATION_TOKEN, + WHATSAPP_APP_SECRET, ] ): logger.trace( - "Missing required environment variables for WhatsApp transport. Keeping it disabled." + """Missing required environment variables for WhatsApp transport: + WHATSAPP_TOKEN + WHATSAPP_PHONE_NUMBER_ID + WHATSAPP_WEBHOOK_VERIFICATION_TOKEN + WHATSAPP_APP_SECRET +Keeping it disabled. + """ ) return From 6381335346ba9622f2eced03aca07891af0c4223 Mon Sep 17 00:00:00 2001 From: vipyne Date: Thu, 16 Oct 2025 10:37:46 -0500 Subject: [PATCH 098/158] Add --whatsapp flag to runner --- CHANGELOG.md | 2 ++ src/pipecat/runner/run.py | 45 ++++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cd715776..2b078fe80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `--whatsapp` flag to runner to better surface WhatsApp transport logs. + - Added `on_connected` and `on_disconnected` events to TTS and STT websocket-based services. diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index 4663b115d..a3e2984e8 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -166,6 +166,7 @@ def _create_server_app( host: str = "localhost", proxy: str, esp32_mode: bool = False, + whatsapp_enabled: bool = False, folder: Optional[str] = None, ): """Create FastAPI app with transport-specific routes.""" @@ -182,7 +183,8 @@ def _create_server_app( # Set up transport-specific routes if transport_type == "webrtc": _setup_webrtc_routes(app, esp32_mode=esp32_mode, host=host, folder=folder) - _setup_whatsapp_routes(app) + if whatsapp_enabled: + _setup_whatsapp_routes(app) elif transport_type == "daily": _setup_daily_routes(app) elif transport_type in TELEPHONY_TRANSPORTS: @@ -289,32 +291,37 @@ def _add_lifespan_to_app(app: FastAPI, new_lifespan): def _setup_whatsapp_routes(app: FastAPI): """Set up WebRTC-specific routes.""" - WHATSAPP_TOKEN = os.getenv("WHATSAPP_TOKEN") - WHATSAPP_PHONE_NUMBER_ID = os.getenv("WHATSAPP_PHONE_NUMBER_ID") - WHATSAPP_WEBHOOK_VERIFICATION_TOKEN = os.getenv("WHATSAPP_WEBHOOK_VERIFICATION_TOKEN") WHATSAPP_APP_SECRET = os.getenv("WHATSAPP_APP_SECRET") + WHATSAPP_PHONE_NUMBER_ID = os.getenv("WHATSAPP_PHONE_NUMBER_ID") + WHATSAPP_TOKEN = os.getenv("WHATSAPP_TOKEN") + WHATSAPP_WEBHOOK_VERIFICATION_TOKEN = os.getenv("WHATSAPP_WEBHOOK_VERIFICATION_TOKEN") if not all( [ - WHATSAPP_TOKEN, - WHATSAPP_PHONE_NUMBER_ID, - WHATSAPP_WEBHOOK_VERIFICATION_TOKEN, WHATSAPP_APP_SECRET, + WHATSAPP_PHONE_NUMBER_ID, + WHATSAPP_TOKEN, + WHATSAPP_WEBHOOK_VERIFICATION_TOKEN, ] ): - logger.trace( + logger.error( """Missing required environment variables for WhatsApp transport: - WHATSAPP_TOKEN - WHATSAPP_PHONE_NUMBER_ID - WHATSAPP_WEBHOOK_VERIFICATION_TOKEN WHATSAPP_APP_SECRET -Keeping it disabled. + WHATSAPP_PHONE_NUMBER_ID + WHATSAPP_TOKEN + WHATSAPP_WEBHOOK_VERIFICATION_TOKEN """ ) return - # only import WhatsApp dependencies if appropriate env vars are present try: + from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI + + from pipecat.transports.smallwebrtc.connection import SmallWebRTCConnection + from pipecat.transports.smallwebrtc.request_handler import ( + SmallWebRTCRequest, + SmallWebRTCRequestHandler, + ) from pipecat.transports.whatsapp.api import WhatsAppWebhookRequest from pipecat.transports.whatsapp.client import WhatsAppClient except ImportError as e: @@ -690,6 +697,12 @@ def main(): parser.add_argument( "--verbose", "-v", action="count", default=0, help="Increase logging verbosity" ) + parser.add_argument( + "--whatsapp", + action="store_true", + default=False, + help="Ensure requried WhatsApp environment variables are present", + ) args = parser.parse_args() @@ -732,10 +745,11 @@ def main(): print() if args.esp32: print(f"πŸš€ Bot ready! (ESP32 mode)") - print(f" β†’ Open http://{args.host}:{args.port}/client in your browser") + elif args.whatsapp: + print(f"πŸš€ Bot ready! (WhatsApp)") else: print(f"πŸš€ Bot ready!") - print(f" β†’ Open http://{args.host}:{args.port}/client in your browser") + print(f" β†’ Open http://{args.host}:{args.port}/client in your browser") print() elif args.transport == "daily": print() @@ -753,6 +767,7 @@ def main(): host=args.host, proxy=args.proxy, esp32_mode=args.esp32, + whatsapp_enabled=args.whatsapp, folder=args.folder, ) From 1b9e96c0168facfef2f704af0d329553135eda49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 15 Oct 2025 22:37:16 -0700 Subject: [PATCH 099/158] PipelineTask: fix task cancellation issues --- CHANGELOG.md | 8 ++- src/pipecat/pipeline/runner.py | 8 ++- src/pipecat/pipeline/task.py | 99 +++++++++++++++------------------- tests/test_pipeline.py | 31 +++++------ 4 files changed, 67 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cd715776..c9f645589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,12 +25,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CartesiaSTTService` now inherits from `WebsocketSTTService`. -- Package upgrades: +# Package upgrades: - `openai` upgraded to support up to 2.x.x. - `openpipe` upgraded to support up to 5.x.x. ### Fixed +- Fixed multiple pipeline task cancellation issues. `asyncio.CancelledError` is + now handled properly in `PipelineTask` making it possible to cancel an asyncio + task that it's executing a `PipelineRunner` cleanly. Also, + `PipelineTask.cancel()` does not block anymore waiting for the `CancelFrame` + to reach the end of the pipeline (going back to the behavior in < 0.0.83). + - Fixed an issue in `ElevenLabsTTSService` and `ElevenLabsHttpTTSService` where the Flash models would split words, resulting in a space being inserted between words. diff --git a/src/pipecat/pipeline/runner.py b/src/pipecat/pipeline/runner.py index 9d82fcd88..c7e246681 100644 --- a/src/pipecat/pipeline/runner.py +++ b/src/pipecat/pipeline/runner.py @@ -70,11 +70,15 @@ class PipelineRunner(BaseObject): """ logger.debug(f"Runner {self} started running {task}") self._tasks[task.name] = task - params = PipelineTaskParams(loop=self._loop) + + # PipelineTask handles asyncio.CancelledError to shutdown the pipeline + # properly and re-raises it in case there's more cleanup to do. try: + params = PipelineTaskParams(loop=self._loop) await task.run(params) except asyncio.CancelledError: - await self._cancel() + pass + del self._tasks[task.name] # Cleanup base object. diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index f6d17262e..a511db12a 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -269,6 +269,9 @@ class PipelineTask(BasePipelineTask): # StopFrame) has been received at the end of the pipeline. self._pipeline_end_event = asyncio.Event() + # This event is set when the pipeline truly finishes. + self._pipeline_finished_event = asyncio.Event() + # This is the final pipeline. It is composed of a source processor, # followed by the user pipeline, and ending with a sink processor. The # source allows us to receive and react to upstream frames, and the sink @@ -401,11 +404,7 @@ class PipelineTask(BasePipelineTask): await self.queue_frame(EndFrame()) async def cancel(self): - """Immediately stop the running pipeline. - - Cancels all running tasks and stops frame processing without - waiting for completion. - """ + """Request the running pipeline to cancel.""" if not self._finished: await self._cancel() @@ -417,51 +416,38 @@ class PipelineTask(BasePipelineTask): """ if self.has_finished(): return - cleanup_pipeline = True + + # Setup processors. + await self._setup(params) + + # Create all main tasks and wait for the main push task. This is the + # task that pushes frames to the very beginning of our pipeline (i.e. to + # our controlled source processor). + await self._create_tasks() + try: - # Setup processors. - await self._setup(params) - - # Create all main tasks and wait of the main push task. This is the - # task that pushes frames to the very beginning of our pipeline (our - # controlled source processor). - push_task = await self._create_tasks() - await push_task - - # We have already cleaned up the pipeline inside the task. - cleanup_pipeline = False - - # Pipeline has finished nicely. - self._finished = True + # Wait for pipeline to finish. + await self._wait_for_pipeline_finished() except asyncio.CancelledError: - # Raise exception back to the pipeline runner so it can cancel this - # task properly. + logger.debug(f"Pipeline task {self} got cancelled from outside...") + # We have been cancelled from outside, let's just cancel everything. + await self._cancel() + # Wait again for pipeline to finish. This time we have really + # cancelled, so it should really finish. + await self._wait_for_pipeline_finished() + # Re-raise in case there's more cleanup to do. raise finally: # We can reach this point for different reasons: # - # 1. The task has finished properly (e.g. `EndFrame`). - # 2. By calling `PipelineTask.cancel()`. - # 3. By asyncio task cancellation. - # - # Case (1) will execute the code below without issues because - # `self._finished` is true. - # - # Case (2) will execute the code below without issues because - # `self._cancelled` is true. - # - # Case (3) will raise the exception above (because we are cancelling - # the asyncio task). This will be then captured by the - # `PipelineRunner` which will call `PipelineTask.cancel()` and - # therefore becoming case (2). - if self._finished or self._cancelled: - logger.debug(f"Pipeline task {self} is finishing cleanup...") - await self._cancel_tasks() - await self._cleanup(cleanup_pipeline) - if self._check_dangling_tasks: - self._print_dangling_tasks() - self._finished = True - logger.debug(f"Pipeline task {self} has finished") + # 1. The pipeline task has finished (try case). + # 2. By an asyncio task cancellation (except case). + logger.debug(f"Pipeline task {self} is finishing...") + await self._cancel_tasks() + if self._check_dangling_tasks: + self._print_dangling_tasks() + self._finished = True + logger.debug(f"Pipeline task {self} has finished") async def queue_frame(self, frame: Frame): """Queue a single frame to be pushed down the pipeline. @@ -489,19 +475,7 @@ class PipelineTask(BasePipelineTask): if not self._cancelled: logger.debug(f"Cancelling pipeline task {self}") self._cancelled = True - cancel_frame = CancelFrame() - # Make sure everything is cleaned up downstream. This is sent - # out-of-band from the main streaming task which is what we want since - # we want to cancel right away. - await self._pipeline.queue_frame(cancel_frame) - # Wait for CancelFrame to make it through the pipeline. - await self._wait_for_pipeline_end(cancel_frame) - # Only cancel the push task, we don't want to be able to process any - # other frame after cancel. Everything else will be cancelled in - # run(). - if self._process_push_task: - await self._task_manager.cancel_task(self._process_push_task) - self._process_push_task = None + await self.queue_frame(CancelFrame()) async def _create_tasks(self): """Create and start all pipeline processing tasks.""" @@ -603,6 +577,17 @@ class PipelineTask(BasePipelineTask): self._pipeline_end_event.clear() + # We are really done. + self._pipeline_finished_event.set() + + async def _wait_for_pipeline_finished(self): + await self._pipeline_finished_event.wait() + self._pipeline_finished_event.clear() + # Make sure we wait for the main task to complete. + if self._process_push_task: + await self._process_push_task + self._process_push_task = None + async def _setup(self, params: PipelineTaskParams): """Set up the pipeline task and all processors.""" mgr_params = TaskManagerParams(loop=params.loop) diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index df4af49b9..7d9ebec6f 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -254,7 +254,7 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): try: await asyncio.wait_for( - asyncio.shield(task.run(PipelineTaskParams(loop=asyncio.get_event_loop()))), + task.run(PipelineTaskParams(loop=asyncio.get_event_loop())), timeout=1.0, ) except asyncio.TimeoutError: @@ -290,7 +290,7 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): await task.queue_frame(TextFrame(text="Hello!")) try: await asyncio.wait_for( - asyncio.shield(task.run(PipelineTaskParams(loop=asyncio.get_event_loop()))), + task.run(PipelineTaskParams(loop=asyncio.get_event_loop())), timeout=1.0, ) except asyncio.TimeoutError: @@ -301,11 +301,8 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): identity = IdentityFilter() pipeline = Pipeline([identity]) task = PipelineTask(pipeline, idle_timeout_secs=0.2) - try: - await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) - assert False - except asyncio.CancelledError: - assert True + # This shouldn't freeze, so nothing to check really. + await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) async def test_no_idle_task(self): identity = IdentityFilter() @@ -313,7 +310,7 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): task = PipelineTask(pipeline, idle_timeout_secs=0.2, cancel_on_idle_timeout=False) try: await asyncio.wait_for( - asyncio.shield(task.run(PipelineTaskParams(loop=asyncio.get_event_loop()))), + task.run(PipelineTaskParams(loop=asyncio.get_event_loop())), timeout=0.3, ) except asyncio.TimeoutError: @@ -332,11 +329,7 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): ), idle_timeout_secs=0.3, ) - try: - await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) - assert False - except asyncio.CancelledError: - assert True + await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) async def test_idle_task_event_handler_no_frames(self): identity = IdentityFilter() @@ -351,11 +344,8 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): idle_timeout = True await task.cancel() - try: - await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) - assert False - except asyncio.CancelledError: - assert idle_timeout + await task.run(PipelineTaskParams(loop=asyncio.get_event_loop())) + assert idle_timeout async def test_idle_task_event_handler_quiet_user(self): identity = IdentityFilter() @@ -416,12 +406,15 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase): asyncio.create_task(delayed_frames()), ] - await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) + _, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) diff_time = time.time() - start_time self.assertGreater(diff_time, sleep_time_secs * 3) + # Wait for the pending tasks to complete. + await asyncio.gather(*pending) + async def test_task_cancel_timeout(self): class CancelFilter(FrameProcessor): def __init__(self, **kwargs): From 8d8503bca7ac3a21ca9ad3eeb516cacdea648346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 15 Oct 2025 16:20:12 -0700 Subject: [PATCH 100/158] OpenAITTSService: allow updating instructions and speed --- CHANGELOG.md | 5 +++- src/pipecat/services/openai/tts.py | 43 +++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5f114054..938b175c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for updating `OpenAITTSService` settings (`instructions` and + `speed`) at runtime via `TTSUpdateSettingsFrame`. + - Added `--whatsapp` flag to runner to better surface WhatsApp transport logs. - Added `on_connected` and `on_disconnected` events to TTS and STT @@ -27,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CartesiaSTTService` now inherits from `WebsocketSTTService`. -# Package upgrades: +- Package upgrades: - `openai` upgraded to support up to 2.x.x. - `openpipe` upgraded to support up to 5.x.x. diff --git a/src/pipecat/services/openai/tts.py b/src/pipecat/services/openai/tts.py index a67392caf..cdf0d11ac 100644 --- a/src/pipecat/services/openai/tts.py +++ b/src/pipecat/services/openai/tts.py @@ -14,6 +14,7 @@ from typing import AsyncGenerator, Dict, Literal, Optional from loguru import logger from openai import AsyncOpenAI, BadRequestError +from pydantic import BaseModel from pipecat.frames.frames import ( ErrorFrame, @@ -55,6 +56,17 @@ class OpenAITTSService(TTSService): OPENAI_SAMPLE_RATE = 24000 # OpenAI TTS always outputs at 24kHz + class InputParams(BaseModel): + """Input parameters for OpenAI TTS configuration. + + Parameters: + instructions: Instructions to guide voice synthesis behavior. + speed: Voice speed control (0.25 to 4.0, default 1.0). + """ + + instructions: Optional[str] = None + speed: Optional[float] = None + def __init__( self, *, @@ -65,6 +77,7 @@ class OpenAITTSService(TTSService): sample_rate: Optional[int] = None, instructions: Optional[str] = None, speed: Optional[float] = None, + params: Optional[InputParams] = None, **kwargs, ): """Initialize OpenAI TTS service. @@ -77,7 +90,11 @@ class OpenAITTSService(TTSService): sample_rate: Output audio sample rate in Hz. If None, uses OpenAI's default 24kHz. instructions: Optional instructions to guide voice synthesis behavior. speed: Voice speed control (0.25 to 4.0, default 1.0). + params: Optional synthesis controls (acting instructions, speed, ...). **kwargs: Additional keyword arguments passed to TTSService. + + .. deprecated:: 0.0.91 + The `instructions` and `speed` parameters are deprecated, use `InputParams` instead. """ if sample_rate and sample_rate != self.OPENAI_SAMPLE_RATE: logger.warning( @@ -86,12 +103,26 @@ class OpenAITTSService(TTSService): ) super().__init__(sample_rate=sample_rate, **kwargs) - self._speed = speed self.set_model_name(model) self.set_voice(voice) - self._instructions = instructions self._client = AsyncOpenAI(api_key=api_key, base_url=base_url) + if instructions or speed: + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "The `instructions` and `speed` parameters are deprecated, use `InputParams` instead.", + DeprecationWarning, + stacklevel=2, + ) + + self._settings = { + "instructions": params.instructions if params else instructions, + "speed": params.speed if params else speed, + } + def can_generate_metrics(self) -> bool: """Check if this service can generate processing metrics. @@ -144,11 +175,11 @@ class OpenAITTSService(TTSService): "response_format": "pcm", } - if self._instructions: - create_params["instructions"] = self._instructions + if self._settings["instructions"]: + create_params["instructions"] = self._settings["instructions"] - if self._speed: - create_params["speed"] = self._speed + if self._settings["speed"]: + create_params["speed"] = self._settings["speed"] async with self._client.audio.speech.with_streaming_response.create( **create_params From d12134038b1e4ba63fbc122f3f9ab5acc50d6256 Mon Sep 17 00:00:00 2001 From: shreyas-sarvam Date: Fri, 17 Oct 2025 10:07:58 +0530 Subject: [PATCH 101/158] chore: Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 938b175c1..89078ebf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for `bulbul:v3` model in `SarvamTTSService` and `SarvamHttpTTSService`. + - Added support for updating `OpenAITTSService` settings (`instructions` and `speed`) at runtime via `TTSUpdateSettingsFrame`. From d1d74c571c9edf20c086d5e1fca4c9ef1ebb3e8b Mon Sep 17 00:00:00 2001 From: Nimo Beeren Date: Fri, 17 Oct 2025 10:38:06 +0200 Subject: [PATCH 102/158] add ellipsis character to sentence ending punctuation list --- src/pipecat/utils/string.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pipecat/utils/string.py b/src/pipecat/utils/string.py index c9cb05142..11964d23f 100644 --- a/src/pipecat/utils/string.py +++ b/src/pipecat/utils/string.py @@ -47,6 +47,7 @@ SENTENCE_ENDING_PUNCTUATION: FrozenSet[str] = frozenset( "!", "?", ";", + "…", # East Asian punctuation (Chinese (Traditional & Simplified), Japanese, Korean) "。", # Ideographic full stop "?", # Full-width question mark From d5f2dcfac0a020d8077988de71be47b210d90211 Mon Sep 17 00:00:00 2001 From: dan-ince-aai Date: Fri, 17 Oct 2025 11:32:06 +0100 Subject: [PATCH 103/158] lint --- src/pipecat/services/assemblyai/stt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index d87261f86..c255cd4c4 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -183,7 +183,7 @@ class AssemblyAISTTService(STTService): params[k] = str(v).lower() else: params[k] = v - + if params: query_string = urlencode(params) return f"{self._api_endpoint_base_url}?{query_string}" From 3ba6b5565949023f0f31beac883e114e1b0df7a4 Mon Sep 17 00:00:00 2001 From: dan-ince-aai Date: Fri, 17 Oct 2025 11:38:03 +0100 Subject: [PATCH 104/158] feat: multilingual + changelog updates --- CHANGELOG.md | 4 ++++ src/pipecat/services/assemblyai/models.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 424d6a420..45131e331 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 ### Added +- Added `keyterms_prompt` parameter to `AssemblyAIConnectionParams`. + +- Added `speech_model` parameter to `AssemblyAIConnectionParams` to access the multilingual model. + - The runner `--folder` argument now supports downloading files from subdirectories. diff --git a/src/pipecat/services/assemblyai/models.py b/src/pipecat/services/assemblyai/models.py index d263d113d..52ea87d87 100644 --- a/src/pipecat/services/assemblyai/models.py +++ b/src/pipecat/services/assemblyai/models.py @@ -109,6 +109,7 @@ class AssemblyAIConnectionParams(BaseModel): min_end_of_turn_silence_when_confident: Minimum silence duration when confident about end-of-turn. max_turn_silence: Maximum silence duration before forcing end-of-turn. keyterms_prompt: List of key terms to guide transcription. Will be JSON serialized before sending. + speech_model: Select between English and multilingual models. Defaults to "universal-streaming-english". """ sample_rate: int = 16000 @@ -119,3 +120,6 @@ class AssemblyAIConnectionParams(BaseModel): min_end_of_turn_silence_when_confident: Optional[int] = None max_turn_silence: Optional[int] = None keyterms_prompt: Optional[List[str]] = None + speech_model: Literal["universal-streaming-english", "universal-streaming-multilingual"] = ( + "universal-streaming-english" + ) From 3e8a7cc2544b98b995917e9bddb11b5fdcdf6c7b Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Fri, 17 Oct 2025 08:57:45 -0300 Subject: [PATCH 105/158] Adding support for trickle ICE to the SmallWebRTCTransport. --- CHANGELOG.md | 2 + src/pipecat/runner/run.py | 8 ++++ .../transports/smallwebrtc/connection.py | 5 +++ .../transports/smallwebrtc/request_handler.py | 42 +++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 938b175c1..6d581ccce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for trickle ICE to the `SmallWebRTCTransport`. + - Added support for updating `OpenAITTSService` settings (`instructions` and `speed`) at runtime via `TTSUpdateSettingsFrame`. diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index a3e2984e8..9b1d233f6 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -204,6 +204,7 @@ def _setup_webrtc_routes( from pipecat.transports.smallwebrtc.connection import SmallWebRTCConnection from pipecat.transports.smallwebrtc.request_handler import ( + SmallWebRTCPatchRequest, SmallWebRTCRequest, SmallWebRTCRequestHandler, ) @@ -256,6 +257,13 @@ def _setup_webrtc_routes( ) return answer + @app.patch("/api/offer") + async def ice_candidate(request: SmallWebRTCPatchRequest): + """Handle WebRTC new ice candidate requests.""" + logger.debug(f"Received patch request: {request}") + await small_webrtc_handler.handle_patch_request(request) + return {"status": "success"} + @asynccontextmanager async def smallwebrtc_lifespan(app: FastAPI): """Manage FastAPI application lifecycle and cleanup connections.""" diff --git a/src/pipecat/transports/smallwebrtc/connection.py b/src/pipecat/transports/smallwebrtc/connection.py index c77f4e77e..60dd7798c 100644 --- a/src/pipecat/transports/smallwebrtc/connection.py +++ b/src/pipecat/transports/smallwebrtc/connection.py @@ -689,3 +689,8 @@ class SmallWebRTCConnection(BaseObject): )() if track: track.set_enabled(signalling_message.enabled) + + async def add_ice_candidate(self, candidate): + """Handle incoming ICE candidates.""" + logger.debug(f"Adding remote candidate: {candidate}") + await self.pc.addIceCandidate(candidate) diff --git a/src/pipecat/transports/smallwebrtc/request_handler.py b/src/pipecat/transports/smallwebrtc/request_handler.py index 00d9ebb6a..b2c02a03e 100644 --- a/src/pipecat/transports/smallwebrtc/request_handler.py +++ b/src/pipecat/transports/smallwebrtc/request_handler.py @@ -14,6 +14,7 @@ from dataclasses import dataclass from enum import Enum from typing import Any, Awaitable, Callable, Dict, List, Optional +from aiortc.sdp import candidate_from_sdp from fastapi import HTTPException from loguru import logger @@ -39,6 +40,34 @@ class SmallWebRTCRequest: request_data: Optional[Any] = None +@dataclass +class IceCandidate: + """The remote ice candidate object received from the peer connection. + + Parameters: + candidate: The ice candidate patch SDP string (Session Description Protocol). + sdp_mid: The SDP mid for the candidate patch. + sdp_mline_index: The SDP mline index for the candidate patch. + """ + + candidate: str + sdp_mid: str + sdp_mline_index: int + + +@dataclass +class SmallWebRTCPatchRequest: + """Small WebRTC transport session arguments for the runner. + + Parameters: + pc_id: Identifier for the peer connection. + candidates: A list of ICE candidate patches. + """ + + pc_id: str + candidates: List[IceCandidate] + + class ConnectionMode(Enum): """Enum defining the connection handling modes.""" @@ -197,6 +226,19 @@ class SmallWebRTCRequestHandler: logger.debug(f"SmallWebRTC request details: {request}") raise + async def handle_patch_request(self, request: SmallWebRTCPatchRequest): + """Handle a SmallWebRTC patch candidate request.""" + peer_connection = self._pcs_map.get(request.pc_id) + + if not peer_connection: + raise HTTPException(status_code=404, detail="Peer connection not found") + + for c in request.candidates: + candidate = candidate_from_sdp(c.candidate) + candidate.sdpMid = c.sdp_mid + candidate.sdpMLineIndex = c.sdp_mline_index + await peer_connection.add_ice_candidate(candidate) + async def close(self): """Clear the connection map.""" coros = [pc.disconnect() for pc in self._pcs_map.values()] From 855f4842dd9f156835db322a07cd3ec1d8f57031 Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Fri, 17 Oct 2025 10:10:19 -0300 Subject: [PATCH 106/158] Creating the WebRTC routes that mimic the ones provided by Pipecat Cloud. --- src/pipecat/runner/run.py | 84 +++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 7 deletions(-) diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index 9b1d233f6..a90c96ac5 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -70,12 +70,14 @@ import asyncio import mimetypes import os import sys +import uuid from contextlib import asynccontextmanager +from http import HTTPMethod from pathlib import Path -from typing import Optional +from typing import Any, Dict, List, Optional, TypedDict import aiohttp -from fastapi.responses import FileResponse +from fastapi.responses import FileResponse, Response from loguru import logger from pipecat.runner.types import ( @@ -202,8 +204,9 @@ def _setup_webrtc_routes( try: from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI - from pipecat.transports.smallwebrtc.connection import SmallWebRTCConnection + from pipecat.transports.smallwebrtc.connection import IceServer, SmallWebRTCConnection from pipecat.transports.smallwebrtc.request_handler import ( + IceCandidate, SmallWebRTCPatchRequest, SmallWebRTCRequest, SmallWebRTCRequestHandler, @@ -212,6 +215,16 @@ def _setup_webrtc_routes( logger.error(f"WebRTC transport dependencies not installed: {e}") return + class IceConfig(TypedDict): + iceServers: List[IceServer] + + class StartBotResult(TypedDict, total=False): + sessionId: str + iceConfig: Optional[IceConfig] + + # In-memory store of active sessions: session_id -> session info + active_sessions: Dict[str, Dict[str, Any]] = {} + # Mount the frontend app.mount("/client", SmallWebRTCPrebuiltUI) @@ -264,6 +277,67 @@ def _setup_webrtc_routes( await small_webrtc_handler.handle_patch_request(request) return {"status": "success"} + @app.post("/start") + async def rtvi_start(request: Request): + """Mimic Pipecat Cloud's /start endpoint.""" + # Parse the request body + try: + request_data = await request.json() + logger.debug(f"Received request: {request_data}") + except Exception as e: + logger.error(f"Failed to parse request body: {e}") + request_data = {} + + # Store session info immediately in memory, replicate the behavior expected on Pipecat Cloud + session_id = str(uuid.uuid4()) + active_sessions[session_id] = request_data + + result: StartBotResult = {"sessionId": session_id} + if request_data.get("enableDefaultIceServers"): + result["iceConfig"] = IceConfig( + iceServers=[IceServer(urls="stun:stun.l.google.com:19302")] + ) + + return result + + @app.api_route( + "/sessions/{session_id}/{path:path}", + methods=["GET", "POST", "PUT", "PATCH", "DELETE"], + ) + async def proxy_request( + session_id: str, path: str, request: Request, background_tasks: BackgroundTasks + ): + """Mimic Pipecat Cloud's proxy.""" + active_session = active_sessions.get(session_id) + if not active_session: + return Response(content="Invalid or not-yet-ready session_id", status_code=404) + + if path.endswith("api/offer"): + # Parse the request body and convert to SmallWebRTCRequest + try: + request_data = await request.json() + if request.method == HTTPMethod.POST.value: + webrtc_request = SmallWebRTCRequest( + sdp=request_data["sdp"], + type=request_data["type"], + pc_id=request_data.get("pc_id"), + restart_pc=request_data.get("restart_pc"), + request_data=request_data, + ) + return await offer(webrtc_request, background_tasks) + elif request.method == HTTPMethod.PATCH.value: + patch_request = SmallWebRTCPatchRequest( + pc_id=request_data["pc_id"], + candidates=[IceCandidate(**c) for c in request_data.get("candidates", [])], + ) + return await ice_candidate(patch_request) + except Exception as e: + logger.error(f"Failed to parse WebRTC request: {e}") + return Response(content="Invalid WebRTC request", status_code=400) + + logger.info(f"Received request for path: {path}") + return Response(status_code=200) + @asynccontextmanager async def smallwebrtc_lifespan(app: FastAPI): """Manage FastAPI application lifecycle and cleanup connections.""" @@ -503,8 +577,6 @@ def _setup_daily_routes(app: FastAPI): else: logger.debug("No body data provided in request") - import aiohttp - from pipecat.runner.daily import configure async with aiohttp.ClientSession() as session: @@ -592,8 +664,6 @@ def _setup_telephony_routes(app: FastAPI, *, transport_type: str, proxy: str): async def _run_daily_direct(): """Run Daily bot with direct connection (no FastAPI server).""" try: - import aiohttp - from pipecat.runner.daily import configure except ImportError as e: logger.error("Daily transport dependencies not installed.") From f2c61ac9fd7aa4e626c03a56f19b4cc7ab411a13 Mon Sep 17 00:00:00 2001 From: Sam Sykes Date: Fri, 17 Oct 2025 14:34:37 +0100 Subject: [PATCH 107/158] Fix for AdditionVocabEntry without sounds_like items. --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- src/pipecat/services/speechmatics/stt.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d581ccce..209c9cb11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,9 +33,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CartesiaSTTService` now inherits from `WebsocketSTTService`. - Package upgrades: + - `openai` upgraded to support up to 2.x.x. - `openpipe` upgraded to support up to 5.x.x. +- `SpeechmaticsSTTService` updated dependencies for `speechmatics-rt>=0.5.0`. + ### Fixed - Fixed multiple pipeline task cancellation issues. `asyncio.CancelledError` is @@ -59,6 +62,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 incorrectly 16-bit aligned audio frames, potentially leading to internal errors or static audio. +- Fixed an issue in `SpeechmaticsSTTService` where `AdditionalVocabEntry` items + needed to have `sounds_like` for the session to start. + ### Other - Added foundational example `47-sentry-metrics.py`, demonstrating how to use the diff --git a/pyproject.toml b/pyproject.toml index 0d94d0fa3..65546311c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,7 +102,7 @@ silero = [ "onnxruntime>=1.20.1,<2" ] simli = [ "simli-ai~=0.1.10"] soniox = [ "pipecat-ai[websockets-base]" ] soundfile = [ "soundfile~=0.13.0" ] -speechmatics = [ "speechmatics-rt>=0.4.0" ] +speechmatics = [ "speechmatics-rt>=0.5.0" ] strands = [ "strands-agents>=1.9.1,<2" ] tavus=[] together = [] diff --git a/src/pipecat/services/speechmatics/stt.py b/src/pipecat/services/speechmatics/stt.py index 2c1db2a15..901edb0e8 100644 --- a/src/pipecat/services/speechmatics/stt.py +++ b/src/pipecat/services/speechmatics/stt.py @@ -620,7 +620,7 @@ class SpeechmaticsSTTService(STTService): transcription_config.additional_vocab = [ { "content": e.content, - "sounds_like": e.sounds_like, + **({"sounds_like": e.sounds_like} if e.sounds_like else {}), } for e in self._params.additional_vocab ] From a67a765783dccbf4893d535722d8d15f6c7894e9 Mon Sep 17 00:00:00 2001 From: nbyers-altira Date: Fri, 17 Oct 2025 13:49:52 -0400 Subject: [PATCH 108/158] add changelog, run linter --- CHANGELOG.md | 3 +++ src/pipecat/services/riva/stt.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d581ccce..96406e52f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue in `RivaSTTService` where a runtime error occurred due to a + mismatch in the _handle_transcription method's signature. + - Fixed multiple pipeline task cancellation issues. `asyncio.CancelledError` is now handled properly in `PipelineTask` making it possible to cancel an asyncio task that it's executing a `PipelineRunner` cleanly. Also, diff --git a/src/pipecat/services/riva/stt.py b/src/pipecat/services/riva/stt.py index ccd166536..eddd3da9e 100644 --- a/src/pipecat/services/riva/stt.py +++ b/src/pipecat/services/riva/stt.py @@ -583,7 +583,9 @@ class RivaSegmentedSTTService(SegmentedSTTService): self._config.language_code = self._language @traced_stt - async def _handle_transcription(self, transcript: str, is_final: bool, language: Optional[Language] = None): + async def _handle_transcription( + self, transcript: str, is_final: bool, language: Optional[Language] = None + ): """Handle a transcription result with tracing.""" pass From a4867f61aa51497c78e2043e55f037519051c9f7 Mon Sep 17 00:00:00 2001 From: nbyers-altira Date: Fri, 17 Oct 2025 13:51:49 -0400 Subject: [PATCH 109/158] be a tad more precise in changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96406e52f..5d8d44887 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,8 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Fixed an issue in `RivaSTTService` where a runtime error occurred due to a - mismatch in the _handle_transcription method's signature. +- Fixed an issue in `RivaSegmentedSTTService` where a runtime error occurred due + to a mismatch in the _handle_transcription method's signature. - Fixed multiple pipeline task cancellation issues. `asyncio.CancelledError` is now handled properly in `PipelineTask` making it possible to cancel an asyncio From 7ce370ccc6541d38f86d39264f7c4496e7d442a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Fri, 17 Oct 2025 15:13:20 -0700 Subject: [PATCH 110/158] BaseOutputTransport: simplify bot speaking logic --- src/pipecat/transports/base_output.py | 125 ++++++++++++++------------ 1 file changed, 67 insertions(+), 58 deletions(-) diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index 9d4630100..ba508000f 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -410,6 +410,13 @@ class BaseOutputTransport(FrameProcessor): # Indicates if the bot is currently speaking. self._bot_speaking = False + # Last time a BotSpeakingFrame was pushed. + self._bot_speaking_frame_time = 0 + # How often a BotSpeakingFrame should be pushed (value should be + # lower than the audio chunks). + self._bot_speaking_frame_period = 0.2 + # Last time the bot actually spoke. + self._bot_speech_last_time = 0 self._audio_task: Optional[asyncio.Task] = None self._video_task: Optional[asyncio.Task] = None @@ -601,39 +608,71 @@ class BaseOutputTransport(FrameProcessor): async def _bot_started_speaking(self): """Handle bot started speaking event.""" - if not self._bot_speaking: - logger.debug( - f"Bot{f' [{self._destination}]' if self._destination else ''} started speaking" - ) + if self._bot_speaking: + return - downstream_frame = BotStartedSpeakingFrame() - downstream_frame.transport_destination = self._destination - upstream_frame = BotStartedSpeakingFrame() - upstream_frame.transport_destination = self._destination - await self._transport.push_frame(downstream_frame) - await self._transport.push_frame(upstream_frame, FrameDirection.UPSTREAM) + logger.debug( + f"Bot{f' [{self._destination}]' if self._destination else ''} started speaking" + ) - self._bot_speaking = True + downstream_frame = BotStartedSpeakingFrame() + downstream_frame.transport_destination = self._destination + upstream_frame = BotStartedSpeakingFrame() + upstream_frame.transport_destination = self._destination + await self._transport.push_frame(downstream_frame) + await self._transport.push_frame(upstream_frame, FrameDirection.UPSTREAM) + + self._bot_speaking = True async def _bot_stopped_speaking(self): """Handle bot stopped speaking event.""" - if self._bot_speaking: - logger.debug( - f"Bot{f' [{self._destination}]' if self._destination else ''} stopped speaking" - ) + if not self._bot_speaking: + return - downstream_frame = BotStoppedSpeakingFrame() - downstream_frame.transport_destination = self._destination - upstream_frame = BotStoppedSpeakingFrame() - upstream_frame.transport_destination = self._destination - await self._transport.push_frame(downstream_frame) - await self._transport.push_frame(upstream_frame, FrameDirection.UPSTREAM) + logger.debug( + f"Bot{f' [{self._destination}]' if self._destination else ''} stopped speaking" + ) - self._bot_speaking = False + downstream_frame = BotStoppedSpeakingFrame() + downstream_frame.transport_destination = self._destination + upstream_frame = BotStoppedSpeakingFrame() + upstream_frame.transport_destination = self._destination + await self._transport.push_frame(downstream_frame) + await self._transport.push_frame(upstream_frame, FrameDirection.UPSTREAM) - # Clean audio buffer (there could be tiny left overs if not multiple - # to our output chunk size). - self._audio_buffer = bytearray() + self._bot_speaking = False + + # Clean audio buffer (there could be tiny left overs if not multiple + # to our output chunk size). + self._audio_buffer = bytearray() + + async def _bot_currently_speaking(self): + """Handle bot speaking event.""" + await self._bot_started_speaking() + + diff_time = time.time() - self._bot_speaking_frame_time + if diff_time >= self._bot_speaking_frame_period: + await self._transport.push_frame(BotSpeakingFrame()) + await self._transport.push_frame(BotSpeakingFrame(), FrameDirection.UPSTREAM) + self._bot_speaking_frame_time = time.time() + + self._bot_speech_last_time = time.time() + + async def _maybe_bot_currently_speaking(self, frame: SpeechOutputAudioRawFrame): + if not is_silence(frame.audio): + await self._bot_currently_speaking() + else: + silence_duration = time.time() - self._bot_speech_last_time + if silence_duration > BOT_VAD_STOP_SECS: + await self._bot_stopped_speaking() + + async def _handle_bot_speech(self, frame: Frame): + # TTS case. + if isinstance(frame, TTSAudioRawFrame): + await self._bot_currently_speaking() + # Speech stream case. + elif isinstance(frame, SpeechOutputAudioRawFrame): + await self._maybe_bot_currently_speaking(frame) async def _handle_frame(self, frame: Frame): """Handle various frame types with appropriate processing. @@ -641,7 +680,9 @@ class BaseOutputTransport(FrameProcessor): Args: frame: The frame to handle. """ - if isinstance(frame, OutputImageRawFrame): + if isinstance(frame, OutputAudioRawFrame): + await self._handle_bot_speech(frame) + elif isinstance(frame, OutputImageRawFrame): await self._set_video_image(frame) elif isinstance(frame, SpriteFrame): await self._set_video_images(frame.images) @@ -705,39 +746,7 @@ class BaseOutputTransport(FrameProcessor): async def _audio_task_handler(self): """Main audio processing task handler.""" - # Push a BotSpeakingFrame every 200ms, we don't really need to push it - # at every audio chunk. If the audio chunk is bigger than 200ms, push at - # every audio chunk. - TOTAL_CHUNK_MS = self._params.audio_out_10ms_chunks * 10 - BOT_SPEAKING_CHUNK_PERIOD = max(int(200 / TOTAL_CHUNK_MS), 1) - bot_speaking_counter = 0 - speech_last_speaking_time = 0 - async for frame in self._next_frame(): - # Notify the bot started speaking upstream if necessary and that - # it's actually speaking. - is_speaking = False - if isinstance(frame, TTSAudioRawFrame): - is_speaking = True - elif isinstance(frame, SpeechOutputAudioRawFrame): - if not is_silence(frame.audio): - is_speaking = True - speech_last_speaking_time = time.time() - else: - silence_duration = time.time() - speech_last_speaking_time - if silence_duration > BOT_VAD_STOP_SECS: - await self._bot_stopped_speaking() - - if is_speaking: - await self._bot_started_speaking() - if bot_speaking_counter % BOT_SPEAKING_CHUNK_PERIOD == 0: - await self._transport.push_frame(BotSpeakingFrame()) - await self._transport.push_frame( - BotSpeakingFrame(), FrameDirection.UPSTREAM - ) - bot_speaking_counter = 0 - bot_speaking_counter += 1 - # No need to push EndFrame, it's pushed from process_frame(). if isinstance(frame, EndFrame): break From 040774495022895bc5bca8442a0dfa54c2e80a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Fri, 17 Oct 2025 21:55:20 -0700 Subject: [PATCH 111/158] BaseOutputTransport: simplify process_frame --- src/pipecat/transports/base_output.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index ba508000f..3edf434a9 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -293,15 +293,15 @@ class BaseOutputTransport(FrameProcessor): """ await super().process_frame(frame, direction) - # - # System frames (like InterruptionFrame) are pushed immediately. Other - # frames require order so they are put in the sink queue. - # if isinstance(frame, StartFrame): # Push StartFrame before start(), because we want StartFrame to be # processed by every processor before any other frame is processed. await self.push_frame(frame, direction) await self.start(frame) + elif isinstance(frame, EndFrame): + await self.stop(frame) + # Keep pushing EndFrame down so all the pipeline stops nicely. + await self.push_frame(frame, direction) elif isinstance(frame, CancelFrame): await self.cancel(frame) await self.push_frame(frame, direction) @@ -314,21 +314,6 @@ class BaseOutputTransport(FrameProcessor): await self.write_dtmf(frame) elif isinstance(frame, SystemFrame): await self.push_frame(frame, direction) - # Control frames. - elif isinstance(frame, EndFrame): - await self.stop(frame) - # Keep pushing EndFrame down so all the pipeline stops nicely. - await self.push_frame(frame, direction) - elif isinstance(frame, MixerControlFrame): - await self._handle_frame(frame) - # Other frames. - elif isinstance(frame, OutputAudioRawFrame): - await self._handle_frame(frame) - elif isinstance(frame, (OutputImageRawFrame, SpriteFrame)): - await self._handle_frame(frame) - # TODO(aleix): Images and audio should support presentation timestamps. - elif frame.pts: - await self._handle_frame(frame) elif direction == FrameDirection.UPSTREAM: await self.push_frame(frame, direction) else: From ae5c5ed7f6e9d8555871089357270b482238e9ab Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 25 Sep 2025 09:46:33 -0400 Subject: [PATCH 112/158] Update `AWSNovaSonicLLMService` to work with `LLMContext` and `LLMContextAggregatorPair` --- CHANGELOG.md | 12 + .../20a-persistent-context-openai.py | 4 +- .../20c-persistent-context-anthropic.py | 4 +- .../20d-persistent-context-gemini.py | 5 +- .../20e-persistent-context-aws-nova-sonic.py | 6 +- examples/foundational/40-aws-nova-sonic.py | 9 +- .../services/aws_nova_sonic_adapter.py | 122 +++++- .../processors/aggregators/llm_context.py | 41 +- .../services/aws/nova_sonic/context.py | 367 ------------------ src/pipecat/services/aws/nova_sonic/llm.py | 288 +++++++++----- tests/test_llm_context.py | 208 ++++++++++ 11 files changed, 580 insertions(+), 486 deletions(-) delete mode 100644 src/pipecat/services/aws/nova_sonic/context.py create mode 100644 tests/test_llm_context.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 71e5300c0..feb86e5d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Expanded support for universal `LLMContext` to `AWSNovaSonicLLMService`. + As a reminder, the context-setup pattern when using `LLMContext` is: + + ```python + context = LLMContext(messages, tools) + context_aggregator = LLMContextAggregatorPair(context) + ``` + + (Note that even though `AWSNovaSonicLLMService` now supports the universal + `LLMContext`, it is not meant to be swapped out for another LLM service at + runtime.) + - Added support for `bulbul:v3` model in `SarvamTTSService` and `SarvamHttpTTSService`. - Added `keyterms_prompt` parameter to `AssemblyAIConnectionParams`. diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index 93c1fa438..a3f9f005a 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -67,11 +67,11 @@ async def save_conversation(params: FunctionCallParams): timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") filename = f"{BASE_FILENAME}{timestamp}.json" logger.debug( - f"writing conversation to {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" + f"writing conversation to {filename}\n{json.dumps(params.context.get_messages_for_persistent_storage(), indent=4)}" ) try: with open(filename, "w") as file: - messages = params.context.get_messages() + messages = params.context.get_messages_for_persistent_storage() # remove the last message, which is the instruction we just gave to save the conversation messages.pop() json.dump(messages, file, indent=2) diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 411a976b8..7a0953290 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -68,12 +68,12 @@ async def save_conversation(params: FunctionCallParams): timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") filename = f"{BASE_FILENAME}{timestamp}.json" logger.debug( - f"writing conversation to {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" + f"writing conversation to {filename}\n{json.dumps(params.context.get_messages_for_persistent_storage(), indent=4)}" ) try: with open(filename, "w") as file: # todo: extract 'system' into the first message in the list - messages = params.context.get_messages() + messages = params.context.get_messages_for_persistent_storage() # remove the last message, which is the instruction we just gave to save the conversation messages.pop() json.dump(messages, file, indent=2) diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index 8dad8148d..0e52c0308 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -86,12 +86,11 @@ async def save_conversation(params: FunctionCallParams): timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") filename = f"{BASE_FILENAME}{timestamp}.json" logger.debug( - f"writing conversation to {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" + f"writing conversation to {filename}\n{json.dumps(params.context.get_messages_for_persistent_storage(), indent=4)}" ) try: with open(filename, "w") as file: - # todo: extract 'system' into the first message in the list - messages = params.context.get_messages() + messages = params.context.get_messages_for_persistent_storage() # remove the last message (the instruction to save the context) messages.pop() json.dump(messages, file, indent=2) diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index 2161aff8f..4ff31dcc0 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -20,6 +20,8 @@ from pipecat.frames.frames import LLMRunFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -223,13 +225,13 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames) llm.register_function("load_conversation", load_conversation) - context = OpenAILLMContext( + context = LLMContext( messages=[ {"role": "system", "content": f"{system_instruction}"}, ], tools=tools, ) - context_aggregator = llm.create_context_aggregator(context) + context_aggregator = LLMContextAggregatorPair(context) pipeline = Pipeline( [ diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py index d9bd2257d..e5e36e404 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -18,7 +18,8 @@ from pipecat.frames.frames import LLMRunFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.aws.nova_sonic.llm import AWSNovaSonicLLMService @@ -119,9 +120,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm.register_function("get_current_weather", fetch_weather_from_api) # Set up context and context management. - # AWSNovaSonicService will adapt OpenAI LLM context objects with standard message format to - # what's expected by Nova Sonic. - context = OpenAILLMContext( + context = LLMContext( messages=[ {"role": "system", "content": f"{system_instruction}"}, { @@ -131,7 +130,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ], tools=tools, ) - context_aggregator = llm.create_context_aggregator(context) + context_aggregator = LLMContextAggregatorPair(context) # Build the pipeline pipeline = Pipeline( diff --git a/src/pipecat/adapters/services/aws_nova_sonic_adapter.py b/src/pipecat/adapters/services/aws_nova_sonic_adapter.py index 64319d266..60f12798b 100644 --- a/src/pipecat/adapters/services/aws_nova_sonic_adapter.py +++ b/src/pipecat/adapters/services/aws_nova_sonic_adapter.py @@ -6,13 +6,47 @@ """AWS Nova Sonic LLM adapter for Pipecat.""" +import copy import json -from typing import Any, Dict, List, TypedDict +from dataclasses import dataclass +from enum import Enum +from typing import Any, Dict, List, Optional, TypedDict + +from loguru import logger from pipecat.adapters.base_llm_adapter import BaseLLMAdapter from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema -from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_context import LLMContext, LLMContextMessage + + +class Role(Enum): + """Roles supported in AWS Nova Sonic conversations. + + Parameters: + SYSTEM: System-level messages (not used in conversation history). + USER: Messages sent by the user. + ASSISTANT: Messages sent by the assistant. + TOOL: Messages sent by tools (not used in conversation history). + """ + + SYSTEM = "SYSTEM" + USER = "USER" + ASSISTANT = "ASSISTANT" + TOOL = "TOOL" + + +@dataclass +class AWSNovaSonicConversationHistoryMessage: + """A single message in AWS Nova Sonic conversation history. + + Parameters: + role: The role of the message sender (USER or ASSISTANT only). + text: The text content of the message. + """ + + role: Role # only USER and ASSISTANT + text: str class AWSNovaSonicLLMInvocationParams(TypedDict): @@ -21,7 +55,9 @@ class AWSNovaSonicLLMInvocationParams(TypedDict): This is a placeholder until support for universal LLMContext machinery is added for AWS Nova Sonic. """ - pass + system_instruction: Optional[str] + messages: List[AWSNovaSonicConversationHistoryMessage] + tools: List[Dict[str, Any]] class AWSNovaSonicLLMAdapter(BaseLLMAdapter[AWSNovaSonicLLMInvocationParams]): @@ -34,7 +70,7 @@ class AWSNovaSonicLLMAdapter(BaseLLMAdapter[AWSNovaSonicLLMInvocationParams]): @property def id_for_llm_specific_messages(self) -> str: """Get the identifier used in LLMSpecificMessage instances for AWS Nova Sonic.""" - raise NotImplementedError("Universal LLMContext is not yet supported for AWS Nova Sonic.") + return "aws-nova-sonic" def get_llm_invocation_params(self, context: LLMContext) -> AWSNovaSonicLLMInvocationParams: """Get AWS Nova Sonic-specific LLM invocation parameters from a universal LLM context. @@ -47,7 +83,13 @@ class AWSNovaSonicLLMAdapter(BaseLLMAdapter[AWSNovaSonicLLMInvocationParams]): Returns: Dictionary of parameters for invoking AWS Nova Sonic's LLM API. """ - raise NotImplementedError("Universal LLMContext is not yet supported for AWS Nova Sonic.") + messages = self._from_universal_context_messages(self.get_messages(context)) + return { + "system_instruction": messages.system_instruction, + "messages": messages.messages, + # NOTE: LLMContext's tools are guaranteed to be a ToolsSchema (or NOT_GIVEN) + "tools": self.from_standard_tools(context.tools) or [], + } def get_messages_for_logging(self, context) -> List[Dict[str, Any]]: """Get messages from a universal LLM context in a format ready for logging about AWS Nova Sonic. @@ -62,7 +104,75 @@ class AWSNovaSonicLLMAdapter(BaseLLMAdapter[AWSNovaSonicLLMInvocationParams]): Returns: List of messages in a format ready for logging about AWS Nova Sonic. """ - raise NotImplementedError("Universal LLMContext is not yet supported for AWS Nova Sonic.") + return self._from_universal_context_messages(self.get_messages(context)).messages + + @dataclass + class ConvertedMessages: + """Container for Google-formatted messages converted from universal context.""" + + messages: List[AWSNovaSonicConversationHistoryMessage] + system_instruction: Optional[str] = None + + def _from_universal_context_messages( + self, universal_context_messages: List[LLMContextMessage] + ) -> ConvertedMessages: + system_instruction = None + messages = [] + + # Bail if there are no messages + if not universal_context_messages: + return self.ConvertedMessages() + + universal_context_messages = copy.deepcopy(universal_context_messages) + + # If we have a "system" message as our first message, let's pull that out into "instruction" + if universal_context_messages[0].get("role") == "system": + system = universal_context_messages.pop(0) + content = system.get("content") + if isinstance(content, str): + system_instruction = content + elif isinstance(content, list): + system_instruction = content[0].get("text") + if system_instruction: + self._system_instruction = system_instruction + + # Process remaining messages to fill out conversation history. + # Nova Sonic supports "user" and "assistant" messages in history. + for universal_context_message in universal_context_messages: + message = self._from_universal_context_message(universal_context_message) + if message: + messages.append(message) + + return self.ConvertedMessages(messages=messages, system_instruction=system_instruction) + + def _from_universal_context_message(self, message) -> AWSNovaSonicConversationHistoryMessage: + """Convert standard message format to Nova Sonic format. + + Args: + message: Standard message dictionary to convert. + + Returns: + Nova Sonic conversation history message, or None if not convertible. + """ + role = message.get("role") + if message.get("role") == "user" or message.get("role") == "assistant": + content = message.get("content") + if isinstance(message.get("content"), list): + content = "" + for c in message.get("content"): + if c.get("type") == "text": + content += " " + c.get("text") + else: + logger.error( + f"Unhandled content type in context message: {c.get('type')} - {message}" + ) + # There won't be content if this is an assistant tool call entry. + # We're ignoring those since they can't be loaded into AWS Nova Sonic conversation + # history + if content: + return AWSNovaSonicConversationHistoryMessage(role=Role[role.upper()], text=content) + # NOTE: we're ignoring messages with role "tool" since they can't be loaded into AWS Nova + # Sonic conversation history @staticmethod def _to_aws_nova_sonic_function_format(function: FunctionSchema) -> Dict[str, Any]: diff --git a/src/pipecat/processors/aggregators/llm_context.py b/src/pipecat/processors/aggregators/llm_context.py index 8b677cf02..e98bc743e 100644 --- a/src/pipecat/processors/aggregators/llm_context.py +++ b/src/pipecat/processors/aggregators/llm_context.py @@ -15,9 +15,10 @@ service-specific adapter. """ import base64 +import copy import io from dataclasses import dataclass -from typing import Any, List, Optional, TypeAlias, Union +from typing import TYPE_CHECKING, Any, List, Optional, TypeAlias, Union from loguru import logger from openai._types import NOT_GIVEN as OPEN_AI_NOT_GIVEN @@ -31,6 +32,9 @@ from PIL import Image from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.frames.frames import AudioRawFrame +if TYPE_CHECKING: + from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext + # "Re-export" types from OpenAI that we're using as universal context types. # NOTE: if universal message types need to someday diverge from OpenAI's, we # should consider managing our own definitions. But we should do so carefully, @@ -65,6 +69,26 @@ class LLMContext: and content formatting. """ + @staticmethod + def from_openai_context(openai_context: "OpenAILLMContext") -> "LLMContext": + """Create a universal LLM context from an OpenAI-specific context. + + NOTE: this should only be used internally, for facilitating migration + from OpenAILLMContext to LLMContext. New user code should use + LLMContext directly. + + Args: + openai_context: The OpenAI LLM context to convert. + + Returns: + New LLMContext instance with converted messages and settings. + """ + return LLMContext( + messages=openai_context.get_messages(), + tools=openai_context.tools, + tool_choice=openai_context.tool_choice, + ) + def __init__( self, messages: Optional[List[LLMContextMessage]] = None, @@ -107,6 +131,21 @@ class LLMContext: ) return filtered_messages + def get_messages_for_persistent_storage( + self, system_instruction: Optional[str] = None + ) -> List[LLMContextMessage]: + """Get messages formatted for persistent storage. + + Args: + system_instruction: Optional system instruction to ensure is + included as the first message in the returned list, if not + already present. + """ + messages = copy.deepcopy(self.get_messages()) + if system_instruction and (not messages or messages[0].get("role") != "system"): + messages.insert(0, {"role": "system", "content": system_instruction}) + return messages + @property def tools(self) -> ToolsSchema | NotGiven: """Get the tools list. diff --git a/src/pipecat/services/aws/nova_sonic/context.py b/src/pipecat/services/aws/nova_sonic/context.py deleted file mode 100644 index 86aa0f0b5..000000000 --- a/src/pipecat/services/aws/nova_sonic/context.py +++ /dev/null @@ -1,367 +0,0 @@ -# -# Copyright (c) 2025, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -"""Context management for AWS Nova Sonic LLM service. - -This module provides specialized context aggregators and message handling for AWS Nova Sonic, -including conversation history management and role-specific message processing. -""" - -import copy -from dataclasses import dataclass, field -from enum import Enum - -from loguru import logger - -from pipecat.frames.frames import ( - BotStoppedSpeakingFrame, - DataFrame, - Frame, - FunctionCallResultFrame, - InterruptionFrame, - LLMFullResponseEndFrame, - LLMFullResponseStartFrame, - LLMMessagesAppendFrame, - LLMMessagesUpdateFrame, - LLMSetToolChoiceFrame, - LLMSetToolsFrame, - TextFrame, - UserImageRawFrame, -) -from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.aws.nova_sonic.frames import AWSNovaSonicFunctionCallResultFrame -from pipecat.services.openai.llm import ( - OpenAIAssistantContextAggregator, - OpenAIUserContextAggregator, -) - - -class Role(Enum): - """Roles supported in AWS Nova Sonic conversations. - - Parameters: - SYSTEM: System-level messages (not used in conversation history). - USER: Messages sent by the user. - ASSISTANT: Messages sent by the assistant. - TOOL: Messages sent by tools (not used in conversation history). - """ - - SYSTEM = "SYSTEM" - USER = "USER" - ASSISTANT = "ASSISTANT" - TOOL = "TOOL" - - -@dataclass -class AWSNovaSonicConversationHistoryMessage: - """A single message in AWS Nova Sonic conversation history. - - Parameters: - role: The role of the message sender (USER or ASSISTANT only). - text: The text content of the message. - """ - - role: Role # only USER and ASSISTANT - text: str - - -@dataclass -class AWSNovaSonicConversationHistory: - """Complete conversation history for AWS Nova Sonic initialization. - - Parameters: - system_instruction: System-level instruction for the conversation. - messages: List of conversation messages between user and assistant. - """ - - system_instruction: str = None - messages: list[AWSNovaSonicConversationHistoryMessage] = field(default_factory=list) - - -class AWSNovaSonicLLMContext(OpenAILLMContext): - """Specialized LLM context for AWS Nova Sonic service. - - Extends OpenAI context with Nova Sonic-specific message handling, - conversation history management, and text buffering capabilities. - """ - - def __init__(self, messages=None, tools=None, **kwargs): - """Initialize AWS Nova Sonic LLM context. - - Args: - messages: Initial messages for the context. - tools: Available tools for the context. - **kwargs: Additional arguments passed to parent class. - """ - super().__init__(messages=messages, tools=tools, **kwargs) - self.__setup_local() - - def __setup_local(self, system_instruction: str = ""): - self._assistant_text = "" - self._user_text = "" - self._system_instruction = system_instruction - - @staticmethod - def upgrade_to_nova_sonic( - obj: OpenAILLMContext, system_instruction: str - ) -> "AWSNovaSonicLLMContext": - """Upgrade an OpenAI context to AWS Nova Sonic context. - - Args: - obj: The OpenAI context to upgrade. - system_instruction: System instruction for the context. - - Returns: - The upgraded AWS Nova Sonic context. - """ - if isinstance(obj, OpenAILLMContext) and not isinstance(obj, AWSNovaSonicLLMContext): - obj.__class__ = AWSNovaSonicLLMContext - obj.__setup_local(system_instruction) - return obj - - # NOTE: this method has the side-effect of updating _system_instruction from messages - def get_messages_for_initializing_history(self) -> AWSNovaSonicConversationHistory: - """Get conversation history for initializing AWS Nova Sonic session. - - Processes stored messages and extracts system instruction and conversation - history in the format expected by AWS Nova Sonic. - - Returns: - Formatted conversation history with system instruction and messages. - """ - history = AWSNovaSonicConversationHistory(system_instruction=self._system_instruction) - - # Bail if there are no messages - if not self.messages: - return history - - messages = copy.deepcopy(self.messages) - - # If we have a "system" message as our first message, let's pull that out into "instruction" - if messages[0].get("role") == "system": - system = messages.pop(0) - content = system.get("content") - if isinstance(content, str): - history.system_instruction = content - elif isinstance(content, list): - history.system_instruction = content[0].get("text") - if history.system_instruction: - self._system_instruction = history.system_instruction - - # Process remaining messages to fill out conversation history. - # Nova Sonic supports "user" and "assistant" messages in history. - for message in messages: - history_message = self.from_standard_message(message) - if history_message: - history.messages.append(history_message) - - return history - - def get_messages_for_persistent_storage(self): - """Get messages formatted for persistent storage. - - Returns: - List of messages including system instruction if present. - """ - messages = super().get_messages_for_persistent_storage() - # If we have a system instruction and messages doesn't already contain it, add it - if self._system_instruction and not (messages and messages[0].get("role") == "system"): - messages.insert(0, {"role": "system", "content": self._system_instruction}) - return messages - - def from_standard_message(self, message) -> AWSNovaSonicConversationHistoryMessage: - """Convert standard message format to Nova Sonic format. - - Args: - message: Standard message dictionary to convert. - - Returns: - Nova Sonic conversation history message, or None if not convertible. - """ - role = message.get("role") - if message.get("role") == "user" or message.get("role") == "assistant": - content = message.get("content") - if isinstance(message.get("content"), list): - content = "" - for c in message.get("content"): - if c.get("type") == "text": - content += " " + c.get("text") - else: - logger.error( - f"Unhandled content type in context message: {c.get('type')} - {message}" - ) - # There won't be content if this is an assistant tool call entry. - # We're ignoring those since they can't be loaded into AWS Nova Sonic conversation - # history - if content: - return AWSNovaSonicConversationHistoryMessage(role=Role[role.upper()], text=content) - # NOTE: we're ignoring messages with role "tool" since they can't be loaded into AWS Nova - # Sonic conversation history - - def buffer_user_text(self, text): - """Buffer user text for later flushing to context. - - Args: - text: User text to buffer. - """ - self._user_text += f" {text}" if self._user_text else text - # logger.debug(f"User text buffered: {self._user_text}") - - def flush_aggregated_user_text(self) -> str: - """Flush buffered user text to context as a complete message. - - Returns: - The flushed user text, or empty string if no text was buffered. - """ - if not self._user_text: - return "" - user_text = self._user_text - message = { - "role": "user", - "content": [{"type": "text", "text": user_text}], - } - self._user_text = "" - self.add_message(message) - # logger.debug(f"Context updated (user): {self.get_messages_for_logging()}") - return user_text - - def buffer_assistant_text(self, text): - """Buffer assistant text for later flushing to context. - - Args: - text: Assistant text to buffer. - """ - self._assistant_text += text - # logger.debug(f"Assistant text buffered: {self._assistant_text}") - - def flush_aggregated_assistant_text(self): - """Flush buffered assistant text to context as a complete message.""" - if not self._assistant_text: - return - message = { - "role": "assistant", - "content": [{"type": "text", "text": self._assistant_text}], - } - self._assistant_text = "" - self.add_message(message) - # logger.debug(f"Context updated (assistant): {self.get_messages_for_logging()}") - - -@dataclass -class AWSNovaSonicMessagesUpdateFrame(DataFrame): - """Frame containing updated AWS Nova Sonic context. - - Parameters: - context: The updated AWS Nova Sonic LLM context. - """ - - context: AWSNovaSonicLLMContext - - -class AWSNovaSonicUserContextAggregator(OpenAIUserContextAggregator): - """Context aggregator for user messages in AWS Nova Sonic conversations. - - Extends the OpenAI user context aggregator to emit Nova Sonic-specific - context update frames. - """ - - async def process_frame( - self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM - ): - """Process frames and emit Nova Sonic-specific context updates. - - Args: - frame: The frame to process. - direction: The direction the frame is traveling. - """ - await super().process_frame(frame, direction) - - # Parent does not push LLMMessagesUpdateFrame - if isinstance(frame, LLMMessagesUpdateFrame): - await self.push_frame(AWSNovaSonicMessagesUpdateFrame(context=self._context)) - - -class AWSNovaSonicAssistantContextAggregator(OpenAIAssistantContextAggregator): - """Context aggregator for assistant messages in AWS Nova Sonic conversations. - - Provides specialized handling for assistant responses and function calls - in AWS Nova Sonic context, with custom frame processing logic. - """ - - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process frames with Nova Sonic-specific logic. - - Args: - frame: The frame to process. - direction: The direction the frame is traveling. - """ - # HACK: For now, disable the context aggregator by making it just pass through all frames - # that the parent handles (except the function call stuff, which we still need). - # For an explanation of this hack, see - # AWSNovaSonicLLMService._report_assistant_response_text_added. - if isinstance( - frame, - ( - InterruptionFrame, - LLMFullResponseStartFrame, - LLMFullResponseEndFrame, - TextFrame, - LLMMessagesAppendFrame, - LLMMessagesUpdateFrame, - LLMSetToolsFrame, - LLMSetToolChoiceFrame, - UserImageRawFrame, - BotStoppedSpeakingFrame, - ), - ): - await self.push_frame(frame, direction) - else: - await super().process_frame(frame, direction) - - async def handle_function_call_result(self, frame: FunctionCallResultFrame): - """Handle function call results for AWS Nova Sonic. - - Args: - frame: The function call result frame to handle. - """ - await super().handle_function_call_result(frame) - - # The standard function callback code path pushes the FunctionCallResultFrame from the LLM - # itself, so we didn't have a chance to add the result to the AWS Nova Sonic server-side - # context. Let's push a special frame to do that. - await self.push_frame( - AWSNovaSonicFunctionCallResultFrame(result_frame=frame), FrameDirection.UPSTREAM - ) - - -@dataclass -class AWSNovaSonicContextAggregatorPair: - """Pair of user and assistant context aggregators for AWS Nova Sonic. - - Parameters: - _user: The user context aggregator. - _assistant: The assistant context aggregator. - """ - - _user: AWSNovaSonicUserContextAggregator - _assistant: AWSNovaSonicAssistantContextAggregator - - def user(self) -> AWSNovaSonicUserContextAggregator: - """Get the user context aggregator. - - Returns: - The user context aggregator instance. - """ - return self._user - - def assistant(self) -> AWSNovaSonicAssistantContextAggregator: - """Get the assistant context aggregator. - - Returns: - The assistant context aggregator instance. - """ - return self._assistant diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 2801de688..c1f941db4 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -25,7 +25,7 @@ from loguru import logger from pydantic import BaseModel, Field from pipecat.adapters.schemas.tools_schema import ToolsSchema -from pipecat.adapters.services.aws_nova_sonic_adapter import AWSNovaSonicLLMAdapter +from pipecat.adapters.services.aws_nova_sonic_adapter import AWSNovaSonicLLMAdapter, Role from pipecat.frames.frames import ( BotStoppedSpeakingFrame, CancelFrame, @@ -33,35 +33,30 @@ from pipecat.frames.frames import ( Frame, FunctionCallFromLLM, InputAudioRawFrame, - InterimTranscriptionFrame, + InterruptionFrame, LLMContextFrame, LLMFullResponseEndFrame, LLMFullResponseStartFrame, - LLMTextFrame, StartFrame, TranscriptionFrame, TTSAudioRawFrame, TTSStartedFrame, TTSStoppedFrame, TTSTextFrame, + UserStartedSpeakingFrame, + UserStoppedSpeakingFrame, ) +from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response import ( LLMAssistantAggregatorParams, LLMUserAggregatorParams, ) +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.processors.aggregators.openai_llm_context import ( OpenAILLMContext, OpenAILLMContextFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.aws.nova_sonic.context import ( - AWSNovaSonicAssistantContextAggregator, - AWSNovaSonicContextAggregatorPair, - AWSNovaSonicLLMContext, - AWSNovaSonicUserContextAggregator, - Role, -) -from pipecat.services.aws.nova_sonic.frames import AWSNovaSonicFunctionCallResultFrame from pipecat.services.llm_service import LLMService from pipecat.utils.time import time_now_iso8601 @@ -70,7 +65,7 @@ try: BedrockRuntimeClient, InvokeModelWithBidirectionalStreamOperationInput, ) - from aws_sdk_bedrock_runtime.config import Config + from aws_sdk_bedrock_runtime.config import Config, HTTPAuthSchemeResolver, SigV4AuthScheme from aws_sdk_bedrock_runtime.models import ( BidirectionalInputPayloadPart, InvokeModelWithBidirectionalStreamInput, @@ -78,8 +73,8 @@ try: InvokeModelWithBidirectionalStreamOperationOutput, InvokeModelWithBidirectionalStreamOutput, ) - from smithy_aws_core.auth.sigv4 import SigV4AuthScheme - from smithy_aws_core.identity.static import StaticCredentialsResolver + from smithy_aws_core.credentials_resolvers.static import StaticCredentialsResolver + from smithy_aws_core.identity import AWSCredentialsIdentity from smithy_core.aio.eventstream import DuplexEventStream except ModuleNotFoundError as e: logger.error(f"Exception: {e}") @@ -231,7 +226,7 @@ class AWSNovaSonicLLMService(LLMService): self._system_instruction = system_instruction self._tools = tools self._send_transcription_frames = send_transcription_frames - self._context: Optional[AWSNovaSonicLLMContext] = None + self._context: Optional[LLMContext] = None self._stream: Optional[ DuplexEventStream[ InvokeModelWithBidirectionalStreamInput, @@ -244,14 +239,19 @@ class AWSNovaSonicLLMService(LLMService): self._input_audio_content_name: Optional[str] = None self._content_being_received: Optional[CurrentContent] = None self._assistant_is_responding = False + self._needs_re_push_assistant_text = False self._ready_to_send_context = False self._handling_bot_stopped_speaking = False self._triggering_assistant_response = False + self._waiting_for_trigger_transcription = False self._disconnecting = False self._connected_time: Optional[float] = None self._wants_connection = False + self._user_text_buffer = "" + self._assistant_text_buffer = "" + self._completed_tool_calls = set() - file_path = files("pipecat.services.aws.nova_sonic").joinpath("ready.wav") + file_path = files("pipecat.services.aws_nova_sonic").joinpath("ready.wav") with wave.open(file_path.open("rb"), "rb") as wav_file: self._assistant_response_trigger_audio = wav_file.readframes(wav_file.getnframes()) @@ -302,12 +302,12 @@ class AWSNovaSonicLLMService(LLMService): logger.debug("Resetting conversation") await self._handle_bot_stopped_speaking(delay_to_catch_trailing_assistant_text=False) - # Carry over previous context through disconnect + # Grab context to carry through disconnect/reconnect context = self._context - await self._disconnect() - self._context = context + await self._disconnect() await self._start_connecting() + await self._handle_context(context) # # frame processing @@ -322,28 +322,35 @@ class AWSNovaSonicLLMService(LLMService): """ await super().process_frame(frame, direction) - if isinstance(frame, OpenAILLMContextFrame): - await self._handle_context(frame.context) - elif isinstance(frame, LLMContextFrame): - raise NotImplementedError( - "Universal LLMContext is not yet supported for AWS Nova Sonic." + if isinstance(frame, (LLMContextFrame, OpenAILLMContextFrame)): + context = ( + frame.context + if isinstance(frame, LLMContextFrame) + else LLMContext.from_openai_context(frame.context) ) + await self._handle_context(context) elif isinstance(frame, InputAudioRawFrame): await self._handle_input_audio_frame(frame) elif isinstance(frame, BotStoppedSpeakingFrame): await self._handle_bot_stopped_speaking(delay_to_catch_trailing_assistant_text=True) - elif isinstance(frame, AWSNovaSonicFunctionCallResultFrame): - await self._handle_function_call_result(frame) + elif isinstance(frame, InterruptionFrame): + await self._handle_interruption_frame() await self.push_frame(frame, direction) - async def _handle_context(self, context: OpenAILLMContext): + async def _handle_context(self, context: LLMContext): + if self._disconnecting: + return + if not self._context: - # We got our initial context - try to finish connecting - self._context = AWSNovaSonicLLMContext.upgrade_to_nova_sonic( - context, self._system_instruction - ) + # We got our initial context + # Try to finish connecting + self._context = context await self._finish_connecting_if_context_available() + else: + # We got an updated context + # Send results for any newly-completed function calls + await self._process_completed_function_calls(send_new_results=True) async def _handle_input_audio_frame(self, frame: InputAudioRawFrame): # Wait until we're done sending the assistant response trigger audio before sending audio @@ -393,9 +400,9 @@ class AWSNovaSonicLLMService(LLMService): else: await finalize_assistant_response() - async def _handle_function_call_result(self, frame: AWSNovaSonicFunctionCallResultFrame): - result = frame.result_frame - await self._send_tool_result(tool_call_id=result.tool_call_id, result=result.result) + async def _handle_interruption_frame(self): + if self._assistant_is_responding: + self._needs_re_push_assistant_text = True # # LLM communication: lifecycle @@ -429,7 +436,18 @@ class AWSNovaSonicLLMService(LLMService): await self._finish_connecting_if_context_available() except Exception as e: logger.error(f"{self} initialization error: {e}") - await self._disconnect() + self._disconnect() + + async def _process_completed_function_calls(self, send_new_results: bool): + # Check for set of completed function calls in the context + for message in self._context.get_messages(): + if message.get("role") and message.get("content") != "IN_PROGRESS": + tool_call_id = message.get("tool_call_id") + if tool_call_id and tool_call_id not in self._completed_tool_calls: + # Found a newly-completed function call - send the result to the service + if send_new_results: + await self._send_tool_result(tool_call_id, message.get("content")) + self._completed_tool_calls.add(tool_call_id) async def _finish_connecting_if_context_available(self): # We can only finish connecting once we've gotten our initial context and we're ready to @@ -439,30 +457,38 @@ class AWSNovaSonicLLMService(LLMService): logger.info("Finishing connecting (setting up session)...") + # Initialize our bookkeeping of already-completed tool calls in the + # context + await self._process_completed_function_calls(send_new_results=False) + # Read context - history = self._context.get_messages_for_initializing_history() + adapter: AWSNovaSonicLLMAdapter = self.get_llm_adapter() + llm_connection_params = adapter.get_llm_invocation_params(self._context) # Send prompt start event, specifying tools. # Tools from context take priority over self._tools. tools = ( - self._context.tools - if self._context.tools - else self.get_llm_adapter().from_standard_tools(self._tools) + llm_connection_params["tools"] + if llm_connection_params["tools"] + else adapter.from_standard_tools(self._tools) ) logger.debug(f"Using tools: {tools}") await self._send_prompt_start_event(tools) # Send system instruction. # Instruction from context takes priority over self._system_instruction. - # (NOTE: this prioritizing occurred automatically behind the scenes: the context was - # initialized with self._system_instruction and then updated itself from its messages when - # get_messages_for_initializing_history() was called). - logger.debug(f"Using system instruction: {history.system_instruction}") - if history.system_instruction: - await self._send_text_event(text=history.system_instruction, role=Role.SYSTEM) + system_instruction = ( + llm_connection_params["system_instruction"] + if llm_connection_params["system_instruction"] + else self._system_instruction + ) + logger.debug(f"Using system instruction: {system_instruction}") + if system_instruction: + await self._send_text_event(text=system_instruction, role=Role.SYSTEM) # Send conversation history - for message in history.messages: + for message in llm_connection_params["messages"]: + # logger.debug(f"Seeding conversation history with message: {message}") await self._send_text_event(text=message.text, role=message.role) # Start audio input @@ -492,9 +518,12 @@ class AWSNovaSonicLLMService(LLMService): await self._send_session_end_events() self._client = None + # Clean up context + self._context = None + # Clean up stream if self._stream: - await self._stream.input_stream.close() + await self._stream.close() self._stream = None # NOTE: see explanation of HACK, below @@ -510,15 +539,23 @@ class AWSNovaSonicLLMService(LLMService): self._receive_task = None # Reset remaining connection-specific state + # Should be all private state except: + # - _wants_connection + # - _assistant_response_trigger_audio self._prompt_name = None self._input_audio_content_name = None self._content_being_received = None self._assistant_is_responding = False + self._needs_re_push_assistant_text = False self._ready_to_send_context = False self._handling_bot_stopped_speaking = False self._triggering_assistant_response = False + self._waiting_for_trigger_transcription = False self._disconnecting = False self._connected_time = None + self._user_text_buffer = "" + self._assistant_text_buffer = "" + self._completed_tool_calls = set() logger.info("Finished disconnecting") except Exception as e: @@ -528,11 +565,15 @@ class AWSNovaSonicLLMService(LLMService): config = Config( endpoint_uri=f"https://bedrock-runtime.{self._region}.amazonaws.com", region=self._region, - aws_access_key_id=self._access_key_id, - aws_secret_access_key=self._secret_access_key, - aws_session_token=self._session_token, - aws_credentials_identity_resolver=StaticCredentialsResolver(), - auth_schemes={"aws.auth#sigv4": SigV4AuthScheme(service="bedrock")}, + aws_credentials_identity_resolver=StaticCredentialsResolver( + credentials=AWSCredentialsIdentity( + access_key_id=self._access_key_id, + secret_access_key=self._secret_access_key, + session_token=self._session_token, + ) + ), + http_auth_scheme_resolver=HTTPAuthSchemeResolver(), + http_auth_schemes={"aws.auth#sigv4": SigV4AuthScheme()}, ) return BedrockRuntimeClient(config=config) @@ -826,6 +867,10 @@ class AWSNovaSonicLLMService(LLMService): # Handle the LLM completion ending await self._handle_completion_end_event(event_json) except Exception as e: + if self._disconnecting: + # Errors are kind of expected while disconnecting, so just + # ignore them and do nothing + return logger.error(f"{self} error processing responses: {e}") if self._wants_connection: await self.reset_conversation() @@ -956,7 +1001,7 @@ class AWSNovaSonicLLMService(LLMService): async def _report_assistant_response_started(self): logger.debug("Assistant response started") - # Report that the assistant has started their response. + # Report the start of the assistant response. await self.push_frame(LLMFullResponseStartFrame()) # Report that equivalent of TTS (this is a speech-to-speech model) started @@ -968,23 +1013,16 @@ class AWSNovaSonicLLMService(LLMService): logger.debug(f"Assistant response text added: {text}") - # Report some text added to the ongoing assistant response - await self.push_frame(LLMTextFrame(text)) - - # Report some text added to the *equivalent* of TTS (this is a speech-to-speech model) + # Report the text of the assistant response. await self.push_frame(TTSTextFrame(text)) - # TODO: this is a (hopefully temporary) HACK. Here we directly manipulate the context rather - # than relying on the frames pushed to the assistant context aggregator. The pattern of - # receiving full-sentence text after the assistant has spoken does not easily fit with the - # Pipecat expectation of chunks of text streaming in while the assistant is speaking. - # Interruption handling was especially challenging. Rather than spend days trying to fit a - # square peg in a round hole, I decided on this hack for the time being. We can most cleanly - # abandon this hack if/when AWS Nova Sonic implements streaming smaller text chunks - # interspersed with audio. Note that when we move away from this hack, we need to make sure - # that on an interruption we avoid sending LLMFullResponseEndFrame, which gets the - # LLMAssistantContextAggregator into a bad state. - self._context.buffer_assistant_text(text) + # HACK: here we're also buffering the assistant text ourselves as a + # backup rather than relying solely on the assistant context aggregator + # to do it, because the text arrives from Nova Sonic only after all the + # assistant audio frames have been pushed, meaning that if an + # interruption frame were to arrive we would lose all of it (the text + # frames sitting in the queue would be wiped). + self._assistant_text_buffer += text async def _report_assistant_response_ended(self): if not self._context: # should never happen @@ -992,14 +1030,25 @@ class AWSNovaSonicLLMService(LLMService): logger.debug("Assistant response ended") - # Report that the assistant has finished their response. + # If an interruption frame arrived while the assistant was responding + # we probably lost all of the assistant text (see HACK, above), so + # re-push it downstream to the aggregator now. + if self._needs_re_push_assistant_text: + # We also need to re-push the LLMFullResponseStartFrame since the + # TTSTextFrame would be ignored otherwise (the interruption frame + # would have cleared the assistant aggregator state). + await self.push_frame(LLMFullResponseStartFrame()) + await self.push_frame(TTSTextFrame(self._assistant_text_buffer)) + self._needs_re_push_assistant_text = False + + # Report the end of the assistant response. await self.push_frame(LLMFullResponseEndFrame()) # Report that equivalent of TTS (this is a speech-to-speech model) stopped. await self.push_frame(TTSStoppedFrame()) - # For an explanation of this hack, see _report_assistant_response_text_added. - self._context.flush_aggregated_assistant_text() + # Clear out the buffered assistant text + self._assistant_text_buffer = "" # # user transcription reporting @@ -1016,33 +1065,71 @@ class AWSNovaSonicLLMService(LLMService): logger.debug(f"User transcription text added: {text}") - # Manually add new user transcription text to context. - # We can't rely on the user context aggregator to do this since it's upstream from the LLM. - self._context.buffer_user_text(text) - - # Report that some new user transcription text is available. - if self._send_transcription_frames: - await self.push_frame( - InterimTranscriptionFrame(text=text, user_id="", timestamp=time_now_iso8601()) - ) + # HACK: here we're buffering the user text ourselves rather than + # relying on the upstream user context aggregator to do it, because the + # text arrives in fairly large chunks spaced fairly far apart in time. + # That means the user text would be split between different messages in + # context. Even if we sent placeholder InterimTranscriptionFrames in + # between each TranscriptionFrame to tell the aggregator to hold off on + # finalizing the user message, the aggregator would likely get the last + # chunk too late. + self._user_text_buffer += f" {text}" if self._user_text_buffer else text async def _report_user_transcription_ended(self): if not self._context: # should never happen return - # Manually add user transcription to context (if any has been buffered). - # We can't rely on the user context aggregator to do this since it's upstream from the LLM. - transcription = self._context.flush_aggregated_user_text() - - if not transcription: - return - logger.debug(f"User transcription ended") - if self._send_transcription_frames: - await self.push_frame( - TranscriptionFrame(text=transcription, user_id="", timestamp=time_now_iso8601()) + # Report to the upstream user context aggregator that some new user + # transcription text is available. + + # HACK: Check if this transcription was triggered by our own + # assistant response trigger. If so, we need to wrap it with + # UserStarted/StoppedSpeakingFrames; otherwise the user aggregator + # would fire an EmulatedUserStartedSpeakingFrame, which would + # trigger an interruption, which would prevent us from writing the + # assistant response to context. + # + # Sending an EmulateUserStartedSpeakingFrame ourselves doesn't + # work: it just causes the interruption we're trying to avoid. + # + # Setting enable_emulated_vad_interruptions also doesn't work: at + # the time the user aggregator receives the TranscriptionFrame, it + # doesn't yet know the assistant has started responding, so it + # doesn't know that emulating the user starting to speak would + # cause an interruption. + should_wrap_in_user_started_stopped_speaking_frames = ( + self._waiting_for_trigger_transcription + and self._user_text_buffer.strip().lower() == "ready" + ) + + # Start wrapping the upstream transcription in UserStarted/StoppedSpeakingFrames if needed + if should_wrap_in_user_started_stopped_speaking_frames: + logger.debug( + "Wrapping assistant response trigger transcription with upstream UserStarted/StoppedSpeakingFrames" ) + await self.push_frame(UserStartedSpeakingFrame(), direction=FrameDirection.UPSTREAM) + + # Send the transcription upstream for the user context aggregator + frame = TranscriptionFrame( + text=self._user_text_buffer, user_id="", timestamp=time_now_iso8601() + ) + await self.push_frame(frame, direction=FrameDirection.UPSTREAM) + + # Finish wrapping the upstream transcription in UserStarted/StoppedSpeakingFrames if needed + if should_wrap_in_user_started_stopped_speaking_frames: + await self.push_frame(UserStoppedSpeakingFrame(), direction=FrameDirection.UPSTREAM) + + # Also send the transcription downstream if requested + if self._send_transcription_frames: + await self.push_frame(frame, direction=FrameDirection.DOWNSTREAM) + + # Clear out the buffered user text + self._user_text_buffer = "" + + # We're no longer waiting for a trigger transcription + self._waiting_for_trigger_transcription = False # # context @@ -1054,23 +1141,26 @@ class AWSNovaSonicLLMService(LLMService): *, user_params: LLMUserAggregatorParams = LLMUserAggregatorParams(), assistant_params: LLMAssistantAggregatorParams = LLMAssistantAggregatorParams(), - ) -> AWSNovaSonicContextAggregatorPair: + ) -> LLMContextAggregatorPair: """Create context aggregator pair for managing conversation context. + NOTE: this method exists only for backward compatibility. New code + should instead do: + context = LLMContext(...) + context_aggregator = LLMContextAggregatorPair(context) + Args: - context: The OpenAI LLM context to upgrade. + context: The OpenAI LLM context. user_params: Parameters for the user context aggregator. assistant_params: Parameters for the assistant context aggregator. Returns: A pair of user and assistant context aggregators. """ - context.set_llm_adapter(self.get_llm_adapter()) - - user = AWSNovaSonicUserContextAggregator(context=context, params=user_params) - assistant = AWSNovaSonicAssistantContextAggregator(context=context, params=assistant_params) - - return AWSNovaSonicContextAggregatorPair(user, assistant) + context = LLMContext.from_openai_context(context) + return LLMContextAggregatorPair( + context, user_params=user_params, assistant_params=assistant_params + ) # # assistant response trigger (HACK) @@ -1108,6 +1198,8 @@ class AWSNovaSonicLLMService(LLMService): try: logger.debug("Sending assistant response trigger...") + self._waiting_for_trigger_transcription = True + chunk_duration = 0.02 # what we might get from InputAudioRawFrame chunk_size = int( chunk_duration diff --git a/tests/test_llm_context.py b/tests/test_llm_context.py new file mode 100644 index 000000000..3dd84bd2c --- /dev/null +++ b/tests/test_llm_context.py @@ -0,0 +1,208 @@ +# +# Copyright (c) 2024-2025 Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import unittest + +from pipecat.processors.aggregators.llm_context import LLMContext, LLMSpecificMessage + + +class TestGetMessagesForPersistentStorage(unittest.TestCase): + """Test suite for LLMContext.get_messages_for_persistent_storage method.""" + + def test_no_system_instruction_returns_messages_as_is(self): + """Test that without system instruction, messages are returned unchanged.""" + messages = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there!"}, + ] + context = LLMContext(messages=messages) + + result = context.get_messages_for_persistent_storage() + + self.assertEqual(result, messages) + self.assertEqual(len(result), 2) + + def test_empty_messages_with_system_instruction_adds_system_message(self): + """Test that system instruction is added when messages list is empty.""" + context = LLMContext() + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + self.assertEqual(len(result), 1) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[0]["content"], system_instruction) + + def test_non_system_first_message_prepends_system_instruction(self): + """Test that system instruction is prepended when first message is not system.""" + messages = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there!"}, + ] + context = LLMContext(messages=messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + self.assertEqual(len(result), 3) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[0]["content"], system_instruction) + self.assertEqual(result[1], messages[0]) + self.assertEqual(result[2], messages[1]) + + def test_existing_system_message_not_duplicated(self): + """Test that system instruction is not added when first message is already system.""" + messages = [ + {"role": "system", "content": "Existing system message"}, + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there!"}, + ] + context = LLMContext(messages=messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + self.assertEqual(len(result), 3) + self.assertEqual(result, messages) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[0]["content"], "Existing system message") + + def test_empty_system_instruction_does_not_add_message(self): + """Test that empty system instruction does not add a system message.""" + messages = [{"role": "user", "content": "Hello"}] + context = LLMContext(messages=messages) + + result = context.get_messages_for_persistent_storage("") + + self.assertEqual(result, messages) + self.assertEqual(len(result), 1) + + def test_none_system_instruction_does_not_add_message(self): + """Test that None system instruction does not add a system message.""" + messages = [{"role": "user", "content": "Hello"}] + context = LLMContext(messages=messages) + + result = context.get_messages_for_persistent_storage(None) + + self.assertEqual(result, messages) + self.assertEqual(len(result), 1) + + def test_whitespace_only_system_instruction_adds_message(self): + """Test that whitespace-only system instruction still adds a system message.""" + messages = [{"role": "user", "content": "Hello"}] + context = LLMContext(messages=messages) + system_instruction = " " + + result = context.get_messages_for_persistent_storage(system_instruction) + + self.assertEqual(len(result), 2) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[0]["content"], system_instruction) + + def test_with_llm_specific_messages(self): + """Test that method works correctly with LLMSpecificMessage objects.""" + llm_specific = LLMSpecificMessage( + llm="test-llm", message={"role": "user", "content": "Specific"} + ) + messages = [{"role": "user", "content": "Standard message"}, llm_specific] + context = LLMContext(messages=messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + self.assertEqual(len(result), 3) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[0]["content"], system_instruction) + self.assertEqual(result[1], messages[0]) + self.assertEqual(result[2], llm_specific) + + def test_system_message_detection_case_sensitivity(self): + """Test that system message detection is case sensitive.""" + messages = [ + {"role": "System", "content": "Mixed case system"}, # Capital S + {"role": "user", "content": "Hello"}, + ] + context = LLMContext(messages=messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + # Should prepend because "System" != "system" + self.assertEqual(len(result), 3) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[0]["content"], system_instruction) + self.assertEqual(result[1], messages[0]) + + def test_message_without_role_key_does_not_crash(self): + """Test that messages without 'role' key are handled gracefully.""" + messages = [{"content": "Message without role"}, {"role": "user", "content": "Hello"}] + context = LLMContext(messages=messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + # Should prepend system instruction since first message doesn't have role="system" + self.assertEqual(len(result), 3) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[0]["content"], system_instruction) + + def test_original_messages_not_modified(self): + """Test that the original messages list is not modified.""" + original_messages = [{"role": "user", "content": "Hello"}] + context = LLMContext(messages=original_messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + # Original messages should remain unchanged + self.assertEqual(len(original_messages), 1) + self.assertEqual(original_messages[0]["role"], "user") + + # Result should have system message prepended + self.assertEqual(len(result), 2) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[1], original_messages[0]) + + def test_complex_message_structure_preserved(self): + """Test that complex message structures are preserved.""" + complex_message = { + "role": "user", + "content": [ + {"type": "text", "text": "Complex message"}, + {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}, + ], + } + messages = [complex_message] + context = LLMContext(messages=messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + self.assertEqual(len(result), 2) + self.assertEqual(result[0]["role"], "system") + self.assertEqual(result[1], complex_message) + self.assertEqual(result[1]["content"], complex_message["content"]) + + def test_deep_copy_prevents_nested_mutation(self): + """Test that deep copy prevents mutation of nested message content.""" + nested_content = {"nested": {"data": "original"}} + complex_message = {"role": "user", "content": nested_content} + messages = [complex_message] + context = LLMContext(messages=messages) + system_instruction = "You are a helpful assistant." + + result = context.get_messages_for_persistent_storage(system_instruction) + + # Modify the nested content in the result + result[1]["content"]["nested"]["data"] = "modified" + + # Original message should remain unchanged + self.assertEqual(complex_message["content"]["nested"]["data"], "original") + self.assertEqual(context.get_messages()[0]["content"]["nested"]["data"], "original") + + +if __name__ == "__main__": + unittest.main() From 860c39d1b1adbbab2a1168b74a5f5ae20b6ced09 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 29 Sep 2025 10:56:00 -0400 Subject: [PATCH 113/158] Get rid of `LLMContext.get_messages_for_persistent_storage()`. The reason for its `system_instruction` argument was to support usage with LLMs where you might pass the system instruction as a parameter to the `LLMService` rather than specifying it in the context. But as I thought about it more I became unconvinced that the `system_instruction` argument was really beneficial: - If you specified your system instruction in your context in the first place, it'll still be there when you read messages for persistent storage - If you didn't specify your system instruction in the context and instead passed it in as an `LLMService` parameter, you most likely *don't* want it to be in the context when you read messages for persistent storage - ...and if you really really do need to inject it at the start of the context, it's quite easy to do anyway And if we remove the `system_instruction` argument from `get_messages_for_persistent_storage()`, then it's essentially just `get_messages()`. --- .../20a-persistent-context-openai.py | 4 +- .../20c-persistent-context-anthropic.py | 5 +- .../20d-persistent-context-gemini.py | 4 +- .../20e-persistent-context-aws-nova-sonic.py | 2 +- .../processors/aggregators/llm_context.py | 15 -- tests/test_llm_context.py | 208 ------------------ 6 files changed, 7 insertions(+), 231 deletions(-) delete mode 100644 tests/test_llm_context.py diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index a3f9f005a..93c1fa438 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -67,11 +67,11 @@ async def save_conversation(params: FunctionCallParams): timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") filename = f"{BASE_FILENAME}{timestamp}.json" logger.debug( - f"writing conversation to {filename}\n{json.dumps(params.context.get_messages_for_persistent_storage(), indent=4)}" + f"writing conversation to {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" ) try: with open(filename, "w") as file: - messages = params.context.get_messages_for_persistent_storage() + messages = params.context.get_messages() # remove the last message, which is the instruction we just gave to save the conversation messages.pop() json.dump(messages, file, indent=2) diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 7a0953290..e8822bbc6 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -68,12 +68,11 @@ async def save_conversation(params: FunctionCallParams): timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") filename = f"{BASE_FILENAME}{timestamp}.json" logger.debug( - f"writing conversation to {filename}\n{json.dumps(params.context.get_messages_for_persistent_storage(), indent=4)}" + f"writing conversation to {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" ) try: with open(filename, "w") as file: - # todo: extract 'system' into the first message in the list - messages = params.context.get_messages_for_persistent_storage() + messages = params.context.get_messages() # remove the last message, which is the instruction we just gave to save the conversation messages.pop() json.dump(messages, file, indent=2) diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index 0e52c0308..b32c2fd5b 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -86,11 +86,11 @@ async def save_conversation(params: FunctionCallParams): timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") filename = f"{BASE_FILENAME}{timestamp}.json" logger.debug( - f"writing conversation to {filename}\n{json.dumps(params.context.get_messages_for_persistent_storage(), indent=4)}" + f"writing conversation to {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" ) try: with open(filename, "w") as file: - messages = params.context.get_messages_for_persistent_storage() + messages = params.context.get_messages() # remove the last message (the instruction to save the context) messages.pop() json.dump(messages, file, indent=2) diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index 4ff31dcc0..0bc3a0d4e 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -77,7 +77,7 @@ async def save_conversation(params: FunctionCallParams): filename = f"{BASE_FILENAME}{timestamp}.json" try: with open(filename, "w") as file: - messages = params.context.get_messages_for_persistent_storage() + messages = params.context.get_messages() # remove the last few messages. in reverse order, they are: # - the in progress save tool call # - the invocation of the save tool call diff --git a/src/pipecat/processors/aggregators/llm_context.py b/src/pipecat/processors/aggregators/llm_context.py index e98bc743e..b60c029cb 100644 --- a/src/pipecat/processors/aggregators/llm_context.py +++ b/src/pipecat/processors/aggregators/llm_context.py @@ -131,21 +131,6 @@ class LLMContext: ) return filtered_messages - def get_messages_for_persistent_storage( - self, system_instruction: Optional[str] = None - ) -> List[LLMContextMessage]: - """Get messages formatted for persistent storage. - - Args: - system_instruction: Optional system instruction to ensure is - included as the first message in the returned list, if not - already present. - """ - messages = copy.deepcopy(self.get_messages()) - if system_instruction and (not messages or messages[0].get("role") != "system"): - messages.insert(0, {"role": "system", "content": system_instruction}) - return messages - @property def tools(self) -> ToolsSchema | NotGiven: """Get the tools list. diff --git a/tests/test_llm_context.py b/tests/test_llm_context.py deleted file mode 100644 index 3dd84bd2c..000000000 --- a/tests/test_llm_context.py +++ /dev/null @@ -1,208 +0,0 @@ -# -# Copyright (c) 2024-2025 Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -import unittest - -from pipecat.processors.aggregators.llm_context import LLMContext, LLMSpecificMessage - - -class TestGetMessagesForPersistentStorage(unittest.TestCase): - """Test suite for LLMContext.get_messages_for_persistent_storage method.""" - - def test_no_system_instruction_returns_messages_as_is(self): - """Test that without system instruction, messages are returned unchanged.""" - messages = [ - {"role": "user", "content": "Hello"}, - {"role": "assistant", "content": "Hi there!"}, - ] - context = LLMContext(messages=messages) - - result = context.get_messages_for_persistent_storage() - - self.assertEqual(result, messages) - self.assertEqual(len(result), 2) - - def test_empty_messages_with_system_instruction_adds_system_message(self): - """Test that system instruction is added when messages list is empty.""" - context = LLMContext() - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - self.assertEqual(len(result), 1) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[0]["content"], system_instruction) - - def test_non_system_first_message_prepends_system_instruction(self): - """Test that system instruction is prepended when first message is not system.""" - messages = [ - {"role": "user", "content": "Hello"}, - {"role": "assistant", "content": "Hi there!"}, - ] - context = LLMContext(messages=messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - self.assertEqual(len(result), 3) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[0]["content"], system_instruction) - self.assertEqual(result[1], messages[0]) - self.assertEqual(result[2], messages[1]) - - def test_existing_system_message_not_duplicated(self): - """Test that system instruction is not added when first message is already system.""" - messages = [ - {"role": "system", "content": "Existing system message"}, - {"role": "user", "content": "Hello"}, - {"role": "assistant", "content": "Hi there!"}, - ] - context = LLMContext(messages=messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - self.assertEqual(len(result), 3) - self.assertEqual(result, messages) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[0]["content"], "Existing system message") - - def test_empty_system_instruction_does_not_add_message(self): - """Test that empty system instruction does not add a system message.""" - messages = [{"role": "user", "content": "Hello"}] - context = LLMContext(messages=messages) - - result = context.get_messages_for_persistent_storage("") - - self.assertEqual(result, messages) - self.assertEqual(len(result), 1) - - def test_none_system_instruction_does_not_add_message(self): - """Test that None system instruction does not add a system message.""" - messages = [{"role": "user", "content": "Hello"}] - context = LLMContext(messages=messages) - - result = context.get_messages_for_persistent_storage(None) - - self.assertEqual(result, messages) - self.assertEqual(len(result), 1) - - def test_whitespace_only_system_instruction_adds_message(self): - """Test that whitespace-only system instruction still adds a system message.""" - messages = [{"role": "user", "content": "Hello"}] - context = LLMContext(messages=messages) - system_instruction = " " - - result = context.get_messages_for_persistent_storage(system_instruction) - - self.assertEqual(len(result), 2) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[0]["content"], system_instruction) - - def test_with_llm_specific_messages(self): - """Test that method works correctly with LLMSpecificMessage objects.""" - llm_specific = LLMSpecificMessage( - llm="test-llm", message={"role": "user", "content": "Specific"} - ) - messages = [{"role": "user", "content": "Standard message"}, llm_specific] - context = LLMContext(messages=messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - self.assertEqual(len(result), 3) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[0]["content"], system_instruction) - self.assertEqual(result[1], messages[0]) - self.assertEqual(result[2], llm_specific) - - def test_system_message_detection_case_sensitivity(self): - """Test that system message detection is case sensitive.""" - messages = [ - {"role": "System", "content": "Mixed case system"}, # Capital S - {"role": "user", "content": "Hello"}, - ] - context = LLMContext(messages=messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - # Should prepend because "System" != "system" - self.assertEqual(len(result), 3) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[0]["content"], system_instruction) - self.assertEqual(result[1], messages[0]) - - def test_message_without_role_key_does_not_crash(self): - """Test that messages without 'role' key are handled gracefully.""" - messages = [{"content": "Message without role"}, {"role": "user", "content": "Hello"}] - context = LLMContext(messages=messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - # Should prepend system instruction since first message doesn't have role="system" - self.assertEqual(len(result), 3) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[0]["content"], system_instruction) - - def test_original_messages_not_modified(self): - """Test that the original messages list is not modified.""" - original_messages = [{"role": "user", "content": "Hello"}] - context = LLMContext(messages=original_messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - # Original messages should remain unchanged - self.assertEqual(len(original_messages), 1) - self.assertEqual(original_messages[0]["role"], "user") - - # Result should have system message prepended - self.assertEqual(len(result), 2) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[1], original_messages[0]) - - def test_complex_message_structure_preserved(self): - """Test that complex message structures are preserved.""" - complex_message = { - "role": "user", - "content": [ - {"type": "text", "text": "Complex message"}, - {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}, - ], - } - messages = [complex_message] - context = LLMContext(messages=messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - self.assertEqual(len(result), 2) - self.assertEqual(result[0]["role"], "system") - self.assertEqual(result[1], complex_message) - self.assertEqual(result[1]["content"], complex_message["content"]) - - def test_deep_copy_prevents_nested_mutation(self): - """Test that deep copy prevents mutation of nested message content.""" - nested_content = {"nested": {"data": "original"}} - complex_message = {"role": "user", "content": nested_content} - messages = [complex_message] - context = LLMContext(messages=messages) - system_instruction = "You are a helpful assistant." - - result = context.get_messages_for_persistent_storage(system_instruction) - - # Modify the nested content in the result - result[1]["content"]["nested"]["data"] = "modified" - - # Original message should remain unchanged - self.assertEqual(complex_message["content"]["nested"]["data"], "original") - self.assertEqual(context.get_messages()[0]["content"]["nested"]["data"], "original") - - -if __name__ == "__main__": - unittest.main() From 4820f1c059037e39a515d63a97af14ff7f1041a2 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 29 Sep 2025 11:46:56 -0400 Subject: [PATCH 114/158] Address some `AWSNovaSonicLLMService` context-recording edge cases --- src/pipecat/services/aws/nova_sonic/llm.py | 54 +++++++++++++++------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index c1f941db4..658bdbd89 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -212,6 +212,11 @@ class AWSNovaSonicLLMService(LLMService): system_instruction: System-level instruction for the model. tools: Available tools/functions for the model to use. send_transcription_frames: Whether to emit transcription frames. + + .. deprecated:: 0.0.87 + This parameter is deprecated and will be removed in a future version. + Transcription frames are always sent. + **kwargs: Additional arguments passed to the parent LLMService. """ super().__init__(**kwargs) @@ -225,7 +230,19 @@ class AWSNovaSonicLLMService(LLMService): self._params = params or Params() self._system_instruction = system_instruction self._tools = tools - self._send_transcription_frames = send_transcription_frames + + if not send_transcription_frames: + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "`send_transcription_frames` is deprecated and will be removed in a future version. " + "Transcription frames are always sent.", + DeprecationWarning, + stacklevel=2, + ) + self._context: Optional[LLMContext] = None self._stream: Optional[ DuplexEventStream[ @@ -239,7 +256,7 @@ class AWSNovaSonicLLMService(LLMService): self._input_audio_content_name: Optional[str] = None self._content_being_received: Optional[CurrentContent] = None self._assistant_is_responding = False - self._needs_re_push_assistant_text = False + self._may_need_repush_assistant_text = False self._ready_to_send_context = False self._handling_bot_stopped_speaking = False self._triggering_assistant_response = False @@ -402,7 +419,7 @@ class AWSNovaSonicLLMService(LLMService): async def _handle_interruption_frame(self): if self._assistant_is_responding: - self._needs_re_push_assistant_text = True + self._may_need_repush_assistant_text = True # # LLM communication: lifecycle @@ -546,7 +563,7 @@ class AWSNovaSonicLLMService(LLMService): self._input_audio_content_name = None self._content_being_received = None self._assistant_is_responding = False - self._needs_re_push_assistant_text = False + self._may_need_repush_assistant_text = False self._ready_to_send_context = False self._handling_bot_stopped_speaking = False self._triggering_assistant_response = False @@ -1031,15 +1048,24 @@ class AWSNovaSonicLLMService(LLMService): logger.debug("Assistant response ended") # If an interruption frame arrived while the assistant was responding - # we probably lost all of the assistant text (see HACK, above), so + # we may have lost all of the assistant text (see HACK, above), so # re-push it downstream to the aggregator now. - if self._needs_re_push_assistant_text: - # We also need to re-push the LLMFullResponseStartFrame since the - # TTSTextFrame would be ignored otherwise (the interruption frame - # would have cleared the assistant aggregator state). - await self.push_frame(LLMFullResponseStartFrame()) - await self.push_frame(TTSTextFrame(self._assistant_text_buffer)) - self._needs_re_push_assistant_text = False + if self._may_need_repush_assistant_text: + # Just in case, check that assistant text hasn't already made it + # into the context (sometimes it does, despite the interruption). + messages = self._context.get_messages() + last_message = messages[-1] if messages else None + if ( + not last_message + or last_message.get("role") != "assistant" + or last_message.get("content") != self._assistant_text_buffer + ): + # We also need to re-push the LLMFullResponseStartFrame since the + # TTSTextFrame would be ignored otherwise (the interruption frame + # would have cleared the assistant aggregator state). + await self.push_frame(LLMFullResponseStartFrame()) + await self.push_frame(TTSTextFrame(self._assistant_text_buffer)) + self._may_need_repush_assistant_text = False # Report the end of the assistant response. await self.push_frame(LLMFullResponseEndFrame()) @@ -1121,10 +1147,6 @@ class AWSNovaSonicLLMService(LLMService): if should_wrap_in_user_started_stopped_speaking_frames: await self.push_frame(UserStoppedSpeakingFrame(), direction=FrameDirection.UPSTREAM) - # Also send the transcription downstream if requested - if self._send_transcription_frames: - await self.push_frame(frame, direction=FrameDirection.DOWNSTREAM) - # Clear out the buffered user text self._user_text_buffer = "" From 87d9e8c9cd78b0a73459cb7e4ba20c6e85d7364a Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 16 Oct 2025 14:51:27 -0400 Subject: [PATCH 115/158] Re-apply a couple of recent changes to `AWSNovaSonicLLMService` that were lost in a rebase --- src/pipecat/services/aws/nova_sonic/llm.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 658bdbd89..2731c1b42 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -65,7 +65,7 @@ try: BedrockRuntimeClient, InvokeModelWithBidirectionalStreamOperationInput, ) - from aws_sdk_bedrock_runtime.config import Config, HTTPAuthSchemeResolver, SigV4AuthScheme + from aws_sdk_bedrock_runtime.config import Config from aws_sdk_bedrock_runtime.models import ( BidirectionalInputPayloadPart, InvokeModelWithBidirectionalStreamInput, @@ -73,8 +73,8 @@ try: InvokeModelWithBidirectionalStreamOperationOutput, InvokeModelWithBidirectionalStreamOutput, ) - from smithy_aws_core.credentials_resolvers.static import StaticCredentialsResolver - from smithy_aws_core.identity import AWSCredentialsIdentity + from smithy_aws_core.auth.sigv4 import SigV4AuthScheme + from smithy_aws_core.identity.static import StaticCredentialsResolver from smithy_core.aio.eventstream import DuplexEventStream except ModuleNotFoundError as e: logger.error(f"Exception: {e}") @@ -453,7 +453,7 @@ class AWSNovaSonicLLMService(LLMService): await self._finish_connecting_if_context_available() except Exception as e: logger.error(f"{self} initialization error: {e}") - self._disconnect() + await self._disconnect() async def _process_completed_function_calls(self, send_new_results: bool): # Check for set of completed function calls in the context @@ -582,15 +582,11 @@ class AWSNovaSonicLLMService(LLMService): config = Config( endpoint_uri=f"https://bedrock-runtime.{self._region}.amazonaws.com", region=self._region, - aws_credentials_identity_resolver=StaticCredentialsResolver( - credentials=AWSCredentialsIdentity( - access_key_id=self._access_key_id, - secret_access_key=self._secret_access_key, - session_token=self._session_token, - ) - ), - http_auth_scheme_resolver=HTTPAuthSchemeResolver(), - http_auth_schemes={"aws.auth#sigv4": SigV4AuthScheme()}, + aws_access_key_id=self._access_key_id, + aws_secret_access_key=self._secret_access_key, + aws_session_token=self._session_token, + aws_credentials_identity_resolver=StaticCredentialsResolver(), + auth_schemes={"aws.auth#sigv4": SigV4AuthScheme(service="bedrock")}, ) return BedrockRuntimeClient(config=config) From f13e006db2b18a58e56a7313bd7753af95a7b366 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 16 Oct 2025 14:52:10 -0400 Subject: [PATCH 116/158] Bump version in deprecation message in docstring --- src/pipecat/services/aws/nova_sonic/llm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 2731c1b42..32d6d4975 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -213,7 +213,7 @@ class AWSNovaSonicLLMService(LLMService): tools: Available tools/functions for the model to use. send_transcription_frames: Whether to emit transcription frames. - .. deprecated:: 0.0.87 + .. deprecated:: 0.0.91 This parameter is deprecated and will be removed in a future version. Transcription frames are always sent. From 4d499324d100a5c6055788cc487aa5237437b35c Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 16 Oct 2025 14:57:03 -0400 Subject: [PATCH 117/158] Re-apply a change to `AWSNovaSonicLLMService` that was lost in a rebase --- src/pipecat/services/aws/nova_sonic/llm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 32d6d4975..5df3bbd21 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -268,7 +268,7 @@ class AWSNovaSonicLLMService(LLMService): self._assistant_text_buffer = "" self._completed_tool_calls = set() - file_path = files("pipecat.services.aws_nova_sonic").joinpath("ready.wav") + file_path = files("pipecat.services.aws.nova_sonic").joinpath("ready.wav") with wave.open(file_path.open("rb"), "rb") as wav_file: self._assistant_response_trigger_audio = wav_file.readframes(wav_file.getnframes()) From ff0b38859bc422144f1322541ad6081021994d0b Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 16 Oct 2025 15:13:21 -0400 Subject: [PATCH 118/158] Remove AWS Nova Sonic's context.py, which was always concerned with types for internal use only. Now those types are either gone or moved elsewhere. --- .../services/aws_nova_sonic/context.py | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/pipecat/services/aws_nova_sonic/context.py diff --git a/src/pipecat/services/aws_nova_sonic/context.py b/src/pipecat/services/aws_nova_sonic/context.py deleted file mode 100644 index 05a24f337..000000000 --- a/src/pipecat/services/aws_nova_sonic/context.py +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright (c) 2025, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -"""Context management for AWS Nova Sonic LLM service. - -This module provides specialized context aggregators and message handling for AWS Nova Sonic, -including conversation history management and role-specific message processing. -""" - -import warnings - -from pipecat.services.aws.nova_sonic.context import * - -with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - "Types in pipecat.services.aws_nova_sonic.context are deprecated. " - "Please use the equivalent types from " - "pipecat.services.aws.nova_sonic.context instead.", - DeprecationWarning, - stacklevel=2, - ) From 3b40079120e9a92a5fada0f9e03a41d9b0c2f871 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 16 Oct 2025 17:36:59 -0400 Subject: [PATCH 119/158] Add a detailed warning when trying to import things from pipecat.services.aws_nova_sonic.context or pipecat.services.aws.nova_sonic.context --- .../services/aws/nova_sonic/context.py | 87 +++++++++++++++++++ .../services/aws_nova_sonic/context.py | 87 +++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 src/pipecat/services/aws/nova_sonic/context.py create mode 100644 src/pipecat/services/aws_nova_sonic/context.py diff --git a/src/pipecat/services/aws/nova_sonic/context.py b/src/pipecat/services/aws/nova_sonic/context.py new file mode 100644 index 000000000..864ae783e --- /dev/null +++ b/src/pipecat/services/aws/nova_sonic/context.py @@ -0,0 +1,87 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Context management for AWS Nova Sonic LLM service. + +This module provides specialized context aggregators and message handling for AWS Nova Sonic, +including conversation history management and role-specific message processing. + +.. deprecated:: 0.0.91 + AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. + Using the new patterns should allow you to not need types from this module. + + BEFORE: + ``` + # Setup + context = OpenAILLMContext(messages, tools) + context_aggregator = llm.create_context_aggregator(context) + + # Context frame type + frame: OpenAILLMContextFrame + + # Context type + context: AWSNovaSonicLLMContext + # or + context: OpenAILLMContext + + # Reading messages from context + messages = context.messages + ``` + + AFTER: + ``` + # Setup + context = LLMContext(messages, tools) + context_aggregator = LLMContextAggregatorPair(context) + + # Context frame type + frame: LLMContextFrame + + # Context type + context: LLMContext + + # Reading messages from context + messages = context.get_messages() + ``` +""" + +import warnings + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.aws.nova_sonic.context are deprecated. \n" + "AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. \n" + "Using the new patterns should allow you to not need types from this module.\n\n" + "BEFORE:\n" + "```\n" + "# Setup\n" + "context = OpenAILLMContext(messages, tools)\n" + "context_aggregator = llm.create_context_aggregator(context)\n\n" + "# Context frame type\n" + "frame: OpenAILLMContextFrame\n\n" + "# Context type\n" + "context: AWSNovaSonicLLMContext\n" + "# or\n" + "context: OpenAILLMContext\n\n" + "# Reading messages from context\n" + "messages = context.messages\n" + "```\n\n" + "AFTER:\n" + "```\n" + "# Setup\n" + "context = LLMContext(messages, tools)\n" + "context_aggregator = LLMContextAggregatorPair(context)\n\n" + "# Context frame type\n" + "frame: LLMContextFrame\n\n" + "# Context type\n" + "context: LLMContext\n\n" + "# Reading messages from context\n" + "messages = context.messages\n" + "```", + DeprecationWarning, + stacklevel=2, + ) diff --git a/src/pipecat/services/aws_nova_sonic/context.py b/src/pipecat/services/aws_nova_sonic/context.py new file mode 100644 index 000000000..b54f80185 --- /dev/null +++ b/src/pipecat/services/aws_nova_sonic/context.py @@ -0,0 +1,87 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Context management for AWS Nova Sonic LLM service. + +This module provides specialized context aggregators and message handling for AWS Nova Sonic, +including conversation history management and role-specific message processing. + +.. deprecated:: 0.0.91 + AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. + Using the new patterns should allow you to not need types from this module. + + BEFORE: + ``` + # Setup + context = OpenAILLMContext(messages, tools) + context_aggregator = llm.create_context_aggregator(context) + + # Context frame type + frame: OpenAILLMContextFrame + + # Context type + context: AWSNovaSonicLLMContext + # or + context: OpenAILLMContext + + # Reading messages from context + messages = context.messages + ``` + + AFTER: + ``` + # Setup + context = LLMContext(messages, tools) + context_aggregator = LLMContextAggregatorPair(context) + + # Context frame type + frame: LLMContextFrame + + # Context type + context: LLMContext + + # Reading messages from context + messages = context.get_messages() + ``` +""" + +import warnings + +with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "Types in pipecat.services.aws_nova_sonic.context are deprecated. \n" + "AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. \n" + "Using the new patterns should allow you to not need types from this module.\n\n" + "BEFORE:\n" + "```\n" + "# Setup\n" + "context = OpenAILLMContext(messages, tools)\n" + "context_aggregator = llm.create_context_aggregator(context)\n\n" + "# Context frame type\n" + "frame: OpenAILLMContextFrame\n\n" + "# Context type\n" + "context: AWSNovaSonicLLMContext\n" + "# or\n" + "context: OpenAILLMContext\n\n" + "# Reading messages from context\n" + "messages = context.messages\n" + "```\n\n" + "AFTER:\n" + "```\n" + "# Setup\n" + "context = LLMContext(messages, tools)\n" + "context_aggregator = LLMContextAggregatorPair(context)\n\n" + "# Context frame type\n" + "frame: LLMContextFrame\n\n" + "# Context type\n" + "context: LLMContext\n\n" + "# Reading messages from context\n" + "messages = context.messages\n" + "```", + DeprecationWarning, + stacklevel=2, + ) From 6cae61f2cc6a92f17cafa1be89046473ef315f66 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 20 Oct 2025 09:46:32 -0400 Subject: [PATCH 120/158] Add a bit more detail to CHANGELOG entry about AWSNovaSonicLLMService's support for LLMContext --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index feb86e5d2..11ee10764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `LLMContext`, it is not meant to be swapped out for another LLM service at runtime.) + Worth noting: whether or not you use the new context-setup pattern with + `AWSNovaSonicLLMService`, some types have changed under the hood: + + ```python + ## BEFORE: + + # Context frame type + frame: OpenAILLMContextFrame + + # Context type + context: AWSNovaSonicLLMContext + # or + context: OpenAILLMContext + + # Reading messages from context + messages = context.messages + + ## AFTER: + # Context frame type + frame: LLMContextFrame + + # Context type + context: LLMContext + + # Reading messages from context + messages = context.get_messages() + ``` + - Added support for `bulbul:v3` model in `SarvamTTSService` and `SarvamHttpTTSService`. - Added `keyterms_prompt` parameter to `AssemblyAIConnectionParams`. From 02a88022ddd0d2b625441a6f7be18c2cace7c49e Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 20 Oct 2025 10:06:09 -0400 Subject: [PATCH 121/158] Add a bit more detail to CHANGELOG related to AWSNovaSonicLLMService's support for LLMContext --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11ee10764..fb4209309 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ```python ## BEFORE: + # Context aggregator type + context_aggregator: AWSNovaSonicContextAggregatorPair + # Context frame type frame: OpenAILLMContextFrame @@ -39,6 +42,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 messages = context.messages ## AFTER: + + # Context aggregator type + context_aggregator: LLMContextAggregatorPair + # Context frame type frame: LLMContextFrame @@ -85,6 +92,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `SpeechmaticsSTTService` updated dependencies for `speechmatics-rt>=0.5.0`. +### Deprecated + +- The `send_transcription_frames` argument to `AWSNovaSonicLLMService` is + deprecated. Transcription frames are now always sent. They go upstream, to be + handled by the user context aggregator. See "Changed" section for details. + +- Types in `pipecat.services.aws.nova_sonic.context` have been deprecated due + to changes to support `LLMContext`. See "Changed" section for details. + ### Fixed - Fixed an issue in `RivaSegmentedSTTService` where a runtime error occurred due From ec3cd241823467271431d88fba4bf630a22db3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 20 Oct 2025 06:56:41 -0700 Subject: [PATCH 122/158] pyproject: update daily-python to 0.20.0 --- CHANGELOG.md | 3 ++- pyproject.toml | 2 +- uv.lock | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71e5300c0..234b9ce90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `keyterms_prompt` parameter to `AssemblyAIConnectionParams`. - Added `speech_model` parameter to `AssemblyAIConnectionParams` to access the multilingual model. -- + - Added support for trickle ICE to the `SmallWebRTCTransport`. - Added support for updating `OpenAITTSService` settings (`instructions` and @@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Package upgrades: + - `daily-python` upgraded to 0.20.0. - `openai` upgraded to support up to 2.x.x. - `openpipe` upgraded to support up to 5.x.x. diff --git a/pyproject.toml b/pyproject.toml index 65546311c..c3bac258a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ azure = [ "azure-cognitiveservices-speech~=1.42.0"] cartesia = [ "cartesia~=2.0.3", "pipecat-ai[websockets-base]" ] cerebras = [] deepseek = [] -daily = [ "daily-python~=0.19.9" ] +daily = [ "daily-python~=0.20.0" ] deepgram = [ "deepgram-sdk~=4.7.0" ] elevenlabs = [ "pipecat-ai[websockets-base]" ] fal = [ "fal-client~=0.5.9" ] diff --git a/uv.lock b/uv.lock index dec765912..96761c015 100644 --- a/uv.lock +++ b/uv.lock @@ -1282,13 +1282,13 @@ wheels = [ [[package]] name = "daily-python" -version = "0.19.9" +version = "0.20.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/85/6064c3225e5b190e522e8f3bc6a460efc5e3e6632f16fd5f9799c44ba57a/daily_python-0.19.9-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:cbc558ad7d49e79b550bf7567b9ceae75e2864d4fcaf41c90377b620e38a2461", size = 13365213, upload-time = "2025-09-06T00:31:00.224Z" }, - { url = "https://files.pythonhosted.org/packages/23/58/af986c6881180a46a7b60dd418ce58d6d7c0c4ffc48d261748067c679317/daily_python-0.19.9-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:446bb9ee848d88bc68ca29a2216793c9b5ebaf5991bf604daf76f7c5a53d5919", size = 11711673, upload-time = "2025-09-06T00:31:02.526Z" }, - { url = "https://files.pythonhosted.org/packages/9d/48/1cad4c3e92cdb5ef06467d972c76a510fe5e807513334b10ad7f8c21bf74/daily_python-0.19.9-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2facaf82b614404c642c70bbf0874fb045d8ad46400acb051470cd4df93cb4db", size = 13679393, upload-time = "2025-09-06T00:31:04.999Z" }, - { url = "https://files.pythonhosted.org/packages/3c/e9/354f4699619e83d13e266256b2352b21741ac527e3e5ab5f2264d5c482cd/daily_python-0.19.9-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ffc205efca7b47739efd358febab17577248c8db2ebc4d17d819307a83b9eefc", size = 14221932, upload-time = "2025-09-06T00:31:07.471Z" }, + { url = "https://files.pythonhosted.org/packages/9b/02/ce81ebf11a04cd133a5539e08f85060574711fff05a1d6ad29705f0755c1/daily_python-0.20.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:7da3f1df8cd9ef7f7fcc96ce688348dc903f62d82b6dd155a53bc64b7a74f3a7", size = 13259887, upload-time = "2025-10-16T22:14:12.262Z" }, + { url = "https://files.pythonhosted.org/packages/4a/1e/51f06f3486c978e1184af2271e800ce6a6e8a8f95d61ee6624bae88ae9cd/daily_python-0.20.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:d02fd7b8c8079ceaa550ef23db052cdf70a8ffaf8ab6a8bc1a1e97bf0b939464", size = 11642453, upload-time = "2025-10-16T22:14:14.477Z" }, + { url = "https://files.pythonhosted.org/packages/71/c9/f767f0b479abd39330569ad61fb9db4661aae56cd74bb27c6f3483595463/daily_python-0.20.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a5c8718982c221dc18b41fb0692c9f8435f115f72e74994c94d3b9c6dad7c534", size = 13634216, upload-time = "2025-10-16T22:14:16.235Z" }, + { url = "https://files.pythonhosted.org/packages/e8/10/5c6d7b000bee36c2a0587a092a34c7486d2de831fc8e44ed42b16a6bd99f/daily_python-0.20.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca9132aef1bdb5be663d1894b440dab1f998ebb3f45dfc31d44effabded4bc08", size = 14282189, upload-time = "2025-10-16T22:14:18.229Z" }, ] [[package]] @@ -4637,7 +4637,7 @@ requires-dist = [ { name = "azure-cognitiveservices-speech", marker = "extra == 'azure'", specifier = "~=1.42.0" }, { name = "cartesia", marker = "extra == 'cartesia'", specifier = "~=2.0.3" }, { name = "coremltools", marker = "extra == 'local-smart-turn'", specifier = ">=8.0" }, - { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.19.9" }, + { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.20.0" }, { name = "deepgram-sdk", marker = "extra == 'deepgram'", specifier = "~=4.7.0" }, { name = "docstring-parser", specifier = "~=0.16" }, { name = "einops", marker = "extra == 'moondream'", specifier = "~=0.8.0" }, @@ -4708,7 +4708,7 @@ requires-dist = [ { name = "simli-ai", marker = "extra == 'simli'", specifier = "~=0.1.10" }, { name = "soundfile", marker = "extra == 'soundfile'", specifier = "~=0.13.0" }, { name = "soxr", specifier = "~=0.5.0" }, - { name = "speechmatics-rt", marker = "extra == 'speechmatics'", specifier = ">=0.4.0" }, + { name = "speechmatics-rt", marker = "extra == 'speechmatics'", specifier = ">=0.5.0" }, { name = "strands-agents", marker = "extra == 'strands'", specifier = ">=1.9.1,<2" }, { name = "tenacity", marker = "extra == 'livekit'", specifier = ">=8.2.3,<10.0.0" }, { name = "timm", marker = "extra == 'moondream'", specifier = "~=1.0.13" }, @@ -6619,14 +6619,14 @@ wheels = [ [[package]] name = "speechmatics-rt" -version = "0.4.2" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/2e/d694390d58b9b6807280441d1275856f5a316c3e8a815c2037502636bbea/speechmatics_rt-0.4.2.tar.gz", hash = "sha256:c0f7ed34442b0f505a12d1b19c8cc8dc2cc0b1a423aeb5669ca0738fc5e59f0d", size = 26142, upload-time = "2025-09-30T10:50:36.804Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/26/10359e1f16c2aa6a198eb11a9056f4a86a8bb8d4e610bbbe4a118b227b59/speechmatics_rt-0.5.0.tar.gz", hash = "sha256:ca974a186a012f946fd997deeaf3bf1c4f203f6d6e05a866172d27709183afc8", size = 26832, upload-time = "2025-10-15T15:54:25.695Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/c7/2cd551c71e14256ca463f31feec17f466b57c2730d636e20803e7a541104/speechmatics_rt-0.4.2-py3-none-any.whl", hash = "sha256:70b91ff750e2f7516eaf1839d39f7a8ac65ff6665638b837cf67bab9cc9967bc", size = 32131, upload-time = "2025-09-30T10:50:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/47/2e/9931ebe9360e9d385c68826b33137c2c9a4cfa361cd929d1ac6e72ebfe53/speechmatics_rt-0.5.0-py3-none-any.whl", hash = "sha256:58151488f891fa00cf7054f0cfab1b1eb94b55c3441be587f7941c726caef991", size = 32850, upload-time = "2025-10-15T15:54:24.5Z" }, ] [[package]] From a0c93ab6dec68b98f79eccf9e0e98947c951d1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 20 Oct 2025 09:07:50 -0700 Subject: [PATCH 123/158] update CHANGELOG cosmetics --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 815057f6b..f13b3cece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,11 +56,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 messages = context.get_messages() ``` -- Added support for `bulbul:v3` model in `SarvamTTSService` and `SarvamHttpTTSService`. +- Added support for `bulbul:v3` model in `SarvamTTSService` and + `SarvamHttpTTSService`. - Added `keyterms_prompt` parameter to `AssemblyAIConnectionParams`. -- Added `speech_model` parameter to `AssemblyAIConnectionParams` to access the multilingual model. +- Added `speech_model` parameter to `AssemblyAIConnectionParams` to access the + multilingual model. - Added support for trickle ICE to the `SmallWebRTCTransport`. @@ -97,7 +99,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `send_transcription_frames` argument to `AWSNovaSonicLLMService` is deprecated. Transcription frames are now always sent. They go upstream, to be - handled by the user context aggregator. See "Changed" section for details. + handled by the user context aggregator. See "Added" section for details. - Types in `pipecat.services.aws.nova_sonic.context` have been deprecated due to changes to support `LLMContext`. See "Changed" section for details. From abf015026177d4c9f5d60485af89ce30a3d7b61b Mon Sep 17 00:00:00 2001 From: vipyne Date: Wed, 15 Oct 2025 10:45:36 -0500 Subject: [PATCH 124/158] add 47-custom-frame-processor.py to foundational examples --- .../foundational/47-custom-frame-processor.py | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 examples/foundational/47-custom-frame-processor.py diff --git a/examples/foundational/47-custom-frame-processor.py b/examples/foundational/47-custom-frame-processor.py new file mode 100644 index 000000000..7de01ab17 --- /dev/null +++ b/examples/foundational/47-custom-frame-processor.py @@ -0,0 +1,223 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import io +import os +import re + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams +from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import ( + BotStartedSpeakingFrame, + BotStoppedSpeakingFrame, + CancelFrame, + EndFrame, + Frame, + FunctionCallResultFrame, + InputAudioRawFrame, + InterruptionFrame, + LLMRunFrame, + LLMTextFrame, + StartFrame, + UserStartedSpeakingFrame, + UserStoppedSpeakingFrame, + VADUserStartedSpeakingFrame, +) +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.frame_processor import FrameDirection, FrameProcessor +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.openai.llm import OpenAILLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams + +load_dotenv(override=True) + + +class CustomFrameProcessor(FrameProcessor): + """CustomFrameProcessor does 3 things: + + 1. keeps count of `InputAudioRawFrame` frames and logs count + when a `UserStoppedSpeakingFrame` is emitted. + + 2. Filters `LLMTextFrame` frames and replaces "the" with "the pumpkin". + + 3. Logs the following frames: + BotStartedSpeakingFrame + BotStoppedSpeakingFrame + CancelFrame + EndFrame + InterruptionFrame + StartFrame + UserStartedSpeakingFrame + VADUserStartedSpeakingFrame + + 4. Always pushes all frames + + """ + + def __init__(self): + super().__init__() + self._raw_audio_input_frame_count = 0 + + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + + #### 1. + # InputAudioRawFrames are noisy- probably don't want to log every instance + # keep a count and only log it when we see `UserStoppedSpeakingFrame` + if isinstance(frame, InputAudioRawFrame): + self._raw_audio_input_frame_count = self._raw_audio_input_frame_count + 1 + await self.push_frame(frame, direction) + + elif isinstance(frame, UserStoppedSpeakingFrame): + logger.info( + f"* * frame: {frame}; number of `InputAudioRawFrame` frames so far: {self._raw_audio_input_frame_count}" + ) + await self.push_frame(frame, direction) + + #### 2. + # everytime the LLM's response includes "the", replace it with "the pumpkin" + elif isinstance(frame, LLMTextFrame): + if "the" in frame.text: + text = re.sub(r" the\b", " the pumpkin", frame.text) + frame.text = text + await self.push_frame(frame, direction) + + #### 3. + # frames types to log + elif isinstance( + frame, + ( + BotStartedSpeakingFrame, + BotStoppedSpeakingFrame, + CancelFrame, + EndFrame, + InterruptionFrame, + StartFrame, + UserStartedSpeakingFrame, + VADUserStartedSpeakingFrame, + ), + ): + logger.info(f"* * frame: {frame}") + await self.push_frame(frame, direction) + + #### 4. + # ALWAYS push all other frames + else: + # SUPER IMPORTANT: always push every frame! + await self.push_frame(frame, direction) + + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ) + + llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + + custom_frame_processor = CustomFrameProcessor() + + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = LLMContext(messages) + context_aggregator = LLMContextAggregatorPair(context) + + pipeline = Pipeline( + [ + transport.input(), + stt, + context_aggregator.user(), + llm, + custom_frame_processor, # filter and log frames + tts, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {client}") + # Kick off the conversation. + messages.append( + { + "role": "system", + "content": "Please introduce yourself to the user and inform them that your responses illustrate use of a Custom Frame Processor.", + } + ) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() From de46631bac1eddc7a01a8ac29bb90c47bdddb3e6 Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Mon, 20 Oct 2025 18:39:00 -0300 Subject: [PATCH 125/158] Fixed an issue where the RTVIProcessor was sending duplicate UserStartedSpeakingFrame and UserStoppedSpeakingFrame messages. --- CHANGELOG.md | 3 +++ src/pipecat/processors/frameworks/rtvi.py | 1 + 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f13b3cece..4763aecd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue where the `RTVIProcessor` was sending duplicate + `UserStartedSpeakingFrame` and `UserStoppedSpeakingFrame` messages. + - Fixed an issue in `RivaSegmentedSTTService` where a runtime error occurred due to a mismatch in the _handle_transcription method's signature. diff --git a/src/pipecat/processors/frameworks/rtvi.py b/src/pipecat/processors/frameworks/rtvi.py index 393cb7115..08d127ef6 100644 --- a/src/pipecat/processors/frameworks/rtvi.py +++ b/src/pipecat/processors/frameworks/rtvi.py @@ -1018,6 +1018,7 @@ class RTVIObserver(BaseObserver): if ( isinstance(frame, (UserStartedSpeakingFrame, UserStoppedSpeakingFrame)) + and (direction == FrameDirection.DOWNSTREAM) and self._params.user_speaking_enabled ): await self._handle_interruptions(frame) From 427efecf5bf4eb358d19eca4cbafbd04ca62c07a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 21 Oct 2025 09:21:30 -0400 Subject: [PATCH 126/158] Organize the env.example file --- env.example | 200 +++++++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 88 deletions(-) diff --git a/env.example b/env.example index f707f49c9..2865772ea 100644 --- a/env.example +++ b/env.example @@ -4,6 +4,9 @@ AICOUSTICS_LICENSE_KEY=... # Anthropic ANTHROPIC_API_KEY=... +# Assembly AI +ASSEMBLYAI_API_KEY=... + # Async ASYNCAI_API_KEY=... ASYNCAI_VOICE_ID=... @@ -21,12 +24,19 @@ AZURE_CHATGPT_API_KEY=... AZURE_CHATGPT_ENDPOINT=https://... AZURE_CHATGPT_MODEL=... +AZURE_REALTIME_API_KEY=... +AZURE_REALTIME_BASE_URL=... + AZURE_DALLE_API_KEY=... AZURE_DALLE_ENDPOINT=https://... AZURE_DALLE_MODEL=... # Cartesia CARTESIA_API_KEY=... +CARTESIA_VOICE_ID=... + +# Cerebras +CEREBRAS_API_KEY=... # Daily DAILY_API_KEY=... @@ -35,57 +45,48 @@ DAILY_SAMPLE_ROOM_URL=https://... # Deepgram DEEPGRAM_API_KEY=... +# DeepSeek +DEEPSEEK_API_KEY=... + # ElevenLabs ELEVENLABS_API_KEY=... ELEVENLABS_VOICE_ID=... -# Neuphonic -NEUPHONIC_API_KEY=... - # Fal FAL_KEY=... # Fireworks FIREWORKS_API_KEY=... +# Fish Audio +FISH_API_KEY=... + # Gladia GLADIA_API_KEY=... GLADIA_REGION=... # Google GOOGLE_API_KEY=... -GOOGLE_CLOUD_PROJECT_ID=... -GOOGLE_TEST_CREDENTIALS=... GOOGLE_VERTEX_TEST_CREDENTIALS=... +GOOGLE_CLOUD_PROJECT_ID=... +GOOGLE_CLOUD_LOCATION=... +GOOGLE_TEST_CREDENTIALS=... + +# Grok +GROK_API_KEY=... + +# Groq +GROQ_API_KEY=... + +# Heygen +HEYGEN_API_KEY=... # Hume HUME_API_KEY=... +HUME_VOICE_ID=... -# LMNT -LMNT_API_KEY=... -LMNT_VOICE_ID=... - -# Perplexity -PERPLEXITY_API_KEY=... - -# PlayHT -PLAYHT_USER_ID=... -PLAYHT_API_KEY=... - -# OpenAI -OPENAI_API_KEY=... - -# OpenPipe -OPENPIPE_API_KEY=... - -# Tavus -TAVUS_API_KEY=... -TAVUS_REPLICA_ID=... -TAVUS_PERSONA_ID=... - -# Simli -SIMLI_API_KEY=... -SIMLI_FACE_ID=... +# Inworld +INWORLD_API_KEY=... # Krisp KRISP_MODEL_PATH=... @@ -93,77 +94,100 @@ KRISP_MODEL_PATH=... # Krisp Viva KRISP_VIVA_MODEL_PATH=... -# DeepSeek -DEEPSEEK_API_KEY=... +# LiveKit +LIVEKIT_API_KEY=... +LIVEKIT_API_SECRET=... -# Groq -GROQ_API_KEY=... - -# Grok -GROK_API_KEY=... - -# Inworld -INWORLD_API_KEY=... - -# Together.ai -TOGETHER_API_KEY=... - -# Cerebras -CEREBRAS_API_KEY=... - -# Fish Audio -FISH_API_KEY=... - -# Assembly AI -ASSEMBLYAI_API_KEY=... - -# OpenRouter -OPENROUTER_API_KEY=... - -# Piper -PIPER_BASE_URL=... - -# Smart turn -LOCAL_SMART_TURN_MODEL_PATH=... -FAL_SMART_TURN_API_KEY=... - -# Twilio -TWILIO_ACCOUNT_SID=... -TWILIO_AUTH_TOKEN=... +# LMNT +LMNT_API_KEY=... +LMNT_VOICE_ID=... # MiniMax MINIMAX_API_KEY=... MINIMAX_GROUP_ID=... -# Sarvam AI -SARVAM_API_KEY=... - -# Soniox -SONIOX_API_KEY= - -# Speechmatics -SPEECHMATICS_API_KEY=... - -# SambaNova -SAMBANOVA_API_KEY=... - -# Sentry -SENTRY_DSN=... - -# Heygen -HEYGEN_API_KEY=... - # Mistral MISTRAL_API_KEY=... +# Neuphonic +NEUPHONIC_API_KEY=... + # NVIDIA NVIDIA_API_KEY=... +# OpenAI +OPENAI_API_KEY=... + +# OpenPipe +OPENPIPE_API_KEY=... + +# OpenRouter +OPENROUTER_API_KEY=... + +# Perplexity +PERPLEXITY_API_KEY=... + +# Picovoice Koala +KOALA_ACCESS_KEY=... + +# Piper +PIPER_BASE_URL=... + +# PlayHT +PLAYHT_USER_ID=... +PLAYHT_API_KEY=... + +# Plivo +PLIVO_AUTH_ID=... +PLIVO_AUTH_TOKEN=... + # Qwen QWEN_API_KEY=... +# Rime +RIME_API_KEY=... +RIME_VOICE_ID=... + +# SambaNova +SAMBANOVA_API_KEY=... + +# Sarvam AI +SARVAM_API_KEY=... + +# Sentry +SENTRY_DSN=... + +# Simli +SIMLI_API_KEY=... +SIMLI_FACE_ID=... + +# Smart turn +LOCAL_SMART_TURN_MODEL_PATH=... +FAL_SMART_TURN_API_KEY=... + +# Soniox +SONIOX_API_KEY=... + +# Speechmatics +SPEECHMATICS_API_KEY=... + +# Tavus +TAVUS_API_KEY=... +TAVUS_REPLICA_ID=... + +# Telnyx +TELNYX_API_KEY=... +TELNYX_ACCOUNT_SID=... + +# Together.ai +TOGETHER_API_KEY=... + +# Twilio +TWILIO_ACCOUNT_SID=... +TWILIO_AUTH_TOKEN=... + # WhatsApp -WHATSAPP_TOKEN= -WHATSAPP_WEBHOOK_VERIFICATION_TOKEN= -WHATSAPP_PHONE_NUMBER_ID= -WHATSAPP_APP_SECRET= \ No newline at end of file +WHATSAPP_TOKEN=... +WHATSAPP_WEBHOOK_VERIFICATION_TOKEN=... +WHATSAPP_PHONE_NUMBER_ID=... +WHATSAPP_APP_SECRET=... \ No newline at end of file From fbf274374cf5a61d42d2dbad9b36d43e32d5c0ef Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 21 Oct 2025 09:56:31 -0400 Subject: [PATCH 127/158] Add back types that were removed, when they should only have been deprecated --- .../services/aws/nova_sonic/context.py | 365 +++++++++++++++++- .../services/aws_nova_sonic/context.py | 76 +--- 2 files changed, 367 insertions(+), 74 deletions(-) diff --git a/src/pipecat/services/aws/nova_sonic/context.py b/src/pipecat/services/aws/nova_sonic/context.py index 864ae783e..d21a14608 100644 --- a/src/pipecat/services/aws/nova_sonic/context.py +++ b/src/pipecat/services/aws/nova_sonic/context.py @@ -10,7 +10,8 @@ This module provides specialized context aggregators and message handling for AW including conversation history management and role-specific message processing. .. deprecated:: 0.0.91 - AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. + AWS Nova Sonic no longer uses types from this module under the hood. + It now uses `LLMContext` and `LLMContextAggregatorPair`. Using the new patterns should allow you to not need types from this module. BEFORE: @@ -53,8 +54,10 @@ import warnings with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( - "Types in pipecat.services.aws.nova_sonic.context are deprecated. \n" - "AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. \n" + "Types in pipecat.services.aws.nova_sonic.context (or " + "pipecat.services.aws_nova_sonic.context) are deprecated. \n" + "AWS Nova Sonic no longer uses types from this module under the hood. \n" + "It now uses `LLMContext` and `LLMContextAggregatorPair`. \n" "Using the new patterns should allow you to not need types from this module.\n\n" "BEFORE:\n" "```\n" @@ -85,3 +88,359 @@ with warnings.catch_warnings(): DeprecationWarning, stacklevel=2, ) + +import copy +from dataclasses import dataclass, field +from enum import Enum + +from loguru import logger + +from pipecat.frames.frames import ( + BotStoppedSpeakingFrame, + DataFrame, + Frame, + FunctionCallResultFrame, + InterruptionFrame, + LLMFullResponseEndFrame, + LLMFullResponseStartFrame, + LLMMessagesAppendFrame, + LLMMessagesUpdateFrame, + LLMSetToolChoiceFrame, + LLMSetToolsFrame, + TextFrame, + UserImageRawFrame, +) +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.processors.frame_processor import FrameDirection +from pipecat.services.aws.nova_sonic.frames import AWSNovaSonicFunctionCallResultFrame +from pipecat.services.openai.llm import ( + OpenAIAssistantContextAggregator, + OpenAIUserContextAggregator, +) + + +class Role(Enum): + """Roles supported in AWS Nova Sonic conversations. + + Parameters: + SYSTEM: System-level messages (not used in conversation history). + USER: Messages sent by the user. + ASSISTANT: Messages sent by the assistant. + TOOL: Messages sent by tools (not used in conversation history). + """ + + SYSTEM = "SYSTEM" + USER = "USER" + ASSISTANT = "ASSISTANT" + TOOL = "TOOL" + + +@dataclass +class AWSNovaSonicConversationHistoryMessage: + """A single message in AWS Nova Sonic conversation history. + + Parameters: + role: The role of the message sender (USER or ASSISTANT only). + text: The text content of the message. + """ + + role: Role # only USER and ASSISTANT + text: str + + +@dataclass +class AWSNovaSonicConversationHistory: + """Complete conversation history for AWS Nova Sonic initialization. + + Parameters: + system_instruction: System-level instruction for the conversation. + messages: List of conversation messages between user and assistant. + """ + + system_instruction: str = None + messages: list[AWSNovaSonicConversationHistoryMessage] = field(default_factory=list) + + +class AWSNovaSonicLLMContext(OpenAILLMContext): + """Specialized LLM context for AWS Nova Sonic service. + + Extends OpenAI context with Nova Sonic-specific message handling, + conversation history management, and text buffering capabilities. + """ + + def __init__(self, messages=None, tools=None, **kwargs): + """Initialize AWS Nova Sonic LLM context. + + Args: + messages: Initial messages for the context. + tools: Available tools for the context. + **kwargs: Additional arguments passed to parent class. + """ + super().__init__(messages=messages, tools=tools, **kwargs) + self.__setup_local() + + def __setup_local(self, system_instruction: str = ""): + self._assistant_text = "" + self._user_text = "" + self._system_instruction = system_instruction + + @staticmethod + def upgrade_to_nova_sonic( + obj: OpenAILLMContext, system_instruction: str + ) -> "AWSNovaSonicLLMContext": + """Upgrade an OpenAI context to AWS Nova Sonic context. + + Args: + obj: The OpenAI context to upgrade. + system_instruction: System instruction for the context. + + Returns: + The upgraded AWS Nova Sonic context. + """ + if isinstance(obj, OpenAILLMContext) and not isinstance(obj, AWSNovaSonicLLMContext): + obj.__class__ = AWSNovaSonicLLMContext + obj.__setup_local(system_instruction) + return obj + + # NOTE: this method has the side-effect of updating _system_instruction from messages + def get_messages_for_initializing_history(self) -> AWSNovaSonicConversationHistory: + """Get conversation history for initializing AWS Nova Sonic session. + + Processes stored messages and extracts system instruction and conversation + history in the format expected by AWS Nova Sonic. + + Returns: + Formatted conversation history with system instruction and messages. + """ + history = AWSNovaSonicConversationHistory(system_instruction=self._system_instruction) + + # Bail if there are no messages + if not self.messages: + return history + + messages = copy.deepcopy(self.messages) + + # If we have a "system" message as our first message, let's pull that out into "instruction" + if messages[0].get("role") == "system": + system = messages.pop(0) + content = system.get("content") + if isinstance(content, str): + history.system_instruction = content + elif isinstance(content, list): + history.system_instruction = content[0].get("text") + if history.system_instruction: + self._system_instruction = history.system_instruction + + # Process remaining messages to fill out conversation history. + # Nova Sonic supports "user" and "assistant" messages in history. + for message in messages: + history_message = self.from_standard_message(message) + if history_message: + history.messages.append(history_message) + + return history + + def get_messages_for_persistent_storage(self): + """Get messages formatted for persistent storage. + + Returns: + List of messages including system instruction if present. + """ + messages = super().get_messages_for_persistent_storage() + # If we have a system instruction and messages doesn't already contain it, add it + if self._system_instruction and not (messages and messages[0].get("role") == "system"): + messages.insert(0, {"role": "system", "content": self._system_instruction}) + return messages + + def from_standard_message(self, message) -> AWSNovaSonicConversationHistoryMessage: + """Convert standard message format to Nova Sonic format. + + Args: + message: Standard message dictionary to convert. + + Returns: + Nova Sonic conversation history message, or None if not convertible. + """ + role = message.get("role") + if message.get("role") == "user" or message.get("role") == "assistant": + content = message.get("content") + if isinstance(message.get("content"), list): + content = "" + for c in message.get("content"): + if c.get("type") == "text": + content += " " + c.get("text") + else: + logger.error( + f"Unhandled content type in context message: {c.get('type')} - {message}" + ) + # There won't be content if this is an assistant tool call entry. + # We're ignoring those since they can't be loaded into AWS Nova Sonic conversation + # history + if content: + return AWSNovaSonicConversationHistoryMessage(role=Role[role.upper()], text=content) + # NOTE: we're ignoring messages with role "tool" since they can't be loaded into AWS Nova + # Sonic conversation history + + def buffer_user_text(self, text): + """Buffer user text for later flushing to context. + + Args: + text: User text to buffer. + """ + self._user_text += f" {text}" if self._user_text else text + # logger.debug(f"User text buffered: {self._user_text}") + + def flush_aggregated_user_text(self) -> str: + """Flush buffered user text to context as a complete message. + + Returns: + The flushed user text, or empty string if no text was buffered. + """ + if not self._user_text: + return "" + user_text = self._user_text + message = { + "role": "user", + "content": [{"type": "text", "text": user_text}], + } + self._user_text = "" + self.add_message(message) + # logger.debug(f"Context updated (user): {self.get_messages_for_logging()}") + return user_text + + def buffer_assistant_text(self, text): + """Buffer assistant text for later flushing to context. + + Args: + text: Assistant text to buffer. + """ + self._assistant_text += text + # logger.debug(f"Assistant text buffered: {self._assistant_text}") + + def flush_aggregated_assistant_text(self): + """Flush buffered assistant text to context as a complete message.""" + if not self._assistant_text: + return + message = { + "role": "assistant", + "content": [{"type": "text", "text": self._assistant_text}], + } + self._assistant_text = "" + self.add_message(message) + # logger.debug(f"Context updated (assistant): {self.get_messages_for_logging()}") + + +@dataclass +class AWSNovaSonicMessagesUpdateFrame(DataFrame): + """Frame containing updated AWS Nova Sonic context. + + Parameters: + context: The updated AWS Nova Sonic LLM context. + """ + + context: AWSNovaSonicLLMContext + + +class AWSNovaSonicUserContextAggregator(OpenAIUserContextAggregator): + """Context aggregator for user messages in AWS Nova Sonic conversations. + + Extends the OpenAI user context aggregator to emit Nova Sonic-specific + context update frames. + """ + + async def process_frame( + self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM + ): + """Process frames and emit Nova Sonic-specific context updates. + + Args: + frame: The frame to process. + direction: The direction the frame is traveling. + """ + await super().process_frame(frame, direction) + + # Parent does not push LLMMessagesUpdateFrame + if isinstance(frame, LLMMessagesUpdateFrame): + await self.push_frame(AWSNovaSonicMessagesUpdateFrame(context=self._context)) + + +class AWSNovaSonicAssistantContextAggregator(OpenAIAssistantContextAggregator): + """Context aggregator for assistant messages in AWS Nova Sonic conversations. + + Provides specialized handling for assistant responses and function calls + in AWS Nova Sonic context, with custom frame processing logic. + """ + + async def process_frame(self, frame: Frame, direction: FrameDirection): + """Process frames with Nova Sonic-specific logic. + + Args: + frame: The frame to process. + direction: The direction the frame is traveling. + """ + # HACK: For now, disable the context aggregator by making it just pass through all frames + # that the parent handles (except the function call stuff, which we still need). + # For an explanation of this hack, see + # AWSNovaSonicLLMService._report_assistant_response_text_added. + if isinstance( + frame, + ( + InterruptionFrame, + LLMFullResponseStartFrame, + LLMFullResponseEndFrame, + TextFrame, + LLMMessagesAppendFrame, + LLMMessagesUpdateFrame, + LLMSetToolsFrame, + LLMSetToolChoiceFrame, + UserImageRawFrame, + BotStoppedSpeakingFrame, + ), + ): + await self.push_frame(frame, direction) + else: + await super().process_frame(frame, direction) + + async def handle_function_call_result(self, frame: FunctionCallResultFrame): + """Handle function call results for AWS Nova Sonic. + + Args: + frame: The function call result frame to handle. + """ + await super().handle_function_call_result(frame) + + # The standard function callback code path pushes the FunctionCallResultFrame from the LLM + # itself, so we didn't have a chance to add the result to the AWS Nova Sonic server-side + # context. Let's push a special frame to do that. + await self.push_frame( + AWSNovaSonicFunctionCallResultFrame(result_frame=frame), FrameDirection.UPSTREAM + ) + + +@dataclass +class AWSNovaSonicContextAggregatorPair: + """Pair of user and assistant context aggregators for AWS Nova Sonic. + + Parameters: + _user: The user context aggregator. + _assistant: The assistant context aggregator. + """ + + _user: AWSNovaSonicUserContextAggregator + _assistant: AWSNovaSonicAssistantContextAggregator + + def user(self) -> AWSNovaSonicUserContextAggregator: + """Get the user context aggregator. + + Returns: + The user context aggregator instance. + """ + return self._user + + def assistant(self) -> AWSNovaSonicAssistantContextAggregator: + """Get the assistant context aggregator. + + Returns: + The assistant context aggregator instance. + """ + return self._assistant diff --git a/src/pipecat/services/aws_nova_sonic/context.py b/src/pipecat/services/aws_nova_sonic/context.py index b54f80185..5728e4ff0 100644 --- a/src/pipecat/services/aws_nova_sonic/context.py +++ b/src/pipecat/services/aws_nova_sonic/context.py @@ -10,78 +10,12 @@ This module provides specialized context aggregators and message handling for AW including conversation history management and role-specific message processing. .. deprecated:: 0.0.91 - AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. + AWS Nova Sonic no longer uses types from this module under the hood. + It now uses `LLMContext` and `LLMContextAggregatorPair`. Using the new patterns should allow you to not need types from this module. - BEFORE: - ``` - # Setup - context = OpenAILLMContext(messages, tools) - context_aggregator = llm.create_context_aggregator(context) - - # Context frame type - frame: OpenAILLMContextFrame - - # Context type - context: AWSNovaSonicLLMContext - # or - context: OpenAILLMContext - - # Reading messages from context - messages = context.messages - ``` - - AFTER: - ``` - # Setup - context = LLMContext(messages, tools) - context_aggregator = LLMContextAggregatorPair(context) - - # Context frame type - frame: LLMContextFrame - - # Context type - context: LLMContext - - # Reading messages from context - messages = context.get_messages() - ``` + See deprecation warning in pipecat.services.aws.nova_sonic.context for more + details. """ -import warnings - -with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - "Types in pipecat.services.aws_nova_sonic.context are deprecated. \n" - "AWS Nova Sonic now supports `LLMContext` and `LLMContextAggregatorPair`. \n" - "Using the new patterns should allow you to not need types from this module.\n\n" - "BEFORE:\n" - "```\n" - "# Setup\n" - "context = OpenAILLMContext(messages, tools)\n" - "context_aggregator = llm.create_context_aggregator(context)\n\n" - "# Context frame type\n" - "frame: OpenAILLMContextFrame\n\n" - "# Context type\n" - "context: AWSNovaSonicLLMContext\n" - "# or\n" - "context: OpenAILLMContext\n\n" - "# Reading messages from context\n" - "messages = context.messages\n" - "```\n\n" - "AFTER:\n" - "```\n" - "# Setup\n" - "context = LLMContext(messages, tools)\n" - "context_aggregator = LLMContextAggregatorPair(context)\n\n" - "# Context frame type\n" - "frame: LLMContextFrame\n\n" - "# Context type\n" - "context: LLMContext\n\n" - "# Reading messages from context\n" - "messages = context.messages\n" - "```", - DeprecationWarning, - stacklevel=2, - ) +from pipecat.services.aws.nova_sonic.context import * From 2f92cb878114dcf33642bfc59720f1148b73a4fd Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Tue, 21 Oct 2025 11:41:52 -0300 Subject: [PATCH 128/158] Fixed an issue in the runner's proxy_request where a session that exists but has empty data was being treated as invalid. --- src/pipecat/runner/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index a90c96ac5..1e690145d 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -309,7 +309,7 @@ def _setup_webrtc_routes( ): """Mimic Pipecat Cloud's proxy.""" active_session = active_sessions.get(session_id) - if not active_session: + if active_session is None: return Response(content="Invalid or not-yet-ready session_id", status_code=404) if path.endswith("api/offer"): From 0502ec6c440c660cd1f92c27d01370110aa173d3 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 21 Oct 2025 11:57:02 -0400 Subject: [PATCH 129/158] Changelog entry for PR #2877 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4763aecd2..311b2b94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added an ellipsis character (`…`) to the end of sentence detection in the + string utils. + - Expanded support for universal `LLMContext` to `AWSNovaSonicLLMService`. As a reminder, the context-setup pattern when using `LLMContext` is: @@ -110,7 +113,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `UserStartedSpeakingFrame` and `UserStoppedSpeakingFrame` messages. - Fixed an issue in `RivaSegmentedSTTService` where a runtime error occurred due - to a mismatch in the _handle_transcription method's signature. + to a mismatch in the `_handle_transcription` method's signature. - Fixed multiple pipeline task cancellation issues. `asyncio.CancelledError` is now handled properly in `PipelineTask` making it possible to cancel an asyncio From 8b24bae9c5f60e1af56859a541c66bb9a4a0bbb9 Mon Sep 17 00:00:00 2001 From: vipyne Date: Tue, 21 Oct 2025 11:42:06 -0500 Subject: [PATCH 130/158] pr notes --- .../foundational/47-custom-frame-processor.py | 100 +++++------------- 1 file changed, 25 insertions(+), 75 deletions(-) diff --git a/examples/foundational/47-custom-frame-processor.py b/examples/foundational/47-custom-frame-processor.py index 7de01ab17..26fe86391 100644 --- a/examples/foundational/47-custom-frame-processor.py +++ b/examples/foundational/47-custom-frame-processor.py @@ -16,20 +16,9 @@ from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnal from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.frames.frames import ( - BotStartedSpeakingFrame, - BotStoppedSpeakingFrame, - CancelFrame, - EndFrame, Frame, - FunctionCallResultFrame, - InputAudioRawFrame, - InterruptionFrame, LLMRunFrame, - LLMTextFrame, - StartFrame, - UserStartedSpeakingFrame, - UserStoppedSpeakingFrame, - VADUserStartedSpeakingFrame, + MetricsFrame, ) from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -48,76 +37,42 @@ from pipecat.transports.daily.transport import DailyParams load_dotenv(override=True) -class CustomFrameProcessor(FrameProcessor): - """CustomFrameProcessor does 3 things: +def format_metrics(metrics, indent=0): + lines = [] + tab = "\t" * indent - 1. keeps count of `InputAudioRawFrame` frames and logs count - when a `UserStoppedSpeakingFrame` is emitted. + for metric in metrics: + lines.append(tab + type(metric).__name__) + for field, value in vars(metric).items(): + if hasattr(value, "__dict__") and not isinstance( + value, (str, int, float, bool, type(None)) + ): + lines.append(f"{tab}\t{field}={type(value).__name__}") + for k, v in vars(value).items(): + lines.append(f"{tab}\t\t{k}={repr(v)}") + else: + lines.append(f"{tab}\t{field}={repr(value)}") - 2. Filters `LLMTextFrame` frames and replaces "the" with "the pumpkin". + return "\n".join(lines) - 3. Logs the following frames: - BotStartedSpeakingFrame - BotStoppedSpeakingFrame - CancelFrame - EndFrame - InterruptionFrame - StartFrame - UserStartedSpeakingFrame - VADUserStartedSpeakingFrame - 4. Always pushes all frames +class MetricsFrameLogger(FrameProcessor): + """MetricsFrameLogger logs all MetericsFrames. + AND it Always pushes all frames. """ def __init__(self): super().__init__() - self._raw_audio_input_frame_count = 0 async def process_frame(self, frame: Frame, direction: FrameDirection): await super().process_frame(frame, direction) - #### 1. - # InputAudioRawFrames are noisy- probably don't want to log every instance - # keep a count and only log it when we see `UserStoppedSpeakingFrame` - if isinstance(frame, InputAudioRawFrame): - self._raw_audio_input_frame_count = self._raw_audio_input_frame_count + 1 + if isinstance(frame, MetricsFrame): + logger.info(f"{frame.name}\n {format_metrics(frame.data)}") await self.push_frame(frame, direction) - elif isinstance(frame, UserStoppedSpeakingFrame): - logger.info( - f"* * frame: {frame}; number of `InputAudioRawFrame` frames so far: {self._raw_audio_input_frame_count}" - ) - await self.push_frame(frame, direction) - - #### 2. - # everytime the LLM's response includes "the", replace it with "the pumpkin" - elif isinstance(frame, LLMTextFrame): - if "the" in frame.text: - text = re.sub(r" the\b", " the pumpkin", frame.text) - frame.text = text - await self.push_frame(frame, direction) - - #### 3. - # frames types to log - elif isinstance( - frame, - ( - BotStartedSpeakingFrame, - BotStoppedSpeakingFrame, - CancelFrame, - EndFrame, - InterruptionFrame, - StartFrame, - UserStartedSpeakingFrame, - VADUserStartedSpeakingFrame, - ), - ): - logger.info(f"* * frame: {frame}") - await self.push_frame(frame, direction) - - #### 4. - # ALWAYS push all other frames + # ALWAYS push all frames else: # SUPER IMPORTANT: always push every frame! await self.push_frame(frame, direction) @@ -155,7 +110,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) - custom_frame_processor = CustomFrameProcessor() + metrics_frame_processor = MetricsFrameLogger() messages = [ { @@ -173,10 +128,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): stt, context_aggregator.user(), llm, - custom_frame_processor, # filter and log frames tts, transport.output(), context_aggregator.assistant(), + metrics_frame_processor, # pretty print metrics frames ] ) @@ -193,12 +148,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") # Kick off the conversation. - messages.append( - { - "role": "system", - "content": "Please introduce yourself to the user and inform them that your responses illustrate use of a Custom Frame Processor.", - } - ) + messages.append({"role": "system", "content": "Please introduce yourself to the user."}) await task.queue_frames([LLMRunFrame()]) @transport.event_handler("on_client_disconnected") From 23385ca3d2d1645d0c6e4710916c35d11e7d8112 Mon Sep 17 00:00:00 2001 From: vipyne Date: Tue, 21 Oct 2025 11:43:47 -0500 Subject: [PATCH 131/158] replace foundational example 08-bots-arguing.py with 08-custom-frame-processor.py --- examples/foundational/08-bots-arguing.py | 147 ------------------ ...cessor.py => 08-custom-frame-processor.py} | 13 +- 2 files changed, 5 insertions(+), 155 deletions(-) delete mode 100644 examples/foundational/08-bots-arguing.py rename examples/foundational/{47-custom-frame-processor.py => 08-custom-frame-processor.py} (97%) diff --git a/examples/foundational/08-bots-arguing.py b/examples/foundational/08-bots-arguing.py deleted file mode 100644 index b84e945c3..000000000 --- a/examples/foundational/08-bots-arguing.py +++ /dev/null @@ -1,147 +0,0 @@ -import asyncio -import logging -import os -from typing import Tuple - -import aiohttp -from dotenv import load_dotenv - -from pipecat.frames.frames import AudioFrame, EndFrame, ImageFrame, LLMContextFrame, TextFrame -from pipecat.pipeline.pipeline import Pipeline -from pipecat.processors.aggregators import SentenceAggregator -from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair -from pipecat.runner.daily import configure -from pipecat.services.azure import AzureLLMService, AzureTTSService -from pipecat.services.elevenlabs import ElevenLabsTTSService -from pipecat.services.fal import FalImageGenService -from pipecat.transports.daily.transport import DailyTransport - -load_dotenv(override=True) - -logging.basicConfig(format=f"%(levelno)s %(asctime)s %(message)s") -logger = logging.getLogger("pipecat") -logger.setLevel(logging.DEBUG) - - -async def main(): - async with aiohttp.ClientSession() as session: - (room_url, _) = await configure(session) - - transport = DailyTransport( - room_url, - None, - "Respond bot", - duration_minutes=10, - mic_enabled=True, - mic_sample_rate=16000, - camera_enabled=True, - camera_width=1024, - camera_height=1024, - ) - - llm = AzureLLMService( - api_key=os.getenv("AZURE_CHATGPT_API_KEY"), - endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"), - model=os.getenv("AZURE_CHATGPT_MODEL"), - ) - tts1 = AzureTTSService( - api_key=os.getenv("AZURE_SPEECH_API_KEY"), - region=os.getenv("AZURE_SPEECH_REGION"), - ) - tts2 = ElevenLabsTTSService( - api_key=os.getenv("ELEVENLABS_API_KEY"), - voice_id="jBpfuIE2acCO8z3wKNLl", - ) - dalle = FalImageGenService( - params=FalImageGenService.InputParams(image_size="1024x1024"), - aiohttp_session=session, - key=os.getenv("FAL_KEY"), - ) - - bot1_messages = [ - { - "role": "system", - "content": "You are a stern librarian. You strongly believe that a hot dog is a sandwich. Start by stating this fact in a few sentences, then be prepared to debate this with the user. You shouldn't ever compromise on the fundamental truth that a hot dog is a sandwich. Your responses should only be a few sentences long.", - }, - ] - bot2_messages = [ - { - "role": "system", - "content": "You are a silly cat, and you strongly believe that a hot dog is not a sandwich. Debate this with the user, only responding with a few sentences. Don't ever accept that a hot dog is a sandwich.", - }, - ] - - async def get_text_and_audio(messages) -> Tuple[str, bytearray]: - """This function streams text from the LLM and uses the TTS service to convert - that text to speech as it's received. - """ - source_queue = asyncio.Queue() - sink_queue = asyncio.Queue() - sentence_aggregator = SentenceAggregator() - pipeline = Pipeline([llm, sentence_aggregator, tts1], source_queue, sink_queue) - - await source_queue.put(LLMContextFrame(LLMContext(messages))) - await source_queue.put(EndFrame()) - await pipeline.run_pipeline() - - message = "" - all_audio = bytearray() - while sink_queue.qsize(): - frame = sink_queue.get_nowait() - if isinstance(frame, TextFrame): - message += frame.text - elif isinstance(frame, AudioFrame): - all_audio.extend(frame.audio) - - return (message, all_audio) - - async def get_bot1_statement(): - message, audio = await get_text_and_audio(bot1_messages) - - bot1_messages.append({"role": "assistant", "content": message}) - bot2_messages.append({"role": "user", "content": message}) - - return audio - - async def get_bot2_statement(): - message, audio = await get_text_and_audio(bot2_messages) - - bot2_messages.append({"role": "assistant", "content": message}) - bot1_messages.append({"role": "user", "content": message}) - - return audio - - async def argue(): - for i in range(100): - print(f"In iteration {i}") - - bot1_description = "A woman conservatively dressed as a librarian in a library surrounded by books, cartoon, serious, highly detailed" - - (audio1, image_data1) = await asyncio.gather( - get_bot1_statement(), dalle.run_image_gen(bot1_description) - ) - await transport.send_queue.put( - [ - ImageFrame(image_data1[1], image_data1[2]), - AudioFrame(audio1), - ] - ) - - bot2_description = "A cat dressed in a hot dog costume, cartoon, bright colors, funny, highly detailed" - - (audio2, image_data2) = await asyncio.gather( - get_bot2_statement(), dalle.run_image_gen(bot2_description) - ) - await transport.send_queue.put( - [ - ImageFrame(image_data2[1], image_data2[2]), - AudioFrame(audio2), - ] - ) - - await asyncio.gather(transport.run(), argue()) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/foundational/47-custom-frame-processor.py b/examples/foundational/08-custom-frame-processor.py similarity index 97% rename from examples/foundational/47-custom-frame-processor.py rename to examples/foundational/08-custom-frame-processor.py index 26fe86391..20da4f876 100644 --- a/examples/foundational/47-custom-frame-processor.py +++ b/examples/foundational/08-custom-frame-processor.py @@ -57,13 +57,10 @@ def format_metrics(metrics, indent=0): class MetricsFrameLogger(FrameProcessor): - """MetricsFrameLogger logs all MetericsFrames. + """MetricsFrameLogger formats and logs all MetericsFrames""" - AND it Always pushes all frames. - """ - - def __init__(self): - super().__init__() + def __init__(self, **kwargs): + super().__init__(**kwargs) async def process_frame(self, frame: Frame, direction: FrameDirection): await super().process_frame(frame, direction) @@ -110,8 +107,6 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) - metrics_frame_processor = MetricsFrameLogger() - messages = [ { "role": "system", @@ -122,6 +117,8 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): context = LLMContext(messages) context_aggregator = LLMContextAggregatorPair(context) + metrics_frame_processor = MetricsFrameLogger() + pipeline = Pipeline( [ transport.input(), From 9f66b0ba41d0ee89f78404d25755dd3d8afb5585 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 21 Oct 2025 08:24:00 -0400 Subject: [PATCH 132/158] Add Pipecat CLI to README's ecosystem section --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 6b00832f3..ea25d492c 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,10 @@ Looking to build structured conversations? Check out [Pipecat Flows](https://git Want to build beautiful and engaging experiences? Checkout the [Voice UI Kit](https://github.com/pipecat-ai/voice-ui-kit), a collection of components, hooks and templates for building voice AI applications quickly. +### πŸ› οΈ Create and deploy projects + +Create a new project in under a minute with the [Pipecat CLI](https://github.com/pipecat-ai/pipecat-cli). Then use the CLI to monitor and deploy your agent to production. + ### πŸ” Debugging Looking for help debugging your pipeline and processors? Check out [Whisker](https://github.com/pipecat-ai/whisker), a real-time Pipecat debugger. From 52ab0eccc0f285a3eccb6afa7fc3ce51fb1e3bc8 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 21 Oct 2025 15:52:27 -0400 Subject: [PATCH 133/158] Quickstart to use Pipecat CLI --- CHANGELOG.md | 2 +- examples/quickstart/README.md | 22 ++++++++++++++++------ examples/quickstart/pcc-deploy.toml | 5 +++++ examples/quickstart/pyproject.toml | 7 ++++--- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4763aecd2..945b25667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,7 +110,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `UserStartedSpeakingFrame` and `UserStoppedSpeakingFrame` messages. - Fixed an issue in `RivaSegmentedSTTService` where a runtime error occurred due - to a mismatch in the _handle_transcription method's signature. + to a mismatch in the `_handle_transcription` method's signature. - Fixed multiple pipeline task cancellation issues. `asyncio.CancelledError` is now handled properly in `PipelineTask` making it possible to cancel an asyncio diff --git a/examples/quickstart/README.md b/examples/quickstart/README.md index cf7c2de1a..91a3fd888 100644 --- a/examples/quickstart/README.md +++ b/examples/quickstart/README.md @@ -73,13 +73,13 @@ Transform your local bot into a production-ready service. Pipecat Cloud handles 1. [Sign up for Pipecat Cloud](https://pipecat.daily.co/sign-up). -2. Install the Pipecat Cloud CLI: +2. Install the Pipecat CLI: ```bash - uv add pipecatcloud + uv tool install pipecat-ai-cli ``` -> πŸ’‘ Tip: You can run the `pipecatcloud` CLI using the `pcc` alias. +> πŸ’‘ Tip: You can run the `pipecat` CLI using the `pc` alias. 3. Set up Docker for building your bot image: @@ -113,12 +113,22 @@ secret_set = "quickstart-secrets" > πŸ’‘ Tip: [Set up `image_credentials`](https://docs.pipecat.ai/deployment/pipecat-cloud/fundamentals/secrets#image-pull-secrets) in your TOML file for authenticated image pulls +### Log in to Pipecat Cloud + +To start using the CLI, authenticate to Pipecat Cloud: + +```bash +pipecat cloud auth login +``` + +You'll be presented with a link that you can click to authenticate your client. + ### Configure secrets Upload your API keys to Pipecat Cloud's secure storage: ```bash -uv run pcc secrets set quickstart-secrets --file .env +pipecat cloud secrets set quickstart-secrets --file .env ``` This creates a secret set called `quickstart-secrets` (matching your TOML file) and uploads all your API keys from `.env`. @@ -128,13 +138,13 @@ This creates a secret set called `quickstart-secrets` (matching your TOML file) Build your Docker image and push to Docker Hub: ```bash -uv run pcc docker build-push +pipecat cloud docker build-push ``` Deploy to Pipecat Cloud: ```bash -uv run pcc deploy +pipecat cloud deploy ``` ### Connect to your agent diff --git a/examples/quickstart/pcc-deploy.toml b/examples/quickstart/pcc-deploy.toml index 28413327f..ff77c45dc 100644 --- a/examples/quickstart/pcc-deploy.toml +++ b/examples/quickstart/pcc-deploy.toml @@ -1,6 +1,11 @@ agent_name = "quickstart" image = "your_username/quickstart:0.1" secret_set = "quickstart-secrets" +agent_profile = "agent-1x" + +# RECOMMENDED: Set an image pull secret: +# https://docs.pipecat.ai/deployment/pipecat-cloud/fundamentals/secrets#image-pull-secrets +# image_credentials = "your_image_pull_secret" [scaling] min_agents = 1 diff --git a/examples/quickstart/pyproject.toml b/examples/quickstart/pyproject.toml index 5d9df3eb4..863e350d4 100644 --- a/examples/quickstart/pyproject.toml +++ b/examples/quickstart/pyproject.toml @@ -4,13 +4,14 @@ version = "0.1.0" description = "Quickstart example for building voice AI bots with Pipecat" requires-python = ">=3.10" dependencies = [ - "pipecat-ai[webrtc,daily,silero,deepgram,openai,cartesia,local-smart-turn-v3,runner]>=0.0.86", - "pipecatcloud>=0.2.4" + "pipecat-ai[webrtc,daily,silero,deepgram,openai,cartesia,local-smart-turn-v3,runner]", + "pipecat-ai-cli" ] [dependency-groups] dev = [ - "ruff~=0.12.1", + "pyright>=1.1.404,<2", + "ruff>=0.12.11,<1", ] [tool.ruff] From 776a3526f9d0192e373bb1d9de9e1c703cf36fc7 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 21 Oct 2025 17:35:57 -0400 Subject: [PATCH 134/158] Add `messages` property to `LLMContext` for usage parity with `OpenAILLMContext`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This wasn't really an issue before, when folks were *knowingly* migrating from `OpenAILLMContext` to `LLMContext`. But in the latest AWS Nova Sonic change, we're swapping it out from under folks, so this kind of compatibility is more important. For context, the reason we *didn't* offer the `messages` property earlier was to aid in the development of `LLMContext`β€”we wanted to draw attention to all the places where messages were being read from context, so we could find the places where we might need to pass an argument to the read. --- CHANGELOG.md | 6 ------ .../processors/aggregators/llm_context.py | 16 +++++++++++++++- src/pipecat/services/aws/nova_sonic/context.py | 10 ---------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 311b2b94f..34cee203f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,9 +41,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # or context: OpenAILLMContext - # Reading messages from context - messages = context.messages - ## AFTER: # Context aggregator type @@ -54,9 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Context type context: LLMContext - - # Reading messages from context - messages = context.get_messages() ``` - Added support for `bulbul:v3` model in `SarvamTTSService` and diff --git a/src/pipecat/processors/aggregators/llm_context.py b/src/pipecat/processors/aggregators/llm_context.py index b60c029cb..913566909 100644 --- a/src/pipecat/processors/aggregators/llm_context.py +++ b/src/pipecat/processors/aggregators/llm_context.py @@ -106,6 +106,19 @@ class LLMContext: self._tools: ToolsSchema | NotGiven = LLMContext._normalize_and_validate_tools(tools) self._tool_choice: LLMContextToolChoice | NotGiven = tool_choice + @property + def messages(self) -> List[LLMContextMessage]: + """Get the current messages list. + + NOTE: This is equivalent to calling `get_messages()` with no filter. If + you want to filter out LLM-specific messages that don't pertain to your + LLM, use `get_messages()` directly. + + Returns: + List of conversation messages. + """ + return self.get_messages() + def get_messages(self, llm_specific_filter: Optional[str] = None) -> List[LLMContextMessage]: """Get the current messages list. @@ -113,7 +126,8 @@ class LLMContext: llm_specific_filter: Optional filter to return LLM-specific messages for the given LLM, in addition to the standard messages. If messages end up being filtered, an error will be - logged. + logged; this is intended to catch accidental use of + incompatible LLM-specific messages. Returns: List of conversation messages. diff --git a/src/pipecat/services/aws/nova_sonic/context.py b/src/pipecat/services/aws/nova_sonic/context.py index d21a14608..c9aab439f 100644 --- a/src/pipecat/services/aws/nova_sonic/context.py +++ b/src/pipecat/services/aws/nova_sonic/context.py @@ -27,9 +27,6 @@ including conversation history management and role-specific message processing. context: AWSNovaSonicLLMContext # or context: OpenAILLMContext - - # Reading messages from context - messages = context.messages ``` AFTER: @@ -43,9 +40,6 @@ including conversation history management and role-specific message processing. # Context type context: LLMContext - - # Reading messages from context - messages = context.get_messages() ``` """ @@ -70,8 +64,6 @@ with warnings.catch_warnings(): "context: AWSNovaSonicLLMContext\n" "# or\n" "context: OpenAILLMContext\n\n" - "# Reading messages from context\n" - "messages = context.messages\n" "```\n\n" "AFTER:\n" "```\n" @@ -82,8 +74,6 @@ with warnings.catch_warnings(): "frame: LLMContextFrame\n\n" "# Context type\n" "context: LLMContext\n\n" - "# Reading messages from context\n" - "messages = context.messages\n" "```", DeprecationWarning, stacklevel=2, From 1e8629bf96a245ecf9dc4d6866ff5eaa8dd5bac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 20 Oct 2025 17:06:13 -0700 Subject: [PATCH 135/158] runner: allow passing an api_key to configure --- src/pipecat/runner/daily.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pipecat/runner/daily.py b/src/pipecat/runner/daily.py index 5bff7d060..568d54381 100644 --- a/src/pipecat/runner/daily.py +++ b/src/pipecat/runner/daily.py @@ -76,6 +76,7 @@ class DailyRoomConfig(BaseModel): async def configure( aiohttp_session: aiohttp.ClientSession, *, + api_key: Optional[str] = None, room_exp_duration: Optional[float] = 2.0, token_exp_duration: Optional[float] = 2.0, sip_caller_phone: Optional[str] = None, @@ -92,6 +93,7 @@ async def configure( Args: aiohttp_session: HTTP session for making API requests. + api_key: Daily API key. room_exp_duration: Room expiration time in hours. token_exp_duration: Token expiration time in hours. sip_caller_phone: Phone number or identifier for SIP display name. @@ -129,7 +131,7 @@ async def configure( config = await configure(session, room_properties=custom_props) """ # Check for required API key - api_key = os.getenv("DAILY_API_KEY") + api_key = api_key or os.getenv("DAILY_API_KEY") if not api_key: raise Exception( "DAILY_API_KEY environment variable is required. " From 576bd67e85c1e7d909b15a59b8180f450468c56e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 20 Oct 2025 17:06:33 -0700 Subject: [PATCH 136/158] runner: add body field to RunnerArguments --- CHANGELOG.md | 3 +++ src/pipecat/runner/types.py | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 311b2b94f..4004bfd24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- `RunnerArguments` now include the `body` field, so there's no need to add it + to subclasses. Also, all `RunnerArguments` fields are now keyword-only. + - `CartesiaSTTService` now inherits from `WebsocketSTTService`. - Package upgrades: diff --git a/src/pipecat/runner/types.py b/src/pipecat/runner/types.py index 89aecd84c..fab2404f3 100644 --- a/src/pipecat/runner/types.py +++ b/src/pipecat/runner/types.py @@ -20,9 +20,11 @@ from fastapi import WebSocket class RunnerArguments: """Base class for runner session arguments.""" - handle_sigint: bool = field(init=False) - handle_sigterm: bool = field(init=False) - pipeline_idle_timeout_secs: int = field(init=False) + # Use kw_only so subclasses don't need to worry about ordering. + handle_sigint: bool = field(init=False, kw_only=True) + handle_sigterm: bool = field(init=False, kw_only=True) + pipeline_idle_timeout_secs: int = field(init=False, kw_only=True) + body: Optional[Any] = field(default_factory=dict, kw_only=True) def __post_init__(self): self.handle_sigint = False @@ -42,7 +44,6 @@ class DailyRunnerArguments(RunnerArguments): room_url: str token: Optional[str] = None - body: Optional[Any] = field(default_factory=dict) @dataclass @@ -55,7 +56,6 @@ class WebSocketRunnerArguments(RunnerArguments): """ websocket: WebSocket - body: Optional[Any] = field(default_factory=dict) @dataclass From 459af58540e9c19ad4278e54105332ddd093e91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 20 Oct 2025 17:06:53 -0700 Subject: [PATCH 137/158] runner: allow starting a bot from Daily's /start endpoint --- CHANGELOG.md | 4 +++ src/pipecat/runner/run.py | 68 ++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4004bfd24..cb0ecae43 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 ### Added +- It is now possible to start a bot from the `/start` endpoint when using the + runner Daily's transport. This follows the Pipecat Cloud format with + `createDailyRoom` and `body` fields in the POST request body. + - Added an ellipsis character (`…`) to the end of sentence detection in the string utils. diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index 1e690145d..4c1d3135d 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -82,6 +82,7 @@ from loguru import logger from pipecat.runner.types import ( DailyRunnerArguments, + RunnerArguments, SmallWebRTCRunnerArguments, WebSocketRunnerArguments, ) @@ -529,9 +530,9 @@ def _setup_daily_routes(app: FastAPI): """Set up Daily-specific routes.""" @app.get("/") - async def start_agent(): + async def create_room_and_start_agent(): """Launch a Daily bot and redirect to room.""" - print("Starting bot with Daily transport") + print("Starting bot with Daily transport and redirecting to Daily room") import aiohttp @@ -546,11 +547,11 @@ def _setup_daily_routes(app: FastAPI): asyncio.create_task(bot_module.bot(runner_args)) return RedirectResponse(room_url) - async def _handle_rtvi_request(request: Request): - """Common handler for both /start and /connect endpoints. + @app.post("/start") + async def start_agent(request: Request): + """Handler for /start endpoints. Expects POST body like:: - { "createDailyRoom": true, "dailyRoomProperties": { "start_video_off": true }, @@ -567,45 +568,32 @@ def _setup_daily_routes(app: FastAPI): logger.error(f"Failed to parse request body: {e}") request_data = {} - # Extract the body data that should be passed to the bot - # This mimics Pipecat Cloud's behavior - bot_body = request_data.get("body", {}) + create_daily_room = request_data.get("createDailyRoom", False) + body = request_data.get("body", {}) - # Log the extracted body data for debugging - if bot_body: - logger.info(f"Extracted body data for bot: {bot_body}") + bot_module = _get_bot_module() + + result = None + if create_daily_room: + import aiohttp + + from pipecat.runner.daily import configure + + async with aiohttp.ClientSession() as session: + room_url, token = await configure(session) + runner_args = DailyRunnerArguments(room_url=room_url, token=token, body=body) + result = { + "dailyRoom": room_url, + "dailyToken": token, + "sessionId": str(uuid.uuid4()), + } else: - logger.debug("No body data provided in request") + runner_args = RunnerArguments(body=body) - from pipecat.runner.daily import configure + # Start the bot in the background + asyncio.create_task(bot_module.bot(runner_args)) - async with aiohttp.ClientSession() as session: - room_url, token = await configure(session) - - # Start the bot in the background with extracted body data - bot_module = _get_bot_module() - runner_args = DailyRunnerArguments(room_url=room_url, token=token, body=bot_body) - asyncio.create_task(bot_module.bot(runner_args)) - # Match PCC /start endpoint response format: - return {"dailyRoom": room_url, "dailyToken": token} - - @app.post("/start") - async def rtvi_start(request: Request): - """Launch a Daily bot and return connection info for RTVI clients.""" - return await _handle_rtvi_request(request) - - @app.post("/connect") - async def rtvi_connect(request: Request): - """Launch a Daily bot and return connection info for RTVI clients. - - .. deprecated:: 0.0.78 - Use /start instead. This endpoint will be removed in a future version. - """ - logger.warning( - "DEPRECATED: /connect endpoint is deprecated. Please use /start instead. " - "This endpoint will be removed in a future version." - ) - return await _handle_rtvi_request(request) + return result def _setup_telephony_routes(app: FastAPI, *, transport_type: str, proxy: str): From 5849485bc603a19e09ddfc5d2e716457ca6f79b3 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 20 Oct 2025 20:20:33 -0400 Subject: [PATCH 138/158] fix: AWSBedrockLLMService compatibility for newer Claude models --- CHANGELOG.md | 6 +++ .../foundational/07m-interruptible-aws.py | 4 +- .../foundational/14r-function-calling-aws.py | 4 +- src/pipecat/services/aws/llm.py | 49 +++++++++++++------ 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 311b2b94f..6ccaf1798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue where the `RTVIProcessor` was sending duplicate `UserStartedSpeakingFrame` and `UserStoppedSpeakingFrame` messages. +- Fixed an issue in `AWSBedrockLLMService` where both `temperature` and `top_p` + were always sent together, causing conflicts with models like Claude Sonnet 4.5 + that don't allow both parameters simultaneously. The service now only includes + inference parameters that are explicitly set, and `InputParams` defaults have + been changed to `None` to rely on AWS Bedrock's built-in model defaults. + - Fixed an issue in `RivaSegmentedSTTService` where a runtime error occurred due to a mismatch in the `_handle_transcription` method's signature. diff --git a/examples/foundational/07m-interruptible-aws.py b/examples/foundational/07m-interruptible-aws.py index 9343797a9..2d3bb1dac 100644 --- a/examples/foundational/07m-interruptible-aws.py +++ b/examples/foundational/07m-interruptible-aws.py @@ -67,8 +67,8 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AWSBedrockLLMService( aws_region="us-west-2", - model="us.anthropic.claude-3-5-haiku-20241022-v1:0", - params=AWSBedrockLLMService.InputParams(temperature=0.8, latency="optimized"), + model="us.anthropic.claude-haiku-4-5-20251001-v1:0", + params=AWSBedrockLLMService.InputParams(temperature=0.8), ) messages = [ diff --git a/examples/foundational/14r-function-calling-aws.py b/examples/foundational/14r-function-calling-aws.py index 03aa7bb96..15f7e37a0 100644 --- a/examples/foundational/14r-function-calling-aws.py +++ b/examples/foundational/14r-function-calling-aws.py @@ -79,8 +79,8 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AWSBedrockLLMService( aws_region="us-west-2", - model="us.anthropic.claude-3-5-haiku-20241022-v1:0", - params=AWSBedrockLLMService.InputParams(temperature=0.8, latency="optimized"), + model="us.anthropic.claude-haiku-4-5-20251001-v1:0", + params=AWSBedrockLLMService.InputParams(temperature=0.8), ) # You can also register a function_name of None to get all functions diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index 377848f76..716aee776 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -720,11 +720,11 @@ class AWSBedrockLLMService(LLMService): additional_model_request_fields: Additional model-specific parameters. """ - max_tokens: Optional[int] = Field(default_factory=lambda: 4096, ge=1) - temperature: Optional[float] = Field(default_factory=lambda: 0.7, ge=0.0, le=1.0) - top_p: Optional[float] = Field(default_factory=lambda: 0.999, ge=0.0, le=1.0) + max_tokens: Optional[int] = Field(default=None, ge=1) + temperature: Optional[float] = Field(default=None, ge=0.0, le=1.0) + top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0) stop_sequences: Optional[List[str]] = Field(default_factory=lambda: []) - latency: Optional[str] = Field(default_factory=lambda: "standard") + latency: Optional[str] = Field(default=None) additional_model_request_fields: Optional[Dict[str, Any]] = Field(default_factory=dict) def __init__( @@ -801,6 +801,24 @@ class AWSBedrockLLMService(LLMService): """ return True + def _build_inference_config(self) -> Dict[str, Any]: + """Build inference config with only the parameters that are set. + + This prevents conflicts with models (e.g., Claude Sonnet 4.5) that don't + allow certain parameter combinations like temperature and top_p together. + + Returns: + Dictionary containing only the inference parameters that are not None. + """ + inference_config = {} + if self._settings["max_tokens"] is not None: + inference_config["maxTokens"] = self._settings["max_tokens"] + if self._settings["temperature"] is not None: + inference_config["temperature"] = self._settings["temperature"] + if self._settings["top_p"] is not None: + inference_config["topP"] = self._settings["top_p"] + return inference_config + async def run_inference(self, context: LLMContext | OpenAILLMContext) -> Optional[str]: """Run a one-shot, out-of-band (i.e. out-of-pipeline) inference with the given LLM context. @@ -826,16 +844,16 @@ class AWSBedrockLLMService(LLMService): model_id = self.model_name # Prepare request parameters + inference_config = self._build_inference_config() + request_params = { "modelId": model_id, "messages": messages, - "inferenceConfig": { - "maxTokens": 8192, - "temperature": 0.7, - "topP": 0.9, - }, } + if inference_config: + request_params["inferenceConfig"] = inference_config + if system: request_params["system"] = system @@ -974,21 +992,20 @@ class AWSBedrockLLMService(LLMService): tools = params_from_context["tools"] tool_choice = params_from_context["tool_choice"] - # Set up inference config - inference_config = { - "maxTokens": self._settings["max_tokens"], - "temperature": self._settings["temperature"], - "topP": self._settings["top_p"], - } + # Set up inference config - only include parameters that are set + inference_config = self._build_inference_config() # Prepare request parameters request_params = { "modelId": self.model_name, "messages": messages, - "inferenceConfig": inference_config, "additionalModelRequestFields": self._settings["additional_model_request_fields"], } + # Only add inference config if it has parameters + if inference_config: + request_params["inferenceConfig"] = inference_config + # Add system message if system: request_params["system"] = system From cc4c96d0990a82b340a0c7291aea8de994a538c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 21 Oct 2025 19:00:51 -0700 Subject: [PATCH 139/158] update CHANGELOG for 0.0.91 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ca9da580..06e7b998b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.0.91] - 2025-10-21 ### Added From 49239a23c668766d024b7624cf5ebb2959f536da Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 21 Oct 2025 23:27:38 -0400 Subject: [PATCH 140/158] Upgrade aws_sdk_bedrock_runtime to v0.1.1 --- CHANGELOG.md | 7 ++++++ pyproject.toml | 2 +- uv.lock | 66 +++++++++++++++++++++++++------------------------- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06e7b998b..6e727c42e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- Upgraded `aws_sdk_bedrock_runtime` to v0.1.1 to resolve potential CPU issues + when running `AWSNovaSonicLLMService`. + ## [0.0.91] - 2025-10-21 ### Added diff --git a/pyproject.toml b/pyproject.toml index c3bac258a..fd992fa86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ anthropic = [ "anthropic~=0.49.0" ] assemblyai = [ "pipecat-ai[websockets-base]" ] asyncai = [ "pipecat-ai[websockets-base]" ] aws = [ "aioboto3~=15.0.0", "pipecat-ai[websockets-base]" ] -aws-nova-sonic = [ "aws_sdk_bedrock_runtime~=0.1.0; python_version>='3.12'" ] +aws-nova-sonic = [ "aws_sdk_bedrock_runtime~=0.1.1; python_version>='3.12'" ] azure = [ "azure-cognitiveservices-speech~=1.42.0"] cartesia = [ "cartesia~=2.0.3", "pipecat-ai[websockets-base]" ] cerebras = [] diff --git a/uv.lock b/uv.lock index 96761c015..8fae18c09 100644 --- a/uv.lock +++ b/uv.lock @@ -410,16 +410,16 @@ wheels = [ [[package]] name = "aws-sdk-bedrock-runtime" -version = "0.1.0" +version = "0.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smithy-aws-core", extra = ["eventstream", "json"], marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, { name = "smithy-http", extra = ["awscrt"], marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/e1/39971b907c83a7525bab112c9b395e1bb6d4bc23bc1712d6d7a050662217/aws_sdk_bedrock_runtime-0.1.0.tar.gz", hash = "sha256:bd062de5a48404f64e1dfe6fb8841fbbf68e8f1798c357d14eb427274cb96a2b", size = 85419, upload-time = "2025-09-29T19:40:01.855Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/78/48574454b3cac869df67665e4a403ebfc3abfcfba2c2ff01ccfd67d55f8f/aws_sdk_bedrock_runtime-0.1.1.tar.gz", hash = "sha256:c896f99e675c3a1ab600633a07b785f3dc9fe8ab94f640b1f992b63da2dfc784", size = 82446, upload-time = "2025-10-21T20:25:25.845Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/e1/5b36bffe85010cdcd44730d1c2d5244653d57c002f440141d7fc3b9f1347/aws_sdk_bedrock_runtime-0.1.0-py3-none-any.whl", hash = "sha256:aac6ff47069d456ca5e23083d96a01e3e0cbc215414e6753c289d7d9efef3335", size = 78853, upload-time = "2025-09-29T19:40:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/83/07/62c0b70223d178c138f29124ac2f7973a6ba803abc7735b6a01a85217f3d/aws_sdk_bedrock_runtime-0.1.1-py3-none-any.whl", hash = "sha256:c0336b377b2112cf88197d3d44302fbeb3efb1101989fa49ae55e78f49cfe345", size = 74954, upload-time = "2025-10-21T20:25:24.973Z" }, ] [[package]] @@ -433,31 +433,31 @@ wheels = [ [[package]] name = "awscrt" -version = "0.28.1" +version = "0.28.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a0/1c/5c9e6a7375c2a1355aadeb2d06c96c95934ec37ff29ebaab2919f59c3ff1/awscrt-0.28.1.tar.gz", hash = "sha256:70a28fd6ff3e0abb7854ea8a9133bc9e5de681a0d9bdbd8a599a23d13a448685", size = 37956730, upload-time = "2025-09-19T00:58:31.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/1b/a885a699217967c3ff0e1c49ac5b1e2a050d1a8b87d1e85e958a56e3d3f5/awscrt-0.28.2.tar.gz", hash = "sha256:9715a888f2042e710dc8aeb355963a29b77e7a4cc25a14659cebd21a5fa476c1", size = 37894849, upload-time = "2025-10-14T19:06:16.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/75/dd62276f2907a9ffcf9f8f780c08ce9938bd0550a15c887db198b47f24d3/awscrt-0.28.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:47f885104065918d311102e2b08b943966717c0f3b0c5de5908d2fd08de32198", size = 3376838, upload-time = "2025-09-19T00:57:32.988Z" }, - { url = "https://files.pythonhosted.org/packages/a7/93/562709cdf13a7606548426ecc31326ba3f6839f91e98a1e9230208308afb/awscrt-0.28.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3df2316e77ad88c456b7eb2c9928007d379ed892154c1969d35b98653617e576", size = 3821522, upload-time = "2025-09-19T00:57:35.456Z" }, - { url = "https://files.pythonhosted.org/packages/43/f0/6c6ff81f5a4c6d085eb450854149087bf9240c37c467c747521f47901b32/awscrt-0.28.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3a060d930939f142345f46a344e19ffc0dada657b04d02216b8adffba550c0a0", size = 4087344, upload-time = "2025-09-19T00:57:36.62Z" }, - { url = "https://files.pythonhosted.org/packages/37/0a/71c097505add4ceea4ac05153311715acb7489cd82ec69db4570130f4698/awscrt-0.28.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43f81ca6bfe85c38ad9765605aaaa646a1ed6fd7210dbedf67c113dd245f425e", size = 3745148, upload-time = "2025-09-19T00:57:38Z" }, - { url = "https://files.pythonhosted.org/packages/79/1b/2b02b705a47b64e6c4d401087ddd30d4ad9af70172812ae8c62fb2b7a70c/awscrt-0.28.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fc8e2307d9dbe76842015a14701ff7e9cf2619d674621b2d55b769414e17b3fc", size = 3972439, upload-time = "2025-09-19T00:57:39.74Z" }, - { url = "https://files.pythonhosted.org/packages/f1/19/429c81c7a0d81a5edce9cc6d9a878c8b65d8b5b69fa5a2725a6e0b1380c1/awscrt-0.28.1-cp310-cp310-win32.whl", hash = "sha256:6e7b094587e5332d428300340dcc18794a1fcfa76d636f216fc0f5c8405ba604", size = 3915231, upload-time = "2025-09-19T00:57:41.096Z" }, - { url = "https://files.pythonhosted.org/packages/83/81/769ad51fc6dcfd8bf9e0aa59c252013da0eb9e32c050ecbd1fc25f71689a/awscrt-0.28.1-cp310-cp310-win_amd64.whl", hash = "sha256:ac02f10f7384fdb68187f8d5d94743a271b16fa94be81481ce7684942f6a4b35", size = 4051668, upload-time = "2025-09-19T00:57:42.696Z" }, - { url = "https://files.pythonhosted.org/packages/9e/55/0ee537d146f24d6e76eaf02d462a83c572788233603bb9bda969fbf23307/awscrt-0.28.1-cp311-abi3-macosx_10_15_universal2.whl", hash = "sha256:cb36052f9aa34e77687a8037559bbea331fc9d5d77cd71ab0cf4e6d72af73f72", size = 3376673, upload-time = "2025-09-19T00:57:43.875Z" }, - { url = "https://files.pythonhosted.org/packages/f0/54/12700a4b9545680baa3e2d4d0e543bb4775a639df56ee51cbb29b71e0947/awscrt-0.28.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc59829152a5806eb2708aca5c5084c11dd18ecbe765e03eb314d5a360eeaa62", size = 3782870, upload-time = "2025-09-19T00:57:45.737Z" }, - { url = "https://files.pythonhosted.org/packages/1d/e7/7b189ace9e187b9b55ed4a6ec9a451579b2f16bd01d402f79a19cc8e1603/awscrt-0.28.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d2f20bc774599b9d85ce66689415da529ddd1d2215da818e005deedc4688fe61", size = 4048789, upload-time = "2025-09-19T00:57:47.327Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e0/2e5472019906dfcc5fadcdba4bad9e69dabb95bbc0c110cfe555ee8461dc/awscrt-0.28.1-cp311-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:491b8b9c73a288cfd5e0cbdac16aabb5313d5cfc33bbe461763a5ddc26624f70", size = 3687832, upload-time = "2025-09-19T00:57:48.563Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7e05d371bb888ee9f15e83d189287838f7b6ea40dfc91eacb3acd24b8529/awscrt-0.28.1-cp311-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:4c6c7125b7e9fcc999eb685d1cace8d4f2ffc63f8f3d8ef7f77e1a97d9552863", size = 3913378, upload-time = "2025-09-19T00:57:50.185Z" }, - { url = "https://files.pythonhosted.org/packages/79/6b/a542a65a22edb85d64742970c21721e66e0f9f67911a11c7a5c3626a1b17/awscrt-0.28.1-cp311-abi3-win32.whl", hash = "sha256:1dcb33d7cf8f69881ac6ef75a5b9b40816be58678b1bb07ccbe0230281bdbc81", size = 3912809, upload-time = "2025-09-19T00:57:51.797Z" }, - { url = "https://files.pythonhosted.org/packages/df/64/16cc8a0011e3ca5dda13605befa7e6db29bfb3073c67f6e8dad90be0a8ae/awscrt-0.28.1-cp311-abi3-win_amd64.whl", hash = "sha256:670caaf556876913bcfb9d8183d43d67a6c7b52998f2f398abd1c21632a006f8", size = 4048979, upload-time = "2025-09-19T00:57:53.061Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ac/debbd3a2f03c5953b56b1c3b321bab16293f857ea3005e3f7e5dded5e0b2/awscrt-0.28.1-cp313-abi3-macosx_10_15_universal2.whl", hash = "sha256:22311d25135b937ee5617e35a6554961727527dcfa3e06efdefe187a6abe65c4", size = 3375565, upload-time = "2025-09-19T00:57:54.598Z" }, - { url = "https://files.pythonhosted.org/packages/ea/4f/9388917ad45c043acd7c4ab2c28b9e2b5ddf29e21a82bfc01a7626c18c04/awscrt-0.28.1-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e58740cf0e41552fdf7909e10814b312ab090ebe54741354a61507e0c6d4ebfd", size = 3775366, upload-time = "2025-09-19T00:57:56.238Z" }, - { url = "https://files.pythonhosted.org/packages/8a/e3/3ef301cdef76b22ce14b041e04c6cf65ba4491d00e9f5b400c0699f6c63e/awscrt-0.28.1-cp313-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e69f163a207a8b172abbfea1f51045301ed1ac8bbaf76958a6b5e81d72e5b89", size = 4043403, upload-time = "2025-09-19T00:57:57.4Z" }, - { url = "https://files.pythonhosted.org/packages/60/9c/4f89922333724c4da851752549ca97dd147420734ef6c4ece56d5dd65e09/awscrt-0.28.1-cp313-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:592f4b234ecafa6cde86e55e42c4fe84c4e1ffe9fb11b0a8b8f0ffb8c62fa2cc", size = 3678742, upload-time = "2025-09-19T00:57:59.055Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d4/adb97ba5f888ed201aa1f9e9f8d6cfc0dbaf80f0e937b3acb7411febdaa8/awscrt-0.28.1-cp313-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b16321f1d2bf5b4991a213059c1b5dc07954edfc424d154b093824465ec94ce2", size = 3908438, upload-time = "2025-09-19T00:58:00.71Z" }, - { url = "https://files.pythonhosted.org/packages/41/ac/600ea0a6f4ba6543c50417c8e78b09f2cd73dd0f0d4c3e9e52220a8badbe/awscrt-0.28.1-cp313-abi3-win32.whl", hash = "sha256:3e0a23635aa75b4af163ff9bf5a0873928369b1ac32c8b1351741a95472ccf71", size = 3907625, upload-time = "2025-09-19T00:58:03.235Z" }, - { url = "https://files.pythonhosted.org/packages/9e/24/d22c7197b1e53c76b5eb71d640a4728b9b7621075d8dbcc054e16b5b98f0/awscrt-0.28.1-cp313-abi3-win_amd64.whl", hash = "sha256:9849c88ca0830396724acf988e2759895118fe7dd2a23dab21978c8600d01a11", size = 4043878, upload-time = "2025-09-19T00:58:04.595Z" }, + { url = "https://files.pythonhosted.org/packages/73/b4/1a566e493bdfa6e918ba78bcd2e45dda99a25407a4fd974db2666228d154/awscrt-0.28.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:bec19c0dd780293a26c809aabb9f7675b28cb3a1bf05b4a5bc9f28d5ced75a81", size = 3380735, upload-time = "2025-10-14T19:05:16.58Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/6602a87aead1d413c7bd77d059b301745146635cda99ee2a61ec0d23691e/awscrt-0.28.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01f33076759ba6285f25ccc6016355607df2e715d0bab3a1ef2416b87a6c3ade", size = 3827084, upload-time = "2025-10-14T19:05:19.335Z" }, + { url = "https://files.pythonhosted.org/packages/d8/62/61fe39ae5950ad00e10dcbf6e4f4f344dc93957757160c0000390331a11b/awscrt-0.28.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b5c807b9972795ce54c05aea6918c60983c51d879ebbff7a67adb8b0d28a121", size = 4092678, upload-time = "2025-10-14T19:05:20.8Z" }, + { url = "https://files.pythonhosted.org/packages/25/7d/e38f18cfb203e8f09842c0e3f422992887ce285ecc3bf18816d559a13c80/awscrt-0.28.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf4ff9c8c6a233246320c2d41d939b6e25cdae97728d827186e4771a9edda688", size = 3749978, upload-time = "2025-10-14T19:05:22.16Z" }, + { url = "https://files.pythonhosted.org/packages/16/6f/e8a3c0daed8f7b60c76fc2721bd4e83580ddecace24e0cb0ebb99564f699/awscrt-0.28.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c738b83b66d1a8b43089556247fbe4adf2b73d610c7938d3bae1718a0fe8b1d", size = 3977237, upload-time = "2025-10-14T19:05:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/92/3d/8400203f02dd924bcc8255703179b0c26efd03c84f838db6f026fcef9ba6/awscrt-0.28.2-cp310-cp310-win32.whl", hash = "sha256:23c30004c736a2f826a32c9720f1ccf71e8e4deb8535da5915d6073604853098", size = 3919413, upload-time = "2025-10-14T19:05:24.477Z" }, + { url = "https://files.pythonhosted.org/packages/c0/5e/b5ccf377880a70425b100f1e5f5ba516ff75e291585b3dc129239fbd1ec3/awscrt-0.28.2-cp310-cp310-win_amd64.whl", hash = "sha256:859ae8a195d51f15b631147d6792953a563bfe0a1cc7a75b6750977634de54b8", size = 4056024, upload-time = "2025-10-14T19:05:25.956Z" }, + { url = "https://files.pythonhosted.org/packages/ed/79/94e9f0ee7c60ec6233c7ad6293589c56d5145172e49eb5328eda37d3fdd1/awscrt-0.28.2-cp311-abi3-macosx_10_15_universal2.whl", hash = "sha256:025eab99b58586d8c95f8fafe1f4695ad477eda20d1207240ee4f8ee79742059", size = 3381061, upload-time = "2025-10-14T19:05:27.187Z" }, + { url = "https://files.pythonhosted.org/packages/2d/b8/0da80dd58682ddf3ec204e877d5891198654647c085e65b6b8eacd214edb/awscrt-0.28.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5c18d035d6cd92228e1db2f043517c1bcf9e0f6430c0af60cc34257dcca092c", size = 3788011, upload-time = "2025-10-14T19:05:28.768Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d2/f51cf4364364399fe90d557e2fed14c1f114720191a5825898b1242bd607/awscrt-0.28.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c75f077e90d0220a49b75a9bca914e5aa1a3c8f28af6bce4d0332be0b98dd3cb", size = 4055226, upload-time = "2025-10-14T19:05:30.054Z" }, + { url = "https://files.pythonhosted.org/packages/41/47/0fde8738a8c76de278ce431d8468ef18aeaca424329decca9ad5092df812/awscrt-0.28.2-cp311-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1432c5c59a7e36b33eb2746cfbf30058f19ed43f2c117863897681f70bc246ba", size = 3692839, upload-time = "2025-10-14T19:05:31.471Z" }, + { url = "https://files.pythonhosted.org/packages/18/25/cb3762f6b47fe503eea7f337eca7cfd044ab28bcc2452fbf298c6492ec8b/awscrt-0.28.2-cp311-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f96703c30b22ba1e43e1bb2fe996ac7af513bea411c54dbf09a3a1af329b9a76", size = 3918023, upload-time = "2025-10-14T19:05:33.162Z" }, + { url = "https://files.pythonhosted.org/packages/95/0a/0b609acd45dbb83c04c7ecb8c7c789f5c15bbdd422129360bde093bc4a99/awscrt-0.28.2-cp311-abi3-win32.whl", hash = "sha256:3e94f63497b454d30892d7a7ce917a451c6f33590964d3a475d93f93b20083b6", size = 3917048, upload-time = "2025-10-14T19:05:34.745Z" }, + { url = "https://files.pythonhosted.org/packages/d1/38/bf33abd6d09c8572f8e09488db2b0a60124767d7f5d6d9a33cf8b051b7af/awscrt-0.28.2-cp311-abi3-win_amd64.whl", hash = "sha256:3e094772b1f6fd0f8c5f7cf37655d0984739f99493f66f534979a2a7bb7fc9f6", size = 4052877, upload-time = "2025-10-14T19:05:36.01Z" }, + { url = "https://files.pythonhosted.org/packages/10/71/4be198e472d95702434cee1f9dd889c56e22bea8554b466fad754148fd24/awscrt-0.28.2-cp313-abi3-macosx_10_15_universal2.whl", hash = "sha256:5fda9e7d0eb800491fadebe2b6c2560ac2f5742b60f4106440dca4b49da7fb03", size = 3379585, upload-time = "2025-10-14T19:05:37.225Z" }, + { url = "https://files.pythonhosted.org/packages/43/09/77084249d07dca71352341ad3fbcfa75deaccf25bd65f9fdbb36ce1f978b/awscrt-0.28.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:994a795bdc83344922a15891abb30155ec292093e856eef3929dd63dd6cadaca", size = 3779843, upload-time = "2025-10-14T19:05:38.774Z" }, + { url = "https://files.pythonhosted.org/packages/a6/bb/fcee9365e58e5860582398317571a9a5517da258cd81c3d987b9882f61d4/awscrt-0.28.2-cp313-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28537c4517168927ef74aa007a2e0c9f436921227934d82da31e9a1cec7e0c4a", size = 4049154, upload-time = "2025-10-14T19:05:40.301Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8e/ac92b2707dbe05e56d0dd5af73cb4e07a3da4aee66936071123966523759/awscrt-0.28.2-cp313-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b9fc6be63832da3ff244d56c7d9a43326d89d79e68162419c35f33e6ad033be0", size = 3683672, upload-time = "2025-10-14T19:05:41.536Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d0/15308ec37e762691f5d1871b0f1a6e462da8e421c6c38d6724e3cf0994b2/awscrt-0.28.2-cp313-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:efb57103a368de1d33148cb70a382c4f82ac376c744de9484e0f621cef8313f3", size = 3912823, upload-time = "2025-10-14T19:05:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/bc/cd/7693b1d72069908b7a3ee30e4ef2b5fc8f54948a96397729277cb0b0c7b4/awscrt-0.28.2-cp313-abi3-win32.whl", hash = "sha256:594dc61f4f0c1c9fb7292364d25c21810b3608cd67c0de78a032ad48f7bfd88c", size = 3911514, upload-time = "2025-10-14T19:05:45.019Z" }, + { url = "https://files.pythonhosted.org/packages/93/d6/5d8545c967690f03d55d44ed56ceff26d88363cd7d0435fd80a1c843ac2a/awscrt-0.28.2-cp313-abi3-win_amd64.whl", hash = "sha256:a17f0ab9dc5e5301da0fb00ccc4511a136d13abbd4a9564827547333fcd7ba16", size = 4047912, upload-time = "2025-10-14T19:05:46.302Z" }, ] [[package]] @@ -4633,7 +4633,7 @@ requires-dist = [ { name = "aiortc", marker = "extra == 'webrtc'", specifier = ">=1.13.0,<2" }, { name = "anthropic", marker = "extra == 'anthropic'", specifier = "~=0.49.0" }, { name = "audioop-lts", marker = "python_full_version >= '3.13'", specifier = "~=0.2.1" }, - { name = "aws-sdk-bedrock-runtime", marker = "python_full_version >= '3.12' and extra == 'aws-nova-sonic'", specifier = "~=0.1.0" }, + { name = "aws-sdk-bedrock-runtime", marker = "python_full_version >= '3.12' and extra == 'aws-nova-sonic'", specifier = "~=0.1.1" }, { name = "azure-cognitiveservices-speech", marker = "extra == 'azure'", specifier = "~=1.42.0" }, { name = "cartesia", marker = "extra == 'cartesia'", specifier = "~=2.0.3" }, { name = "coremltools", marker = "extra == 'local-smart-turn'", specifier = ">=8.0" }, @@ -6483,16 +6483,16 @@ wheels = [ [[package]] name = "smithy-aws-core" -version = "0.1.0" +version = "0.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aws-sdk-signers", marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, { name = "smithy-http", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/e8/8cef48be92ed09a112c54747a4515313ba96e767e7e0118a769aeb147e07/smithy_aws_core-0.1.0.tar.gz", hash = "sha256:5f197b69ad1380e9118e1e3c9032e0e305525ef56fb4fc97dea6414281065526", size = 11135, upload-time = "2025-09-29T19:37:13.072Z" } +sdist = { url = "https://files.pythonhosted.org/packages/56/d3/f847e0fd36b95aa36ce3a4c9ce1a08e16b2aa9a56b71714045c9c924e282/smithy_aws_core-0.1.1.tar.gz", hash = "sha256:78dfd7040fc2bc72b6af293096642fc9a7bfd2db28ddbdf7c4110535eab9d662", size = 11196, upload-time = "2025-10-21T20:21:18.648Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/7e/6d05275646bc2cdf7b0749e9bd54958a4e808aafeee4d8ff2fdaa8233dc2/smithy_aws_core-0.1.0-py3-none-any.whl", hash = "sha256:a8cda4011562f45f1fc5957c3a981b6016d736178450e5f2a1586937632af487", size = 18959, upload-time = "2025-09-29T19:37:12.041Z" }, + { url = "https://files.pythonhosted.org/packages/d0/04/87cb06f0f6d664b5cffdef6d4042dd52c11c138436084d30ffdaa3543031/smithy_aws_core-0.1.1-py3-none-any.whl", hash = "sha256:0d1634f276c2999dc2a04fafef63b9d28309de50d939d1d49df952773a7063c4", size = 18963, upload-time = "2025-10-21T20:21:17.692Z" }, ] [package.optional-dependencies] @@ -6526,14 +6526,14 @@ wheels = [ [[package]] name = "smithy-http" -version = "0.1.0" +version = "0.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/62/5ba46c7432fbb0852acf8340402879ba53bb4c009b875e1b5b2e9df844ff/smithy_http-0.1.0.tar.gz", hash = "sha256:ed44552531f594e31101f7186c7b01b508ecd38a860b45390a1cce7da700df4b", size = 28269, upload-time = "2025-09-29T19:37:18.629Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/1c/44e99a7dfb8c39bf0c3d998accdf4573a7a3488863b90f21af260cec2d45/smithy_http-0.2.0.tar.gz", hash = "sha256:2382562fa9af326be455f14b18615f16ffe9db756e51b2a4ca0d23e1b881cff8", size = 26729, upload-time = "2025-10-21T20:21:06.146Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/23/d18076ea45b3000c5e9eb8ebd75a4ea1b65b5c59e5c2080a119e2679dfba/smithy_http-0.1.0-py3-none-any.whl", hash = "sha256:7657aaf4b9e025cb9d317406f417b49cf19fba9d1b2ab4f5e6d9dc5a2dd7cdba", size = 38995, upload-time = "2025-09-29T19:37:17.506Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e2/d475fad81ac74ec0e145cb6d72afe5ecde4e2358bd632c2fd5d3f4bc87dc/smithy_http-0.2.0-py3-none-any.whl", hash = "sha256:49ee2402d7737798d70f99f491fbfb2a5767283ae562e21b6f86e3fd14f3e3e0", size = 37328, upload-time = "2025-10-21T20:21:05.362Z" }, ] [package.optional-dependencies] From f1040100f4a1e105c25d404e96bcc2a95abfe0dd Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 22 Oct 2025 08:58:14 -0400 Subject: [PATCH 141/158] Update ServiceSwitcher and LLMSwitcher docstrings --- src/pipecat/pipeline/llm_switcher.py | 29 +++++++++++++-- src/pipecat/pipeline/service_switcher.py | 47 +++++++++++++++++++++--- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/pipecat/pipeline/llm_switcher.py b/src/pipecat/pipeline/llm_switcher.py index a15e0a3c2..50d919263 100644 --- a/src/pipecat/pipeline/llm_switcher.py +++ b/src/pipecat/pipeline/llm_switcher.py @@ -14,20 +14,41 @@ from pipecat.services.llm_service import LLMService class LLMSwitcher(ServiceSwitcher[StrategyType]): - """A pipeline that switches between different LLMs at runtime.""" + """A pipeline that switches between different LLMs at runtime. + + Example:: + + llm_switcher = LLMSwitcher( + llms=[openai_llm, anthropic_llm], + strategy_type=ServiceSwitcherStrategyManual + ) + """ def __init__(self, llms: List[LLMService], strategy_type: Type[StrategyType]): - """Initialize the service switcher with a list of LLMs and a switching strategy.""" + """Initialize the service switcher with a list of LLMs and a switching strategy. + + Args: + llms: List of LLM services to switch between. + strategy_type: The strategy class to use for switching between LLMs. + """ super().__init__(llms, strategy_type) @property def llms(self) -> List[LLMService]: - """Get the list of LLMs managed by this switcher.""" + """Get the list of LLMs managed by this switcher. + + Returns: + List of LLM services managed by this switcher. + """ return self.services @property def active_llm(self) -> Optional[LLMService]: - """Get the currently active LLM, if any.""" + """Get the currently active LLM. + + Returns: + The currently active LLM service, or None if no LLM is active. + """ return self.strategy.active_service async def run_inference(self, context: LLMContext) -> Optional[str]: diff --git a/src/pipecat/pipeline/service_switcher.py b/src/pipecat/pipeline/service_switcher.py index eea55e68d..095f211ea 100644 --- a/src/pipecat/pipeline/service_switcher.py +++ b/src/pipecat/pipeline/service_switcher.py @@ -21,10 +21,22 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor class ServiceSwitcherStrategy: - """Base class for service switching strategies.""" + """Base class for service switching strategies. + + Note: + Strategy classes are instantiated internally by ServiceSwitcher. + Developers should pass the strategy class (not an instance) to ServiceSwitcher. + """ def __init__(self, services: List[FrameProcessor]): - """Initialize the service switcher strategy with a list of services.""" + """Initialize the service switcher strategy with a list of services. + + Note: + This is called internally by ServiceSwitcher. Do not instantiate directly. + + Args: + services: List of frame processors to switch between. + """ self.services = services self.active_service: Optional[FrameProcessor] = None @@ -46,10 +58,24 @@ class ServiceSwitcherStrategyManual(ServiceSwitcherStrategy): This strategy allows the user to manually select which service is active. The initial active service is the first one in the list. + + Example:: + + stt_switcher = ServiceSwitcher( + services=[stt_1, stt_2], + strategy_type=ServiceSwitcherStrategyManual + ) """ def __init__(self, services: List[FrameProcessor]): - """Initialize the manual service switcher strategy with a list of services.""" + """Initialize the manual service switcher strategy with a list of services. + + Note: + This is called internally by ServiceSwitcher. Do not instantiate directly. + + Args: + services: List of frame processors to switch between. + """ super().__init__(services) self.active_service = services[0] if services else None @@ -85,7 +111,12 @@ class ServiceSwitcher(ParallelPipeline, Generic[StrategyType]): """A pipeline that switches between different services at runtime.""" def __init__(self, services: List[FrameProcessor], strategy_type: Type[StrategyType]): - """Initialize the service switcher with a list of services and a switching strategy.""" + """Initialize the service switcher with a list of services and a switching strategy. + + Args: + services: List of frame processors to switch between. + strategy_type: The strategy class to use for switching between services. + """ strategy = strategy_type(services) super().__init__(*self._make_pipeline_definitions(services, strategy)) self.services = services @@ -100,7 +131,13 @@ class ServiceSwitcher(ParallelPipeline, Generic[StrategyType]): active_service: FrameProcessor, direction: FrameDirection, ): - """Initialize the service switcher filter with a strategy and direction.""" + """Initialize the service switcher filter with a strategy and direction. + + Args: + wrapped_service: The service that this filter wraps. + active_service: The currently active service. + direction: The direction of frame flow to filter. + """ async def filter(_: Frame) -> bool: return self._wrapped_service == self._active_service From 5b921fc054f9feb8a08cf38e9ee8c74ae9af0be3 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 22 Oct 2025 10:48:48 -0400 Subject: [PATCH 142/158] fix: FunctionFilter adds block_system_frame arg --- CHANGELOG.md | 8 + examples/foundational/48-service-switcher.py | 138 ++++++++++++++++++ src/pipecat/pipeline/service_switcher.py | 6 +- .../processors/filters/function_filter.py | 21 ++- 4 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 examples/foundational/48-service-switcher.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e727c42e..a07832525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- `FunctionFilter` now has a `block_system_frames` arg, which controls whether + or not SystemFrames are filtered. + - Upgraded `aws_sdk_bedrock_runtime` to v0.1.1 to resolve potential CPU issues when running `AWSNovaSonicLLMService`. +### Fixed + +- Fixed an issue in `ServiceSwitcher` where the `STTService`s would result in + all STT services producing `TranscriptionFrame`s. + ## [0.0.91] - 2025-10-21 ### Added diff --git a/examples/foundational/48-service-switcher.py b/examples/foundational/48-service-switcher.py new file mode 100644 index 000000000..e003db7eb --- /dev/null +++ b/examples/foundational/48-service-switcher.py @@ -0,0 +1,138 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams +from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import LLMRunFrame, ManuallySwitchServiceFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.service_switcher import ServiceSwitcher, ServiceSwitcherStrategyManual +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.stt import CartesiaSTTService +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.openai.llm import OpenAILLMService +from pipecat.services.stt_service import STTService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzerV3(params=SmartTurnParams()), + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt_cartesia = CartesiaSTTService(api_key=os.getenv("CARTESIA_API_KEY")) + stt_deepgram = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + stt_switcher = ServiceSwitcher( + services=[stt_cartesia, stt_deepgram], strategy_type=ServiceSwitcherStrategyManual + ) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ) + + llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = LLMContext(messages) + context_aggregator = LLMContextAggregatorPair(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt_switcher, + context_aggregator.user(), # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + messages.append({"role": "system", "content": "Please introduce yourself to the user."}) + await task.queue_frames([LLMRunFrame()]) + await asyncio.sleep(15) + print(f"Switching to {stt_deepgram}") + await task.queue_frames([ManuallySwitchServiceFrame(service=stt_deepgram)]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/src/pipecat/pipeline/service_switcher.py b/src/pipecat/pipeline/service_switcher.py index 095f211ea..3cfcd0fd8 100644 --- a/src/pipecat/pipeline/service_switcher.py +++ b/src/pipecat/pipeline/service_switcher.py @@ -138,13 +138,13 @@ class ServiceSwitcher(ParallelPipeline, Generic[StrategyType]): active_service: The currently active service. direction: The direction of frame flow to filter. """ + self._wrapped_service = wrapped_service + self._active_service = active_service async def filter(_: Frame) -> bool: return self._wrapped_service == self._active_service - super().__init__(filter, direction) - self._wrapped_service = wrapped_service - self._active_service = active_service + super().__init__(filter, direction, block_system_frames=True) async def process_frame(self, frame, direction): """Process a frame through the filter, handling special internal filter-updating frames.""" diff --git a/src/pipecat/processors/filters/function_filter.py b/src/pipecat/processors/filters/function_filter.py index e663b81f4..5bc6e1eb9 100644 --- a/src/pipecat/processors/filters/function_filter.py +++ b/src/pipecat/processors/filters/function_filter.py @@ -12,7 +12,7 @@ allowing for flexible frame filtering logic in processing pipelines. from typing import Awaitable, Callable -from pipecat.frames.frames import EndFrame, Frame, SystemFrame +from pipecat.frames.frames import CancelFrame, EndFrame, Frame, StartFrame, SystemFrame from pipecat.processors.frame_processor import FrameDirection, FrameProcessor @@ -28,6 +28,7 @@ class FunctionFilter(FrameProcessor): self, filter: Callable[[Frame], Awaitable[bool]], direction: FrameDirection = FrameDirection.DOWNSTREAM, + block_system_frames: bool = False, ): """Initialize the function filter. @@ -36,10 +37,12 @@ class FunctionFilter(FrameProcessor): frame should pass through, False otherwise. direction: The direction to apply filtering. Only frames moving in this direction will be filtered. Defaults to DOWNSTREAM. + block_system_frames: Whether to block system frames. Defaults to False. """ super().__init__() self._filter = filter self._direction = direction + self._block_system_frames = block_system_frames # # Frame processor @@ -49,9 +52,19 @@ class FunctionFilter(FrameProcessor): # direction of this gate def _should_passthrough_frame(self, frame, direction): """Check if a frame should pass through without filtering.""" - # Ignore system frames, end frames and frames that are not following the - # direction of this gate - return isinstance(frame, (SystemFrame, EndFrame)) or direction != self._direction + # Always passthrough frames in the wrong direction + if direction != self._direction: + return True + + # Always passthrough lifecycle frames + if isinstance(frame, (StartFrame, EndFrame, CancelFrame)): + return True + + # If not blocking system frames, passthrough all other system frames + if not self._block_system_frames and isinstance(frame, SystemFrame): + return True + + return False async def process_frame(self, frame: Frame, direction: FrameDirection): """Process a frame through the filter. From ec890a834fa63917ac277529ab9b5f7f3530acef Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 22 Oct 2025 10:59:43 -0400 Subject: [PATCH 143/158] Rename to filter_system_frames --- CHANGELOG.md | 2 +- examples/foundational/48-service-switcher.py | 27 ++++++++++++++----- src/pipecat/pipeline/service_switcher.py | 2 +- .../processors/filters/function_filter.py | 12 ++++----- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a07832525..3cbf4a57e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- `FunctionFilter` now has a `block_system_frames` arg, which controls whether +- `FunctionFilter` now has a `filter_system_frames` arg, which controls whether or not SystemFrames are filtered. - Upgraded `aws_sdk_bedrock_runtime` to v0.1.1 to resolve potential CPU issues diff --git a/examples/foundational/48-service-switcher.py b/examples/foundational/48-service-switcher.py index e003db7eb..d0e15d2d3 100644 --- a/examples/foundational/48-service-switcher.py +++ b/examples/foundational/48-service-switcher.py @@ -26,8 +26,9 @@ from pipecat.runner.utils import create_transport from pipecat.services.cartesia.stt import CartesiaSTTService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.deepgram.tts import DeepgramTTSService +from pipecat.services.google.llm import GoogleLLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.stt_service import STTService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams @@ -68,12 +69,20 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): services=[stt_cartesia, stt_deepgram], strategy_type=ServiceSwitcherStrategyManual ) - tts = CartesiaTTSService( + tts_cartesia = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", + ) + tts_deepgram = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY")) + tts_switcher = ServiceSwitcher( + services=[tts_cartesia, tts_deepgram], strategy_type=ServiceSwitcherStrategyManual ) - llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + llm_openai = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + llm_google = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY")) + llm_switcher = ServiceSwitcher( + services=[llm_openai, llm_google], strategy_type=ServiceSwitcherStrategyManual + ) messages = [ { @@ -90,8 +99,8 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): transport.input(), # Transport user input stt_switcher, context_aggregator.user(), # User responses - llm, # LLM - tts, # TTS + llm_switcher, # LLM + tts_switcher, # TTS transport.output(), # Transport bot output context_aggregator.assistant(), # Assistant spoken responses ] @@ -115,6 +124,12 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(15) print(f"Switching to {stt_deepgram}") await task.queue_frames([ManuallySwitchServiceFrame(service=stt_deepgram)]) + await asyncio.sleep(15) + print(f"Switching to {llm_google}") + await task.queue_frames([ManuallySwitchServiceFrame(service=llm_google)]) + await asyncio.sleep(15) + print(f"Switching to {tts_deepgram}") + await task.queue_frames([ManuallySwitchServiceFrame(service=tts_deepgram)]) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/src/pipecat/pipeline/service_switcher.py b/src/pipecat/pipeline/service_switcher.py index 3cfcd0fd8..8895d663c 100644 --- a/src/pipecat/pipeline/service_switcher.py +++ b/src/pipecat/pipeline/service_switcher.py @@ -144,7 +144,7 @@ class ServiceSwitcher(ParallelPipeline, Generic[StrategyType]): async def filter(_: Frame) -> bool: return self._wrapped_service == self._active_service - super().__init__(filter, direction, block_system_frames=True) + super().__init__(filter, direction, filter_system_frames=True) async def process_frame(self, frame, direction): """Process a frame through the filter, handling special internal filter-updating frames.""" diff --git a/src/pipecat/processors/filters/function_filter.py b/src/pipecat/processors/filters/function_filter.py index 5bc6e1eb9..556f2bc87 100644 --- a/src/pipecat/processors/filters/function_filter.py +++ b/src/pipecat/processors/filters/function_filter.py @@ -28,7 +28,7 @@ class FunctionFilter(FrameProcessor): self, filter: Callable[[Frame], Awaitable[bool]], direction: FrameDirection = FrameDirection.DOWNSTREAM, - block_system_frames: bool = False, + filter_system_frames: bool = False, ): """Initialize the function filter. @@ -37,19 +37,17 @@ class FunctionFilter(FrameProcessor): frame should pass through, False otherwise. direction: The direction to apply filtering. Only frames moving in this direction will be filtered. Defaults to DOWNSTREAM. - block_system_frames: Whether to block system frames. Defaults to False. + filter_system_frames: Whether to filter system frames. Defaults to False. """ super().__init__() self._filter = filter self._direction = direction - self._block_system_frames = block_system_frames + self._filter_system_frames = filter_system_frames # # Frame processor # - # Ignore system frames, end frames and frames that are not following the - # direction of this gate def _should_passthrough_frame(self, frame, direction): """Check if a frame should pass through without filtering.""" # Always passthrough frames in the wrong direction @@ -60,8 +58,8 @@ class FunctionFilter(FrameProcessor): if isinstance(frame, (StartFrame, EndFrame, CancelFrame)): return True - # If not blocking system frames, passthrough all other system frames - if not self._block_system_frames and isinstance(frame, SystemFrame): + # If not filtering system frames, passthrough all other system frames + if not self._filter_system_frames and isinstance(frame, SystemFrame): return True return False From ea6e146f2d29dad4584ea5d959cc14e9ed71870f Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 22 Oct 2025 11:14:27 -0400 Subject: [PATCH 144/158] Update `TestServiceSwitcher` to exercise targeting system frames only to the active service --- tests/test_service_switcher.py | 41 ++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/tests/test_service_switcher.py b/tests/test_service_switcher.py index bf80d842e..83d2d226b 100644 --- a/tests/test_service_switcher.py +++ b/tests/test_service_switcher.py @@ -7,10 +7,12 @@ """Unit tests for ServiceSwitcher and related components.""" import unittest +from dataclasses import dataclass from pipecat.frames.frames import ( Frame, ManuallySwitchServiceFrame, + SystemFrame, TextFrame, ) from pipecat.pipeline.pipeline import Pipeline @@ -52,6 +54,13 @@ class MockFrameProcessor(FrameProcessor): self.frame_count = 0 +@dataclass +class DummySystemFrame(SystemFrame): + """A dummy system frame for testing purposes.""" + + text: str = "" + + class TestServiceSwitcherStrategyManual(unittest.IsolatedAsyncioTestCase): """Test cases for ServiceSwitcherStrategyManual.""" @@ -140,14 +149,22 @@ class TestServiceSwitcher(unittest.IsolatedAsyncioTestCase): # Send some test frames frames_to_send = [ TextFrame(text="Hello 1"), + DummySystemFrame(text="System Message 1"), TextFrame(text="Hello 2"), + DummySystemFrame(text="System Message 2"), TextFrame(text="Hello 3"), ] await run_test( switcher, frames_to_send=frames_to_send, - expected_down_frames=[TextFrame, TextFrame, TextFrame], + expected_down_frames=[ + DummySystemFrame, + DummySystemFrame, + TextFrame, + TextFrame, + TextFrame, + ], expected_up_frames=[], # Expect no error frames ) @@ -156,7 +173,13 @@ class TestServiceSwitcher(unittest.IsolatedAsyncioTestCase): text_frames = [f for f in self.service1.processed_frames if isinstance(f, TextFrame)] self.assertEqual(len(text_frames), 3) - # Check that other services don't receive text frames (they might get StartFrame/EndFrame) + # Only service1 should have processed the system frames + system_frames = [ + f for f in self.service1.processed_frames if isinstance(f, DummySystemFrame) + ] + self.assertEqual(len(system_frames), 2) + + # Check that other services don't receive text frames (they still get StartFrame/EndFrame) service2_text_frames = [ f for f in self.service2.processed_frames if isinstance(f, TextFrame) ] @@ -166,10 +189,24 @@ class TestServiceSwitcher(unittest.IsolatedAsyncioTestCase): self.assertEqual(len(service2_text_frames), 0) self.assertEqual(len(service3_text_frames), 0) + # Check that other services don't receive dummy system frames (they still get StartFrame/EndFrame) + service2_system_frames = [ + f for f in self.service2.processed_frames if isinstance(f, DummySystemFrame) + ] + service3_system_frames = [ + f for f in self.service3.processed_frames if isinstance(f, DummySystemFrame) + ] + self.assertEqual(len(service2_system_frames), 0) + self.assertEqual(len(service3_system_frames), 0) + # Verify the actual text frames processed for i, frame in enumerate(text_frames): self.assertEqual(frame.text, f"Hello {i + 1}") + # Verify the actual system frames processed + for i, frame in enumerate(system_frames): + self.assertEqual(frame.text, f"System Message {i + 1}") + async def test_service_switching(self): """Test that after service switching using ManuallySwitchServiceFrame, the new active service receives frames while others don't.""" switcher = ServiceSwitcher(self.services, ServiceSwitcherStrategyManual) From 60e9817f166182c0cf891b1c527c4bd21256b7b2 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 22 Oct 2025 16:48:30 -0400 Subject: [PATCH 145/158] fix: add support for DAILY_SAMPLE_ROOM_URL when calling /start for DailyTransport --- CHANGELOG.md | 5 +++++ src/pipecat/runner/run.py | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e727c42e..397d47123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `aws_sdk_bedrock_runtime` to v0.1.1 to resolve potential CPU issues when running `AWSNovaSonicLLMService`. +### Fixed + +- Fixed an issue in the runner where starting a DailyTransport room via + `/start` didn't support using the `DAILY_SAMPLE_ROOM_URL` env var. + ## [0.0.91] - 2025-10-21 ### Added diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index 4c1d3135d..b5a805c1f 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -573,8 +573,14 @@ def _setup_daily_routes(app: FastAPI): bot_module = _get_bot_module() + existing_room_url = os.getenv("DAILY_SAMPLE_ROOM_URL") + result = None - if create_daily_room: + + # Configure room if: + # 1. Explicitly requested via createDailyRoom in payload + # 2. Using pre-configured room from DAILY_SAMPLE_ROOM_URL env var + if create_daily_room or existing_room_url: import aiohttp from pipecat.runner.daily import configure From 99d94fc62506b67a256826d8ce756ace30ad3114 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 24 Oct 2025 10:04:02 -0400 Subject: [PATCH 146/158] Update Gemini service to use "user" role for function responses, as shown in the Gemini docs --- src/pipecat/adapters/services/gemini_adapter.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/pipecat/adapters/services/gemini_adapter.py b/src/pipecat/adapters/services/gemini_adapter.py index abe33a8ec..53e4e4ee0 100644 --- a/src/pipecat/adapters/services/gemini_adapter.py +++ b/src/pipecat/adapters/services/gemini_adapter.py @@ -242,7 +242,7 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): Converts to Google Content with:: Content( - role="model", + role="user", parts=[Part(function_call=FunctionCall(name="search", args={"query": "test"}))] ) """ @@ -273,7 +273,7 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): ) ) elif role == "tool": - role = "model" + role = "user" try: response = json.loads(message["content"]) if isinstance(response, dict): @@ -285,11 +285,9 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): # This occurs with a UserImageFrame, for example, where we get a plain "COMPLETED" string. response_dict = {"value": message["content"]} parts.append( - Part( - function_response=FunctionResponse( - name="tool_call_result", # seems to work to hard-code the same name every time - response=response_dict, - ) + Part.from_function_response( + name="tool_call_result", # seems to work to hard-code the same name every time + response=response_dict, ) ) elif isinstance(content, str): From 4ad15f9a01e43cece28f11be08e683b9e482d48d Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 24 Oct 2025 10:38:17 -0400 Subject: [PATCH 147/158] Update Gemini service to include function name when sending function responses in context --- .../adapters/services/anthropic_adapter.py | 2 +- .../adapters/services/bedrock_adapter.py | 2 +- .../adapters/services/gemini_adapter.py | 106 +++++++++++++----- 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/src/pipecat/adapters/services/anthropic_adapter.py b/src/pipecat/adapters/services/anthropic_adapter.py index adfe81005..a106b4de4 100644 --- a/src/pipecat/adapters/services/anthropic_adapter.py +++ b/src/pipecat/adapters/services/anthropic_adapter.py @@ -110,7 +110,7 @@ class AnthropicLLMAdapter(BaseLLMAdapter[AnthropicLLMInvocationParams]): system = NOT_GIVEN messages = [] - # first, map messages using self._from_universal_context_message(m) + # First, map messages using self._from_universal_context_message(m) try: messages = [self._from_universal_context_message(m) for m in universal_context_messages] except Exception as e: diff --git a/src/pipecat/adapters/services/bedrock_adapter.py b/src/pipecat/adapters/services/bedrock_adapter.py index 681dfb3dc..852ea17a4 100644 --- a/src/pipecat/adapters/services/bedrock_adapter.py +++ b/src/pipecat/adapters/services/bedrock_adapter.py @@ -107,7 +107,7 @@ class AWSBedrockLLMAdapter(BaseLLMAdapter[AWSBedrockLLMInvocationParams]): system = None messages = [] - # first, map messages using self._from_universal_context_message(m) + # First, map messages using self._from_universal_context_message(m) try: messages = [self._from_universal_context_message(m) for m in universal_context_messages] except Exception as e: diff --git a/src/pipecat/adapters/services/gemini_adapter.py b/src/pipecat/adapters/services/gemini_adapter.py index 53e4e4ee0..75577f160 100644 --- a/src/pipecat/adapters/services/gemini_adapter.py +++ b/src/pipecat/adapters/services/gemini_adapter.py @@ -8,8 +8,8 @@ import base64 import json -from dataclasses import dataclass -from typing import Any, Dict, List, Optional, TypedDict +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional, Tuple, TypedDict from loguru import logger from openai import NotGiven @@ -133,6 +133,28 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): messages: List[Content] system_instruction: Optional[str] = None + @dataclass + class MessageConversionResult: + """Result of converting a single universal context message to Google format. + + Either content (a Google Content object) or a system instruction string + is guaranteed to be set. + + Also returns a tool call ID to name mapping for any tool calls + discovered in the message. + """ + + content: Optional[Content] = None + system_instruction: Optional[str] = None + tool_call_id_to_name_mapping: Dict[str, str] = field(default_factory=dict) + + @dataclass + class MessageConversionParams: + """Parameters for converting a single universal context message to Google format.""" + + already_have_system_instruction: bool + tool_call_id_to_name_mapping: Dict[str, str] + def _from_universal_context_messages( self, universal_context_messages: List[LLMContextMessage] ) -> ConvertedMessages: @@ -156,24 +178,26 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): """ system_instruction = None messages = [] + tool_call_id_to_name_mapping = {} # Process each message, preserving Google-formatted messages and converting others for message in universal_context_messages: - if isinstance(message, LLMSpecificMessage): - # Assume that LLMSpecificMessage wraps a message in Google format - messages.append(message.message) - continue - - # Convert standard format to Google format - converted = self._from_standard_message( - message, already_have_system_instruction=bool(system_instruction) + result = self._from_universal_context_message( + message, + params=self.MessageConversionParams( + already_have_system_instruction=bool(system_instruction), + tool_call_id_to_name_mapping=tool_call_id_to_name_mapping, + ), ) - if isinstance(converted, Content): - # Regular (non-system) message - messages.append(converted) - else: - # System instruction - system_instruction = converted + # Each result is either a Content or a system instruction + if result.content: + messages.append(result.content) + elif result.system_instruction: + system_instruction = result.system_instruction + + # Merge tool call ID to name mapping + if result.tool_call_id_to_name_mapping: + tool_call_id_to_name_mapping.update(result.tool_call_id_to_name_mapping) # Check if we only have function-related messages (no regular text) has_regular_messages = any( @@ -193,9 +217,16 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): return self.ConvertedMessages(messages=messages, system_instruction=system_instruction) + def _from_universal_context_message( + self, message: LLMContextMessage, *, params: MessageConversionParams + ) -> MessageConversionResult: + if isinstance(message, LLMSpecificMessage): + return message.message + return self._from_standard_message(message, params=params) + def _from_standard_message( - self, message: LLMStandardMessage, already_have_system_instruction: bool - ) -> Content | str: + self, message: LLMStandardMessage, *, params: MessageConversionParams + ) -> MessageConversionResult: """Convert standard universal context message to Google Content object. Handles conversion of text, images, and function calls to Google's @@ -205,10 +236,11 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): Args: message: Message in standard universal context format. already_have_system_instruction: Whether we already have a system instruction + params: Parameters for conversion. Returns: - Content object with role and parts, or a plain string for system - messages. + MessageConversionResult containing either a Content object or a + system instruction string. Examples: Standard text message:: @@ -248,26 +280,36 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): """ role = message["role"] content = message.get("content", []) + if role == "system": - if already_have_system_instruction: + if params.already_have_system_instruction: role = "user" # Convert system message to user role if we already have a system instruction else: - # System instructions are returned as plain text + system_instruction: str = None if isinstance(content, str): - return content + system_instruction = content elif isinstance(content, list): # If content is a list, we assume it's a list of text parts, per the standard - return " ".join(part["text"] for part in content if part.get("type") == "text") + system_instruction = " ".join( + part["text"] for part in content if part.get("type") == "text" + ) + if system_instruction: + return self.MessageConversionResult(system_instruction=system_instruction) elif role == "assistant": role = "model" parts = [] + tool_call_id_to_name_mapping = {} + if message.get("tool_calls"): for tc in message["tool_calls"]: + id = tc["id"] + name = tc["function"]["name"] + tool_call_id_to_name_mapping[id] = name parts.append( Part( function_call=FunctionCall( - name=tc["function"]["name"], + name=name, args=json.loads(tc["function"]["arguments"]), ) ) @@ -284,9 +326,16 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): # Response might not be JSON-deserializable. # This occurs with a UserImageFrame, for example, where we get a plain "COMPLETED" string. response_dict = {"value": message["content"]} + + # Get function name from mapping using tool_call_id, or fallback + tool_call_id = message.get("tool_call_id") + function_name = "tool_call_result" # Default fallback + if tool_call_id and tool_call_id in params.tool_call_id_to_name_mapping: + function_name = params.tool_call_id_to_name_mapping[tool_call_id] + parts.append( Part.from_function_response( - name="tool_call_result", # seems to work to hard-code the same name every time + name=function_name, response=response_dict, ) ) @@ -310,4 +359,7 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): audio_bytes = base64.b64decode(input_audio["data"]) parts.append(Part(inline_data=Blob(mime_type="audio/wav", data=audio_bytes))) - return Content(role=role, parts=parts) + return self.MessageConversionResult( + content=Content(role=role, parts=parts), + tool_call_id_to_name_mapping=tool_call_id_to_name_mapping, + ) From 949b8070230e496db1509c7b3a31939977de94e2 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 24 Oct 2025 11:36:25 -0400 Subject: [PATCH 148/158] Close genai client more gracefully to avoid printed warnings. We're now following the genai library guidance: https://github.com/googleapis/python-genai?tab=readme-ov-file#close-a-client --- src/pipecat/services/google/llm.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index b45c276d0..c59cb41ae 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -1034,6 +1034,23 @@ class GoogleLLMService(LLMService): if context: await self._process_context(context) + async def stop(self, frame): + """Override stop to gracefully close the client.""" + await super().stop(frame) + await self._close_client() + + async def cancel(self, frame): + """Override cancel to gracefully close the client.""" + await super().cancel(frame) + await self._close_client() + + async def _close_client(self): + try: + await self._client.aio.aclose() + except Exception: + # Do nothing - we're shutting down anyway + pass + def create_context_aggregator( self, context: OpenAILLMContext, From 71841f71ef243aea2643cc0cd69b4cc273c31072 Mon Sep 17 00:00:00 2001 From: Pyae Sone Myo <66440980+Rickaym@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:47:46 +0630 Subject: [PATCH 149/158] fix: use correct property names --- src/pipecat/pipeline/task_observer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/pipeline/task_observer.py b/src/pipecat/pipeline/task_observer.py index 7bf83480d..98ff7c91e 100644 --- a/src/pipecat/pipeline/task_observer.py +++ b/src/pipecat/pipeline/task_observer.py @@ -189,7 +189,7 @@ class TaskObserver(BaseObserver): if isinstance(data, FramePushed): if on_push_frame_deprecated: await observer.on_push_frame( - data.src, data.dst, data.frame, data.direction, data.timestamp + data.source, data.destination, data.frame, data.direction, data.timestamp ) else: await observer.on_push_frame(data) From 6feaf9178920f9fddef5961ab2b431101e7d41de Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 27 Oct 2025 09:42:24 -0400 Subject: [PATCH 150/158] Fix a bug in `GeminiLLMAdapter`'s handling of Gemini-specific context messages --- src/pipecat/adapters/services/gemini_adapter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/adapters/services/gemini_adapter.py b/src/pipecat/adapters/services/gemini_adapter.py index 75577f160..26f037127 100644 --- a/src/pipecat/adapters/services/gemini_adapter.py +++ b/src/pipecat/adapters/services/gemini_adapter.py @@ -221,7 +221,7 @@ class GeminiLLMAdapter(BaseLLMAdapter[GeminiLLMInvocationParams]): self, message: LLMContextMessage, *, params: MessageConversionParams ) -> MessageConversionResult: if isinstance(message, LLMSpecificMessage): - return message.message + return self.MessageConversionResult(content=message.message) return self._from_standard_message(message, params=params) def _from_standard_message( From 8096e62b347a28463e146880ae53601563651b75 Mon Sep 17 00:00:00 2001 From: vipyne Date: Mon, 27 Oct 2025 11:27:30 -0500 Subject: [PATCH 151/158] cleanup logger message --- src/pipecat/services/azure/realtime/llm.py | 2 +- src/pipecat/services/openai_realtime_beta/azure.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/azure/realtime/llm.py b/src/pipecat/services/azure/realtime/llm.py index fb420e10b..1193b82d4 100644 --- a/src/pipecat/services/azure/realtime/llm.py +++ b/src/pipecat/services/azure/realtime/llm.py @@ -52,7 +52,7 @@ class AzureRealtimeLLMService(OpenAIRealtimeLLMService): # handle disconnections in the send/recv code paths. return - logger.info(f"Connecting to {self.base_url}, api key: {self.api_key}") + logger.info(f"Connecting to {self.base_url}") self._websocket = await websocket_connect( uri=self.base_url, additional_headers={ diff --git a/src/pipecat/services/openai_realtime_beta/azure.py b/src/pipecat/services/openai_realtime_beta/azure.py index 784438e81..2ec556ee3 100644 --- a/src/pipecat/services/openai_realtime_beta/azure.py +++ b/src/pipecat/services/openai_realtime_beta/azure.py @@ -70,7 +70,7 @@ class AzureRealtimeBetaLLMService(OpenAIRealtimeBetaLLMService): # handle disconnections in the send/recv code paths. return - logger.info(f"Connecting to {self.base_url}, api key: {self.api_key}") + logger.info(f"Connecting to {self.base_url}") self._websocket = await websocket_connect( uri=self.base_url, additional_headers={ From f2cfbee3c3bdc861e1e4c79a71e53fc16dd2496d Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 27 Oct 2025 16:18:31 -0400 Subject: [PATCH 152/158] Remove development runner requirement for proxy --- src/pipecat/runner/run.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index b5a805c1f..28ca81bb9 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -794,10 +794,6 @@ def main(): logger.error("For ESP32, you need to specify `--host IP` so we can do SDP munging.") return - if args.transport in TELEPHONY_TRANSPORTS and not args.proxy: - logger.error(f"For telephony transports, you need to specify `--proxy PROXY`.") - return - # Log level logger.remove() logger.add(sys.stderr, level="TRACE" if args.verbose else "DEBUG") From f3c4bf08ddae22b3f46d666f357f4a737002aa8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 27 Oct 2025 17:52:19 -0700 Subject: [PATCH 153/158] DailyTransport: don't timeout prematurely on join --- CHANGELOG.md | 2 ++ src/pipecat/transports/daily/transport.py | 43 ++++++++++------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd1091e87..a5813a5b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue where `DailyTransport` would timeout prematurely on join. + - Fixed an issue in the runner where starting a DailyTransport room via `/start` didn't support using the `DAILY_SAMPLE_ROOM_URL` env var. diff --git a/src/pipecat/transports/daily/transport.py b/src/pipecat/transports/daily/transport.py index 7f6b21ee2..675db37e5 100644 --- a/src/pipecat/transports/daily/transport.py +++ b/src/pipecat/transports/daily/transport.py @@ -744,32 +744,27 @@ class DailyTransportClient(EventHandler): self._client.set_user_name(self._bot_name) - try: - (data, error) = await self._join() + (data, error) = await self._join() - if not error: - self._joined = True - self._joining = False - # Increment leave counter if we successfully joined. - self._leave_counter += 1 - - logger.info(f"Joined {self._room_url}") - - if self._params.transcription_enabled: - await self.start_transcription(self._params.transcription_settings) - - await self._callbacks.on_joined(data) - - self._joined_event.set() - else: - error_msg = f"Error joining {self._room_url}: {error}" - logger.error(error_msg) - await self._callbacks.on_error(error_msg) - except asyncio.TimeoutError: - error_msg = f"Time out joining {self._room_url}" - logger.error(error_msg) + if not error: + self._joined = True self._joining = False + # Increment leave counter if we successfully joined. + self._leave_counter += 1 + + logger.info(f"Joined {self._room_url}") + + if self._params.transcription_enabled: + await self.start_transcription(self._params.transcription_settings) + + await self._callbacks.on_joined(data) + + self._joined_event.set() + else: + error_msg = f"Error joining {self._room_url}: {error}" + logger.error(error_msg) await self._callbacks.on_error(error_msg) + self._joining = False async def _join(self): """Execute the actual room join operation.""" @@ -828,7 +823,7 @@ class DailyTransportClient(EventHandler): }, ) - return await asyncio.wait_for(future, timeout=10) + return await future async def leave(self): """Leave the Daily room and cleanup resources.""" From 9ef60bd468f6e7b55c1b45ba0d5c5373be378402 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 28 Oct 2025 11:49:54 -0400 Subject: [PATCH 154/158] Update Cartesia's default model to sonic-3 --- CHANGELOG.md | 3 +++ src/pipecat/services/cartesia/tts.py | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd1091e87..a8d076302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Updated the default model to `sonic-3` for `CartesiaTTSService` and + `CartesiaHttpTTSService`. + - `FunctionFilter` now has a `filter_system_frames` arg, which controls whether or not SystemFrames are filtered. diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index 90f0ac3b4..3c0fe279c 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -119,7 +119,7 @@ class CartesiaTTSService(AudioContextWordTTSService): voice_id: str, cartesia_version: str = "2025-04-16", url: str = "wss://api.cartesia.ai/tts/websocket", - model: str = "sonic-2", + model: str = "sonic-3", sample_rate: Optional[int] = None, encoding: str = "pcm_s16le", container: str = "raw", @@ -135,7 +135,7 @@ class CartesiaTTSService(AudioContextWordTTSService): voice_id: ID of the voice to use for synthesis. cartesia_version: API version string for Cartesia service. url: WebSocket URL for Cartesia TTS API. - model: TTS model to use (e.g., "sonic-2"). + model: TTS model to use (e.g., "sonic-3"). sample_rate: Audio sample rate. If None, uses default. encoding: Audio encoding format. container: Audio container format. @@ -498,7 +498,7 @@ class CartesiaHttpTTSService(TTSService): *, api_key: str, voice_id: str, - model: str = "sonic-2", + model: str = "sonic-3", base_url: str = "https://api.cartesia.ai", cartesia_version: str = "2024-11-13", sample_rate: Optional[int] = None, @@ -512,7 +512,7 @@ class CartesiaHttpTTSService(TTSService): Args: api_key: Cartesia API key for authentication. voice_id: ID of the voice to use for synthesis. - model: TTS model to use (e.g., "sonic-2"). + model: TTS model to use (e.g., "sonic-3"). base_url: Base URL for Cartesia HTTP API. cartesia_version: API version string for Cartesia service. sample_rate: Audio sample rate. If None, uses default. From d82d855c206ca93136ad4772fa4db31b220d9303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 27 Oct 2025 18:24:19 -0700 Subject: [PATCH 155/158] DailyTransport: don't timeout prematurely on leave --- CHANGELOG.md | 3 ++- src/pipecat/transports/daily/transport.py | 19 +++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5813a5b0..3e0486d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Fixed an issue where `DailyTransport` would timeout prematurely on join. +- Fixed an issue where `DailyTransport` would timeout prematurely on join and on + leave. - Fixed an issue in the runner where starting a DailyTransport room via `/start` didn't support using the `DAILY_SAMPLE_ROOM_URL` env var. diff --git a/src/pipecat/transports/daily/transport.py b/src/pipecat/transports/daily/transport.py index 675db37e5..2e48c4d2e 100644 --- a/src/pipecat/transports/daily/transport.py +++ b/src/pipecat/transports/daily/transport.py @@ -849,17 +849,12 @@ class DailyTransportClient(EventHandler): for track_name, _ in self._custom_audio_tracks.items(): await self.remove_custom_audio_track(track_name) - try: - error = await self._leave() - if not error: - logger.info(f"Left {self._room_url}") - await self._callbacks.on_left() - else: - error_msg = f"Error leaving {self._room_url}: {error}" - logger.error(error_msg) - await self._callbacks.on_error(error_msg) - except asyncio.TimeoutError: - error_msg = f"Time out leaving {self._room_url}" + error = await self._leave() + if not error: + logger.info(f"Left {self._room_url}") + await self._callbacks.on_left() + else: + error_msg = f"Error leaving {self._room_url}: {error}" logger.error(error_msg) await self._callbacks.on_error(error_msg) @@ -870,7 +865,7 @@ class DailyTransportClient(EventHandler): future = self._get_event_loop().create_future() self._client.leave(completion=completion_callback(future)) - return await asyncio.wait_for(future, timeout=10) + return await future def _cleanup(self): """Cleanup the Daily client instance.""" From 533372ed3740fbf747c6920f23b31ee3fb98ec34 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 28 Oct 2025 15:39:14 -0400 Subject: [PATCH 156/158] Remove aiohttp_session arg from SarvamTTSService --- CHANGELOG.md | 5 +++++ src/pipecat/services/sarvam/tts.py | 15 --------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce1952c3f..c76d57d13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `aws_sdk_bedrock_runtime` to v0.1.1 to resolve potential CPU issues when running `AWSNovaSonicLLMService`. +### Removed + +- Removed the `aiohttp_session` arg from `SarvamTTSService` as it's no longer + used. + ### Fixed - Fixed an issue where `DailyTransport` would timeout prematurely on join and on diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index 762776d50..7096683eb 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -374,7 +374,6 @@ class SarvamTTSService(InterruptibleTTSService): model: str = "bulbul:v2", voice_id: str = "anushka", url: str = "wss://api.sarvam.ai/text-to-speech/ws", - aiohttp_session: Optional[aiohttp.ClientSession] = None, aggregate_sentences: Optional[bool] = True, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, @@ -388,11 +387,6 @@ class SarvamTTSService(InterruptibleTTSService): Supports "bulbul:v2", "bulbul:v3-beta" and "bulbul:v3". voice_id: Voice identifier for synthesis (default "anushka"). url: WebSocket URL for connecting to the TTS backend (default production URL). - aiohttp_session: Optional shared aiohttp session. To maintain backward compatibility. - - .. deprecated:: 0.0.81 - aiohttp_session is no longer used. This parameter will be removed in a future version. - aggregate_sentences: Whether to merge multiple sentences into one audio chunk (default True). sample_rate: Desired sample rate for the output audio in Hz (overrides default if set). params: Optional input parameters to override global configuration. @@ -413,16 +407,7 @@ class SarvamTTSService(InterruptibleTTSService): **kwargs, ) params = params or SarvamTTSService.InputParams() - if aiohttp_session is not None: - import warnings - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - "The 'aiohttp_session' parameter is deprecated and will be removed in a future version. ", - DeprecationWarning, - stacklevel=2, - ) # WebSocket endpoint URL self._websocket_url = f"{url}?model={model}" self._api_key = api_key From aaebcae2e8f1b13f105a9f3ed80548fe94706989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 28 Oct 2025 17:01:49 -0700 Subject: [PATCH 157/158] pyproject: update daily-python to 0.21.0 --- CHANGELOG.md | 2 ++ pyproject.toml | 2 +- uv.lock | 12 ++++++------ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c76d57d13..d0ad0f9b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Updated `daily-python` to 0.21.0. + - Updated the default model to `sonic-3` for `CartesiaTTSService` and `CartesiaHttpTTSService`. diff --git a/pyproject.toml b/pyproject.toml index fd992fa86..5760361b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ azure = [ "azure-cognitiveservices-speech~=1.42.0"] cartesia = [ "cartesia~=2.0.3", "pipecat-ai[websockets-base]" ] cerebras = [] deepseek = [] -daily = [ "daily-python~=0.20.0" ] +daily = [ "daily-python~=0.21.0" ] deepgram = [ "deepgram-sdk~=4.7.0" ] elevenlabs = [ "pipecat-ai[websockets-base]" ] fal = [ "fal-client~=0.5.9" ] diff --git a/uv.lock b/uv.lock index 8fae18c09..129491a61 100644 --- a/uv.lock +++ b/uv.lock @@ -1282,13 +1282,13 @@ wheels = [ [[package]] name = "daily-python" -version = "0.20.0" +version = "0.21.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/02/ce81ebf11a04cd133a5539e08f85060574711fff05a1d6ad29705f0755c1/daily_python-0.20.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:7da3f1df8cd9ef7f7fcc96ce688348dc903f62d82b6dd155a53bc64b7a74f3a7", size = 13259887, upload-time = "2025-10-16T22:14:12.262Z" }, - { url = "https://files.pythonhosted.org/packages/4a/1e/51f06f3486c978e1184af2271e800ce6a6e8a8f95d61ee6624bae88ae9cd/daily_python-0.20.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:d02fd7b8c8079ceaa550ef23db052cdf70a8ffaf8ab6a8bc1a1e97bf0b939464", size = 11642453, upload-time = "2025-10-16T22:14:14.477Z" }, - { url = "https://files.pythonhosted.org/packages/71/c9/f767f0b479abd39330569ad61fb9db4661aae56cd74bb27c6f3483595463/daily_python-0.20.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a5c8718982c221dc18b41fb0692c9f8435f115f72e74994c94d3b9c6dad7c534", size = 13634216, upload-time = "2025-10-16T22:14:16.235Z" }, - { url = "https://files.pythonhosted.org/packages/e8/10/5c6d7b000bee36c2a0587a092a34c7486d2de831fc8e44ed42b16a6bd99f/daily_python-0.20.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca9132aef1bdb5be663d1894b440dab1f998ebb3f45dfc31d44effabded4bc08", size = 14282189, upload-time = "2025-10-16T22:14:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/ff/11/99590f8b7aad077f3f9b5b59d39b010aee0bd01b14dece8ae1e93d8080e7/daily_python-0.21.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:bdec96417825181559769bb2258ae688d1215949a1878336194e36fb452274a8", size = 13277066, upload-time = "2025-10-29T00:20:49.523Z" }, + { url = "https://files.pythonhosted.org/packages/e5/db/8c57f1a1b713ba3393584ac2be32d8074d3022a2c2c17c28eb4cd2aa3629/daily_python-0.21.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:18677fa1415a0dc48b891cdf2fb8fe9dabc70e1b019d5aaa3d0699ccc8d187c9", size = 11644908, upload-time = "2025-10-29T00:20:52.106Z" }, + { url = "https://files.pythonhosted.org/packages/64/b6/b03f2f58a367d6ef4bb728715471542fdfa68afa8a177670139c3a2aadb7/daily_python-0.21.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:97eb97352fe74227061b678e330b8befcfa4c694feb6eb2b09fe6eacec00ad6d", size = 13652356, upload-time = "2025-10-29T00:20:54.813Z" }, + { url = "https://files.pythonhosted.org/packages/f6/76/bde65f6f8d4c1679dc6c185fa37dae9223f6ddb4b7ced728ef46504956f7/daily_python-0.21.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:68c3e36f609fc2fce79e4d17ecf1021eadd836506db6c5125f95c682bcf3612a", size = 14304643, upload-time = "2025-10-29T00:20:57.194Z" }, ] [[package]] @@ -4637,7 +4637,7 @@ requires-dist = [ { name = "azure-cognitiveservices-speech", marker = "extra == 'azure'", specifier = "~=1.42.0" }, { name = "cartesia", marker = "extra == 'cartesia'", specifier = "~=2.0.3" }, { name = "coremltools", marker = "extra == 'local-smart-turn'", specifier = ">=8.0" }, - { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.20.0" }, + { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.21.0" }, { name = "deepgram-sdk", marker = "extra == 'deepgram'", specifier = "~=4.7.0" }, { name = "docstring-parser", specifier = "~=0.16" }, { name = "einops", marker = "extra == 'moondream'", specifier = "~=0.8.0" }, From ede6c321495a83d0fc5a05a3fd697459643c598a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 28 Oct 2025 14:41:29 -0400 Subject: [PATCH 158/158] Update Simli to align with Pipecat constructor norms --- CHANGELOG.md | 9 +++ examples/foundational/27-simli-layer.py | 6 +- src/pipecat/services/simli/video.py | 94 ++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0ad0f9b6..26ee846bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `daily-python` to 0.21.0. +- `SimliVideoService` now accepts `api_key` and `face_id` parameters directly, + with optional `params` for `max_session_length` and `max_idle_time` + configuration, aligning with other Pipecat service patterns. + - Updated the default model to `sonic-3` for `CartesiaTTSService` and `CartesiaHttpTTSService`. @@ -20,6 +24,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `aws_sdk_bedrock_runtime` to v0.1.1 to resolve potential CPU issues when running `AWSNovaSonicLLMService`. +### Deprecated + +- `SimliVideoService` `simli_config` parameter is deprecated. Use `api_key` and + `face_id` parameters instead. + ### Removed - Removed the `aiohttp_session` arg from `SarvamTTSService` as it's no longer diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index 9479632b5..348cf117b 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -9,7 +9,6 @@ import os from dotenv import load_dotenv from loguru import logger -from simli import SimliConfig from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 @@ -66,11 +65,12 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="a167e0f3-df7e-4d52-a9c3-f949145efdab", + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", ) simli_ai = SimliVideoService( - SimliConfig(os.getenv("SIMLI_API_KEY"), os.getenv("SIMLI_FACE_ID")), + api_key=os.getenv("SIMLI_API_KEY"), + face_id="cace3ef7-a4c4-425d-a8cf-a5358eb0c427", ) llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o-mini") diff --git a/src/pipecat/services/simli/video.py b/src/pipecat/services/simli/video.py index d48a744e0..383a8a3cb 100644 --- a/src/pipecat/services/simli/video.py +++ b/src/pipecat/services/simli/video.py @@ -7,9 +7,12 @@ """Simli video service for real-time avatar generation.""" import asyncio +import warnings +from typing import Optional import numpy as np from loguru import logger +from pydantic import BaseModel from pipecat.frames.frames import ( CancelFrame, @@ -41,30 +44,103 @@ class SimliVideoService(FrameProcessor): audio resampling, video frame processing, and connection management. """ + class InputParams(BaseModel): + """Input parameters for Simli video configuration. + + Parameters: + max_session_length: Absolute maximum session duration in seconds. + Avatar will disconnect after this time even if it's speaking. + max_idle_time: Maximum duration in seconds the avatar is not speaking + before the avatar disconnects. + """ + + max_session_length: Optional[int] = None + max_idle_time: Optional[int] = None + def __init__( self, - simli_config: SimliConfig, + *, + api_key: Optional[str] = None, + face_id: Optional[str] = None, + simli_config: Optional[SimliConfig] = None, use_turn_server: bool = False, latency_interval: int = 0, simli_url: str = "https://api.simli.ai", is_trinity_avatar: bool = False, + params: Optional[InputParams] = None, + **kwargs, ): """Initialize the Simli video service. Args: + api_key: Simli API key for authentication. + face_id: Simli Face ID. For Trinity avatars, specify "faceId/emotionId" + to use a different emotion than the default. simli_config: Configuration object for Simli client settings. - use_turn_server: Whether to use TURN server for connection. Defaults to False. - latency_interval: Latency interval setting for sending health checks to check the latency to Simli Servers. Defaults to 0. - simli_url: URL of the simli servers. Can be changed for custom deployments of enterprise users. - is_trinity_avatar: boolean to tell simli client that this is a Trinity avatar which reduces latency when using Trinity. + Use api_key and face_id instead. + .. deprecated:: 0.0.92 + The 'simli_config' parameter is deprecated and will be removed in a future version. + Please use 'api_key' and 'face_id' parameters instead. + + use_turn_server: Whether to use TURN server for connection. Defaults to False. + latency_interval: Latency interval setting for sending health checks to check + the latency to Simli Servers. Defaults to 0. + simli_url: URL of the simli servers. Can be changed for custom deployments + of enterprise users. + is_trinity_avatar: Boolean to tell simli client that this is a Trinity avatar + which reduces latency when using Trinity. + params: Additional input parameters for session configuration. + **kwargs: Additional arguments passed to the parent FrameProcessor. """ - super().__init__() + super().__init__(**kwargs) + + params = params or SimliVideoService.InputParams() + + # Handle deprecated simli_config parameter + if simli_config is not None: + if api_key is not None or face_id is not None: + raise ValueError( + "Cannot specify both simli_config and api_key/face_id. " + "Please use api_key and face_id (simli_config is deprecated)." + ) + + warnings.warn( + "The 'simli_config' parameter is deprecated and will be removed in a future version. " + "Please use 'api_key' and 'face_id' parameters instead, with optional 'params' for " + "max_session_length and max_idle_time configuration.", + DeprecationWarning, + stacklevel=2, + ) + + # Use the provided simli_config + config = simli_config + else: + # Validate new parameters + if api_key is None: + raise ValueError("api_key is required") + if face_id is None: + raise ValueError("face_id is required") + + # Build SimliConfig from new parameters + # Only pass optional parameters if explicitly provided to use SimliConfig defaults + config_kwargs = { + "apiKey": api_key, + "faceId": face_id, + } + if params.max_session_length is not None: + config_kwargs["maxSessionLength"] = params.max_session_length + if params.max_idle_time is not None: + config_kwargs["maxIdleTime"] = params.max_idle_time + + config = SimliConfig(**config_kwargs) + self._initialized = False - simli_config.maxIdleTime += 5 - simli_config.maxSessionLength += 5 + # Add buffer time to session limits + config.maxIdleTime += 5 + config.maxSessionLength += 5 self._simli_client = SimliClient( - simli_config, + config, use_turn_server, latency_interval, simliURL=simli_url,