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 base64
|
||||||
import json
|
import json
|
||||||
import warnings
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, AsyncGenerator, List, Literal, Mapping, Optional, Self
|
from typing import AsyncGenerator, List, Optional
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
CancelFrame,
|
CancelFrame,
|
||||||
@@ -192,35 +191,16 @@ class CartesiaTTSSettings(TTSSettings):
|
|||||||
"""Settings for Cartesia TTS services.
|
"""Settings for Cartesia TTS services.
|
||||||
|
|
||||||
Parameters:
|
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,
|
generation_config: Generation configuration for Sonic-3 models. Includes volume,
|
||||||
speed (numeric), and emotion (string) parameters.
|
speed (numeric), and emotion (string) parameters.
|
||||||
pronunciation_dict_id: The ID of the pronunciation dictionary to use for
|
pronunciation_dict_id: The ID of the pronunciation dictionary to use for
|
||||||
custom pronunciations.
|
custom pronunciations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
output_container: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
generation_config: GenerationConfig | None | _NotGiven = field(
|
||||||
output_encoding: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
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)
|
pronunciation_dict_id: str | None | _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)
|
|
||||||
|
|
||||||
|
|
||||||
class CartesiaTTSService(AudioContextTTSService):
|
class CartesiaTTSService(AudioContextTTSService):
|
||||||
@@ -228,7 +208,7 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
|
|
||||||
Provides text-to-speech using Cartesia's streaming WebSocket API.
|
Provides text-to-speech using Cartesia's streaming WebSocket API.
|
||||||
Supports word-level timestamps, audio context management, and various voice
|
Supports word-level timestamps, audio context management, and various voice
|
||||||
customization options including speed and emotion controls.
|
customization options including generation configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_settings: CartesiaTTSSettings
|
_settings: CartesiaTTSSettings
|
||||||
@@ -238,20 +218,12 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
language: Language to use for synthesis.
|
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,
|
generation_config: Generation configuration for Sonic-3 models. Includes volume,
|
||||||
speed (numeric), and emotion (string) parameters.
|
speed (numeric), and emotion (string) parameters.
|
||||||
pronunciation_dict_id: The ID of the pronunciation dictionary to use for custom pronunciations.
|
pronunciation_dict_id: The ID of the pronunciation dictionary to use for custom pronunciations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
language: Optional[Language] = Language.EN
|
language: Optional[Language] = Language.EN
|
||||||
speed: Optional[Literal["slow", "normal", "fast"]] = None
|
|
||||||
emotion: Optional[List[str]] = []
|
|
||||||
generation_config: Optional[GenerationConfig] = None
|
generation_config: Optional[GenerationConfig] = None
|
||||||
pronunciation_dict_id: Optional[str] = None
|
pronunciation_dict_id: Optional[str] = None
|
||||||
|
|
||||||
@@ -279,14 +251,14 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
api_key: Cartesia API key for authentication.
|
api_key: Cartesia API key for authentication.
|
||||||
voice_id: ID of the voice to use for synthesis.
|
voice_id: ID of the voice to use for synthesis.
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 0.0.105
|
||||||
Use ``settings=CartesiaTTSSettings(voice=...)`` instead.
|
Use ``settings=CartesiaTTSSettings(voice=...)`` instead.
|
||||||
|
|
||||||
cartesia_version: API version string for Cartesia service.
|
cartesia_version: API version string for Cartesia service.
|
||||||
url: WebSocket URL for Cartesia TTS API.
|
url: WebSocket URL for Cartesia TTS API.
|
||||||
model: TTS model to use (e.g., "sonic-3").
|
model: TTS model to use (e.g., "sonic-3").
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 0.0.105
|
||||||
Use ``settings=CartesiaTTSSettings(model=...)`` instead.
|
Use ``settings=CartesiaTTSSettings(model=...)`` instead.
|
||||||
|
|
||||||
sample_rate: Audio sample rate. If None, uses default.
|
sample_rate: Audio sample rate. If None, uses default.
|
||||||
@@ -294,7 +266,7 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
container: Audio container format.
|
container: Audio container format.
|
||||||
params: Additional input parameters for voice customization.
|
params: Additional input parameters for voice customization.
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 0.0.105
|
||||||
Use ``settings=CartesiaTTSSettings(...)`` instead.
|
Use ``settings=CartesiaTTSSettings(...)`` instead.
|
||||||
|
|
||||||
settings: Runtime-updatable settings. When provided alongside deprecated
|
settings: Runtime-updatable settings. When provided alongside deprecated
|
||||||
@@ -329,9 +301,9 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
# 1. Initialize default_settings with hardcoded defaults
|
# 1. Initialize default_settings with hardcoded defaults
|
||||||
default_settings = CartesiaTTSSettings(
|
default_settings = CartesiaTTSSettings(
|
||||||
model="sonic-3",
|
model="sonic-3",
|
||||||
output_container=container,
|
language=language_to_cartesia_language(Language.EN),
|
||||||
output_encoding=encoding,
|
generation_config=None,
|
||||||
output_sample_rate=0,
|
pronunciation_dict_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Apply direct init arg overrides (deprecated)
|
# 2. Apply direct init arg overrides (deprecated)
|
||||||
@@ -348,10 +320,6 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
if not settings:
|
if not settings:
|
||||||
if params.language is not None:
|
if params.language is not None:
|
||||||
default_settings.language = self.language_to_service_language(params.language)
|
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:
|
if params.generation_config is not None:
|
||||||
default_settings.generation_config = params.generation_config
|
default_settings.generation_config = params.generation_config
|
||||||
if params.pronunciation_dict_id is not None:
|
if params.pronunciation_dict_id is not None:
|
||||||
@@ -387,6 +355,11 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
self._cartesia_version = cartesia_version
|
self._cartesia_version = cartesia_version
|
||||||
self._url = url
|
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
|
self._receive_task = None
|
||||||
|
|
||||||
def can_generate_metrics(self) -> bool:
|
def can_generate_metrics(self) -> bool:
|
||||||
@@ -484,17 +457,6 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
voice_config["mode"] = "id"
|
voice_config["mode"] = "id"
|
||||||
voice_config["id"] = self._settings.voice
|
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 = {
|
msg = {
|
||||||
"transcript": text,
|
"transcript": text,
|
||||||
"continue": continue_transcript,
|
"continue": continue_transcript,
|
||||||
@@ -502,9 +464,9 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
"model_id": self._settings.model,
|
"model_id": self._settings.model,
|
||||||
"voice": voice_config,
|
"voice": voice_config,
|
||||||
"output_format": {
|
"output_format": {
|
||||||
"container": self._settings.output_container,
|
"container": self._output_container,
|
||||||
"encoding": self._settings.output_encoding,
|
"encoding": self._output_encoding,
|
||||||
"sample_rate": self._settings.output_sample_rate,
|
"sample_rate": self._output_sample_rate,
|
||||||
},
|
},
|
||||||
"add_timestamps": add_timestamps,
|
"add_timestamps": add_timestamps,
|
||||||
"use_original_timestamps": False if self._settings.model == "sonic" else True,
|
"use_original_timestamps": False if self._settings.model == "sonic" else True,
|
||||||
@@ -513,9 +475,6 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
if self._settings.language:
|
if self._settings.language:
|
||||||
msg["language"] = self._settings.language
|
msg["language"] = self._settings.language
|
||||||
|
|
||||||
if self._settings.speed:
|
|
||||||
msg["speed"] = self._settings.speed
|
|
||||||
|
|
||||||
if self._settings.generation_config:
|
if self._settings.generation_config:
|
||||||
msg["generation_config"] = self._settings.generation_config.model_dump(
|
msg["generation_config"] = self._settings.generation_config.model_dump(
|
||||||
exclude_none=True
|
exclude_none=True
|
||||||
@@ -533,7 +492,7 @@ class CartesiaTTSService(AudioContextTTSService):
|
|||||||
frame: The start frame containing initialization parameters.
|
frame: The start frame containing initialization parameters.
|
||||||
"""
|
"""
|
||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
self._settings.output_sample_rate = self.sample_rate
|
self._output_sample_rate = self.sample_rate
|
||||||
await self._connect()
|
await self._connect()
|
||||||
|
|
||||||
async def stop(self, frame: EndFrame):
|
async def stop(self, frame: EndFrame):
|
||||||
@@ -728,20 +687,12 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
language: Language to use for synthesis.
|
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,
|
generation_config: Generation configuration for Sonic-3 models. Includes volume,
|
||||||
speed (numeric), and emotion (string) parameters.
|
speed (numeric), and emotion (string) parameters.
|
||||||
pronunciation_dict_id: The ID of the pronunciation dictionary to use for custom pronunciations.
|
pronunciation_dict_id: The ID of the pronunciation dictionary to use for custom pronunciations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
language: Optional[Language] = Language.EN
|
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
|
generation_config: Optional[GenerationConfig] = None
|
||||||
pronunciation_dict_id: Optional[str] = None
|
pronunciation_dict_id: Optional[str] = None
|
||||||
|
|
||||||
@@ -767,12 +718,12 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
api_key: Cartesia API key for authentication.
|
api_key: Cartesia API key for authentication.
|
||||||
voice_id: ID of the voice to use for synthesis.
|
voice_id: ID of the voice to use for synthesis.
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 0.0.105
|
||||||
Use ``settings=CartesiaTTSSettings(voice=...)`` instead.
|
Use ``settings=CartesiaTTSSettings(voice=...)`` instead.
|
||||||
|
|
||||||
model: TTS model to use (e.g., "sonic-3").
|
model: TTS model to use (e.g., "sonic-3").
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 0.0.105
|
||||||
Use ``settings=CartesiaTTSSettings(model=...)`` instead.
|
Use ``settings=CartesiaTTSSettings(model=...)`` instead.
|
||||||
|
|
||||||
base_url: Base URL for Cartesia HTTP API.
|
base_url: Base URL for Cartesia HTTP API.
|
||||||
@@ -784,7 +735,7 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
container: Audio container format.
|
container: Audio container format.
|
||||||
params: Additional input parameters for voice customization.
|
params: Additional input parameters for voice customization.
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 0.0.105
|
||||||
Use ``settings=CartesiaTTSSettings(...)`` instead.
|
Use ``settings=CartesiaTTSSettings(...)`` instead.
|
||||||
|
|
||||||
settings: Runtime-updatable settings. When provided alongside deprecated
|
settings: Runtime-updatable settings. When provided alongside deprecated
|
||||||
@@ -794,9 +745,9 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
# 1. Initialize default_settings with hardcoded defaults
|
# 1. Initialize default_settings with hardcoded defaults
|
||||||
default_settings = CartesiaTTSSettings(
|
default_settings = CartesiaTTSSettings(
|
||||||
model="sonic-3",
|
model="sonic-3",
|
||||||
output_container=container,
|
language=language_to_cartesia_language(Language.EN),
|
||||||
output_encoding=encoding,
|
generation_config=None,
|
||||||
output_sample_rate=0,
|
pronunciation_dict_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Apply direct init arg overrides (deprecated)
|
# 2. Apply direct init arg overrides (deprecated)
|
||||||
@@ -813,10 +764,6 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
if not settings:
|
if not settings:
|
||||||
if params.language is not None:
|
if params.language is not None:
|
||||||
default_settings.language = self.language_to_service_language(params.language)
|
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:
|
if params.generation_config is not None:
|
||||||
default_settings.generation_config = params.generation_config
|
default_settings.generation_config = params.generation_config
|
||||||
if params.pronunciation_dict_id is not None:
|
if params.pronunciation_dict_id is not None:
|
||||||
@@ -836,6 +783,11 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
self._base_url = base_url
|
self._base_url = base_url
|
||||||
self._cartesia_version = cartesia_version
|
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._session: aiohttp.ClientSession | None = aiohttp_session
|
||||||
self._owns_session = aiohttp_session is None
|
self._owns_session = aiohttp_session is None
|
||||||
|
|
||||||
@@ -865,7 +817,7 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
frame: The start frame containing initialization parameters.
|
frame: The start frame containing initialization parameters.
|
||||||
"""
|
"""
|
||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
self._settings.output_sample_rate = self.sample_rate
|
self._output_sample_rate = self.sample_rate
|
||||||
if self._owns_session:
|
if self._owns_session:
|
||||||
self._session = aiohttp.ClientSession()
|
self._session = aiohttp.ClientSession()
|
||||||
|
|
||||||
@@ -909,22 +861,12 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
try:
|
try:
|
||||||
voice_config = {"mode": "id", "id": self._settings.voice}
|
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()
|
await self.start_ttfb_metrics()
|
||||||
|
|
||||||
output_format = {
|
output_format = {
|
||||||
"container": self._settings.output_container,
|
"container": self._output_container,
|
||||||
"encoding": self._settings.output_encoding,
|
"encoding": self._output_encoding,
|
||||||
"sample_rate": self._settings.output_sample_rate,
|
"sample_rate": self._output_sample_rate,
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
@@ -937,9 +879,6 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
if self._settings.language:
|
if self._settings.language:
|
||||||
payload["language"] = self._settings.language
|
payload["language"] = self._settings.language
|
||||||
|
|
||||||
if self._settings.speed:
|
|
||||||
payload["speed"] = self._settings.speed
|
|
||||||
|
|
||||||
if self._settings.generation_config:
|
if self._settings.generation_config:
|
||||||
payload["generation_config"] = self._settings.generation_config.model_dump(
|
payload["generation_config"] = self._settings.generation_config.model_dump(
|
||||||
exclude_none=True
|
exclude_none=True
|
||||||
|
|||||||
Reference in New Issue
Block a user