From f31bfcf4ece659a1be7267250ccd6d41c396a51a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 3 Mar 2026 20:29:35 -0500 Subject: [PATCH] Clean up CartesiaTTSSettings: separate init-only vs runtime-updatable fields Move output_container, output_encoding, output_sample_rate out of CartesiaTTSSettings into plain instance attributes since they cannot change at runtime without breaking the audio pipeline. Remove deprecated speed/emotion fields and their dead references in _build_msg() and run_tts(). Remove the from_mapping override that only existed to destructure those now-removed output format fields. --- src/pipecat/services/cartesia/tts.py | 135 ++++++++------------------- 1 file changed, 37 insertions(+), 98 deletions(-) diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index 2aeeefda2..71017074b 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -8,14 +8,13 @@ import base64 import json -import warnings from dataclasses import dataclass, field from enum import Enum -from typing import Any, AsyncGenerator, List, Literal, Mapping, Optional, Self +from typing import AsyncGenerator, List, Optional import aiohttp from loguru import logger -from pydantic import BaseModel, Field +from pydantic import BaseModel from pipecat.frames.frames import ( CancelFrame, @@ -192,35 +191,16 @@ class CartesiaTTSSettings(TTSSettings): """Settings for Cartesia TTS services. Parameters: - output_container: Audio container format (e.g. "raw"). - output_encoding: Audio encoding format (e.g. "pcm_s16le"). - output_sample_rate: Audio sample rate in Hz. - speed: Voice speed control for non-Sonic-3 models (literal values). - emotion: List of emotion controls for non-Sonic-3 models. generation_config: Generation configuration for Sonic-3 models. Includes volume, speed (numeric), and emotion (string) parameters. pronunciation_dict_id: The ID of the pronunciation dictionary to use for custom pronunciations. """ - output_container: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - output_encoding: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - output_sample_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - speed: Literal["slow", "normal", "fast"] | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - emotion: List[str] | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - generation_config: GenerationConfig | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - pronunciation_dict_id: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - - @classmethod - def from_mapping(cls, settings: Mapping[str, Any]) -> Self: - """Construct settings from a plain dict, destructuring legacy nested ``output_format``.""" - flat = dict(settings) - nested = flat.pop("output_format", None) - if isinstance(nested, dict): - flat.setdefault("output_container", nested.get("container")) - flat.setdefault("output_encoding", nested.get("encoding")) - flat.setdefault("output_sample_rate", nested.get("sample_rate")) - return super().from_mapping(flat) + generation_config: GenerationConfig | None | _NotGiven = field( + default_factory=lambda: NOT_GIVEN + ) + pronunciation_dict_id: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) class CartesiaTTSService(AudioContextTTSService): @@ -228,7 +208,7 @@ class CartesiaTTSService(AudioContextTTSService): Provides text-to-speech using Cartesia's streaming WebSocket API. Supports word-level timestamps, audio context management, and various voice - customization options including speed and emotion controls. + customization options including generation configuration. """ _settings: CartesiaTTSSettings @@ -238,20 +218,12 @@ class CartesiaTTSService(AudioContextTTSService): Parameters: language: Language to use for synthesis. - speed: Voice speed control for non-Sonic-3 models (literal values). - emotion: List of emotion controls for non-Sonic-3 models. - - .. deprecated:: 0.0.68 - The `emotion` parameter is deprecated and will be removed in a future version. - generation_config: Generation configuration for Sonic-3 models. Includes volume, speed (numeric), and emotion (string) parameters. pronunciation_dict_id: The ID of the pronunciation dictionary to use for custom pronunciations. """ language: Optional[Language] = Language.EN - speed: Optional[Literal["slow", "normal", "fast"]] = None - emotion: Optional[List[str]] = [] generation_config: Optional[GenerationConfig] = None pronunciation_dict_id: Optional[str] = None @@ -279,14 +251,14 @@ class CartesiaTTSService(AudioContextTTSService): api_key: Cartesia API key for authentication. voice_id: ID of the voice to use for synthesis. - .. deprecated:: 1.0.0 + .. deprecated:: 0.0.105 Use ``settings=CartesiaTTSSettings(voice=...)`` instead. cartesia_version: API version string for Cartesia service. url: WebSocket URL for Cartesia TTS API. model: TTS model to use (e.g., "sonic-3"). - .. deprecated:: 1.0.0 + .. deprecated:: 0.0.105 Use ``settings=CartesiaTTSSettings(model=...)`` instead. sample_rate: Audio sample rate. If None, uses default. @@ -294,7 +266,7 @@ class CartesiaTTSService(AudioContextTTSService): container: Audio container format. params: Additional input parameters for voice customization. - .. deprecated:: 1.0.0 + .. deprecated:: 0.0.105 Use ``settings=CartesiaTTSSettings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated @@ -329,9 +301,9 @@ class CartesiaTTSService(AudioContextTTSService): # 1. Initialize default_settings with hardcoded defaults default_settings = CartesiaTTSSettings( model="sonic-3", - output_container=container, - output_encoding=encoding, - output_sample_rate=0, + language=language_to_cartesia_language(Language.EN), + generation_config=None, + pronunciation_dict_id=None, ) # 2. Apply direct init arg overrides (deprecated) @@ -348,10 +320,6 @@ class CartesiaTTSService(AudioContextTTSService): if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) - if params.speed is not None: - default_settings.speed = params.speed - if params.emotion is not None: - default_settings.emotion = params.emotion if params.generation_config is not None: default_settings.generation_config = params.generation_config if params.pronunciation_dict_id is not None: @@ -387,6 +355,11 @@ class CartesiaTTSService(AudioContextTTSService): self._cartesia_version = cartesia_version self._url = url + # Audio output format — init-only, not runtime-updatable + self._output_container = container + self._output_encoding = encoding + self._output_sample_rate = 0 # Set in start() from self.sample_rate + self._receive_task = None def can_generate_metrics(self) -> bool: @@ -484,17 +457,6 @@ class CartesiaTTSService(AudioContextTTSService): voice_config["mode"] = "id" voice_config["id"] = self._settings.voice - if self._settings.emotion: - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - "The 'emotion' parameter in __experimental_controls is deprecated and will be removed in a future version.", - DeprecationWarning, - stacklevel=2, - ) - voice_config["__experimental_controls"] = {} - voice_config["__experimental_controls"]["emotion"] = self._settings.emotion - msg = { "transcript": text, "continue": continue_transcript, @@ -502,9 +464,9 @@ class CartesiaTTSService(AudioContextTTSService): "model_id": self._settings.model, "voice": voice_config, "output_format": { - "container": self._settings.output_container, - "encoding": self._settings.output_encoding, - "sample_rate": self._settings.output_sample_rate, + "container": self._output_container, + "encoding": self._output_encoding, + "sample_rate": self._output_sample_rate, }, "add_timestamps": add_timestamps, "use_original_timestamps": False if self._settings.model == "sonic" else True, @@ -513,9 +475,6 @@ class CartesiaTTSService(AudioContextTTSService): if self._settings.language: msg["language"] = self._settings.language - if self._settings.speed: - msg["speed"] = self._settings.speed - if self._settings.generation_config: msg["generation_config"] = self._settings.generation_config.model_dump( exclude_none=True @@ -533,7 +492,7 @@ class CartesiaTTSService(AudioContextTTSService): frame: The start frame containing initialization parameters. """ await super().start(frame) - self._settings.output_sample_rate = self.sample_rate + self._output_sample_rate = self.sample_rate await self._connect() async def stop(self, frame: EndFrame): @@ -728,20 +687,12 @@ class CartesiaHttpTTSService(TTSService): Parameters: language: Language to use for synthesis. - speed: Voice speed control for non-Sonic-3 models (literal values). - emotion: List of emotion controls for non-Sonic-3 models. - - .. deprecated:: 0.0.68 - The `emotion` parameter is deprecated and will be removed in a future version. - generation_config: Generation configuration for Sonic-3 models. Includes volume, speed (numeric), and emotion (string) parameters. pronunciation_dict_id: The ID of the pronunciation dictionary to use for custom pronunciations. """ language: Optional[Language] = Language.EN - speed: Optional[Literal["slow", "normal", "fast"]] = None - emotion: Optional[List[str]] = Field(default_factory=list) generation_config: Optional[GenerationConfig] = None pronunciation_dict_id: Optional[str] = None @@ -767,12 +718,12 @@ class CartesiaHttpTTSService(TTSService): api_key: Cartesia API key for authentication. voice_id: ID of the voice to use for synthesis. - .. deprecated:: 1.0.0 + .. deprecated:: 0.0.105 Use ``settings=CartesiaTTSSettings(voice=...)`` instead. model: TTS model to use (e.g., "sonic-3"). - .. deprecated:: 1.0.0 + .. deprecated:: 0.0.105 Use ``settings=CartesiaTTSSettings(model=...)`` instead. base_url: Base URL for Cartesia HTTP API. @@ -784,7 +735,7 @@ class CartesiaHttpTTSService(TTSService): container: Audio container format. params: Additional input parameters for voice customization. - .. deprecated:: 1.0.0 + .. deprecated:: 0.0.105 Use ``settings=CartesiaTTSSettings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated @@ -794,9 +745,9 @@ class CartesiaHttpTTSService(TTSService): # 1. Initialize default_settings with hardcoded defaults default_settings = CartesiaTTSSettings( model="sonic-3", - output_container=container, - output_encoding=encoding, - output_sample_rate=0, + language=language_to_cartesia_language(Language.EN), + generation_config=None, + pronunciation_dict_id=None, ) # 2. Apply direct init arg overrides (deprecated) @@ -813,10 +764,6 @@ class CartesiaHttpTTSService(TTSService): if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) - if params.speed is not None: - default_settings.speed = params.speed - if params.emotion is not None: - default_settings.emotion = params.emotion if params.generation_config is not None: default_settings.generation_config = params.generation_config if params.pronunciation_dict_id is not None: @@ -836,6 +783,11 @@ class CartesiaHttpTTSService(TTSService): self._base_url = base_url self._cartesia_version = cartesia_version + # Audio output format — init-only, not runtime-updatable + self._output_container = container + self._output_encoding = encoding + self._output_sample_rate = 0 # Set in start() from self.sample_rate + self._session: aiohttp.ClientSession | None = aiohttp_session self._owns_session = aiohttp_session is None @@ -865,7 +817,7 @@ class CartesiaHttpTTSService(TTSService): frame: The start frame containing initialization parameters. """ await super().start(frame) - self._settings.output_sample_rate = self.sample_rate + self._output_sample_rate = self.sample_rate if self._owns_session: self._session = aiohttp.ClientSession() @@ -909,22 +861,12 @@ class CartesiaHttpTTSService(TTSService): try: voice_config = {"mode": "id", "id": self._settings.voice} - if self._settings.emotion: - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - "The 'emotion' parameter in voice.__experimental_controls is deprecated and will be removed in a future version.", - DeprecationWarning, - stacklevel=2, - ) - voice_config["__experimental_controls"] = {"emotion": self._settings.emotion} - await self.start_ttfb_metrics() output_format = { - "container": self._settings.output_container, - "encoding": self._settings.output_encoding, - "sample_rate": self._settings.output_sample_rate, + "container": self._output_container, + "encoding": self._output_encoding, + "sample_rate": self._output_sample_rate, } payload = { @@ -937,9 +879,6 @@ class CartesiaHttpTTSService(TTSService): if self._settings.language: payload["language"] = self._settings.language - if self._settings.speed: - payload["speed"] = self._settings.speed - if self._settings.generation_config: payload["generation_config"] = self._settings.generation_config.model_dump( exclude_none=True