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.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user