From bcc2b4def48ef5c0c13544ab526df79fe80531d1 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 24 Feb 2026 10:52:05 -0500 Subject: [PATCH] Make clearer the distinction between "storage-mode" and "delta-mode" usage of `*Settings` objects - Storage mode: for use in `self._settings`. All fields should be specified, i.e. should not be `NOT_GIVEN`. - Delta mode: for use in `*UpdateSettingsFrame`. In service of this, this commit: - Adds a runtime check that all fields are specified in storage mode - Updates all services to specify all fields in stored settings - Updates all services to no longer check for `is_given` in stored settings (not necessary anymore) - Updates relevant docstrings - Renames `update` to `delta` in `*UpdateSettingsFrame` - Updates community integrations guide --- COMMUNITY_INTEGRATIONS.md | 8 +- changelog/3714.added.md | 2 +- changelog/3714.deprecated.2.md | 2 +- .../55a-update-settings-deepgram-flux-stt.py | 2 +- ...-update-settings-deepgram-sagemaker-stt.py | 2 +- .../55a-update-settings-deepgram-stt.py | 2 +- .../55b-update-settings-azure-stt.py | 4 +- .../55c-update-settings-google-stt.py | 2 +- .../55d-update-settings-assemblyai-stt.py | 2 +- .../55e-update-settings-gladia-stt.py | 2 +- ...update-settings-elevenlabs-realtime-stt.py | 2 +- .../55g-update-settings-elevenlabs-stt.py | 2 +- .../55h-update-settings-speechmatics-stt.py | 6 +- .../55i-update-settings-whisper-api-stt.py | 2 +- .../55j-update-settings-sarvam-stt.py | 2 +- .../55k-update-settings-soniox-stt.py | 2 +- .../55l-update-settings-aws-transcribe-stt.py | 2 +- .../55m-update-settings-cartesia-stt.py | 2 +- .../55n-update-settings-cartesia-http-tts.py | 2 +- .../55n-update-settings-cartesia-tts.py | 2 +- ...55o-update-settings-elevenlabs-http-tts.py | 2 +- .../55o-update-settings-elevenlabs-tts.py | 4 +- .../55p-update-settings-openai-tts.py | 2 +- .../55q-update-settings-deepgram-http-tts.py | 4 +- ...-update-settings-deepgram-sagemaker-tts.py | 4 +- .../55q-update-settings-deepgram-tts.py | 4 +- .../55r-update-settings-azure-http-tts.py | 2 +- .../55r-update-settings-azure-tts.py | 2 +- .../55s-update-settings-google-http-tts.py | 2 +- .../55s-update-settings-google-stream-tts.py | 2 +- .../55t-update-settings-playht-tts.py | 2 +- .../55u-update-settings-rime-http-tts.py | 2 +- .../55u-update-settings-rime-tts.py | 2 +- .../55v-update-settings-lmnt-tts.py | 2 +- .../55w-update-settings-fish-tts.py | 2 +- .../55x-update-settings-minimax-tts.py | 2 +- .../55y-update-settings-groq-tts.py | 2 +- .../55z-update-settings-hume-tts.py | 2 +- ...55za-update-settings-neuphonic-http-tts.py | 2 +- .../55za-update-settings-neuphonic-tts.py | 2 +- .../55zb-update-settings-inworld-http-tts.py | 4 +- .../55zb-update-settings-inworld-tts.py | 2 +- .../55zc-update-settings-gemini-tts.py | 2 +- .../55zd-update-settings-aws-polly-tts.py | 2 +- .../55ze-update-settings-sarvam-http-tts.py | 2 +- .../55ze-update-settings-sarvam-tts.py | 2 +- .../55zf-update-settings-camb-tts.py | 2 +- .../55zg-update-settings-hathora-tts.py | 2 +- .../55zh-update-settings-resembleai-tts.py | 2 +- .../55zi-update-settings-azure-llm.py | 2 +- .../55zi-update-settings-openai-llm.py | 2 +- .../55zj-update-settings-anthropic-llm.py | 2 +- .../55zk-update-settings-google-llm.py | 2 +- .../55zk-update-settings-google-vertex-llm.py | 2 +- .../55zl-update-settings-azure-realtime.py | 4 +- .../55zl-update-settings-openai-realtime.py | 4 +- ...55zm-update-settings-gemini-live-vertex.py | 4 +- .../55zm-update-settings-gemini-live.py | 4 +- .../55zn-update-settings-ultravox-realtime.py | 4 +- .../55zo-update-settings-grok-realtime.py | 2 +- .../55zp-update-settings-aws-bedrock-llm.py | 4 +- .../55zq-update-settings-fal-stt.py | 2 +- .../55zr-update-settings-gradium-stt.py | 2 +- .../55zs-update-settings-hathora-stt.py | 2 +- ...zt-update-settings-nvidia-segmented-stt.py | 2 +- .../55zt-update-settings-nvidia-stt.py | 2 +- ...5zu-update-settings-openai-realtime-stt.py | 2 +- .../55zv-update-settings-asyncai-http-tts.py | 2 +- .../55zv-update-settings-asyncai-tts.py | 2 +- .../55zw-update-settings-gradium-tts.py | 2 +- .../55zx-update-settings-cerebras-llm.py | 2 +- .../55zy-update-settings-deepseek-llm.py | 2 +- .../55zz-update-settings-fireworks-llm.py | 2 +- .../55zza-update-settings-grok-llm.py | 2 +- .../55zzb-update-settings-groq-llm.py | 2 +- .../55zzc-update-settings-mistral-llm.py | 2 +- .../55zzd-update-settings-nvidia-llm.py | 2 +- .../55zze-update-settings-ollama-llm.py | 2 +- .../55zzf-update-settings-openrouter-llm.py | 2 +- .../55zzg-update-settings-perplexity-llm.py | 2 +- .../55zzh-update-settings-qwen-llm.py | 2 +- .../55zzi-update-settings-sambanova-llm.py | 2 +- .../55zzj-update-settings-together-llm.py | 2 +- ...5zzk-update-settings-aws-nova-sonic-llm.py | 2 +- .../55zzl-update-settings-nvidia-tts.py | 2 +- .../55zzm-update-settings-speechmatics-tts.py | 2 +- src/pipecat/frames/frames.py | 12 +- src/pipecat/services/ai_service.py | 13 +- src/pipecat/services/anthropic/llm.py | 5 + src/pipecat/services/assemblyai/stt.py | 9 +- src/pipecat/services/asyncai/tts.py | 6 +- src/pipecat/services/aws/llm.py | 6 + src/pipecat/services/aws/nova_sonic/llm.py | 12 +- src/pipecat/services/aws/stt.py | 6 +- src/pipecat/services/aws/tts.py | 1 + src/pipecat/services/azure/stt.py | 7 +- src/pipecat/services/azure/tts.py | 1 + src/pipecat/services/cartesia/stt.py | 8 +- src/pipecat/services/cartesia/tts.py | 25 +- src/pipecat/services/deepgram/flux/stt.py | 6 +- src/pipecat/services/deepgram/stt.py | 16 +- .../services/deepgram/stt_sagemaker.py | 16 +- src/pipecat/services/deepgram/tts.py | 10 +- .../services/deepgram/tts_sagemaker.py | 7 +- src/pipecat/services/elevenlabs/stt.py | 16 +- src/pipecat/services/elevenlabs/tts.py | 32 +-- src/pipecat/services/fal/stt.py | 1 + src/pipecat/services/fish/tts.py | 8 +- src/pipecat/services/gladia/stt.py | 10 +- .../services/google/gemini_live/llm.py | 9 +- src/pipecat/services/google/llm.py | 5 + src/pipecat/services/google/stt.py | 48 ++-- src/pipecat/services/google/tts.py | 39 +-- src/pipecat/services/gradium/stt.py | 17 +- src/pipecat/services/gradium/tts.py | 9 +- src/pipecat/services/grok/realtime/llm.py | 20 +- src/pipecat/services/hume/tts.py | 5 +- src/pipecat/services/inworld/tts.py | 57 ++--- src/pipecat/services/kokoro/tts.py | 1 + src/pipecat/services/llm_service.py | 24 +- src/pipecat/services/lmnt/tts.py | 8 +- src/pipecat/services/minimax/tts.py | 16 +- src/pipecat/services/neuphonic/tts.py | 8 +- src/pipecat/services/nvidia/stt.py | 8 +- src/pipecat/services/nvidia/tts.py | 6 +- src/pipecat/services/openai/base_llm.py | 3 + src/pipecat/services/openai/realtime/llm.py | 19 +- src/pipecat/services/openai/stt.py | 10 +- .../services/openai_realtime_beta/openai.py | 19 +- src/pipecat/services/perplexity/llm.py | 12 +- src/pipecat/services/piper/tts.py | 10 +- src/pipecat/services/playht/tts.py | 10 +- src/pipecat/services/resembleai/tts.py | 1 + src/pipecat/services/rime/tts.py | 100 ++++---- src/pipecat/services/sarvam/stt.py | 34 +-- src/pipecat/services/sarvam/tts.py | 28 ++- src/pipecat/services/settings.py | 232 ++++++++++++------ src/pipecat/services/soniox/stt.py | 12 +- src/pipecat/services/speechmatics/stt.py | 31 +-- src/pipecat/services/speechmatics/tts.py | 2 + src/pipecat/services/stt_service.py | 24 +- src/pipecat/services/tts_service.py | 28 +-- src/pipecat/services/ultravox/llm.py | 18 +- src/pipecat/services/whisper/base_stt.py | 6 +- src/pipecat/services/xtts/tts.py | 1 + 145 files changed, 732 insertions(+), 587 deletions(-) diff --git a/COMMUNITY_INTEGRATIONS.md b/COMMUNITY_INTEGRATIONS.md index 642754451..e11c79f31 100644 --- a/COMMUNITY_INTEGRATIONS.md +++ b/COMMUNITY_INTEGRATIONS.md @@ -260,9 +260,11 @@ class MySTTService(STTService): _settings: MySTTSettings - def __init__(self, *, model: str, region: str, **kwargs): + def __init__(self, *, model: str, language: str, region: str, **kwargs): super().__init__(**kwargs) - self._settings = MySTTSettings(model=model, region=region) + # Initial value must be provided for every field in self._settings + # before service is started + self._settings = MySTTSettings(model=model, language=language, region=region) self._sync_model_name_to_metrics() ``` @@ -298,7 +300,7 @@ async def _update_settings(self, update: STTSettings) -> dict[str, Any]: if "language" in changed: await self._update_language() else: - # TODO: handle changes to other settings soon! + # TODO: this should be temporary - handle changes to other settings soon! self._warn_unhandled_updated_settings(changed.keys() - {"language"}) return changed diff --git a/changelog/3714.added.md b/changelog/3714.added.md index 83084675a..efa54b7d5 100644 --- a/changelog/3714.added.md +++ b/changelog/3714.added.md @@ -12,7 +12,7 @@ ```python await task.queue_frame( - STTUpdateSettingsFrame(update=DeepgramSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=DeepgramSTTSettings(language=Language.ES)) ) ``` diff --git a/changelog/3714.deprecated.2.md b/changelog/3714.deprecated.2.md index 232c1dee5..d386fa5a4 100644 --- a/changelog/3714.deprecated.2.md +++ b/changelog/3714.deprecated.2.md @@ -1 +1 @@ -- Dict-based `*UpdateSettingsFrame(settings={...})` is deprecated in favor of passing typed settings delta objects with `*UpdateSettingsFrame(update={...})`. +- Dict-based `*UpdateSettingsFrame(settings={...})` is deprecated in favor of passing typed settings delta objects with `*UpdateSettingsFrame(delta={...})`. diff --git a/examples/foundational/55a-update-settings-deepgram-flux-stt.py b/examples/foundational/55a-update-settings-deepgram-flux-stt.py index d5fb66a2e..a482e513c 100644 --- a/examples/foundational/55a-update-settings-deepgram-flux-stt.py +++ b/examples/foundational/55a-update-settings-deepgram-flux-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Deepgram Flux STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=DeepgramFluxSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=DeepgramFluxSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py b/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py index 8e45b5f2a..05c92e7e2 100644 --- a/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py +++ b/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py @@ -109,7 +109,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Deepgram SageMaker STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=DeepgramSageMakerSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=DeepgramSageMakerSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55a-update-settings-deepgram-stt.py b/examples/foundational/55a-update-settings-deepgram-stt.py index aea9475a8..39dde69e9 100644 --- a/examples/foundational/55a-update-settings-deepgram-stt.py +++ b/examples/foundational/55a-update-settings-deepgram-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Deepgram STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=DeepgramSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=DeepgramSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55b-update-settings-azure-stt.py b/examples/foundational/55b-update-settings-azure-stt.py index 7fd0d2ca4..96e4041d0 100644 --- a/examples/foundational/55b-update-settings-azure-stt.py +++ b/examples/foundational/55b-update-settings-azure-stt.py @@ -105,9 +105,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Azure STT settings: language=es") - await task.queue_frame( - STTUpdateSettingsFrame(update=AzureSTTSettings(language=Language.ES)) - ) + await task.queue_frame(STTUpdateSettingsFrame(delta=AzureSTTSettings(language=Language.ES))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55c-update-settings-google-stt.py b/examples/foundational/55c-update-settings-google-stt.py index dd33bfe75..dede5b173 100644 --- a/examples/foundational/55c-update-settings-google-stt.py +++ b/examples/foundational/55c-update-settings-google-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Google STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=GoogleSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=GoogleSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55d-update-settings-assemblyai-stt.py b/examples/foundational/55d-update-settings-assemblyai-stt.py index 6d6a2532e..d37c3ec7b 100644 --- a/examples/foundational/55d-update-settings-assemblyai-stt.py +++ b/examples/foundational/55d-update-settings-assemblyai-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating AssemblyAI STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=AssemblyAISTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=AssemblyAISTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55e-update-settings-gladia-stt.py b/examples/foundational/55e-update-settings-gladia-stt.py index a2c6f21fe..e5bd5486a 100644 --- a/examples/foundational/55e-update-settings-gladia-stt.py +++ b/examples/foundational/55e-update-settings-gladia-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Gladia STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=GladiaSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=GladiaSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py b/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py index 9aee04fbb..c3f0a6325 100644 --- a/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py +++ b/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py @@ -106,7 +106,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating ElevenLabs Realtime STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=ElevenLabsRealtimeSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=ElevenLabsRealtimeSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55g-update-settings-elevenlabs-stt.py b/examples/foundational/55g-update-settings-elevenlabs-stt.py index 33844935a..9435bc1ac 100644 --- a/examples/foundational/55g-update-settings-elevenlabs-stt.py +++ b/examples/foundational/55g-update-settings-elevenlabs-stt.py @@ -108,7 +108,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating ElevenLabs STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=ElevenLabsSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=ElevenLabsSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55h-update-settings-speechmatics-stt.py b/examples/foundational/55h-update-settings-speechmatics-stt.py index d041d69d2..c362d2f9f 100644 --- a/examples/foundational/55h-update-settings-speechmatics-stt.py +++ b/examples/foundational/55h-update-settings-speechmatics-stt.py @@ -110,13 +110,13 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Speechmatics STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=SpeechmaticsSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=SpeechmaticsSTTSettings(language=Language.ES)) ) await asyncio.sleep(10) logger.info("Updating Speechmatics STT settings: focus_speakers=['S1']") await task.queue_frame( - STTUpdateSettingsFrame(update=SpeechmaticsSTTSettings(focus_speakers=["S1"])) + STTUpdateSettingsFrame(delta=SpeechmaticsSTTSettings(focus_speakers=["S1"])) ) await asyncio.sleep(10) @@ -125,7 +125,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) await task.queue_frame( STTUpdateSettingsFrame( - update=SpeechmaticsSTTSettings( + delta=SpeechmaticsSTTSettings( speaker_active_format="{text}" ) ) diff --git a/examples/foundational/55i-update-settings-whisper-api-stt.py b/examples/foundational/55i-update-settings-whisper-api-stt.py index 1d5022674..741601c83 100644 --- a/examples/foundational/55i-update-settings-whisper-api-stt.py +++ b/examples/foundational/55i-update-settings-whisper-api-stt.py @@ -108,7 +108,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating OpenAI STT settings: language="es"') - await task.queue_frame(STTUpdateSettingsFrame(update=BaseWhisperSTTSettings(language="es"))) + await task.queue_frame(STTUpdateSettingsFrame(delta=BaseWhisperSTTSettings(language="es"))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55j-update-settings-sarvam-stt.py b/examples/foundational/55j-update-settings-sarvam-stt.py index e39c5cb5a..cab9656f8 100644 --- a/examples/foundational/55j-update-settings-sarvam-stt.py +++ b/examples/foundational/55j-update-settings-sarvam-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Sarvam STT settings: language=en-IN") await task.queue_frame( - STTUpdateSettingsFrame(update=SarvamSTTSettings(language=Language.EN_IN)) + STTUpdateSettingsFrame(delta=SarvamSTTSettings(language=Language.EN_IN)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55k-update-settings-soniox-stt.py b/examples/foundational/55k-update-settings-soniox-stt.py index 2cbcd44f4..85b5d2ba4 100644 --- a/examples/foundational/55k-update-settings-soniox-stt.py +++ b/examples/foundational/55k-update-settings-soniox-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Soniox STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=SonioxSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=SonioxSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55l-update-settings-aws-transcribe-stt.py b/examples/foundational/55l-update-settings-aws-transcribe-stt.py index 0f4c18981..3bfeb2faf 100644 --- a/examples/foundational/55l-update-settings-aws-transcribe-stt.py +++ b/examples/foundational/55l-update-settings-aws-transcribe-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating AWS Transcribe STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=AWSTranscribeSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=AWSTranscribeSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55m-update-settings-cartesia-stt.py b/examples/foundational/55m-update-settings-cartesia-stt.py index 6ba27a85e..a87847a5a 100644 --- a/examples/foundational/55m-update-settings-cartesia-stt.py +++ b/examples/foundational/55m-update-settings-cartesia-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Cartesia STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=CartesiaSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=CartesiaSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55n-update-settings-cartesia-http-tts.py b/examples/foundational/55n-update-settings-cartesia-http-tts.py index 27cee5b8f..02d3bca2a 100644 --- a/examples/foundational/55n-update-settings-cartesia-http-tts.py +++ b/examples/foundational/55n-update-settings-cartesia-http-tts.py @@ -107,7 +107,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating Cartesia HTTP TTS settings: speed increased to 1.5") await task.queue_frame( TTSUpdateSettingsFrame( - update=CartesiaTTSSettings(generation_config=GenerationConfig(speed=1.5)) + delta=CartesiaTTSSettings(generation_config=GenerationConfig(speed=1.5)) ) ) diff --git a/examples/foundational/55n-update-settings-cartesia-tts.py b/examples/foundational/55n-update-settings-cartesia-tts.py index 303c23a25..04e9d8fee 100644 --- a/examples/foundational/55n-update-settings-cartesia-tts.py +++ b/examples/foundational/55n-update-settings-cartesia-tts.py @@ -106,7 +106,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating Cartesia TTS settings: speed increased to 1.5") await task.queue_frame( TTSUpdateSettingsFrame( - update=CartesiaTTSSettings(generation_config=GenerationConfig(speed=1.5)) + delta=CartesiaTTSSettings(generation_config=GenerationConfig(speed=1.5)) ) ) diff --git a/examples/foundational/55o-update-settings-elevenlabs-http-tts.py b/examples/foundational/55o-update-settings-elevenlabs-http-tts.py index a67202702..2ca51730f 100644 --- a/examples/foundational/55o-update-settings-elevenlabs-http-tts.py +++ b/examples/foundational/55o-update-settings-elevenlabs-http-tts.py @@ -107,7 +107,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating ElevenLabs TTS settings: speed=0.7") await task.queue_frame( - TTSUpdateSettingsFrame(update=ElevenLabsHttpTTSSettings(speed=0.7)) + TTSUpdateSettingsFrame(delta=ElevenLabsHttpTTSSettings(speed=0.7)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55o-update-settings-elevenlabs-tts.py b/examples/foundational/55o-update-settings-elevenlabs-tts.py index 3fefa1ffb..ddbfd8b8f 100644 --- a/examples/foundational/55o-update-settings-elevenlabs-tts.py +++ b/examples/foundational/55o-update-settings-elevenlabs-tts.py @@ -102,13 +102,13 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating ElevenLabs TTS settings: speed=0.7") - await task.queue_frame(TTSUpdateSettingsFrame(update=ElevenLabsTTSSettings(speed=0.7))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=ElevenLabsTTSSettings(speed=0.7))) await asyncio.sleep(10) logger.info("Updating ElevenLabs TTS settings: switching to a different voice") await task.queue_frame( TTSUpdateSettingsFrame( - update=ElevenLabsTTSSettings(voice=os.getenv("ELEVENLABS_VOICE_ID_ALT")) + delta=ElevenLabsTTSSettings(voice=os.getenv("ELEVENLABS_VOICE_ID_ALT")) ) ) diff --git a/examples/foundational/55p-update-settings-openai-tts.py b/examples/foundational/55p-update-settings-openai-tts.py index 5aef081fc..fcc24fb76 100644 --- a/examples/foundational/55p-update-settings-openai-tts.py +++ b/examples/foundational/55p-update-settings-openai-tts.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating OpenAI TTS settings: speed=2.0") - await task.queue_frame(TTSUpdateSettingsFrame(update=OpenAITTSSettings(speed=2.0))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=OpenAITTSSettings(speed=2.0))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55q-update-settings-deepgram-http-tts.py b/examples/foundational/55q-update-settings-deepgram-http-tts.py index 64bbea587..d94bf631a 100644 --- a/examples/foundational/55q-update-settings-deepgram-http-tts.py +++ b/examples/foundational/55q-update-settings-deepgram-http-tts.py @@ -106,13 +106,13 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Deepgram TTS settings: voice="aura-2-aries-en"') await task.queue_frame( - TTSUpdateSettingsFrame(update=DeepgramTTSSettings(voice="aura-2-aries-en")) + TTSUpdateSettingsFrame(delta=DeepgramTTSSettings(voice="aura-2-aries-en")) ) await asyncio.sleep(10) logger.info('Updating Deepgram TTS settings: voice="aura-2-luna-en"') await task.queue_frame( - TTSUpdateSettingsFrame(update=DeepgramTTSSettings(voice="aura-2-luna-en")) + TTSUpdateSettingsFrame(delta=DeepgramTTSSettings(voice="aura-2-luna-en")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py b/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py index 35fb7cebe..85087d0d2 100644 --- a/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py +++ b/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py @@ -106,13 +106,13 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Deepgram SageMaker TTS settings: voice="aura-2-aries-en"') await task.queue_frame( - TTSUpdateSettingsFrame(update=DeepgramSageMakerTTSSettings(voice="aura-2-aries-en")) + TTSUpdateSettingsFrame(delta=DeepgramSageMakerTTSSettings(voice="aura-2-aries-en")) ) await asyncio.sleep(10) logger.info('Updating Deepgram SageMaker TTS settings: voice="aura-2-luna-en"') await task.queue_frame( - TTSUpdateSettingsFrame(update=DeepgramSageMakerTTSSettings(voice="aura-2-luna-en")) + TTSUpdateSettingsFrame(delta=DeepgramSageMakerTTSSettings(voice="aura-2-luna-en")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55q-update-settings-deepgram-tts.py b/examples/foundational/55q-update-settings-deepgram-tts.py index 9d94a50da..e205ffa73 100644 --- a/examples/foundational/55q-update-settings-deepgram-tts.py +++ b/examples/foundational/55q-update-settings-deepgram-tts.py @@ -99,13 +99,13 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Deepgram TTS settings: voice="aura-2-aries-en"') await task.queue_frame( - TTSUpdateSettingsFrame(update=DeepgramTTSSettings(voice="aura-2-aries-en")) + TTSUpdateSettingsFrame(delta=DeepgramTTSSettings(voice="aura-2-aries-en")) ) await asyncio.sleep(10) logger.info('Updating Deepgram TTS settings: voice="aura-2-luna-en"') await task.queue_frame( - TTSUpdateSettingsFrame(update=DeepgramTTSSettings(voice="aura-2-luna-en")) + TTSUpdateSettingsFrame(delta=DeepgramTTSSettings(voice="aura-2-luna-en")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55r-update-settings-azure-http-tts.py b/examples/foundational/55r-update-settings-azure-http-tts.py index 3132580ed..0e4df5e7c 100644 --- a/examples/foundational/55r-update-settings-azure-http-tts.py +++ b/examples/foundational/55r-update-settings-azure-http-tts.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Azure TTS settings: rate="0.7", style="sad"') await task.queue_frame( - TTSUpdateSettingsFrame(update=AzureTTSSettings(rate="0.7", style="sad")) + TTSUpdateSettingsFrame(delta=AzureTTSSettings(rate="0.7", style="sad")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55r-update-settings-azure-tts.py b/examples/foundational/55r-update-settings-azure-tts.py index d156eab43..a32dad5ed 100644 --- a/examples/foundational/55r-update-settings-azure-tts.py +++ b/examples/foundational/55r-update-settings-azure-tts.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Azure TTS settings: rate="0.7", style="sad"') await task.queue_frame( - TTSUpdateSettingsFrame(update=AzureTTSSettings(rate="0.7", style="sad")) + TTSUpdateSettingsFrame(delta=AzureTTSSettings(rate="0.7", style="sad")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55s-update-settings-google-http-tts.py b/examples/foundational/55s-update-settings-google-http-tts.py index 6c302411a..ae3070124 100644 --- a/examples/foundational/55s-update-settings-google-http-tts.py +++ b/examples/foundational/55s-update-settings-google-http-tts.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Google HTTP TTS settings: speaking_rate=1.4") await task.queue_frame( - TTSUpdateSettingsFrame(update=GoogleHttpTTSSettings(speaking_rate=1.4)) + TTSUpdateSettingsFrame(delta=GoogleHttpTTSSettings(speaking_rate=1.4)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55s-update-settings-google-stream-tts.py b/examples/foundational/55s-update-settings-google-stream-tts.py index 42e07c64b..1aba64254 100644 --- a/examples/foundational/55s-update-settings-google-stream-tts.py +++ b/examples/foundational/55s-update-settings-google-stream-tts.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Google Stream TTS settings: speaking_rate=1.4") await task.queue_frame( - TTSUpdateSettingsFrame(update=GoogleStreamTTSSettings(speaking_rate=1.4)) + TTSUpdateSettingsFrame(delta=GoogleStreamTTSSettings(speaking_rate=1.4)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55t-update-settings-playht-tts.py b/examples/foundational/55t-update-settings-playht-tts.py index ec468a81c..d79120d99 100644 --- a/examples/foundational/55t-update-settings-playht-tts.py +++ b/examples/foundational/55t-update-settings-playht-tts.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating PlayHT TTS settings: speed=1.3") - await task.queue_frame(TTSUpdateSettingsFrame(update=PlayHTTTSSettings(speed=1.3))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=PlayHTTTSSettings(speed=1.3))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55u-update-settings-rime-http-tts.py b/examples/foundational/55u-update-settings-rime-http-tts.py index 7b1c9b0fe..28e58ba08 100644 --- a/examples/foundational/55u-update-settings-rime-http-tts.py +++ b/examples/foundational/55u-update-settings-rime-http-tts.py @@ -104,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Rime TTS settings: voice=rex") - await task.queue_frame(TTSUpdateSettingsFrame(update=RimeTTSSettings(voice="rex"))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=RimeTTSSettings(voice="rex"))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55u-update-settings-rime-tts.py b/examples/foundational/55u-update-settings-rime-tts.py index 0704645f5..8992cb6db 100644 --- a/examples/foundational/55u-update-settings-rime-tts.py +++ b/examples/foundational/55u-update-settings-rime-tts.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Rime TTS settings: voice=bond") - await task.queue_frame(TTSUpdateSettingsFrame(update=RimeTTSSettings(voice="bond"))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=RimeTTSSettings(voice="bond"))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55v-update-settings-lmnt-tts.py b/examples/foundational/55v-update-settings-lmnt-tts.py index d98462e20..01bc15ddf 100644 --- a/examples/foundational/55v-update-settings-lmnt-tts.py +++ b/examples/foundational/55v-update-settings-lmnt-tts.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating LMNT TTS settings: voice="tyler"') - await task.queue_frame(TTSUpdateSettingsFrame(update=LmntTTSSettings(voice="tyler"))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=LmntTTSSettings(voice="tyler"))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55w-update-settings-fish-tts.py b/examples/foundational/55w-update-settings-fish-tts.py index 82722ec34..72a2160ba 100644 --- a/examples/foundational/55w-update-settings-fish-tts.py +++ b/examples/foundational/55w-update-settings-fish-tts.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Fish Audio TTS settings: prosody_speed=1.5") await task.queue_frame( - TTSUpdateSettingsFrame(update=FishAudioTTSSettings(prosody_speed=1.5)) + TTSUpdateSettingsFrame(delta=FishAudioTTSSettings(prosody_speed=1.5)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55x-update-settings-minimax-tts.py b/examples/foundational/55x-update-settings-minimax-tts.py index 306b8f2bd..fdb486415 100644 --- a/examples/foundational/55x-update-settings-minimax-tts.py +++ b/examples/foundational/55x-update-settings-minimax-tts.py @@ -105,7 +105,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating MiniMax TTS settings: speed=1.5, emotion="happy"') await task.queue_frame( - TTSUpdateSettingsFrame(update=MiniMaxTTSSettings(speed=1.5, emotion="happy")) + TTSUpdateSettingsFrame(delta=MiniMaxTTSSettings(speed=1.5, emotion="happy")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55y-update-settings-groq-tts.py b/examples/foundational/55y-update-settings-groq-tts.py index e6ce851c6..86dc1f98a 100644 --- a/examples/foundational/55y-update-settings-groq-tts.py +++ b/examples/foundational/55y-update-settings-groq-tts.py @@ -98,7 +98,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Groq TTS settings: speed=1.5") - await task.queue_frame(TTSUpdateSettingsFrame(update=GroqTTSSettings(speed=1.5))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=GroqTTSSettings(speed=1.5))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55z-update-settings-hume-tts.py b/examples/foundational/55z-update-settings-hume-tts.py index 427b99bab..493550469 100644 --- a/examples/foundational/55z-update-settings-hume-tts.py +++ b/examples/foundational/55z-update-settings-hume-tts.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info('Updating Hume TTS settings: speed=2.0, description="Speak with excitement"') await task.queue_frame( TTSUpdateSettingsFrame( - update=HumeTTSSettings(speed=2.0, description="Speak with excitement") + delta=HumeTTSSettings(speed=2.0, description="Speak with excitement") ) ) diff --git a/examples/foundational/55za-update-settings-neuphonic-http-tts.py b/examples/foundational/55za-update-settings-neuphonic-http-tts.py index 056b32349..6e1d18e4a 100644 --- a/examples/foundational/55za-update-settings-neuphonic-http-tts.py +++ b/examples/foundational/55za-update-settings-neuphonic-http-tts.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Neuphonic HTTP TTS settings: speed=1.4") - await task.queue_frame(TTSUpdateSettingsFrame(update=NeuphonicTTSSettings(speed=1.4))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=NeuphonicTTSSettings(speed=1.4))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55za-update-settings-neuphonic-tts.py b/examples/foundational/55za-update-settings-neuphonic-tts.py index 187594c7e..861167a20 100644 --- a/examples/foundational/55za-update-settings-neuphonic-tts.py +++ b/examples/foundational/55za-update-settings-neuphonic-tts.py @@ -98,7 +98,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Neuphonic TTS settings: speed=1.4") - await task.queue_frame(TTSUpdateSettingsFrame(update=NeuphonicTTSSettings(speed=1.4))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=NeuphonicTTSSettings(speed=1.4))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zb-update-settings-inworld-http-tts.py b/examples/foundational/55zb-update-settings-inworld-http-tts.py index 933a27013..99353b87f 100644 --- a/examples/foundational/55zb-update-settings-inworld-http-tts.py +++ b/examples/foundational/55zb-update-settings-inworld-http-tts.py @@ -103,9 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Inworld TTS settings: speaking_rate=1.5, temperature=0.8") await task.queue_frame( - TTSUpdateSettingsFrame( - update=InworldTTSSettings(speaking_rate=1.5, temperature=0.8) - ) + TTSUpdateSettingsFrame(delta=InworldTTSSettings(speaking_rate=1.5, temperature=0.8)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zb-update-settings-inworld-tts.py b/examples/foundational/55zb-update-settings-inworld-tts.py index f8a66bdd8..104001c15 100644 --- a/examples/foundational/55zb-update-settings-inworld-tts.py +++ b/examples/foundational/55zb-update-settings-inworld-tts.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Inworld TTS settings: speaking_rate=1.5, temperature=0.8") await task.queue_frame( - TTSUpdateSettingsFrame(update=InworldTTSSettings(speaking_rate=1.5, temperature=0.8)) + TTSUpdateSettingsFrame(delta=InworldTTSSettings(speaking_rate=1.5, temperature=0.8)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zc-update-settings-gemini-tts.py b/examples/foundational/55zc-update-settings-gemini-tts.py index 6af28e69f..21b678047 100644 --- a/examples/foundational/55zc-update-settings-gemini-tts.py +++ b/examples/foundational/55zc-update-settings-gemini-tts.py @@ -108,7 +108,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Gemini TTS settings: prompt="Speak slowly and dramatically"') await task.queue_frame( - TTSUpdateSettingsFrame(update=GeminiTTSSettings(prompt="Speak slowly and dramatically")) + TTSUpdateSettingsFrame(delta=GeminiTTSSettings(prompt="Speak slowly and dramatically")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zd-update-settings-aws-polly-tts.py b/examples/foundational/55zd-update-settings-aws-polly-tts.py index 3d9f72cf4..4392e7b6f 100644 --- a/examples/foundational/55zd-update-settings-aws-polly-tts.py +++ b/examples/foundational/55zd-update-settings-aws-polly-tts.py @@ -98,7 +98,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating AWS Polly TTS settings: rate="fast"') - await task.queue_frame(TTSUpdateSettingsFrame(update=AWSPollyTTSSettings(rate="fast"))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=AWSPollyTTSSettings(rate="fast"))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55ze-update-settings-sarvam-http-tts.py b/examples/foundational/55ze-update-settings-sarvam-http-tts.py index 0afce361a..7832a805a 100644 --- a/examples/foundational/55ze-update-settings-sarvam-http-tts.py +++ b/examples/foundational/55ze-update-settings-sarvam-http-tts.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Sarvam TTS settings: pace=1.5") - await task.queue_frame(TTSUpdateSettingsFrame(update=SarvamHttpTTSSettings(pace=1.5))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=SarvamHttpTTSSettings(pace=1.5))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55ze-update-settings-sarvam-tts.py b/examples/foundational/55ze-update-settings-sarvam-tts.py index 98408c4b8..e63c6046d 100644 --- a/examples/foundational/55ze-update-settings-sarvam-tts.py +++ b/examples/foundational/55ze-update-settings-sarvam-tts.py @@ -98,7 +98,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Sarvam TTS settings: pace=1.5") - await task.queue_frame(TTSUpdateSettingsFrame(update=SarvamTTSSettings(pace=1.5))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=SarvamTTSSettings(pace=1.5))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zf-update-settings-camb-tts.py b/examples/foundational/55zf-update-settings-camb-tts.py index 1fe758849..82cc4a638 100644 --- a/examples/foundational/55zf-update-settings-camb-tts.py +++ b/examples/foundational/55zf-update-settings-camb-tts.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Camb TTS settings: language -> Spanish") - await task.queue_frame(TTSUpdateSettingsFrame(update=CambTTSSettings(language=Language.ES))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=CambTTSSettings(language=Language.ES))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zg-update-settings-hathora-tts.py b/examples/foundational/55zg-update-settings-hathora-tts.py index 363ac7d85..9f6b6bd0a 100644 --- a/examples/foundational/55zg-update-settings-hathora-tts.py +++ b/examples/foundational/55zg-update-settings-hathora-tts.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Hathora TTS settings: speed=1.3") - await task.queue_frame(TTSUpdateSettingsFrame(update=HathoraTTSSettings(speed=1.3))) + await task.queue_frame(TTSUpdateSettingsFrame(delta=HathoraTTSSettings(speed=1.3))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zh-update-settings-resembleai-tts.py b/examples/foundational/55zh-update-settings-resembleai-tts.py index 39b745500..44688ee25 100644 --- a/examples/foundational/55zh-update-settings-resembleai-tts.py +++ b/examples/foundational/55zh-update-settings-resembleai-tts.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating ResembleAI TTS settings: voice (changed)") await task.queue_frame( TTSUpdateSettingsFrame( - update=ResembleAITTSSettings(voice=os.getenv("RESEMBLE_VOICE_UUID_ALT")) + delta=ResembleAITTSSettings(voice=os.getenv("RESEMBLE_VOICE_UUID_ALT")) ) ) diff --git a/examples/foundational/55zi-update-settings-azure-llm.py b/examples/foundational/55zi-update-settings-azure-llm.py index 94cb723e3..43161b103 100644 --- a/examples/foundational/55zi-update-settings-azure-llm.py +++ b/examples/foundational/55zi-update-settings-azure-llm.py @@ -106,7 +106,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Azure LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zi-update-settings-openai-llm.py b/examples/foundational/55zi-update-settings-openai-llm.py index a8c253bc2..d84259cc3 100644 --- a/examples/foundational/55zi-update-settings-openai-llm.py +++ b/examples/foundational/55zi-update-settings-openai-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating OpenAI LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zj-update-settings-anthropic-llm.py b/examples/foundational/55zj-update-settings-anthropic-llm.py index 4c8341a6a..354702880 100644 --- a/examples/foundational/55zj-update-settings-anthropic-llm.py +++ b/examples/foundational/55zj-update-settings-anthropic-llm.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Anthropic LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=AnthropicLLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=AnthropicLLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zk-update-settings-google-llm.py b/examples/foundational/55zk-update-settings-google-llm.py index 140c0fccb..cd03a34cb 100644 --- a/examples/foundational/55zk-update-settings-google-llm.py +++ b/examples/foundational/55zk-update-settings-google-llm.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Google LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=GoogleLLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=GoogleLLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zk-update-settings-google-vertex-llm.py b/examples/foundational/55zk-update-settings-google-vertex-llm.py index 41c0b8a37..3feba582f 100644 --- a/examples/foundational/55zk-update-settings-google-vertex-llm.py +++ b/examples/foundational/55zk-update-settings-google-vertex-llm.py @@ -106,7 +106,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Google Vertex LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=GoogleLLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=GoogleLLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zl-update-settings-azure-realtime.py b/examples/foundational/55zl-update-settings-azure-realtime.py index b8f049db0..247bde14b 100644 --- a/examples/foundational/55zl-update-settings-azure-realtime.py +++ b/examples/foundational/55zl-update-settings-azure-realtime.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating Azure Realtime LLM settings: output_modalities=['text']") await task.queue_frame( LLMUpdateSettingsFrame( - update=OpenAIRealtimeLLMSettings( + delta=OpenAIRealtimeLLMSettings( session_properties=events.SessionProperties(output_modalities=["text"]) ) ) @@ -112,7 +112,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating Azure Realtime LLM settings: output_modalities=['audio']") await task.queue_frame( LLMUpdateSettingsFrame( - update=OpenAIRealtimeLLMSettings( + delta=OpenAIRealtimeLLMSettings( session_properties=events.SessionProperties(output_modalities=["audio"]) ) ) diff --git a/examples/foundational/55zl-update-settings-openai-realtime.py b/examples/foundational/55zl-update-settings-openai-realtime.py index 9c18d528e..f5c4afa26 100644 --- a/examples/foundational/55zl-update-settings-openai-realtime.py +++ b/examples/foundational/55zl-update-settings-openai-realtime.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating OpenAI Realtime LLM settings: output_modalities=['text']") await task.queue_frame( LLMUpdateSettingsFrame( - update=OpenAIRealtimeLLMSettings( + delta=OpenAIRealtimeLLMSettings( session_properties=events.SessionProperties(output_modalities=["text"]) ) ) @@ -111,7 +111,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating OpenAI Realtime LLM settings: output_modalities=['audio']") await task.queue_frame( LLMUpdateSettingsFrame( - update=OpenAIRealtimeLLMSettings( + delta=OpenAIRealtimeLLMSettings( session_properties=events.SessionProperties(output_modalities=["audio"]) ) ) diff --git a/examples/foundational/55zm-update-settings-gemini-live-vertex.py b/examples/foundational/55zm-update-settings-gemini-live-vertex.py index 575fbe090..96bd7a1c6 100644 --- a/examples/foundational/55zm-update-settings-gemini-live-vertex.py +++ b/examples/foundational/55zm-update-settings-gemini-live-vertex.py @@ -91,9 +91,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Gemini Live Vertex LLM settings: temperature=0.1") - await task.queue_frame( - LLMUpdateSettingsFrame(update=GeminiLiveLLMSettings(temperature=0.1)) - ) + await task.queue_frame(LLMUpdateSettingsFrame(delta=GeminiLiveLLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zm-update-settings-gemini-live.py b/examples/foundational/55zm-update-settings-gemini-live.py index 8ad635fd5..a00343ac3 100644 --- a/examples/foundational/55zm-update-settings-gemini-live.py +++ b/examples/foundational/55zm-update-settings-gemini-live.py @@ -89,9 +89,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Gemini Live LLM settings: temperature=0.1") - await task.queue_frame( - LLMUpdateSettingsFrame(update=GeminiLiveLLMSettings(temperature=0.1)) - ) + await task.queue_frame(LLMUpdateSettingsFrame(delta=GeminiLiveLLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zn-update-settings-ultravox-realtime.py b/examples/foundational/55zn-update-settings-ultravox-realtime.py index 967d40741..5bcbded6b 100644 --- a/examples/foundational/55zn-update-settings-ultravox-realtime.py +++ b/examples/foundational/55zn-update-settings-ultravox-realtime.py @@ -112,13 +112,13 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Ultravox Realtime LLM settings: output_medium=text") await task.queue_frame( - LLMUpdateSettingsFrame(update=UltravoxRealtimeLLMSettings(output_medium="text")) + LLMUpdateSettingsFrame(delta=UltravoxRealtimeLLMSettings(output_medium="text")) ) await asyncio.sleep(10) logger.info("Updating Ultravox Realtime LLM settings: output_medium=voice") await task.queue_frame( - LLMUpdateSettingsFrame(update=UltravoxRealtimeLLMSettings(output_medium="voice")) + LLMUpdateSettingsFrame(delta=UltravoxRealtimeLLMSettings(output_medium="voice")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zo-update-settings-grok-realtime.py b/examples/foundational/55zo-update-settings-grok-realtime.py index 7d7370f7b..9444f126a 100644 --- a/examples/foundational/55zo-update-settings-grok-realtime.py +++ b/examples/foundational/55zo-update-settings-grok-realtime.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info("Updating Grok Realtime LLM settings: voice='Rex'") await task.queue_frame( LLMUpdateSettingsFrame( - update=GrokRealtimeLLMSettings( + delta=GrokRealtimeLLMSettings( session_properties=events.SessionProperties(voice="Rex") ) ) diff --git a/examples/foundational/55zp-update-settings-aws-bedrock-llm.py b/examples/foundational/55zp-update-settings-aws-bedrock-llm.py index 1c2781e72..3d3ee8fb5 100644 --- a/examples/foundational/55zp-update-settings-aws-bedrock-llm.py +++ b/examples/foundational/55zp-update-settings-aws-bedrock-llm.py @@ -105,9 +105,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating AWS Bedrock LLM settings: temperature=0.1") - await task.queue_frame( - LLMUpdateSettingsFrame(update=AWSBedrockLLMSettings(temperature=0.1)) - ) + await task.queue_frame(LLMUpdateSettingsFrame(delta=AWSBedrockLLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zq-update-settings-fal-stt.py b/examples/foundational/55zq-update-settings-fal-stt.py index 9792961f2..c0f0a134a 100644 --- a/examples/foundational/55zq-update-settings-fal-stt.py +++ b/examples/foundational/55zq-update-settings-fal-stt.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Fal STT settings: task="translate"') - await task.queue_frame(STTUpdateSettingsFrame(update=FalSTTSettings(task="translate"))) + await task.queue_frame(STTUpdateSettingsFrame(delta=FalSTTSettings(task="translate"))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zr-update-settings-gradium-stt.py b/examples/foundational/55zr-update-settings-gradium-stt.py index 6a1a25c3c..636d27bd8 100644 --- a/examples/foundational/55zr-update-settings-gradium-stt.py +++ b/examples/foundational/55zr-update-settings-gradium-stt.py @@ -104,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Gradium STT settings: delay_in_frames=5") - await task.queue_frame(STTUpdateSettingsFrame(update=GradiumSTTSettings(delay_in_frames=5))) + await task.queue_frame(STTUpdateSettingsFrame(delta=GradiumSTTSettings(delay_in_frames=5))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zs-update-settings-hathora-stt.py b/examples/foundational/55zs-update-settings-hathora-stt.py index f3aca9c89..db5ed4d2a 100644 --- a/examples/foundational/55zs-update-settings-hathora-stt.py +++ b/examples/foundational/55zs-update-settings-hathora-stt.py @@ -104,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Hathora STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=HathoraSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=HathoraSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py b/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py index 624da149e..60a042c5f 100644 --- a/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py +++ b/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating NVIDIA Segmented STT settings: profanity_filter=True") await task.queue_frame( - STTUpdateSettingsFrame(update=NvidiaSegmentedSTTSettings(profanity_filter=True)) + STTUpdateSettingsFrame(delta=NvidiaSegmentedSTTSettings(profanity_filter=True)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zt-update-settings-nvidia-stt.py b/examples/foundational/55zt-update-settings-nvidia-stt.py index 0e7b6a74a..415f10b12 100644 --- a/examples/foundational/55zt-update-settings-nvidia-stt.py +++ b/examples/foundational/55zt-update-settings-nvidia-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating NVIDIA STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=NvidiaSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=NvidiaSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zu-update-settings-openai-realtime-stt.py b/examples/foundational/55zu-update-settings-openai-realtime-stt.py index 1f1592df7..2bcd35f52 100644 --- a/examples/foundational/55zu-update-settings-openai-realtime-stt.py +++ b/examples/foundational/55zu-update-settings-openai-realtime-stt.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating OpenAI Realtime STT settings: language=es") await task.queue_frame( - STTUpdateSettingsFrame(update=OpenAIRealtimeSTTSettings(language=Language.ES)) + STTUpdateSettingsFrame(delta=OpenAIRealtimeSTTSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zv-update-settings-asyncai-http-tts.py b/examples/foundational/55zv-update-settings-asyncai-http-tts.py index 206a80eed..9688f1bac 100644 --- a/examples/foundational/55zv-update-settings-asyncai-http-tts.py +++ b/examples/foundational/55zv-update-settings-asyncai-http-tts.py @@ -108,7 +108,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating AsyncAI HTTP TTS settings: language=es") await task.queue_frame( - TTSUpdateSettingsFrame(update=AsyncAITTSSettings(language=Language.ES)) + TTSUpdateSettingsFrame(delta=AsyncAITTSSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zv-update-settings-asyncai-tts.py b/examples/foundational/55zv-update-settings-asyncai-tts.py index f910e5fe3..fe096b4be 100644 --- a/examples/foundational/55zv-update-settings-asyncai-tts.py +++ b/examples/foundational/55zv-update-settings-asyncai-tts.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating AsyncAI TTS settings: language=es") await task.queue_frame( - TTSUpdateSettingsFrame(update=AsyncAITTSSettings(language=Language.ES)) + TTSUpdateSettingsFrame(delta=AsyncAITTSSettings(language=Language.ES)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zw-update-settings-gradium-tts.py b/examples/foundational/55zw-update-settings-gradium-tts.py index 39090d5fa..d1069bfa4 100644 --- a/examples/foundational/55zw-update-settings-gradium-tts.py +++ b/examples/foundational/55zw-update-settings-gradium-tts.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Gradium TTS settings: voice="LFZvm12tW_z0xfGo"') await task.queue_frame( - TTSUpdateSettingsFrame(update=GradiumTTSSettings(voice="LFZvm12tW_z0xfGo")) + TTSUpdateSettingsFrame(delta=GradiumTTSSettings(voice="LFZvm12tW_z0xfGo")) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zx-update-settings-cerebras-llm.py b/examples/foundational/55zx-update-settings-cerebras-llm.py index 72aa8518d..6123487a3 100644 --- a/examples/foundational/55zx-update-settings-cerebras-llm.py +++ b/examples/foundational/55zx-update-settings-cerebras-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Cerebras LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zy-update-settings-deepseek-llm.py b/examples/foundational/55zy-update-settings-deepseek-llm.py index de4e4149e..60cbab30b 100644 --- a/examples/foundational/55zy-update-settings-deepseek-llm.py +++ b/examples/foundational/55zy-update-settings-deepseek-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating DeepSeek LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zz-update-settings-fireworks-llm.py b/examples/foundational/55zz-update-settings-fireworks-llm.py index d864cacb2..97554ae19 100644 --- a/examples/foundational/55zz-update-settings-fireworks-llm.py +++ b/examples/foundational/55zz-update-settings-fireworks-llm.py @@ -105,7 +105,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Fireworks LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zza-update-settings-grok-llm.py b/examples/foundational/55zza-update-settings-grok-llm.py index dbf07f21d..8ce081e66 100644 --- a/examples/foundational/55zza-update-settings-grok-llm.py +++ b/examples/foundational/55zza-update-settings-grok-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Grok LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzb-update-settings-groq-llm.py b/examples/foundational/55zzb-update-settings-groq-llm.py index 8244f611a..afde4499d 100644 --- a/examples/foundational/55zzb-update-settings-groq-llm.py +++ b/examples/foundational/55zzb-update-settings-groq-llm.py @@ -104,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Groq LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzc-update-settings-mistral-llm.py b/examples/foundational/55zzc-update-settings-mistral-llm.py index 642eda3c5..7eba98e97 100644 --- a/examples/foundational/55zzc-update-settings-mistral-llm.py +++ b/examples/foundational/55zzc-update-settings-mistral-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Mistral LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzd-update-settings-nvidia-llm.py b/examples/foundational/55zzd-update-settings-nvidia-llm.py index 5ffa0ff23..ee57a3a24 100644 --- a/examples/foundational/55zzd-update-settings-nvidia-llm.py +++ b/examples/foundational/55zzd-update-settings-nvidia-llm.py @@ -104,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating NVIDIA LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zze-update-settings-ollama-llm.py b/examples/foundational/55zze-update-settings-ollama-llm.py index ca3714943..e22719ec1 100644 --- a/examples/foundational/55zze-update-settings-ollama-llm.py +++ b/examples/foundational/55zze-update-settings-ollama-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating OLLama LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzf-update-settings-openrouter-llm.py b/examples/foundational/55zzf-update-settings-openrouter-llm.py index 90606a572..fc3732192 100644 --- a/examples/foundational/55zzf-update-settings-openrouter-llm.py +++ b/examples/foundational/55zzf-update-settings-openrouter-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating OpenRouter LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzg-update-settings-perplexity-llm.py b/examples/foundational/55zzg-update-settings-perplexity-llm.py index 771b1c794..f55975685 100644 --- a/examples/foundational/55zzg-update-settings-perplexity-llm.py +++ b/examples/foundational/55zzg-update-settings-perplexity-llm.py @@ -101,7 +101,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Perplexity LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzh-update-settings-qwen-llm.py b/examples/foundational/55zzh-update-settings-qwen-llm.py index 81ace2117..f31dc05a5 100644 --- a/examples/foundational/55zzh-update-settings-qwen-llm.py +++ b/examples/foundational/55zzh-update-settings-qwen-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Qwen LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzi-update-settings-sambanova-llm.py b/examples/foundational/55zzi-update-settings-sambanova-llm.py index 82382a6bd..96122cc03 100644 --- a/examples/foundational/55zzi-update-settings-sambanova-llm.py +++ b/examples/foundational/55zzi-update-settings-sambanova-llm.py @@ -102,7 +102,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating SambaNova LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzj-update-settings-together-llm.py b/examples/foundational/55zzj-update-settings-together-llm.py index 1f0a0557f..710ef894a 100644 --- a/examples/foundational/55zzj-update-settings-together-llm.py +++ b/examples/foundational/55zzj-update-settings-together-llm.py @@ -105,7 +105,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating Together LLM settings: temperature=0.1") - await task.queue_frame(LLMUpdateSettingsFrame(update=OpenAILLMSettings(temperature=0.1))) + await task.queue_frame(LLMUpdateSettingsFrame(delta=OpenAILLMSettings(temperature=0.1))) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py b/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py index 1faafdbac..301270797 100644 --- a/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py +++ b/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info("Updating AWS Nova Sonic LLM settings: temperature=0.1") await task.queue_frame( - LLMUpdateSettingsFrame(update=AWSNovaSonicLLMSettings(temperature=0.1)) + LLMUpdateSettingsFrame(delta=AWSNovaSonicLLMSettings(temperature=0.1)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zzl-update-settings-nvidia-tts.py b/examples/foundational/55zzl-update-settings-nvidia-tts.py index b92651496..a8bd50dcd 100644 --- a/examples/foundational/55zzl-update-settings-nvidia-tts.py +++ b/examples/foundational/55zzl-update-settings-nvidia-tts.py @@ -100,7 +100,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating NVIDIA TTS settings: language="ES_US"') await task.queue_frame( - TTSUpdateSettingsFrame(update=NvidiaTTSSettings(language=Language.ES_US)) + TTSUpdateSettingsFrame(delta=NvidiaTTSSettings(language=Language.ES_US)) ) @transport.event_handler("on_client_disconnected") diff --git a/examples/foundational/55zzm-update-settings-speechmatics-tts.py b/examples/foundational/55zzm-update-settings-speechmatics-tts.py index 36b66fe53..39ed792dd 100644 --- a/examples/foundational/55zzm-update-settings-speechmatics-tts.py +++ b/examples/foundational/55zzm-update-settings-speechmatics-tts.py @@ -104,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await asyncio.sleep(10) logger.info('Updating Speechmatics TTS settings: voice="theo"') await task.queue_frame( - TTSUpdateSettingsFrame(update=SpeechmaticsTTSSettings(voice="theo")) + TTSUpdateSettingsFrame(delta=SpeechmaticsTTSSettings(voice="theo")) ) @transport.event_handler("on_client_disconnected") diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 79aedb771..0d6b3f18a 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -2121,21 +2121,21 @@ class TTSStoppedFrame(ControlFrame): class ServiceUpdateSettingsFrame(ControlFrame): """Base frame for updating service settings. - Supports both a ``settings`` dict (for backward compatibility) and an - ``update`` object. When both are provided, ``update`` takes precedence. + Supports both a ``settings`` dict (for backward compatibility) and a + ``delta`` object. When both are provided, ``delta`` takes precedence. Parameters: settings: Dictionary of setting name to value mappings. .. deprecated:: 0.0.103 - Use ``update`` with a typed settings object instead. + Use ``delta`` with a typed settings object instead. - update: :class:`~pipecat.services.settings.ServiceSettings` object - describing the delta to apply. + delta: :class:`~pipecat.services.settings.ServiceSettings` delta-mode + object describing the fields to change. """ settings: Mapping[str, Any] = field(default_factory=dict) - update: Optional["ServiceSettings"] = None + delta: Optional["ServiceSettings"] = None @dataclass diff --git a/src/pipecat/services/ai_service.py b/src/pipecat/services/ai_service.py index 8b32d7222..f092c2b49 100644 --- a/src/pipecat/services/ai_service.py +++ b/src/pipecat/services/ai_service.py @@ -42,7 +42,7 @@ class AIService(FrameProcessor): **kwargs: Additional arguments passed to the parent FrameProcessor. """ super().__init__(**kwargs) - self._settings: ServiceSettings = ServiceSettings(model="") + self._settings: ServiceSettings = ServiceSettings() # Here in case subclass doesn't implement more specific settings (hopefully shouldn't happen) self._session_properties: Dict[str, Any] = {} self._tracing_enabled: bool = False self._tracing_context = None @@ -73,6 +73,7 @@ class AIService(FrameProcessor): Args: frame: The start frame containing initialization parameters. """ + self._settings.validate_complete() self._tracing_enabled = frame.enable_tracing self._tracing_context = frame.tracing_context @@ -98,10 +99,10 @@ class AIService(FrameProcessor): """ pass - async def _update_settings(self, update: ServiceSettings) -> Dict[str, Any]: - """Apply a settings update and return the changed fields. + async def _update_settings(self, delta: ServiceSettings) -> Dict[str, Any]: + """Apply a settings delta and return the changed fields. - The update is applied to ``_settings`` and a dict mapping each changed + The delta is applied to ``_settings`` and a dict mapping each changed field name to its **pre-update** value is returned. The ``model`` field is handled specially: when it changes, ``set_model_name`` is called. @@ -110,12 +111,12 @@ class AIService(FrameProcessor): to react to specific changed fields (e.g. reconnect on voice change). Args: - update: A settings delta. + delta: A delta-mode settings object. Returns: Dict mapping changed field names to their previous values. """ - changed = self._settings.apply_update(update) + changed = self._settings.apply_update(delta) if "model" in changed: self._sync_model_name_to_metrics() diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index e79792bb0..047159515 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -254,6 +254,11 @@ class AnthropicLLMService(LLMService): temperature=params.temperature, top_k=params.top_k, top_p=params.top_p, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, thinking=params.thinking, extra=params.extra if isinstance(params.extra, dict) else {}, ) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 6a33b6a20..44ae123b7 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -116,6 +116,7 @@ class AssemblyAISTTService(WebsocketSTTService): self._api_key = api_key self._settings = AssemblyAISTTSettings( + model=None, language=language, connection_params=connection_params, ) @@ -186,18 +187,18 @@ class AssemblyAISTTService(WebsocketSTTService): """ return True - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. Args: - update: A :class:`STTSettings` (or ``AssemblyAISTTSettings``) delta. + delta: A :class:`STTSettings` (or ``AssemblyAISTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/asyncai/tts.py b/src/pipecat/services/asyncai/tts.py index 289c327b0..d55062c4f 100644 --- a/src/pipecat/services/asyncai/tts.py +++ b/src/pipecat/services/asyncai/tts.py @@ -176,12 +176,12 @@ class AsyncAITTSService(AudioContextTTSService): self._receive_task = None self._keepalive_task = None - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index de994463b..34e869c69 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -827,6 +827,12 @@ class AWSBedrockLLMService(LLMService): max_tokens=params.max_tokens, temperature=params.temperature, top_p=params.top_p, + top_k=None, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, latency=params.latency, additional_model_request_fields=params.additional_model_request_fields if isinstance(params.additional_model_request_fields, dict) diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index eba5cc21b..e51a1842c 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -267,6 +267,12 @@ class AWSNovaSonicLLMService(LLMService): temperature=params.temperature, max_tokens=params.max_tokens, top_p=params.top_p, + top_k=None, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, endpointing_sensitivity=params.endpointing_sensitivity, ) self._sync_model_name_to_metrics() @@ -338,12 +344,12 @@ class AWSNovaSonicLLMService(LLMService): # settings # - async def _update_settings(self, update: AWSNovaSonicLLMSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: AWSNovaSonicLLMSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/aws/stt.py b/src/pipecat/services/aws/stt.py index c53e3648c..1d8ae84f5 100644 --- a/src/pipecat/services/aws/stt.py +++ b/src/pipecat/services/aws/stt.py @@ -148,12 +148,12 @@ class AWSTranscribeSTTService(WebsocketSTTService): } return encoding_map.get(encoding, encoding) - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/aws/tts.py b/src/pipecat/services/aws/tts.py index 4e071465d..8b06ad7e9 100644 --- a/src/pipecat/services/aws/tts.py +++ b/src/pipecat/services/aws/tts.py @@ -209,6 +209,7 @@ class AWSPollyTTSService(TTSService): self._aws_session = aioboto3.Session() self._settings = AWSPollyTTSSettings( + model=None, voice=voice_id, engine=params.engine, language=self.language_to_service_language(params.language) diff --git a/src/pipecat/services/azure/stt.py b/src/pipecat/services/azure/stt.py index d161b3829..096f236a6 100644 --- a/src/pipecat/services/azure/stt.py +++ b/src/pipecat/services/azure/stt.py @@ -110,6 +110,7 @@ class AzureSTTService(STTService): self._audio_stream = None self._speech_recognizer = None self._settings = AzureSTTSettings( + model=None, region=region, language=language_to_azure_language(language), sample_rate=sample_rate, @@ -134,12 +135,12 @@ class AzureSTTService(STTService): """ return language_to_azure_language(language) - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active recognizer. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) # TODO: someday we could reconnect here to apply updated settings. # Code might look something like the below: diff --git a/src/pipecat/services/azure/tts.py b/src/pipecat/services/azure/tts.py index 7672a846c..3a176055d 100644 --- a/src/pipecat/services/azure/tts.py +++ b/src/pipecat/services/azure/tts.py @@ -156,6 +156,7 @@ class AzureBaseTTSService: params = params or AzureBaseTTSService.InputParams() self._settings = AzureTTSSettings( + model=None, emphasis=params.emphasis, language=self.language_to_service_language(params.language) if params.language diff --git a/src/pipecat/services/cartesia/stt.py b/src/pipecat/services/cartesia/stt.py index 6a30f9a53..20ff04963 100644 --- a/src/pipecat/services/cartesia/stt.py +++ b/src/pipecat/services/cartesia/stt.py @@ -294,16 +294,16 @@ class CartesiaSTTService(WebsocketSTTService): await self._disconnect_websocket() - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta. Args: - update: A :class:`STTSettings` (or ``CartesiaSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``CartesiaSTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) # TODO: someday we could reconnect here to apply updated settings. # Code might look something like the below: diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index 3f6fe2c21..f31cc2421 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -28,7 +28,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import AudioContextTTSService, TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.text.base_text_aggregator import BaseTextAggregator @@ -443,7 +443,7 @@ class CartesiaTTSService(AudioContextTTSService): voice_config["mode"] = "id" voice_config["id"] = self._settings.voice - if is_given(self._settings.emotion) and self._settings.emotion: + if self._settings.emotion: with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( @@ -469,18 +469,18 @@ class CartesiaTTSService(AudioContextTTSService): "use_original_timestamps": False if self._settings.model == "sonic" else True, } - if is_given(self._settings.language) and self._settings.language: + if self._settings.language: msg["language"] = self._settings.language - if is_given(self._settings.speed) and self._settings.speed: + if self._settings.speed: msg["speed"] = self._settings.speed - if is_given(self._settings.generation_config) and self._settings.generation_config: + if self._settings.generation_config: msg["generation_config"] = self._settings.generation_config.model_dump( exclude_none=True ) - if is_given(self._settings.pronunciation_dict_id) and self._settings.pronunciation_dict_id: + if self._settings.pronunciation_dict_id: msg["pronunciation_dict_id"] = self._settings.pronunciation_dict_id return json.dumps(msg) @@ -811,7 +811,7 @@ class CartesiaHttpTTSService(TTSService): try: voice_config = {"mode": "id", "id": self._settings.voice} - if is_given(self._settings.emotion) and self._settings.emotion: + if self._settings.emotion: with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( @@ -836,21 +836,18 @@ class CartesiaHttpTTSService(TTSService): "output_format": output_format, } - if is_given(self._settings.language) and self._settings.language: + if self._settings.language: payload["language"] = self._settings.language - if is_given(self._settings.speed) and self._settings.speed: + if self._settings.speed: payload["speed"] = self._settings.speed - if is_given(self._settings.generation_config) and self._settings.generation_config: + if self._settings.generation_config: payload["generation_config"] = self._settings.generation_config.model_dump( exclude_none=True ) - if ( - is_given(self._settings.pronunciation_dict_id) - and self._settings.pronunciation_dict_id - ): + if self._settings.pronunciation_dict_id: payload["pronunciation_dict_id"] = self._settings.pronunciation_dict_id yield TTSStartedFrame(context_id=context_id) diff --git a/src/pipecat/services/deepgram/flux/stt.py b/src/pipecat/services/deepgram/flux/stt.py index 410d272da..f0018d5c8 100644 --- a/src/pipecat/services/deepgram/flux/stt.py +++ b/src/pipecat/services/deepgram/flux/stt.py @@ -384,12 +384,12 @@ class DeepgramFluxSTTService(WebsocketSTTService): """ return True - async def _update_settings(self, update: DeepgramFluxSTTSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: DeepgramFluxSTTSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index 28b8a211e..0792ea3c9 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -206,25 +206,25 @@ class DeepgramSTTService(STTService): """ return True - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update, keeping ``live_options`` in sync. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta, keeping ``live_options`` in sync. Top-level ``model`` and ``language`` are the source of truth. When - they are given in *update* their values are propagated into + they are given in *delta* their values are propagated into ``live_options``. When only ``live_options`` is given, its ``model`` and ``language`` are propagated *up* to the top-level fields. Any change triggers a WebSocket reconnect. """ # Determine which top-level fields are explicitly provided. - model_given = isinstance(update, DeepgramSTTSettings) and is_given( - getattr(update, "model", NOT_GIVEN) + model_given = isinstance(delta, DeepgramSTTSettings) and is_given( + getattr(delta, "model", NOT_GIVEN) ) - language_given = isinstance(update, DeepgramSTTSettings) and is_given( - getattr(update, "language", NOT_GIVEN) + language_given = isinstance(delta, DeepgramSTTSettings) and is_given( + getattr(delta, "language", NOT_GIVEN) ) - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/deepgram/stt_sagemaker.py b/src/pipecat/services/deepgram/stt_sagemaker.py index 64bb2ba8f..3820f8d84 100644 --- a/src/pipecat/services/deepgram/stt_sagemaker.py +++ b/src/pipecat/services/deepgram/stt_sagemaker.py @@ -163,25 +163,25 @@ class DeepgramSageMakerSTTService(STTService): """ return True - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update, keeping ``live_options`` in sync. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta, keeping ``live_options`` in sync. Top-level ``model`` and ``language`` are the source of truth. When - they are given in *update* their values are propagated into + they are given in *delta* their values are propagated into ``live_options``. When only ``live_options`` is given, its ``model`` and ``language`` are propagated *up* to the top-level fields. Any change triggers a reconnect. """ # Determine which top-level fields are explicitly provided. - model_given = isinstance(update, DeepgramSageMakerSTTSettings) and is_given( - getattr(update, "model", NOT_GIVEN) + model_given = isinstance(delta, DeepgramSageMakerSTTSettings) and is_given( + getattr(delta, "model", NOT_GIVEN) ) - language_given = isinstance(update, DeepgramSageMakerSTTSettings) and is_given( - getattr(update, "language", NOT_GIVEN) + language_given = isinstance(delta, DeepgramSageMakerSTTSettings) and is_given( + getattr(delta, "language", NOT_GIVEN) ) - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/deepgram/tts.py b/src/pipecat/services/deepgram/tts.py index 9216d7aa7..b3973bba2 100644 --- a/src/pipecat/services/deepgram/tts.py +++ b/src/pipecat/services/deepgram/tts.py @@ -109,6 +109,7 @@ class DeepgramTTSService(WebsocketTTSService): self._settings = DeepgramTTSSettings( model=voice, voice=voice, + language=None, encoding=encoding, ) self._sync_model_name_to_metrics() @@ -183,16 +184,16 @@ class DeepgramTTSService(WebsocketTTSService): await self._disconnect_websocket() - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta. Args: - update: A :class:`TTSSettings` (or ``DeepgramTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``DeepgramTTSSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) # Deepgram uses voice as the model, so keep them in sync for metrics if "voice" in changed: @@ -401,6 +402,7 @@ class DeepgramHttpTTSService(TTSService): self._settings = DeepgramTTSSettings( model=voice, voice=voice, + language=None, encoding=encoding, ) self._sync_model_name_to_metrics() diff --git a/src/pipecat/services/deepgram/tts_sagemaker.py b/src/pipecat/services/deepgram/tts_sagemaker.py index 8447c96f0..798a62bf8 100644 --- a/src/pipecat/services/deepgram/tts_sagemaker.py +++ b/src/pipecat/services/deepgram/tts_sagemaker.py @@ -107,6 +107,7 @@ class DeepgramSageMakerTTSService(TTSService): self._settings = DeepgramSageMakerTTSSettings( model=voice, voice=voice, + language=None, encoding=encoding, ) self._sync_model_name_to_metrics() @@ -220,13 +221,13 @@ class DeepgramSageMakerTTSService(TTSService): logger.debug("Disconnected from Deepgram TTS on SageMaker") await self._call_event_handler("on_disconnected") - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and reconnect if necessary. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and reconnect if necessary. Since all settings are part of the SageMaker session query string, any setting change requires reconnecting to apply the new values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/elevenlabs/stt.py b/src/pipecat/services/elevenlabs/stt.py index 5ff91f597..7a821304d 100644 --- a/src/pipecat/services/elevenlabs/stt.py +++ b/src/pipecat/services/elevenlabs/stt.py @@ -302,19 +302,19 @@ class ElevenLabsSTTService(SegmentedSTTService): """ return language_to_elevenlabs_language(language) - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta. Converts language to ElevenLabs format before applying and keeps ``_model_id`` in sync with the model setting. Args: - update: A :class:`STTSettings` (or ``ElevenLabsSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``ElevenLabsSTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if "model" in changed: self._model_id = self._settings.model @@ -541,19 +541,19 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): """ return True - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update and reconnect if anything changed. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta and reconnect if anything changed. Converts language to ElevenLabs format before applying and keeps ``_model_id`` in sync. Args: - update: A :class:`STTSettings` (or ``ElevenLabsRealtimeSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``ElevenLabsRealtimeSTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 8d51e9dde..20d46481d 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -44,7 +44,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import ( AudioContextTTSService, TTSService, @@ -166,7 +166,7 @@ def build_elevenlabs_voice_settings( val = ( getattr(settings, key, None) if isinstance(settings, TTSSettings) else settings.get(key) ) - if val is not None and is_given(val): + if val is not None: voice_settings[key] = val return voice_settings or None @@ -470,24 +470,24 @@ class ElevenLabsTTSService(AudioContextTTSService): voice_settings = {} for key in voice_setting_keys: val = getattr(ts, key, None) - if val is not None and is_given(val): + if val is not None: voice_settings[key] = val return voice_settings or None - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update, reconnecting as needed. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta, reconnecting as needed. Uses the declarative ``URL_FIELDS`` and ``VOICE_SETTINGS_FIELDS`` sets on :class:`ElevenLabsTTSSettings` to decide whether to reconnect the WebSocket or close the current audio context. Args: - update: A :class:`TTSSettings` (or ``ElevenLabsTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``ElevenLabsTTSSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed @@ -967,16 +967,16 @@ class ElevenLabsHttpTTSService(TTSService): def _set_voice_settings(self): return build_elevenlabs_voice_settings(self._settings) - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and rebuild voice settings. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and rebuild voice settings. Args: - update: A :class:`TTSSettings` (or ``ElevenLabsHttpTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``ElevenLabsHttpTTSSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if changed: self._voice_settings = self._set_voice_settings() return changed @@ -1116,10 +1116,7 @@ class ElevenLabsHttpTTSService(TTSService): locator.model_dump() for locator in self._pronunciation_dictionary_locators ] - if ( - is_given(self._settings.apply_text_normalization) - and self._settings.apply_text_normalization is not None - ): + if self._settings.apply_text_normalization is not None: payload["apply_text_normalization"] = self._settings.apply_text_normalization language = self._settings.language @@ -1140,10 +1137,7 @@ class ElevenLabsHttpTTSService(TTSService): params = { "output_format": self._output_format, } - if ( - is_given(self._settings.optimize_streaming_latency) - and self._settings.optimize_streaming_latency is not None - ): + if self._settings.optimize_streaming_latency is not None: params["optimize_streaming_latency"] = self._settings.optimize_streaming_latency try: diff --git a/src/pipecat/services/fal/stt.py b/src/pipecat/services/fal/stt.py index 91b5a25c8..923c3c2ea 100644 --- a/src/pipecat/services/fal/stt.py +++ b/src/pipecat/services/fal/stt.py @@ -224,6 +224,7 @@ class FalSTTService(SegmentedSTTService): self._fal_client = fal_client.AsyncClient(key=api_key or os.getenv("FAL_KEY")) self._settings = FalSTTSettings( + model=None, language=self.language_to_service_language(params.language) if params.language else "en", diff --git a/src/pipecat/services/fish/tts.py b/src/pipecat/services/fish/tts.py index 0d84ea7ab..1927b6cac 100644 --- a/src/pipecat/services/fish/tts.py +++ b/src/pipecat/services/fish/tts.py @@ -196,18 +196,18 @@ class FishAudioTTSService(InterruptibleTTSService): """ return True - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and reconnect if needed. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and reconnect if needed. Any change to voice or model triggers a WebSocket reconnect. Args: - update: A :class:`TTSSettings` (or ``FishAudioTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``FishAudioTTSSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if changed: await self._disconnect() diff --git a/src/pipecat/services/gladia/stt.py b/src/pipecat/services/gladia/stt.py index 8d968e00f..d0a6f5a84 100644 --- a/src/pipecat/services/gladia/stt.py +++ b/src/pipecat/services/gladia/stt.py @@ -280,7 +280,7 @@ class GladiaSTTService(WebsocketSTTService): self._region = region self._url = url self._receive_task = None - self._settings = GladiaSTTSettings(model=model, input_params=params) + self._settings = GladiaSTTSettings(model=model, language=None, input_params=params) self._sync_model_name_to_metrics() # Session management @@ -379,18 +379,18 @@ class GladiaSTTService(WebsocketSTTService): await super().start(frame) await self._connect() - async def _update_settings(self, update: GladiaSTTSettings) -> dict[str, Any]: - """Apply settings update. + async def _update_settings(self, delta: GladiaSTTSettings) -> dict[str, Any]: + """Apply settings delta. Settings are stored but not applied to the active session. Args: - update: A settings delta. + delta: A settings delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/google/gemini_live/llm.py b/src/pipecat/services/google/gemini_live/llm.py index 84b06a86d..037b23bb3 100644 --- a/src/pipecat/services/google/gemini_live/llm.py +++ b/src/pipecat/services/google/gemini_live/llm.py @@ -750,6 +750,9 @@ class GeminiLiveLLMService(LLMService): temperature=params.temperature, top_k=params.top_k, top_p=params.top_p, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, modalities=params.modalities, language=self._language_code, media_resolution=params.media_resolution, @@ -806,12 +809,12 @@ class GeminiLiveLLMService(LLMService): """ return True - async def _update_settings(self, update: LLMSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: LLMSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index 2004aa15a..1c6f56669 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -807,6 +807,11 @@ class GoogleLLMService(LLMService): temperature=params.temperature, top_k=params.top_k, top_p=params.top_p, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, thinking=params.thinking, extra=params.extra if isinstance(params.extra, dict) else {}, ) diff --git a/src/pipecat/services/google/stt.py b/src/pipecat/services/google/stt.py index 72d4f12b6..e294be20a 100644 --- a/src/pipecat/services/google/stt.py +++ b/src/pipecat/services/google/stt.py @@ -554,7 +554,9 @@ class GoogleSTTService(STTService): self._client = speech_v2.SpeechAsyncClient(credentials=creds, client_options=client_options) self._settings = GoogleSTTSettings( + language=None, languages=list(params.language_list), + language_codes=None, model=params.model, use_separate_recognition_per_channel=params.use_separate_recognition_per_channel, enable_automatic_punctuation=params.enable_automatic_punctuation, @@ -597,11 +599,9 @@ class GoogleSTTService(STTService): Returns: List[str]: Google STT language code strings. """ - from pipecat.services.settings import is_given - - if is_given(self._settings.languages): + if self._settings.languages: return [self.language_to_service_language(lang) for lang in self._settings.languages] - if is_given(self._settings.language_codes): + if self._settings.language_codes: return list(self._settings.language_codes) return ["en-US"] @@ -632,8 +632,8 @@ class GoogleSTTService(STTService): logger.debug(f"Switching STT languages to: {languages}") await self._update_settings(GoogleSTTSettings(languages=list(languages))) - async def _update_settings(self, update: GoogleSTTSettings) -> dict[str, Any]: - """Apply settings update and reconnect if anything changed. + async def _update_settings(self, delta: GoogleSTTSettings) -> dict[str, Any]: + """Apply settings delta and reconnect if anything changed. Handles ``language`` from base ``set_language`` by converting it to ``languages``. Emits a deprecation warning if ``language_codes`` is @@ -641,7 +641,7 @@ class GoogleSTTService(STTService): Reconnects the stream on any change. Args: - update: A settings delta. + delta: A settings delta. Returns: Dict mapping changed field names to their previous values. @@ -649,13 +649,13 @@ class GoogleSTTService(STTService): from pipecat.services.settings import is_given # If base set_language sent a Language value, convert to languages list - if is_given(update.language): - update.languages = [update.language] + if is_given(delta.language): + delta.languages = [delta.language] # Clear language so the base class doesn't try to store it - update.language = NOT_GIVEN + delta.language = NOT_GIVEN # Warn on deprecated language_codes usage - if is_given(update.language_codes): + if is_given(delta.language_codes): with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( @@ -665,7 +665,7 @@ class GoogleSTTService(STTService): stacklevel=2, ) - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if changed: await self._reconnect_if_needed() @@ -745,34 +745,34 @@ class GoogleSTTService(STTService): DeprecationWarning, ) # Build a settings delta from the provided options - update = GoogleSTTSettings() + delta = GoogleSTTSettings() if languages is not None: - update.languages = list(languages) + delta.languages = list(languages) if model is not None: - update.model = model + delta.model = model if enable_automatic_punctuation is not None: - update.enable_automatic_punctuation = enable_automatic_punctuation + delta.enable_automatic_punctuation = enable_automatic_punctuation if enable_spoken_punctuation is not None: - update.enable_spoken_punctuation = enable_spoken_punctuation + delta.enable_spoken_punctuation = enable_spoken_punctuation if enable_spoken_emojis is not None: - update.enable_spoken_emojis = enable_spoken_emojis + delta.enable_spoken_emojis = enable_spoken_emojis if profanity_filter is not None: - update.profanity_filter = profanity_filter + delta.profanity_filter = profanity_filter if enable_word_time_offsets is not None: - update.enable_word_time_offsets = enable_word_time_offsets + delta.enable_word_time_offsets = enable_word_time_offsets if enable_word_confidence is not None: - update.enable_word_confidence = enable_word_confidence + delta.enable_word_confidence = enable_word_confidence if enable_interim_results is not None: - update.enable_interim_results = enable_interim_results + delta.enable_interim_results = enable_interim_results if enable_voice_activity_events is not None: - update.enable_voice_activity_events = enable_voice_activity_events + delta.enable_voice_activity_events = enable_voice_activity_events if location is not None: logger.debug(f"Updating location to: {location}") self._location = location - await self._update_settings(update) + await self._update_settings(delta) async def _connect(self): """Initialize streaming recognition config and stream.""" diff --git a/src/pipecat/services/google/tts.py b/src/pipecat/services/google/tts.py index 1103c69e4..3416ee6d4 100644 --- a/src/pipecat/services/google/tts.py +++ b/src/pipecat/services/google/tts.py @@ -608,6 +608,7 @@ class GoogleHttpTTSService(TTSService): self._location = location self._settings = GoogleHttpTTSSettings( + model=None, pitch=params.pitch, rate=params.rate, speaking_rate=params.speaking_rate, @@ -688,20 +689,20 @@ class GoogleHttpTTSService(TTSService): """ return language_to_google_tts_language(language) - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: """Override to handle speaking_rate validation. Args: - update: Settings delta. Can include 'speaking_rate' (float). + delta: Settings delta. Can include 'speaking_rate' (float). """ - if isinstance(update, GoogleHttpTTSSettings) and is_given(update.speaking_rate): - rate_value = float(update.speaking_rate) + if isinstance(delta, GoogleHttpTTSSettings) and is_given(delta.speaking_rate): + rate_value = float(delta.speaking_rate) if not (0.25 <= rate_value <= 2.0): logger.warning( f"Invalid speaking_rate value: {rate_value}. Must be between 0.25 and 2.0" ) - update.speaking_rate = NOT_GIVEN - return await super()._update_settings(update) + delta.speaking_rate = NOT_GIVEN + return await super()._update_settings(delta) def _construct_ssml(self, text: str) -> str: ssml = "" @@ -1021,6 +1022,7 @@ class GoogleTTSService(GoogleBaseTTSService): self._location = location self._settings = GoogleStreamTTSSettings( + model=None, language=self.language_to_service_language(params.language) if params.language else "en-US", @@ -1032,20 +1034,20 @@ class GoogleTTSService(GoogleBaseTTSService): credentials, credentials_path ) - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: """Override to handle speaking_rate validation. Args: - update: Settings delta. Can include 'speaking_rate' (float). + delta: Settings delta. Can include 'speaking_rate' (float). """ - if isinstance(update, GoogleStreamTTSSettings) and is_given(update.speaking_rate): - rate_value = float(update.speaking_rate) + if isinstance(delta, GoogleStreamTTSSettings) and is_given(delta.speaking_rate): + rate_value = float(delta.speaking_rate) if not (0.25 <= rate_value <= 2.0): logger.warning( f"Invalid speaking_rate value: {rate_value}. Must be between 0.25 and 2.0" ) - update.speaking_rate = NOT_GIVEN - return await super()._update_settings(update) + delta.speaking_rate = NOT_GIVEN + return await super()._update_settings(delta) @traced_tts async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: @@ -1230,6 +1232,7 @@ class GeminiTTSService(GoogleBaseTTSService): self._location = location self._model = model self._settings = GeminiTTSSettings( + model=None, language=self.language_to_service_language(params.language) if params.language else "en-US", @@ -1267,19 +1270,19 @@ class GeminiTTSService(GoogleBaseTTSService): f"Current rate of {self.sample_rate}Hz may cause issues." ) - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update with voice validation. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta with voice validation. Args: - update: Settings delta. Can include 'voice', 'prompt', etc. + delta: Settings delta. Can include 'voice', 'prompt', etc. Returns: Dict mapping changed field names to their previous values. """ - if is_given(update.voice) and update.voice not in self.AVAILABLE_VOICES: - logger.warning(f"Voice '{update.voice}' not in known voices list. Using anyway.") + if is_given(delta.voice) and delta.voice not in self.AVAILABLE_VOICES: + logger.warning(f"Voice '{delta.voice}' not in known voices list. Using anyway.") - return await super()._update_settings(update) + return await super()._update_settings(delta) @traced_tts async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: diff --git a/src/pipecat/services/gradium/stt.py b/src/pipecat/services/gradium/stt.py index 1583fac3c..4ec7bf6ff 100644 --- a/src/pipecat/services/gradium/stt.py +++ b/src/pipecat/services/gradium/stt.py @@ -28,7 +28,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import GRADIUM_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language, resolve_language @@ -148,8 +148,9 @@ class GradiumSTTService(WebsocketSTTService): params = params or GradiumSTTService.InputParams() self._settings = GradiumSTTSettings( + model=None, language=params.language, - delay_in_frames=params.delay_in_frames if params.delay_in_frames else NOT_GIVEN, + delay_in_frames=params.delay_in_frames or None, ) self._receive_task = None @@ -171,16 +172,16 @@ class GradiumSTTService(WebsocketSTTService): """ return True - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update, sync params, and reconnect. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta, sync params, and reconnect. Args: - update: A :class:`STTSettings` (or ``GradiumSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``GradiumSTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed @@ -326,11 +327,11 @@ class GradiumSTTService(WebsocketSTTService): json_config = {} if self._json_config: json_config = json.loads(self._json_config) - if is_given(self._settings.language) and self._settings.language: + if self._settings.language: gradium_language = language_to_gradium_language(self._settings.language) if gradium_language: json_config["language"] = gradium_language - if is_given(self._settings.delay_in_frames) and self._settings.delay_in_frames: + if self._settings.delay_in_frames: json_config["delay_in_frames"] = self._settings.delay_in_frames if json_config: setup_msg["json_config"] = json_config diff --git a/src/pipecat/services/gradium/tts.py b/src/pipecat/services/gradium/tts.py index d10f6258d..703289706 100644 --- a/src/pipecat/services/gradium/tts.py +++ b/src/pipecat/services/gradium/tts.py @@ -105,6 +105,7 @@ class GradiumTTSService(AudioContextTTSService): self._settings = GradiumTTSSettings( model=model, voice=voice_id, + language=None, output_format="pcm", ) @@ -119,16 +120,16 @@ class GradiumTTSService(AudioContextTTSService): """ return True - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and reconnect if voice changed. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and reconnect if voice changed. Args: - update: A :class:`TTSSettings` (or ``GradiumTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``GradiumTTSSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if "voice" in changed: await self._disconnect() await self._connect() diff --git a/src/pipecat/services/grok/realtime/llm.py b/src/pipecat/services/grok/realtime/llm.py index 14c93c94a..4f6a62e24 100644 --- a/src/pipecat/services/grok/realtime/llm.py +++ b/src/pipecat/services/grok/realtime/llm.py @@ -151,6 +151,16 @@ class GrokRealtimeLLMService(LLMService): self.base_url = base_url self._settings = GrokRealtimeLLMSettings( + model=None, + temperature=None, + max_tokens=None, + top_p=None, + top_k=None, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, session_properties=session_properties or events.SessionProperties(), ) @@ -358,9 +368,9 @@ class GrokRealtimeLLMService(LLMService): """ # Backward-compatible dict path: frame.settings contains SessionProperties # fields, not our Settings fields, so we construct SessionProperties - # directly. The frame.update path falls through to super, which calls + # directly. The frame.delta path falls through to super, which calls # _update_settings → our override handles the rest. - if isinstance(frame, LLMUpdateSettingsFrame) and frame.update is None: + if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: self._settings.session_properties = events.SessionProperties(**frame.settings) await self._send_session_update() await self.push_frame(frame, direction) @@ -463,13 +473,13 @@ class GrokRealtimeLLMService(LLMService): return await self.push_error(error_msg=f"Error sending client event: {e}", exception=e) - async def _update_settings(self, update): - """Apply a settings update, sending a session update if needed.""" + async def _update_settings(self, delta): + """Apply a settings delta, sending a session update if needed.""" # Capture current sample rates before the update replaces them. input_rate = self._get_configured_sample_rate("input") output_rate = self._get_configured_sample_rate("output") - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if "session_properties" in changed: if input_rate and output_rate: diff --git a/src/pipecat/services/hume/tts.py b/src/pipecat/services/hume/tts.py index b5a064334..4b13226cc 100644 --- a/src/pipecat/services/hume/tts.py +++ b/src/pipecat/services/hume/tts.py @@ -137,6 +137,7 @@ class HumeTTSService(TTSService): params = params or HumeTTSService.InputParams() self._settings = HumeTTSSettings( + model=None, voice=voice_id, description=params.description, speed=params.speed, @@ -210,7 +211,7 @@ class HumeTTSService(TTSService): """Runtime updates via key/value pair. .. deprecated:: 0.0.103 - Use ``TTSUpdateSettingsFrame(update=HumeTTSSettings(...))`` instead. + Use ``TTSUpdateSettingsFrame(delta=HumeTTSSettings(...))`` instead. Args: key: The name of the setting to update. Recognized keys are: @@ -224,7 +225,7 @@ class HumeTTSService(TTSService): warnings.simplefilter("always") warnings.warn( "'update_setting' is deprecated, use " - "'TTSUpdateSettingsFrame(update=HumeTTSSettings(...))' instead.", + "'TTSUpdateSettingsFrame(delta=HumeTTSSettings(...))' instead.", DeprecationWarning, stacklevel=2, ) diff --git a/src/pipecat/services/inworld/tts.py b/src/pipecat/services/inworld/tts.py index 8e457aabc..2f35dc27c 100644 --- a/src/pipecat/services/inworld/tts.py +++ b/src/pipecat/services/inworld/tts.py @@ -29,7 +29,7 @@ from pipecat import version as pipecat_version USER_AGENT = f"pipecat/{pipecat_version()}" from pydantic import BaseModel -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven try: from websockets.asyncio.client import connect as websocket_connect @@ -173,17 +173,16 @@ class InworldHttpTTSService(TTSService): self._settings = InworldTTSSettings( model=model, voice=voice_id, + language=None, audio_encoding=encoding, audio_sample_rate=0, + speaking_rate=params.speaking_rate, + temperature=params.temperature, + timestamp_transport_strategy=params.timestamp_transport_strategy, + auto_mode=None, # Not applicable for HTTP TTS + apply_text_normalization=None, # Not applicable for HTTP TTS ) - if params.temperature is not None: - self._settings.temperature = params.temperature - if params.speaking_rate is not None: - self._settings.speaking_rate = params.speaking_rate - if params.timestamp_transport_strategy is not None: - self._settings.timestamp_transport_strategy = params.timestamp_transport_strategy - self._cumulative_time = 0.0 self._sync_model_name_to_metrics() @@ -286,7 +285,7 @@ class InworldHttpTTSService(TTSService): "audioEncoding": self._settings.audio_encoding, "sampleRateHertz": self._settings.audio_sample_rate, } - if is_given(self._settings.speaking_rate): + if self._settings.speaking_rate is not None: audio_config["speakingRate"] = self._settings.speaking_rate payload = { @@ -296,12 +295,12 @@ class InworldHttpTTSService(TTSService): "audioConfig": audio_config, } - if is_given(self._settings.temperature): + if self._settings.temperature is not None: payload["temperature"] = self._settings.temperature # Use WORD timestamps for simplicity and correct spacing/capitalization payload["timestampType"] = self._timestamp_type - if is_given(self._settings.timestamp_transport_strategy): + if self._settings.timestamp_transport_strategy is not None: payload["timestampTransportStrategy"] = self._settings.timestamp_transport_strategy request_id = str(uuid.uuid4()) @@ -549,25 +548,17 @@ class InworldTTSService(AudioContextTTSService): self._settings = InworldTTSSettings( model=model, voice=voice_id, + language=None, audio_encoding=encoding, audio_sample_rate=0, + speaking_rate=params.speaking_rate, + temperature=params.temperature, + apply_text_normalization=params.apply_text_normalization, + timestamp_transport_strategy=params.timestamp_transport_strategy, + auto_mode=params.auto_mode if params.auto_mode is not None else aggregate_sentences, ) self._timestamp_type = "WORD" - if params.temperature is not None: - self._settings.temperature = params.temperature - if params.speaking_rate is not None: - self._settings.speaking_rate = params.speaking_rate - if params.apply_text_normalization is not None: - self._settings.apply_text_normalization = params.apply_text_normalization - if params.timestamp_transport_strategy is not None: - self._settings.timestamp_transport_strategy = params.timestamp_transport_strategy - - if params.auto_mode is not None: - self._settings.auto_mode = params.auto_mode - else: - self._settings.auto_mode = aggregate_sentences - self._buffer_settings = { "maxBufferDelayMs": params.max_buffer_delay_ms, "bufferCharThreshold": params.buffer_char_threshold, @@ -757,12 +748,12 @@ class InworldTTSService(AudioContextTTSService): await self._disconnect_websocket() - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed @@ -959,7 +950,7 @@ class InworldTTSService(AudioContextTTSService): "audioEncoding": self._settings.audio_encoding, "sampleRateHertz": self._settings.audio_sample_rate, } - if is_given(self._settings.speaking_rate): + if self._settings.speaking_rate is not None: audio_config["speakingRate"] = self._settings.speaking_rate create_config: Dict[str, Any] = { @@ -968,13 +959,13 @@ class InworldTTSService(AudioContextTTSService): "audioConfig": audio_config, } - if is_given(self._settings.temperature): + if self._settings.temperature is not None: create_config["temperature"] = self._settings.temperature - if is_given(self._settings.apply_text_normalization): + if self._settings.apply_text_normalization is not None: create_config["applyTextNormalization"] = self._settings.apply_text_normalization - if is_given(self._settings.auto_mode): + if self._settings.auto_mode is not None: create_config["autoMode"] = self._settings.auto_mode - if is_given(self._settings.timestamp_transport_strategy): + if self._settings.timestamp_transport_strategy is not None: create_config["timestampTransportStrategy"] = ( self._settings.timestamp_transport_strategy ) diff --git a/src/pipecat/services/kokoro/tts.py b/src/pipecat/services/kokoro/tts.py index 6e848ae87..519c565ba 100644 --- a/src/pipecat/services/kokoro/tts.py +++ b/src/pipecat/services/kokoro/tts.py @@ -144,6 +144,7 @@ class KokoroTTSService(TTSService): self._lang_code = language_to_kokoro_language(params.language) self._settings = KokoroTTSSettings( + model=None, voice=voice_id, language=language_to_kokoro_language(params.language), lang_code=language_to_kokoro_language(params.language), diff --git a/src/pipecat/services/llm_service.py b/src/pipecat/services/llm_service.py index e4f1adb45..df1bc6d08 100644 --- a/src/pipecat/services/llm_service.py +++ b/src/pipecat/services/llm_service.py @@ -59,7 +59,7 @@ from pipecat.processors.aggregators.llm_response import ( from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.frame_processor import FrameDirection from pipecat.services.ai_service import AIService -from pipecat.services.settings import LLMSettings, is_given +from pipecat.services.settings import LLMSettings from pipecat.turns.user_turn_completion_mixin import UserTurnCompletionLLMServiceMixin from pipecat.utils.context.llm_context_summarization import ( LLMContextSummarizationUtil, @@ -312,19 +312,21 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService): await self._cancel_sequential_runner_task() await self._cancel_summary_task() - async def _update_settings(self, update: LLMSettings) -> dict[str, Any]: - """Apply a settings update, handling turn-completion fields. + async def _update_settings(self, delta: LLMSettings) -> dict[str, Any]: + """Apply a settings delta, handling turn-completion fields. Args: - update: An LLM settings delta. + delta: An LLM settings delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if "filter_incomplete_user_turns" in changed: - self._filter_incomplete_user_turns = self._settings.filter_incomplete_user_turns + self._filter_incomplete_user_turns = ( + self._settings.filter_incomplete_user_turns or False + ) logger.info( f"{self}: Incomplete turn filtering " f"{'enabled' if self._filter_incomplete_user_turns else 'disabled'}" @@ -349,20 +351,20 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService): elif isinstance(frame, LLMConfigureOutputFrame): self._skip_tts = frame.skip_tts elif isinstance(frame, LLMUpdateSettingsFrame): - if frame.update is not None: - await self._update_settings(frame.update) + if frame.delta is not None: + await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( "Passing a dict via LLMUpdateSettingsFrame(settings={...}) is deprecated " - "since 0.0.103, use LLMUpdateSettingsFrame(update=LLMSettings(...)) instead.", + "since 0.0.103, use LLMUpdateSettingsFrame(delta=LLMSettings(...)) instead.", DeprecationWarning, stacklevel=2, ) - update = type(self._settings).from_mapping(frame.settings) - await self._update_settings(update) + delta = type(self._settings).from_mapping(frame.settings) + await self._update_settings(delta) elif isinstance(frame, LLMContextSummaryRequestFrame): await self._handle_summary_request(frame) diff --git a/src/pipecat/services/lmnt/tts.py b/src/pipecat/services/lmnt/tts.py index f70bfa402..b7ebb19ea 100644 --- a/src/pipecat/services/lmnt/tts.py +++ b/src/pipecat/services/lmnt/tts.py @@ -206,16 +206,16 @@ class LmntTTSService(InterruptibleTTSService): await self._disconnect_websocket() - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta. Args: - update: A :class:`TTSSettings` (or ``LmntTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``LmntTTSSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if changed: await self._disconnect() diff --git a/src/pipecat/services/minimax/tts.py b/src/pipecat/services/minimax/tts.py index 54925f7e4..388b7ee9e 100644 --- a/src/pipecat/services/minimax/tts.py +++ b/src/pipecat/services/minimax/tts.py @@ -26,7 +26,7 @@ from pipecat.frames.frames import ( TTSStartedFrame, TTSStoppedFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -240,13 +240,19 @@ class MiniMaxHttpTTSService(TTSService): self._settings = MiniMaxTTSSettings( model=model, voice=voice_id, + language=None, stream=True, speed=params.speed, volume=params.volume, pitch=params.pitch, + language_boost=None, + emotion=None, + text_normalization=None, + latex_read=None, audio_bitrate=128000, audio_format="pcm", audio_channel=1, + audio_sample_rate=0, ) self._sync_model_name_to_metrics() @@ -351,11 +357,11 @@ class MiniMaxHttpTTSService(TTSService): "vol": self._settings.volume, "pitch": self._settings.pitch, } - if is_given(self._settings.emotion): + if self._settings.emotion is not None: voice_setting["emotion"] = self._settings.emotion - if is_given(self._settings.text_normalization): + if self._settings.text_normalization is not None: voice_setting["text_normalization"] = self._settings.text_normalization - if is_given(self._settings.latex_read): + if self._settings.latex_read is not None: voice_setting["latex_read"] = self._settings.latex_read # Build audio_setting dict for API @@ -374,7 +380,7 @@ class MiniMaxHttpTTSService(TTSService): "model": self._settings.model, "text": text, } - if is_given(self._settings.language_boost): + if self._settings.language_boost is not None: payload["language_boost"] = self._settings.language_boost try: diff --git a/src/pipecat/services/neuphonic/tts.py b/src/pipecat/services/neuphonic/tts.py index dd2360e4c..e076958c4 100644 --- a/src/pipecat/services/neuphonic/tts.py +++ b/src/pipecat/services/neuphonic/tts.py @@ -147,6 +147,7 @@ class NeuphonicTTSService(InterruptibleTTSService): self._api_key = api_key self._url = url self._settings = NeuphonicTTSSettings( + model=None, language=self.language_to_service_language(params.language), speed=params.speed, encoding=encoding, @@ -179,9 +180,9 @@ class NeuphonicTTSService(InterruptibleTTSService): """ return language_to_neuphonic_lang_code(language) - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and reconnect with new configuration.""" - changed = await super()._update_settings(update) + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and reconnect with new configuration.""" + changed = await super()._update_settings(delta) if changed: await self._disconnect() await self._connect() @@ -450,6 +451,7 @@ class NeuphonicHttpTTSService(TTSService): self._session = aiohttp_session self._base_url = url.rstrip("/") self._settings = NeuphonicTTSSettings( + model=None, voice=voice_id, language=self.language_to_service_language(params.language) or "en", speed=params.speed, diff --git a/src/pipecat/services/nvidia/stt.py b/src/pipecat/services/nvidia/stt.py index fd924204e..be9002b14 100644 --- a/src/pipecat/services/nvidia/stt.py +++ b/src/pipecat/services/nvidia/stt.py @@ -579,16 +579,16 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): self._config = self._create_recognition_config() logger.debug(f"Initialized NvidiaSegmentedSTTService with model: {self._settings.model}") - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update and sync internal state. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta and sync internal state. Args: - update: A :class:`STTSettings` (or ``NvidiaSegmentedSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``NvidiaSegmentedSTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if changed: self._config = self._create_recognition_config() diff --git a/src/pipecat/services/nvidia/tts.py b/src/pipecat/services/nvidia/tts.py index ade5da63d..12bcf8c21 100644 --- a/src/pipecat/services/nvidia/tts.py +++ b/src/pipecat/services/nvidia/tts.py @@ -150,12 +150,12 @@ class NvidiaTTSService(TTSService): stacklevel=2, ) - async def _update_settings(self, update: NvidiaTTSSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: NvidiaTTSSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed # TODO: reconnect gRPC client to apply changed settings. diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 960ebd6f7..9ba0583a1 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -144,9 +144,12 @@ class BaseOpenAILLMService(LLMService): seed=params.seed, temperature=params.temperature, top_p=params.top_p, + top_k=None, max_tokens=params.max_tokens, max_completion_tokens=params.max_completion_tokens, service_tier=params.service_tier, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, extra=params.extra if isinstance(params.extra, dict) else {}, ) self._retry_timeout_secs = retry_timeout_secs diff --git a/src/pipecat/services/openai/realtime/llm.py b/src/pipecat/services/openai/realtime/llm.py index d765fea75..39c0290fc 100644 --- a/src/pipecat/services/openai/realtime/llm.py +++ b/src/pipecat/services/openai/realtime/llm.py @@ -178,6 +178,15 @@ class OpenAIRealtimeLLMService(LLMService): self._settings = OpenAIRealtimeLLMSettings( model=model, + temperature=None, + max_tokens=None, + top_p=None, + top_k=None, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, session_properties=session_properties or events.SessionProperties(), ) self._sync_model_name_to_metrics() @@ -415,9 +424,9 @@ class OpenAIRealtimeLLMService(LLMService): """ # Backward-compatible dict path: frame.settings contains SessionProperties # fields, not our Settings fields, so we construct SessionProperties - # directly. The frame.update path falls through to super, which calls + # directly. The frame.delta path falls through to super, which calls # _update_settings → our override handles the rest. - if isinstance(frame, LLMUpdateSettingsFrame) and frame.update is None: + if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: self._settings.session_properties = events.SessionProperties(**frame.settings) await self._send_session_update() await self.push_frame(frame, direction) @@ -536,9 +545,9 @@ class OpenAIRealtimeLLMService(LLMService): # treat a send-side error as fatal. await self.push_error(error_msg=f"Error sending client event: {e}", exception=e) - async def _update_settings(self, update): - """Apply a settings update, sending a session update if needed.""" - changed = await super()._update_settings(update) + async def _update_settings(self, delta): + """Apply a settings delta, sending a session update if needed.""" + changed = await super()._update_settings(delta) if "session_properties" in changed: await self._send_session_update() self._warn_unhandled_updated_settings(changed.keys() - {"session_properties"}) diff --git a/src/pipecat/services/openai/stt.py b/src/pipecat/services/openai/stt.py index 13a37a2b1..8b690d015 100644 --- a/src/pipecat/services/openai/stt.py +++ b/src/pipecat/services/openai/stt.py @@ -35,7 +35,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import OPENAI_REALTIME_TTFS_P99, OPENAI_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.services.whisper.base_stt import BaseWhisperSTTService, Transcription @@ -268,19 +268,19 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): """ return True - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update and send session update if needed. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta and send session update if needed. Keeps ``_language_code`` and ``_prompt`` in sync with settings and sends a ``session.update`` to the server when the session is active. Args: - update: A :class:`STTSettings` (or ``OpenAIRealtimeSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``OpenAIRealtimeSTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index efac34223..6ffccfbef 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -163,6 +163,15 @@ class OpenAIRealtimeBetaLLMService(LLMService): self._settings = OpenAIRealtimeBetaLLMSettings( model=model, + temperature=None, + max_tokens=None, + top_p=None, + top_k=None, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, session_properties=session_properties or events.SessionProperties(), ) self._sync_model_name_to_metrics() @@ -361,9 +370,9 @@ class OpenAIRealtimeBetaLLMService(LLMService): """ # Backward-compatible dict path: frame.settings contains SessionProperties # fields, not our Settings fields, so we construct SessionProperties - # directly. The frame.update path falls through to super, which calls + # directly. The frame.delta path falls through to super, which calls # _update_settings → our override handles the rest. - if isinstance(frame, LLMUpdateSettingsFrame) and frame.update is None: + if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: self._settings.session_properties = events.SessionProperties(**frame.settings) await self._send_session_update() await self.push_frame(frame, direction) @@ -480,9 +489,9 @@ class OpenAIRealtimeBetaLLMService(LLMService): # treat a send-side error as fatal. await self.push_error(error_msg=f"Error sending client event: {e}", exception=e) - async def _update_settings(self, update): - """Apply a settings update, sending a session update if needed.""" - changed = await super()._update_settings(update) + async def _update_settings(self, delta): + """Apply a settings delta, sending a session update if needed.""" + changed = await super()._update_settings(delta) if "session_properties" in changed: await self._send_session_update() return changed diff --git a/src/pipecat/services/perplexity/llm.py b/src/pipecat/services/perplexity/llm.py index 04f25621d..e03bace8d 100644 --- a/src/pipecat/services/perplexity/llm.py +++ b/src/pipecat/services/perplexity/llm.py @@ -11,8 +11,6 @@ an OpenAI-compatible interface. It handles Perplexity's unique token usage reporting patterns while maintaining compatibility with the Pipecat framework. """ -from openai import NOT_GIVEN - from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.llm_context import LLMContext @@ -72,15 +70,15 @@ class PerplexityLLMService(OpenAILLMService): } # Add OpenAI-compatible parameters if they're set - if self._settings.frequency_penalty is not NOT_GIVEN: + if self._settings.frequency_penalty is not None: params["frequency_penalty"] = self._settings.frequency_penalty - if self._settings.presence_penalty is not NOT_GIVEN: + if self._settings.presence_penalty is not None: params["presence_penalty"] = self._settings.presence_penalty - if self._settings.temperature is not NOT_GIVEN: + if self._settings.temperature is not None: params["temperature"] = self._settings.temperature - if self._settings.top_p is not NOT_GIVEN: + if self._settings.top_p is not None: params["top_p"] = self._settings.top_p - if self._settings.max_tokens is not NOT_GIVEN: + if self._settings.max_tokens is not None: params["max_tokens"] = self._settings.max_tokens return params diff --git a/src/pipecat/services/piper/tts.py b/src/pipecat/services/piper/tts.py index 0b43d96d2..e6a2c6943 100644 --- a/src/pipecat/services/piper/tts.py +++ b/src/pipecat/services/piper/tts.py @@ -71,7 +71,7 @@ class PiperTTSService(TTSService): """ super().__init__(**kwargs) - self._settings = PiperTTSSettings(voice=voice_id) + self._settings = PiperTTSSettings(model=None, voice=voice_id, language=None) download_dir = download_dir or Path.cwd() @@ -96,12 +96,12 @@ class PiperTTSService(TTSService): """ return True - async def _update_settings(self, update: PiperTTSSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: PiperTTSSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed # TODO: voice changes would require re-downloading and loading the model. @@ -207,7 +207,7 @@ class PiperHttpTTSService(TTSService): self._base_url = base_url self._session = aiohttp_session - self._settings = PiperHttpTTSSettings(voice=voice_id) + self._settings = PiperHttpTTSSettings(model=None, voice=voice_id, language=None) def can_generate_metrics(self) -> bool: """Check if this service can generate processing metrics. diff --git a/src/pipecat/services/playht/tts.py b/src/pipecat/services/playht/tts.py index ff34725f9..08a87209c 100644 --- a/src/pipecat/services/playht/tts.py +++ b/src/pipecat/services/playht/tts.py @@ -34,7 +34,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import InterruptibleTTSService, TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -204,6 +204,7 @@ class PlayHTTTSService(InterruptibleTTSService): voice_engine=voice_engine, speed=params.speed, seed=params.seed, + playht_sample_rate=0, ) self._sync_model_name_to_metrics() @@ -215,12 +216,12 @@ class PlayHTTTSService(InterruptibleTTSService): """ return True - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta. Settings are stored but not applied to the active connection. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed @@ -569,6 +570,7 @@ class PlayHTHttpTTSService(TTSService): voice_engine=voice_engine, speed=params.speed, seed=params.seed, + playht_sample_rate=0, ) self._sync_model_name_to_metrics() diff --git a/src/pipecat/services/resembleai/tts.py b/src/pipecat/services/resembleai/tts.py index 177a4c10e..b1b8d1de8 100644 --- a/src/pipecat/services/resembleai/tts.py +++ b/src/pipecat/services/resembleai/tts.py @@ -100,6 +100,7 @@ class ResembleAITTSService(AudioContextTTSService): self._api_key = api_key self._url = url self._settings = ResembleAITTSSettings( + model=None, voice=voice_id, precision=precision, output_format=output_format, diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index 6e795cc3d..6c3ff4e23 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -31,7 +31,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import ( AudioContextTTSService, InterruptibleTTSService, @@ -233,27 +233,19 @@ class RimeTTSService(AudioContextTTSService): samplingRate=0, # updated in start() language=self.language_to_service_language(params.language) if params.language - else NOT_GIVEN, - segment=params.segment if params.segment is not None else NOT_GIVEN, + else None, + segment=params.segment, # Arcana params - repetition_penalty=params.repetition_penalty - if params.repetition_penalty is not None - else NOT_GIVEN, - temperature=params.temperature if params.temperature is not None else NOT_GIVEN, - top_p=params.top_p if params.top_p is not None else NOT_GIVEN, + repetition_penalty=params.repetition_penalty, + temperature=params.temperature, + top_p=params.top_p, # Mistv2 params - speedAlpha=params.speed_alpha if params.speed_alpha is not None else NOT_GIVEN, - reduceLatency=params.reduce_latency if params.reduce_latency is not None else NOT_GIVEN, - pauseBetweenBrackets=params.pause_between_brackets - if params.pause_between_brackets is not None - else NOT_GIVEN, - phonemizeBetweenBrackets=params.phonemize_between_brackets - if params.phonemize_between_brackets is not None - else NOT_GIVEN, - noTextNormalization=params.no_text_normalization - if params.no_text_normalization is not None - else NOT_GIVEN, - saveOovs=params.save_oovs if params.save_oovs is not None else NOT_GIVEN, + speedAlpha=params.speed_alpha, + reduceLatency=params.reduce_latency, + pauseBetweenBrackets=params.pause_between_brackets, + phonemizeBetweenBrackets=params.phonemize_between_brackets, + noTextNormalization=params.no_text_normalization, + saveOovs=params.save_oovs, ) self._sync_model_name_to_metrics() @@ -295,32 +287,32 @@ class RimeTTSService(AudioContextTTSService): "audioFormat": self._settings.audioFormat, "samplingRate": self._settings.samplingRate, } - if is_given(self._settings.language): + if self._settings.language is not None: params["lang"] = self._settings.language - if is_given(self._settings.segment): + if self._settings.segment is not None: params["segment"] = self._settings.segment if self._settings.model == "arcana": - if is_given(self._settings.repetition_penalty): + if self._settings.repetition_penalty is not None: params["repetition_penalty"] = self._settings.repetition_penalty - if is_given(self._settings.temperature): + if self._settings.temperature is not None: params["temperature"] = self._settings.temperature - if is_given(self._settings.top_p): + if self._settings.top_p is not None: params["top_p"] = self._settings.top_p else: # mistv2/mist - if is_given(self._settings.speedAlpha): + if self._settings.speedAlpha is not None: params["speedAlpha"] = self._settings.speedAlpha - if is_given(self._settings.reduceLatency): + if self._settings.reduceLatency is not None: params["reduceLatency"] = self._settings.reduceLatency - if is_given(self._settings.pauseBetweenBrackets): + if self._settings.pauseBetweenBrackets is not None: params["pauseBetweenBrackets"] = json.dumps(self._settings.pauseBetweenBrackets) - if is_given(self._settings.phonemizeBetweenBrackets): + if self._settings.phonemizeBetweenBrackets is not None: params["phonemizeBetweenBrackets"] = json.dumps( self._settings.phonemizeBetweenBrackets ) - if is_given(self._settings.noTextNormalization): + if self._settings.noTextNormalization is not None: params["noTextNormalization"] = json.dumps(self._settings.noTextNormalization) - if is_given(self._settings.saveOovs): + if self._settings.saveOovs is not None: params["saveOovs"] = json.dumps(self._settings.saveOovs) return params @@ -350,13 +342,13 @@ class RimeTTSService(AudioContextTTSService): self._extra_msg_fields["inlineSpeedAlpha"] = ",".join(speed_vals + [str(speed)]) return f"[{text}]" - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and reconnect if necessary. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and reconnect if necessary. Since all settings are WebSocket URL query parameters, any setting change requires reconnecting to apply the new values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if changed and self._websocket: await self._disconnect() @@ -665,11 +657,19 @@ class RimeHttpTTSService(TTSService): language=self.language_to_service_language(params.language) if params.language else "eng", + audioFormat="pcm", + samplingRate=0, + segment=None, speedAlpha=params.speed_alpha, reduceLatency=params.reduce_latency, pauseBetweenBrackets=params.pause_between_brackets, phonemizeBetweenBrackets=params.phonemize_between_brackets, - inlineSpeedAlpha=params.inline_speed_alpha if params.inline_speed_alpha else NOT_GIVEN, + noTextNormalization=None, + saveOovs=None, + inlineSpeedAlpha=params.inline_speed_alpha if params.inline_speed_alpha else None, + repetition_penalty=None, + temperature=None, + top_p=None, voice=voice_id, ) self._sync_model_name_to_metrics() @@ -719,7 +719,7 @@ class RimeHttpTTSService(TTSService): "pauseBetweenBrackets": self._settings.pauseBetweenBrackets, "phonemizeBetweenBrackets": self._settings.phonemizeBetweenBrackets, } - if is_given(self._settings.inlineSpeedAlpha): + if self._settings.inlineSpeedAlpha is not None: payload["inlineSpeedAlpha"] = self._settings.inlineSpeedAlpha payload["text"] = text payload["speaker"] = self._settings.voice @@ -846,13 +846,11 @@ class RimeNonJsonTTSService(InterruptibleTTSService): samplingRate=sample_rate, language=self.language_to_service_language(params.language) if params.language - else NOT_GIVEN, - segment=params.segment if params.segment is not None else NOT_GIVEN, - repetition_penalty=params.repetition_penalty - if params.repetition_penalty is not None - else NOT_GIVEN, - temperature=params.temperature if params.temperature is not None else NOT_GIVEN, - top_p=params.top_p if params.top_p is not None else NOT_GIVEN, + else None, + segment=params.segment, + repetition_penalty=params.repetition_penalty, + temperature=params.temperature, + top_p=params.top_p, ) self._sync_model_name_to_metrics() # Add any extra parameters for future compatibility @@ -940,15 +938,15 @@ class RimeNonJsonTTSService(InterruptibleTTSService): "audioFormat": self._settings.audioFormat, "samplingRate": self._settings.samplingRate, } - if is_given(self._settings.language): + if self._settings.language is not None: settings_dict["lang"] = self._settings.language - if is_given(self._settings.segment): + if self._settings.segment is not None: settings_dict["segment"] = self._settings.segment - if is_given(self._settings.repetition_penalty): + if self._settings.repetition_penalty is not None: settings_dict["repetition_penalty"] = self._settings.repetition_penalty - if is_given(self._settings.temperature): + if self._settings.temperature is not None: settings_dict["temperature"] = self._settings.temperature - if is_given(self._settings.top_p): + if self._settings.top_p is not None: settings_dict["top_p"] = self._settings.top_p # Include extras settings_dict.update(self._settings.extra) @@ -1046,13 +1044,13 @@ class RimeNonJsonTTSService(InterruptibleTTSService): except Exception as e: yield ErrorFrame(error=f"Unknown error occurred: {e}") - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and reconnect if necessary. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and reconnect if necessary. Since all settings are WebSocket URL query parameters, any setting change requires reconnecting to apply the new values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if changed: logger.debug("Settings changed, reconnecting WebSocket with new parameters") diff --git a/src/pipecat/services/sarvam/stt.py b/src/pipecat/services/sarvam/stt.py index 0128c1a22..02d6e250f 100644 --- a/src/pipecat/services/sarvam/stt.py +++ b/src/pipecat/services/sarvam/stt.py @@ -274,8 +274,8 @@ class SarvamSTTService(STTService): self._settings = SarvamSTTSettings( model=model, language=params.language, - prompt=params.prompt if params.prompt is not None else NOT_GIVEN, - mode=mode if mode is not None else NOT_GIVEN, + prompt=params.prompt, + mode=mode, vad_signals=params.vad_signals, high_vad_sensitivity=params.high_vad_sensitivity, ) @@ -329,11 +329,11 @@ class SarvamSTTService(STTService): if self._socket_client: await self._socket_client.flush() - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update, validate, sync state, and reconnect. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta, validate, sync state, and reconnect. Args: - update: A :class:`STTSettings` (or ``SarvamSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``SarvamSTTSettings``) delta. Returns: Dict mapping changed field names to their previous values. @@ -342,26 +342,26 @@ class SarvamSTTService(STTService): ValueError: If a setting is not supported by the current model. """ # Validate against model capabilities before applying - if is_given(update.language) and update.language is not None: + if is_given(delta.language) and delta.language is not None: if not self._config.supports_language: raise ValueError( f"Model '{self._settings.model}' does not support language parameter " "(auto-detects language)." ) - if isinstance(update, SarvamSTTSettings): - if is_given(update.prompt) and update.prompt is not None: + if isinstance(delta, SarvamSTTSettings): + if is_given(delta.prompt) and delta.prompt is not None: if not self._config.supports_prompt: raise ValueError( f"Model '{self._settings.model}' does not support prompt parameter." ) - if is_given(update.mode) and update.mode is not None: + if is_given(delta.mode) and delta.mode is not None: if not self._config.supports_mode: raise ValueError( f"Model '{self._settings.model}' does not support mode parameter." ) - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) # TODO: someday we could reconnect here to apply updated settings. # Code might look something like the below: @@ -510,16 +510,12 @@ class SarvamSTTService(STTService): connect_kwargs["language_code"] = language_string # Add mode for models that support it - if self._config.supports_mode and is_given(self._settings.mode): + if self._config.supports_mode and self._settings.mode is not None: connect_kwargs["mode"] = self._settings.mode # Prompt support differs across sarvamai versions. Prefer connect-time prompt # when available and gracefully degrade if the SDK doesn't accept it. - if ( - is_given(self._settings.prompt) - and self._settings.prompt is not None - and self._config.supports_prompt - ): + if self._settings.prompt is not None and self._config.supports_prompt: connect_kwargs["prompt"] = self._settings.prompt def _connect_with_sdk_headers(connect_fn, **kwargs): @@ -561,11 +557,7 @@ class SarvamSTTService(STTService): self._socket_client = await self._websocket_context.__aenter__() # Fallback for SDKs that support runtime prompt updates. - if ( - is_given(self._settings.prompt) - and self._settings.prompt is not None - and self._config.supports_prompt - ): + if self._settings.prompt is not None and self._config.supports_prompt: prompt_setter = getattr(self._socket_client, "set_prompt", None) if callable(prompt_setter): await prompt_setter(self._settings.prompt) diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index 191689f5a..45c283ff1 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -62,7 +62,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.sarvam._sdk import sdk_headers -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import InterruptibleTTSService, TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -486,6 +486,9 @@ class SarvamHttpTTSService(TTSService): True if self._config.preprocessing_always_enabled else params.enable_preprocessing ), pace=pace, + pitch=None, + loudness=None, + temperature=None, model=model, voice=voice_id, ) @@ -559,19 +562,19 @@ class SarvamHttpTTSService(TTSService): "sample_rate": self.sample_rate, "enable_preprocessing": self._settings.enable_preprocessing, "model": self._settings.model, - "pace": self._settings.pace if is_given(self._settings.pace) else 1.0, + "pace": self._settings.pace if self._settings.pace is not None else 1.0, } # Add model-specific parameters based on config if self._config.supports_pitch: - payload["pitch"] = self._settings.pitch if is_given(self._settings.pitch) else 0.0 + payload["pitch"] = self._settings.pitch if self._settings.pitch is not None else 0.0 if self._config.supports_loudness: payload["loudness"] = ( - self._settings.loudness if is_given(self._settings.loudness) else 1.0 + self._settings.loudness if self._settings.loudness is not None else 1.0 ) if self._config.supports_temperature: payload["temperature"] = ( - self._settings.temperature if is_given(self._settings.temperature) else 0.6 + self._settings.temperature if self._settings.temperature is not None else 0.6 ) headers = { @@ -849,6 +852,9 @@ class SarvamTTSService(InterruptibleTTSService): output_audio_codec=params.output_audio_codec, output_audio_bitrate=params.output_audio_bitrate, pace=pace, + pitch=None, + loudness=None, + temperature=None, model=model, voice=voice_id, ) @@ -949,9 +955,9 @@ class SarvamTTSService(InterruptibleTTSService): if isinstance(frame, (LLMFullResponseEndFrame, EndFrame)): await self.flush_audio() - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a settings update and resend config if voice changed.""" - changed = await super()._update_settings(update) + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a settings delta and resend config if voice changed.""" + changed = await super()._update_settings(delta) if changed: await self._send_config() @@ -1027,11 +1033,11 @@ class SarvamTTSService(InterruptibleTTSService): "pace": self._settings.pace, "model": self._settings.model, } - if is_given(self._settings.pitch): + if self._settings.pitch is not None: config_data["pitch"] = self._settings.pitch - if is_given(self._settings.loudness): + if self._settings.loudness is not None: config_data["loudness"] = self._settings.loudness - if is_given(self._settings.temperature): + if self._settings.temperature is not None: config_data["temperature"] = self._settings.temperature logger.debug(f"Config being sent is {config_data}") config_message = {"type": "config", "data": config_data} diff --git a/src/pipecat/services/settings.py b/src/pipecat/services/settings.py index 4664ecd39..7de476c64 100644 --- a/src/pipecat/services/settings.py +++ b/src/pipecat/services/settings.py @@ -6,24 +6,32 @@ """Settings infrastructure for Pipecat AI services. -This module provides dataclass-based settings objects for service configuration. -Each service type has a corresponding settings class (e.g. ``TTSSettings``, -``LLMSettings``) whose fields use the ``NOT_GIVEN`` sentinel to distinguish -"leave unchanged" from an explicit ``None``. +Each service type has a settings dataclass (``LLMSettings``, ``TTSSettings``, +``STTSettings``, or a service-specific subclass). The same class is used in +two distinct modes: -Key concepts: +**Store mode** — the service's ``self._settings`` object that holds the full +current state. Every field must have a real value; ``NOT_GIVEN`` is never +valid here. Services that don't support an inherited field should set it to +``None``. ``validate_complete()`` (called automatically in +``AIService.start()``) enforces this invariant. -- **NOT_GIVEN sentinel**: A value meaning "this field was not provided in the - update". Distinct from ``None`` (which may be a valid value for a setting). -- **Settings as both state and delta**: The same class is used for the - service's current settings *and* for update objects. Fields set to - ``NOT_GIVEN`` are simply skipped when applying an update. -- **apply_update**: Applies a delta onto a target settings object and returns - a dict mapping each changed field name to its previous value. -- **from_mapping**: Constructs a settings object from a plain dict, - supporting field aliases (e.g. ``"voice_id"`` → ``"voice"``). -- **Extras**: Unknown keys land in the ``extra`` dict so services that have - non-standard settings don't lose data. +**Delta mode** — a sparse update object carried by an +``*UpdateSettingsFrame``. Only the fields the caller wants to change are set; +all others remain at their default of ``NOT_GIVEN``. ``apply_update()`` +merges a delta into a store, skipping any ``NOT_GIVEN`` fields. + +Key helpers: + +- ``NOT_GIVEN`` / ``is_given()`` — sentinel and check for "field not provided + in this delta". +- ``apply_update(delta)`` — merge a delta into a store, returning changed + fields. +- ``from_mapping(dict)`` — build a delta from a plain dict (for backward + compatibility with dict-based ``*UpdateSettingsFrame``). +- ``validate_complete()`` — assert that a store has no ``NOT_GIVEN`` fields. +- ``extra`` dict — overflow for service-specific keys that don't map to a + declared field. """ from __future__ import annotations @@ -45,12 +53,15 @@ if TYPE_CHECKING: class _NotGiven: - """Sentinel indicating a settings field was not provided. + """Sentinel meaning "this field was not included in the delta". - ``NOT_GIVEN`` means "the caller did not supply this value" — distinct from - ``None``, which may be a legitimate setting value. It is used as the - default for every settings field so that ``apply_update`` can tell which - fields the caller actually wants to change. + ``NOT_GIVEN`` is distinct from ``None`` (which is a valid stored value, + typically meaning "this service doesn't support this field"). Every + settings field defaults to ``NOT_GIVEN`` so that delta-mode objects are + sparse by default and ``apply_update`` can skip untouched fields. + + ``NOT_GIVEN`` must never appear in a store-mode object — see + ``validate_complete()``. """ _instance: Optional[_NotGiven] = None @@ -68,11 +79,25 @@ class _NotGiven: NOT_GIVEN: _NotGiven = _NotGiven() -"""Singleton sentinel meaning "this field was not included in the update".""" +"""Singleton sentinel meaning "this field was not included in the delta". + +Valid only in delta-mode settings objects. Must never appear in a service's +``self._settings`` (store mode) — use ``None`` instead for unsupported fields. +""" def is_given(value: Any) -> bool: - """Check whether a value was explicitly provided (i.e. is not ``NOT_GIVEN``). + """Check whether a delta field was explicitly provided. + + Typically used when processing a delta to decide whether a field + should be applied:: + + if is_given(delta.voice): + # caller wants to change the voice + ... + + For store-mode objects this always returns ``True`` (since + ``validate_complete`` ensures no ``NOT_GIVEN`` fields remain). Args: value: The value to check. @@ -94,28 +119,38 @@ _S = TypeVar("_S", bound="ServiceSettings") class ServiceSettings: """Base class for runtime-updatable service settings. - These settings represent the subset of a service's configuration that can + These settings capture the subset of a service's configuration that can be changed **while the pipeline is running** (e.g. switching the model or changing the voice). They are *not* meant to capture every constructor parameter — only those that support live updates via ``*UpdateSettingsFrame``. Every AI service type (LLM, TTS, STT) extends this with its own fields. - Fields default to ``NOT_GIVEN`` so that an instance can represent either - the full current state **or** a sparse update delta. Note that in the full - current state, **all fields will be given** (i.e. ``NOT_GIVEN`` is reserved - for update deltas). + Each instance operates in one of two modes (see module docstring): + + - **Store mode** (``self._settings``): holds the full current state. + Every field must be a real value — ``NOT_GIVEN`` is never valid. + Use ``None`` for inherited fields the service doesn't support. + Enforced at runtime by ``validate_complete()``. + - **Delta mode** (``*UpdateSettingsFrame``): a sparse update. + Only fields the caller wants to change are set; all others stay at + the default ``NOT_GIVEN`` and are skipped by ``apply_update()``. Parameters: - model: The model identifier used by the service. + model: The model identifier used by the service. Set to ``None`` + in store mode if the service has no model concept. extra: Overflow dict for service-specific keys that don't map to a declared field. """ # -- common fields ------------------------------------------------------- - model: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - """AI model identifier (e.g. ``"gpt-4o"``, ``"eleven_turbo_v2_5"``).""" + model: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + """AI model identifier (e.g. ``"gpt-4o"``, ``"eleven_turbo_v2_5"``). + + Defaults to ``NOT_GIVEN`` for delta mode. In store mode, set to a + model string or ``None`` if the service has no model concept. + """ extra: Dict[str, Any] = field(default_factory=dict) """Catch-all for service-specific keys that have no declared field.""" @@ -132,10 +167,14 @@ class ServiceSettings: # -- public API ---------------------------------------------------------- def given_fields(self) -> Dict[str, Any]: - """Return a dict of only the fields that were explicitly provided. + """Return a dict of only the fields that are not ``NOT_GIVEN``. - Skips ``NOT_GIVEN`` values and the ``extra`` field itself. Entries - from ``extra`` are included at the top level. + Primarily useful for delta-mode objects to inspect which fields were + set. For a store-mode object this returns all declared fields (since + none should be ``NOT_GIVEN``). + + Skips the ``extra`` field itself but merges its entries into the + returned dict at the top level. Returns: Dictionary mapping field names to their provided values. @@ -150,18 +189,18 @@ class ServiceSettings: result.update(self.extra) return result - def apply_update(self: _S, update: _S) -> Dict[str, Any]: - """Apply *update* onto this settings object, returning changed fields. + def apply_update(self: _S, delta: _S) -> Dict[str, Any]: + """Merge a delta-mode object into this store-mode object. - Only fields in *update* that are **given** (i.e. not ``NOT_GIVEN``) + Only fields in *delta* that are **given** (i.e. not ``NOT_GIVEN``) are considered. A field is "changed" if its new value differs from the current value. - The ``extra`` dicts are merged: keys present in the update overwrite + The ``extra`` dicts are merged: keys present in the delta overwrite keys in the target. Args: - update: A settings object of the same type containing the delta. + delta: A delta-mode settings object of the same type. Returns: A dict mapping each changed field name to its **pre-update** value. @@ -170,7 +209,9 @@ class ServiceSettings: Examples:: + # store-mode object (all fields given) current = TTSSettings(voice="alice", language="en") + # delta-mode object (only voice is set) delta = TTSSettings(voice="bob") changed = current.apply_update(delta) # changed == {"voice": "alice"} @@ -180,7 +221,7 @@ class ServiceSettings: for f in fields(self): if f.name == "extra": continue - new_val = getattr(update, f.name) + new_val = getattr(delta, f.name) if not is_given(new_val): continue old_val = getattr(self, f.name) @@ -189,7 +230,7 @@ class ServiceSettings: changed[f.name] = old_val # Merge extra - for key, new_val in update.extra.items(): + for key, new_val in delta.extra.items(): old_val = self.extra.get(key, NOT_GIVEN) if old_val != new_val: self.extra[key] = new_val @@ -199,10 +240,12 @@ class ServiceSettings: @classmethod def from_mapping(cls: Type[_S], settings: Mapping[str, Any]) -> _S: - """Construct a settings object from a plain dictionary. + """Build a **delta-mode** settings object from a plain dictionary. This exists for backward compatibility with code that passes plain - dicts via ``*UpdateSettingsFrame(settings={...})``. + dicts via ``*UpdateSettingsFrame(settings={...})``. The returned + object is a delta: only the keys present in *settings* are set; + all other fields remain ``NOT_GIVEN``. Keys are matched to dataclass fields by name. Keys listed in ``_aliases`` are translated to their canonical name first. Any @@ -212,13 +255,14 @@ class ServiceSettings: settings: A dictionary of setting names to values. Returns: - A new settings instance with the corresponding fields populated. + A new delta-mode settings instance. Examples:: - update = TTSSettings.from_mapping({"voice_id": "alice", "speed": 1.2}) - # update.voice == "alice" (via alias) - # update.extra == {"speed": 1.2} + delta = TTSSettings.from_mapping({"voice_id": "alice", "speed": 1.2}) + # delta.voice == "alice" (via alias) + # delta.language is NOT_GIVEN (not in the dict) + # delta.extra == {"speed": 1.2} """ field_names = {f.name for f in fields(cls)} - {"extra"} kwargs: Dict[str, Any] = {} @@ -236,6 +280,31 @@ class ServiceSettings: instance.extra = extra return instance + def validate_complete(self) -> None: + """Check that this is a valid store-mode object (no ``NOT_GIVEN`` fields). + + Called automatically by ``AIService.start()`` to catch fields that a + service forgot to initialize in its ``__init__``. Can also be called + manually after constructing a store-mode settings object. + + Logs a warning for each uninitialized field. Failure to initialize + all fields may or may not cause runtime issues — it depends on + whether and how the service actually reads the field — but it indicates + a deviation from expectations and should be fixed. + """ + missing = [ + f.name + for f in fields(self) + if f.name != "extra" and isinstance(getattr(self, f.name), _NotGiven) + ] + if missing: + names = ", ".join(missing) + logger.error( + f"{type(self).__name__}: the following fields are NOT_GIVEN: {names}. " + f"All settings fields should be initialized in the service's " + f"__init__ (use None for unsupported fields)." + ) + def copy(self: _S) -> _S: """Return a deep copy of this settings instance. @@ -254,7 +323,12 @@ class ServiceSettings: class LLMSettings(ServiceSettings): """Runtime-updatable settings for LLM services. - See ``ServiceSettings`` for the general concept. + Used in both store and delta mode — see ``ServiceSettings``. + + These fields are common across LLM providers. Not every provider supports + every field; in store mode, set unsupported fields to ``None`` (e.g. a + service that doesn't support ``seed`` should initialize it as + ``seed=None``). Parameters: model: LLM model identifier. @@ -274,15 +348,15 @@ class LLMSettings(ServiceSettings): and prompts for incomplete turns. """ - temperature: float | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - max_tokens: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - top_p: float | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - top_k: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - frequency_penalty: float | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - presence_penalty: float | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - seed: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - filter_incomplete_user_turns: bool | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - user_turn_completion_config: UserTurnCompletionConfig | _NotGiven = field( + temperature: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + max_tokens: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + top_p: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + top_k: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + frequency_penalty: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + presence_penalty: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + seed: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + filter_incomplete_user_turns: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + user_turn_completion_config: UserTurnCompletionConfig | None | _NotGiven = field( default_factory=lambda: NOT_GIVEN ) @@ -291,23 +365,25 @@ class LLMSettings(ServiceSettings): class TTSSettings(ServiceSettings): """Runtime-updatable settings for TTS services. - See ``ServiceSettings`` for the general concept. + Used in both store and delta mode — see ``ServiceSettings``. + + In store mode, set unsupported fields to ``None`` (e.g. ``language=None`` + if the service doesn't expose a language setting). Parameters: model: TTS model identifier. voice: Voice identifier or name. - language: Language for speech synthesis. The union type reflects the - *input* side: callers may pass a ``Language`` enum or a raw string. - However, the **stored** value is always a service-specific string - — ``TTSService._update_settings`` converts ``Language`` enums via - ``language_to_service_language()`` before writing, and ``__init__`` - methods do the same at construction time. Code that reads - ``self._settings.language`` after initialisation can treat it as - ``str``. + language: Language for speech synthesis. The union type reflects the + *input* side: callers may pass a ``Language`` enum or a raw string + in a delta. However, the **stored** value (in store mode) is + always a service-specific string or ``None`` — + ``TTSService._update_settings`` converts ``Language`` enums via + ``language_to_service_language()`` before writing, and + ``__init__`` methods do the same at construction time. """ voice: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) - language: Language | str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + language: Language | str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) _aliases: ClassVar[Dict[str, str]] = {"voice_id": "voice"} @@ -316,18 +392,20 @@ class TTSSettings(ServiceSettings): class STTSettings(ServiceSettings): """Runtime-updatable settings for STT services. - See ``ServiceSettings`` for the general concept. + Used in both store and delta mode — see ``ServiceSettings``. + + In store mode, set unsupported fields to ``None`` (e.g. ``language=None`` + if the service auto-detects language). Parameters: model: STT model identifier. - language: Language for speech recognition. The union type reflects the - *input* side: callers may pass a ``Language`` enum or a raw string. - However, the **stored** value is always a service-specific string - — ``STTService._update_settings`` converts ``Language`` enums via - ``language_to_service_language()`` before writing, and ``__init__`` - methods do the same at construction time. Code that reads - ``self._settings.language`` after initialisation can treat it as - ``str``. + language: Language for speech recognition. The union type reflects the + *input* side: callers may pass a ``Language`` enum or a raw string + in a delta. However, the **stored** value (in store mode) is + always a service-specific string or ``None`` — + ``STTService._update_settings`` converts ``Language`` enums via + ``language_to_service_language()`` before writing, and + ``__init__`` methods do the same at construction time. """ - language: Language | str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + language: Language | str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) diff --git a/src/pipecat/services/soniox/stt.py b/src/pipecat/services/soniox/stt.py index 630e11862..356b23162 100644 --- a/src/pipecat/services/soniox/stt.py +++ b/src/pipecat/services/soniox/stt.py @@ -225,25 +225,25 @@ class SonioxSTTService(WebsocketSTTService): await super().start(frame) await self._connect() - async def _update_settings(self, update: SonioxSTTSettings) -> dict[str, Any]: - """Apply a settings update, keeping ``input_params`` in sync. + async def _update_settings(self, delta: SonioxSTTSettings) -> dict[str, Any]: + """Apply a settings delta, keeping ``input_params`` in sync. Top-level ``model`` is the source of truth. When it is given in - *update* its value is propagated into ``input_params``. When only + *delta* its value is propagated into ``input_params``. When only ``input_params`` is given, its ``model`` is propagated *up* to the top-level field. Settings are stored but not applied to the active connection. Args: - update: A settings delta. + delta: A settings delta. Returns: Dict mapping changed field names to their previous values. """ - model_given = is_given(getattr(update, "model", NOT_GIVEN)) + model_given = is_given(getattr(delta, "model", NOT_GIVEN)) - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed diff --git a/src/pipecat/services/speechmatics/stt.py b/src/pipecat/services/speechmatics/stt.py index 4a476c01f..61bf8b69f 100644 --- a/src/pipecat/services/speechmatics/stt.py +++ b/src/pipecat/services/speechmatics/stt.py @@ -33,7 +33,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, is_given +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import SPEECHMATICS_TTFS_P99 from pipecat.services.stt_service import STTService from pipecat.transcriptions.language import Language, resolve_language @@ -429,6 +429,7 @@ class SpeechmaticsSTTService(STTService): # Settings — seeded from InputParams self._settings = SpeechmaticsSTTSettings( + model=None, language=params.language, domain=params.domain, turn_detection_mode=params.turn_detection_mode, @@ -492,8 +493,8 @@ class SpeechmaticsSTTService(STTService): await super().start(frame) await self._connect() - async def _update_settings(self, update: SpeechmaticsSTTSettings) -> dict[str, Any]: - """Apply settings update, reconnecting only when necessary. + async def _update_settings(self, delta: SpeechmaticsSTTSettings) -> dict[str, Any]: + """Apply settings delta, reconnecting only when necessary. Fields are classified into three categories (see ``SpeechmaticsSTTSettings``): @@ -506,12 +507,12 @@ class SpeechmaticsSTTService(STTService): time and therefore require a full disconnect / reconnect. Args: - update: A settings delta. + delta: A settings delta. Returns: Dict mapping changed field names to their previous values. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if not changed: return changed @@ -674,21 +675,21 @@ class SpeechmaticsSTTService(STTService): # Language + domain language = s.language config.language = self._language_to_speechmatics_language(language) - config.domain = s.domain if is_given(s.domain) else None + config.domain = s.domain if s.domain is not None else None config.output_locale = self._locale_to_speechmatics_locale(config.language, language) # Speaker config config.speaker_config = SpeakerFocusConfig( - focus_speakers=s.focus_speakers if is_given(s.focus_speakers) else [], - ignore_speakers=s.ignore_speakers if is_given(s.ignore_speakers) else [], - focus_mode=s.focus_mode if is_given(s.focus_mode) else SpeakerFocusMode.RETAIN, + focus_speakers=s.focus_speakers if s.focus_speakers is not None else [], + ignore_speakers=s.ignore_speakers if s.ignore_speakers is not None else [], + focus_mode=s.focus_mode if s.focus_mode is not None else SpeakerFocusMode.RETAIN, ) - config.known_speakers = s.known_speakers if is_given(s.known_speakers) else [] + config.known_speakers = s.known_speakers if s.known_speakers is not None else [] # Custom dictionary - config.additional_vocab = s.additional_vocab if is_given(s.additional_vocab) else [] + config.additional_vocab = s.additional_vocab if s.additional_vocab is not None else [] - # Advanced parameters — only set if given (not NOT_GIVEN or None) + # Advanced parameters — only set if not None for param in [ "operating_point", "max_delay", @@ -703,17 +704,17 @@ class SpeechmaticsSTTService(STTService): "prefer_current_speaker", ]: val = getattr(s, param) - if is_given(val) and val is not None: + if val is not None: setattr(config, param, val) # Extra parameters - if is_given(s.extra_params) and isinstance(s.extra_params, dict): + if isinstance(s.extra_params, dict): for key, value in s.extra_params.items(): if hasattr(config, key): setattr(config, key, value) # Enable sentences - split = s.split_sentences if is_given(s.split_sentences) else False + split = s.split_sentences if s.split_sentences is not None else False config.speech_segment_config = SpeechSegmentConfig(emit_sentences=split or False) return config diff --git a/src/pipecat/services/speechmatics/tts.py b/src/pipecat/services/speechmatics/tts.py index 7c8d9fca5..32fb0c2b3 100644 --- a/src/pipecat/services/speechmatics/tts.py +++ b/src/pipecat/services/speechmatics/tts.py @@ -108,7 +108,9 @@ class SpeechmaticsTTSService(TTSService): params = params or SpeechmaticsTTSService.InputParams() self._settings = SpeechmaticsTTSSettings( + model=None, voice=voice_id, + language=None, max_retries=params.max_retries, ) diff --git a/src/pipecat/services/stt_service.py b/src/pipecat/services/stt_service.py index dfdae6de6..80448223c 100644 --- a/src/pipecat/services/stt_service.py +++ b/src/pipecat/services/stt_service.py @@ -262,8 +262,8 @@ class STTService(AIService): await self._cancel_ttfb_timeout() await self._cancel_keepalive_task() - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply an STT settings update. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply an STT settings delta. Handles ``model`` (via parent). Translates ``Language`` enum values before applying so the stored value is a service-specific string. @@ -272,18 +272,18 @@ class STTService(AIService): changed-field dict. Args: - update: An STT settings delta. + delta: An STT settings delta. Returns: Dict mapping changed field names to their previous values. """ # Translate language *before* applying so the stored value is canonical - if is_given(update.language) and isinstance(update.language, Language): - converted = self.language_to_service_language(update.language) + if is_given(delta.language) and isinstance(delta.language, Language): + converted = self.language_to_service_language(delta.language) if converted is not None: - update.language = converted + delta.language = converted - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) return changed async def process_audio_frame(self, frame: AudioRawFrame, direction: FrameDirection): @@ -349,20 +349,20 @@ class STTService(AIService): await self._handle_vad_user_stopped_speaking(frame) await self.push_frame(frame, direction) elif isinstance(frame, STTUpdateSettingsFrame): - if frame.update is not None: - await self._update_settings(frame.update) + if frame.delta is not None: + await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( "Passing a dict via STTUpdateSettingsFrame(settings={...}) is deprecated " - "since 0.0.103, use STTUpdateSettingsFrame(update=STTSettings(...)) instead.", + "since 0.0.103, use STTUpdateSettingsFrame(delta=STTSettings(...)) instead.", DeprecationWarning, stacklevel=2, ) - update = type(self._settings).from_mapping(frame.settings) - await self._update_settings(update) + delta = type(self._settings).from_mapping(frame.settings) + await self._update_settings(delta) elif isinstance(frame, STTMuteFrame): self._muted = frame.mute logger.debug(f"STT service {'muted' if frame.mute else 'unmuted'}") diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index e7b57833d..59920c342 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -196,9 +196,7 @@ class TTSService(AIService): self._append_trailing_space: bool = append_trailing_space self._init_sample_rate = sample_rate self._sample_rate = 0 - self._settings = TTSSettings( - voice="" - ) # Here in case subclass doesn't implement more specific settings (hopefully shouldn't happen) + self._settings = TTSSettings() # Here in case subclass doesn't implement more specific settings (hopefully shouldn't happen) self._text_aggregator: BaseTextAggregator = text_aggregator or SimpleTextAggregator() if text_aggregator: import warnings @@ -440,24 +438,24 @@ class TTSService(AIService): if not (agg_type == aggregation_type and func == transform_function) ] - async def _update_settings(self, update: TTSSettings) -> dict[str, Any]: - """Apply a TTS settings update. + async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: + """Apply a TTS settings delta. Translates language to service-specific value before applying. Args: - update: A TTS settings delta. + delta: A TTS settings delta. Returns: Dict mapping changed field names to their previous values. """ # Translate language *before* applying so the stored value is canonical - if is_given(update.language) and isinstance(update.language, Language): - converted = self.language_to_service_language(update.language) + if is_given(delta.language) and isinstance(delta.language, Language): + converted = self.language_to_service_language(delta.language) if converted is not None: - update.language = converted + delta.language = converted - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) return changed @@ -548,20 +546,20 @@ class TTSService(AIService): await self.flush_audio() self._processing_text = processing_text elif isinstance(frame, TTSUpdateSettingsFrame): - if frame.update is not None: - await self._update_settings(frame.update) + if frame.delta is not None: + await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( "Passing a dict via TTSUpdateSettingsFrame(settings={...}) is deprecated " - "since 0.0.103, use TTSUpdateSettingsFrame(update=TTSSettings(...)) instead.", + "since 0.0.103, use TTSUpdateSettingsFrame(delta=TTSSettings(...)) instead.", DeprecationWarning, stacklevel=2, ) - update = type(self._settings).from_mapping(frame.settings) - await self._update_settings(update) + delta = type(self._settings).from_mapping(frame.settings) + await self._update_settings(delta) elif isinstance(frame, BotStoppedSpeakingFrame): await self._maybe_resume_frame_processing() await self.push_frame(frame, direction) diff --git a/src/pipecat/services/ultravox/llm.py b/src/pipecat/services/ultravox/llm.py index 436653c7e..11525258a 100644 --- a/src/pipecat/services/ultravox/llm.py +++ b/src/pipecat/services/ultravox/llm.py @@ -177,7 +177,19 @@ class UltravoxRealtimeLLMService(LLMService): **kwargs: Additional arguments passed to parent LLMService. """ super().__init__(**kwargs) - self._settings = UltravoxRealtimeLLMSettings() + self._settings = UltravoxRealtimeLLMSettings( + model=None, + temperature=None, + max_tokens=None, + top_p=None, + top_k=None, + frequency_penalty=None, + presence_penalty=None, + seed=None, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, + output_medium=None, + ) self._params = params if one_shot_selected_tools: if not isinstance(self._params, OneShotInputParams): @@ -325,8 +337,8 @@ class UltravoxRealtimeLLMService(LLMService): await self.cancel_task(self._receive_task, timeout=1.0) self._receive_task = None - async def _update_settings(self, update: UltravoxRealtimeLLMSettings): - changed = await super()._update_settings(update) + async def _update_settings(self, delta: UltravoxRealtimeLLMSettings): + changed = await super()._update_settings(delta) if "output_medium" in changed: await self._update_output_medium(self._settings.output_medium) self._warn_unhandled_updated_settings(changed.keys() - {"output_medium"}) diff --git a/src/pipecat/services/whisper/base_stt.py b/src/pipecat/services/whisper/base_stt.py index 9def3c2f1..9d2b3ab51 100644 --- a/src/pipecat/services/whisper/base_stt.py +++ b/src/pipecat/services/whisper/base_stt.py @@ -174,13 +174,13 @@ class BaseWhisperSTTService(SegmentedSTTService): def _create_client(self, api_key: Optional[str], base_url: Optional[str]): return AsyncOpenAI(api_key=api_key, base_url=base_url) - async def _update_settings(self, update: STTSettings) -> dict[str, Any]: - """Apply a settings update, syncing instance variables. + async def _update_settings(self, delta: STTSettings) -> dict[str, Any]: + """Apply a settings delta, syncing instance variables. Keeps ``_language``, ``_prompt``, and ``_temperature`` in sync with the settings fields. """ - changed = await super()._update_settings(update) + changed = await super()._update_settings(delta) if "language" in changed: self._language = self._settings.language diff --git a/src/pipecat/services/xtts/tts.py b/src/pipecat/services/xtts/tts.py index ba2eb4fc2..ab06ffb5a 100644 --- a/src/pipecat/services/xtts/tts.py +++ b/src/pipecat/services/xtts/tts.py @@ -114,6 +114,7 @@ class XTTSService(TTSService): super().__init__(sample_rate=sample_rate, **kwargs) self._settings = XTTSTTSSettings( + model=None, voice=voice_id, language=self.language_to_service_language(language), base_url=base_url,