Change apply_update / _update_settings return type from set[str] to dict[str, Any]. The dict maps each changed field name to its pre-update value, enabling services to do granular diffing of complex settings objects. Existing call-site patterns ("field" in changed, if changed, iteration) work unchanged; set-difference sites use changed.keys() - {...}.
This commit is contained in:
@@ -267,10 +267,10 @@ class MySTTService(STTService):
|
|||||||
self._settings = MySTTSettings(model=model, region=region)
|
self._settings = MySTTSettings(model=model, region=region)
|
||||||
```
|
```
|
||||||
|
|
||||||
To react to runtime setting changes, override `_update_settings`. The base implementation applies the delta to `self._settings` and returns the set of field names that changed. Your override should call `super()` first, then act on the changed fields:
|
To react to runtime setting changes, override `_update_settings`. The base implementation applies the delta to `self._settings` and returns a `dict` mapping each changed field name to its **pre-update** value. Your override should call `super()` first, then act on the changed fields:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, reconfiguring the recognizer if needed."""
|
"""Apply a settings update, reconfiguring the recognizer if needed."""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
@@ -282,12 +282,14 @@ async def _update_settings(self, update: STTSettings) -> set[str]:
|
|||||||
return changed
|
return changed
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The dict keys work like a set for membership tests (`"language" in changed`) and truthiness (`if changed`). Use `changed.keys() - {"language"}` for set difference, or `changed["language"]` to inspect the previous value of a field.
|
||||||
|
|
||||||
Note that, in this example, the service requires a reconnect to apply the new language. Consider, for each setting, whether your service requires reconnection or can apply changes in-place.
|
Note that, in this example, the service requires a reconnect to apply the new language. Consider, for each setting, whether your service requires reconnection or can apply changes in-place.
|
||||||
|
|
||||||
If your service can't yet apply certain settings at runtime, call `self._warn_unhandled_updated_settings(changed)` with the set of unhandled field names so users get a clear log message:
|
If your service can't yet apply certain settings at runtime, call `self._warn_unhandled_updated_settings(changed)` with the unhandled field names so users get a clear log message:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
if not changed:
|
if not changed:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Provides the foundation for all AI services in the Pipecat framework, including
|
|||||||
model management, settings handling, and frame processing lifecycle methods.
|
model management, settings handling, and frame processing lifecycle methods.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Any, AsyncGenerator, Dict, Set
|
from typing import Any, AsyncGenerator, Dict
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -97,12 +97,13 @@ class AIService(FrameProcessor):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def _update_settings(self, update: ServiceSettings) -> Set[str]:
|
async def _update_settings(self, update: ServiceSettings) -> Dict[str, Any]:
|
||||||
"""Apply a settings update and return the set of changed field names.
|
"""Apply a settings update and return the changed fields.
|
||||||
|
|
||||||
The update is applied to ``_settings`` and the changed-field set is
|
The update is applied to ``_settings`` and a dict mapping each changed
|
||||||
returned. The ``model`` field is handled specially: when it changes,
|
field name to its **pre-update** value is returned. The ``model``
|
||||||
``set_model_name`` is called.
|
field is handled specially: when it changes, ``set_model_name`` is
|
||||||
|
called.
|
||||||
|
|
||||||
Concrete services should override this method (calling ``super()``)
|
Concrete services should override this method (calling ``super()``)
|
||||||
to react to specific changed fields (e.g. reconnect on voice change).
|
to react to specific changed fields (e.g. reconnect on voice change).
|
||||||
@@ -111,7 +112,7 @@ class AIService(FrameProcessor):
|
|||||||
update: A settings delta.
|
update: A settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = self._settings.apply_update(update)
|
changed = self._settings.apply_update(update)
|
||||||
|
|
||||||
@@ -119,16 +120,15 @@ class AIService(FrameProcessor):
|
|||||||
self.set_model_name(self._settings.model)
|
self.set_model_name(self._settings.model)
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
logger.info(f"{self.name}: updated settings fields: {changed}")
|
logger.info(f"{self.name}: updated settings fields: {set(changed)}")
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
def _warn_unhandled_updated_settings(self, unhandled: Set[str]):
|
def _warn_unhandled_updated_settings(self, unhandled):
|
||||||
"""Log a warning for settings changes that won't take effect at runtime.
|
"""Log a warning for settings changes that won't take effect at runtime.
|
||||||
|
|
||||||
Convenience helper for ``_update_settings`` overrides. Call with the
|
Convenience helper for ``_update_settings`` overrides. Accepts any
|
||||||
set of field names that changed but that the service does not (yet)
|
iterable of field names (a ``dict``, ``set``, ``dict_keys``, etc.).
|
||||||
apply at runtime.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
unhandled: Field names that changed but are not applied.
|
unhandled: Field names that changed but are not applied.
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ class AssemblyAISTTService(WebsocketSTTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
@@ -193,7 +193,7 @@ class AssemblyAISTTService(WebsocketSTTService):
|
|||||||
update: A :class:`STTSettings` (or ``AssemblyAISTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``AssemblyAISTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ class AWSNovaSonicLLMService(LLMService):
|
|||||||
# settings
|
# settings
|
||||||
#
|
#
|
||||||
|
|
||||||
async def _update_settings(self, update: LLMSettings) -> set[str]:
|
async def _update_settings(self, update: LLMSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import os
|
|||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ class AWSTranscribeSTTService(WebsocketSTTService):
|
|||||||
}
|
}
|
||||||
return encoding_map.get(encoding, encoding)
|
return encoding_map.get(encoding, encoding)
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Speech SDK for real-time audio transcription.
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ class AzureSTTService(STTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active recognizer.
|
Settings are stored but not applied to the active recognizer.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ the Cartesia Live transcription API for real-time speech recognition.
|
|||||||
import json
|
import json
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -294,14 +294,14 @@ class CartesiaSTTService(WebsocketSTTService):
|
|||||||
|
|
||||||
await self._disconnect_websocket()
|
await self._disconnect_websocket()
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: A :class:`STTSettings` (or ``CartesiaSTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``CartesiaSTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ class CartesiaTTSService(AudioContextWordTTSService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ class DeepgramFluxSTTService(WebsocketSTTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"""Deepgram speech-to-text service implementation."""
|
"""Deepgram speech-to-text service implementation."""
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Dict, Optional
|
from typing import Any, AsyncGenerator, Dict, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ class DeepgramSTTService(STTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, keeping ``live_options`` in sync.
|
"""Apply a settings update, keeping ``live_options`` in sync.
|
||||||
|
|
||||||
Top-level ``model`` and ``language`` are the source of truth. When
|
Top-level ``model`` and ``language`` are the source of truth. When
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ languages, and various Deepgram features.
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ class DeepgramSageMakerSTTService(STTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, keeping ``live_options`` in sync.
|
"""Apply a settings update, keeping ``live_options`` in sync.
|
||||||
|
|
||||||
Top-level ``model`` and ``language`` are the source of truth. When
|
Top-level ``model`` and ``language`` are the source of truth. When
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import io
|
|||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -294,7 +294,7 @@ class ElevenLabsSTTService(SegmentedSTTService):
|
|||||||
"""
|
"""
|
||||||
return language_to_elevenlabs_language(language)
|
return language_to_elevenlabs_language(language)
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Converts language to ElevenLabs format before applying and keeps
|
Converts language to ElevenLabs format before applying and keeps
|
||||||
@@ -304,7 +304,7 @@ class ElevenLabsSTTService(SegmentedSTTService):
|
|||||||
update: A :class:`STTSettings` (or ``ElevenLabsSTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``ElevenLabsSTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
# Convert language to ElevenLabs format before applying
|
# Convert language to ElevenLabs format before applying
|
||||||
if is_given(update.language) and isinstance(update.language, Language):
|
if is_given(update.language) and isinstance(update.language, Language):
|
||||||
@@ -543,7 +543,7 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and reconnect if anything changed.
|
"""Apply a settings update and reconnect if anything changed.
|
||||||
|
|
||||||
Converts language to ElevenLabs format before applying and keeps
|
Converts language to ElevenLabs format before applying and keeps
|
||||||
@@ -553,7 +553,7 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService):
|
|||||||
update: A :class:`STTSettings` (or ``ElevenLabsRealtimeSTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``ElevenLabsRealtimeSTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
# Convert language to ElevenLabs format before applying
|
# Convert language to ElevenLabs format before applying
|
||||||
if is_given(update.language) and isinstance(update.language, Language):
|
if is_given(update.language) and isinstance(update.language, Language):
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ class ElevenLabsTTSService(AudioContextWordTTSService):
|
|||||||
voice_settings[key] = val
|
voice_settings[key] = val
|
||||||
return voice_settings or None
|
return voice_settings or None
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, reconnecting as needed.
|
"""Apply a settings update, reconnecting as needed.
|
||||||
|
|
||||||
Uses the declarative ``URL_FIELDS`` and ``VOICE_SETTINGS_FIELDS``
|
Uses the declarative ``URL_FIELDS`` and ``VOICE_SETTINGS_FIELDS``
|
||||||
@@ -482,7 +482,7 @@ class ElevenLabsTTSService(AudioContextWordTTSService):
|
|||||||
update: A :class:`TTSSettings` (or ``ElevenLabsTTSSettings``) delta.
|
update: A :class:`TTSSettings` (or ``ElevenLabsTTSSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
@@ -520,7 +520,7 @@ class ElevenLabsTTSService(AudioContextWordTTSService):
|
|||||||
# Reconnect applies all settings; only warn about fields not handled
|
# Reconnect applies all settings; only warn about fields not handled
|
||||||
# by voice settings or URL changes.
|
# by voice settings or URL changes.
|
||||||
handled = ElevenLabsTTSSettings.URL_FIELDS | ElevenLabsTTSSettings.VOICE_SETTINGS_FIELDS
|
handled = ElevenLabsTTSSettings.URL_FIELDS | ElevenLabsTTSSettings.VOICE_SETTINGS_FIELDS
|
||||||
self._warn_unhandled_updated_settings(changed - handled)
|
self._warn_unhandled_updated_settings(changed.keys() - handled)
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
@@ -964,14 +964,14 @@ class ElevenLabsHttpTTSService(WordTTSService):
|
|||||||
def _set_voice_settings(self):
|
def _set_voice_settings(self):
|
||||||
return build_elevenlabs_voice_settings(self._settings)
|
return build_elevenlabs_voice_settings(self._settings)
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and rebuild voice settings.
|
"""Apply a settings update and rebuild voice settings.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: A :class:`TTSSettings` (or ``ElevenLabsHttpTTSSettings``) delta.
|
update: A :class:`TTSSettings` (or ``ElevenLabsHttpTTSSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
if changed:
|
if changed:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ transcription using segmented audio processing.
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -251,7 +251,7 @@ class FalSTTService(SegmentedSTTService):
|
|||||||
"""
|
"""
|
||||||
return language_to_fal_language(language)
|
return language_to_fal_language(language)
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, converting language if changed."""
|
"""Apply a settings update, converting language if changed."""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ for streaming text-to-speech synthesis with customizable voice parameters.
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Literal, Optional
|
from typing import Any, AsyncGenerator, Literal, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -184,7 +184,7 @@ class FishAudioTTSService(InterruptibleTTSService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and reconnect if needed.
|
"""Apply a settings update and reconnect if needed.
|
||||||
|
|
||||||
Any change to voice or model triggers a WebSocket reconnect.
|
Any change to voice or model triggers a WebSocket reconnect.
|
||||||
@@ -193,7 +193,7 @@ class FishAudioTTSService(InterruptibleTTSService):
|
|||||||
update: A :class:`TTSSettings` (or ``FishAudioTTSSettings``) delta.
|
update: A :class:`TTSSettings` (or ``FishAudioTTSSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
if changed:
|
if changed:
|
||||||
|
|||||||
@@ -379,7 +379,7 @@ class GladiaSTTService(WebsocketSTTService):
|
|||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
await self._connect()
|
await self._connect()
|
||||||
|
|
||||||
async def _update_settings(self, update: GladiaSTTSettings) -> set[str]:
|
async def _update_settings(self, update: GladiaSTTSettings) -> dict[str, Any]:
|
||||||
"""Apply settings update.
|
"""Apply settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active session.
|
Settings are stored but not applied to the active session.
|
||||||
@@ -388,7 +388,7 @@ class GladiaSTTService(WebsocketSTTService):
|
|||||||
update: A settings delta.
|
update: A settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
|
|||||||
@@ -804,7 +804,7 @@ class GeminiLiveLLMService(LLMService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: LLMSettings) -> set[str]:
|
async def _update_settings(self, update: LLMSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|||||||
@@ -630,7 +630,7 @@ class GoogleSTTService(STTService):
|
|||||||
logger.debug(f"Switching STT languages to: {languages}")
|
logger.debug(f"Switching STT languages to: {languages}")
|
||||||
await self._update_settings(GoogleSTTSettings(languages=list(languages)))
|
await self._update_settings(GoogleSTTSettings(languages=list(languages)))
|
||||||
|
|
||||||
async def _update_settings(self, update: GoogleSTTSettings) -> set[str]:
|
async def _update_settings(self, update: GoogleSTTSettings) -> dict[str, Any]:
|
||||||
"""Apply settings update and reconnect if anything changed.
|
"""Apply settings update and reconnect if anything changed.
|
||||||
|
|
||||||
Handles ``language`` from base ``set_language`` by converting it to
|
Handles ``language`` from base ``set_language`` by converting it to
|
||||||
@@ -642,7 +642,7 @@ class GoogleSTTService(STTService):
|
|||||||
update: A settings delta.
|
update: A settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
from pipecat.services.settings import is_given
|
from pipecat.services.settings import is_given
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from pipecat.utils.tracing.service_decorators import traced_tts
|
|||||||
os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "false"
|
os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "false"
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, List, Literal, Optional
|
from typing import Any, AsyncGenerator, List, Literal, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -680,7 +680,7 @@ class GoogleHttpTTSService(TTSService):
|
|||||||
"""
|
"""
|
||||||
return language_to_google_tts_language(language)
|
return language_to_google_tts_language(language)
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Override to handle speaking_rate validation.
|
"""Override to handle speaking_rate validation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1024,7 +1024,7 @@ class GoogleTTSService(GoogleBaseTTSService):
|
|||||||
credentials, credentials_path
|
credentials, credentials_path
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Override to handle speaking_rate validation.
|
"""Override to handle speaking_rate validation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1259,14 +1259,14 @@ class GeminiTTSService(GoogleBaseTTSService):
|
|||||||
f"Current rate of {self.sample_rate}Hz may cause issues."
|
f"Current rate of {self.sample_rate}Hz may cause issues."
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update with voice validation.
|
"""Apply a settings update with voice validation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: Settings delta. Can include 'voice', 'prompt', etc.
|
update: Settings delta. Can include 'voice', 'prompt', etc.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
if is_given(update.voice) and update.voice not in self.AVAILABLE_VOICES:
|
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.")
|
logger.warning(f"Voice '{update.voice}' not in known voices list. Using anyway.")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ WebSocket API for streaming audio transcription.
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -171,14 +171,14 @@ class GradiumSTTService(WebsocketSTTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, sync params, and reconnect.
|
"""Apply a settings update, sync params, and reconnect.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: A :class:`STTSettings` (or ``GradiumSTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``GradiumSTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
if not changed:
|
if not changed:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -119,14 +119,14 @@ class GradiumTTSService(InterruptibleWordTTSService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and reconnect if voice changed.
|
"""Apply a settings update and reconnect if voice changed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: A :class:`TTSSettings` (or ``GradiumTTSSettings``) delta.
|
update: A :class:`TTSSettings` (or ``GradiumTTSSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
prev_voice = self._voice_id
|
prev_voice = self._voice_id
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ class InworldHttpTTSService(WordTTSService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|||||||
@@ -313,14 +313,14 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService):
|
|||||||
await self._cancel_sequential_runner_task()
|
await self._cancel_sequential_runner_task()
|
||||||
await self._cancel_summary_task()
|
await self._cancel_summary_task()
|
||||||
|
|
||||||
async def _update_settings(self, update: LLMSettings) -> set[str]:
|
async def _update_settings(self, update: LLMSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, handling turn-completion fields.
|
"""Apply a settings update, handling turn-completion fields.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: An LLM settings delta.
|
update: An LLM settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import asyncio
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -181,7 +181,7 @@ class NeuphonicTTSService(InterruptibleTTSService):
|
|||||||
"""
|
"""
|
||||||
return language_to_neuphonic_lang_code(language)
|
return language_to_neuphonic_lang_code(language)
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and reconnect with new configuration."""
|
"""Apply a settings update and reconnect with new configuration."""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
if changed:
|
if changed:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from concurrent.futures import CancelledError as FuturesCancelledError
|
from concurrent.futures import CancelledError as FuturesCancelledError
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, List, Mapping, Optional
|
from typing import Any, AsyncGenerator, List, Mapping, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -579,14 +579,14 @@ class NvidiaSegmentedSTTService(SegmentedSTTService):
|
|||||||
self._config = self._create_recognition_config()
|
self._config = self._create_recognition_config()
|
||||||
logger.debug(f"Initialized NvidiaSegmentedSTTService with model: {self.model_name}")
|
logger.debug(f"Initialized NvidiaSegmentedSTTService with model: {self.model_name}")
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and sync internal state.
|
"""Apply a settings update and sync internal state.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: A :class:`STTSettings` (or ``NvidiaSegmentedSTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``NvidiaSegmentedSTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Provides two STT services:
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Literal, Optional, Union
|
from typing import Any, AsyncGenerator, Literal, Optional, Union
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and send session update if needed.
|
"""Apply a settings update and send session update if needed.
|
||||||
|
|
||||||
Keeps ``_language_code`` and ``_prompt`` in sync with settings
|
Keeps ``_language_code`` and ``_prompt`` in sync with settings
|
||||||
@@ -278,7 +278,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService):
|
|||||||
update: A :class:`STTSettings` (or ``OpenAIRealtimeSTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``OpenAIRealtimeSTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import json
|
|||||||
import struct
|
import struct
|
||||||
import warnings
|
import warnings
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -215,7 +215,7 @@ class PlayHTTTSService(InterruptibleTTSService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update.
|
"""Apply a settings update.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using Rime's API for streaming and batch audio synthesis.
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -271,7 +271,7 @@ class RimeTTSService(AudioContextWordTTSService):
|
|||||||
self._extra_msg_fields["inlineSpeedAlpha"] = ",".join(speed_vals + [str(speed)])
|
self._extra_msg_fields["inlineSpeedAlpha"] = ",".join(speed_vals + [str(speed)])
|
||||||
return f"[{text}]"
|
return f"[{text}]"
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and reconnect if voice changed."""
|
"""Apply a settings update and reconnect if voice changed."""
|
||||||
prev_voice = self._voice_id
|
prev_voice = self._voice_id
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
@@ -977,7 +977,7 @@ class RimeNonJsonTTSService(InterruptibleTTSService):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
yield ErrorFrame(error=f"Unknown error occurred: {e}")
|
yield ErrorFrame(error=f"Unknown error occurred: {e}")
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and reconnect if necessary.
|
"""Apply a settings update and reconnect if necessary.
|
||||||
|
|
||||||
Since all settings are WebSocket URL query parameters,
|
Since all settings are WebSocket URL query parameters,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ can handle multiple audio formats for Indian language speech recognition.
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Dict, Literal, Optional
|
from typing import Any, AsyncGenerator, Dict, Literal, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -306,14 +306,14 @@ class SarvamSTTService(STTService):
|
|||||||
if self._socket_client:
|
if self._socket_client:
|
||||||
await self._socket_client.flush()
|
await self._socket_client.flush()
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, validate, sync state, and reconnect.
|
"""Apply a settings update, validate, sync state, and reconnect.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: A :class:`STTSettings` (or ``SarvamSTTSettings``) delta.
|
update: A :class:`STTSettings` (or ``SarvamSTTSettings``) delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If a setting is not supported by the current model.
|
ValueError: If a setting is not supported by the current model.
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import base64
|
|||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import AsyncGenerator, Dict, List, Optional, Tuple
|
from typing import Any, AsyncGenerator, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -953,12 +953,12 @@ class SarvamTTSService(InterruptibleTTSService):
|
|||||||
if isinstance(frame, (LLMFullResponseEndFrame, EndFrame)):
|
if isinstance(frame, (LLMFullResponseEndFrame, EndFrame)):
|
||||||
await self.flush_audio()
|
await self.flush_audio()
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update and resend config if voice changed."""
|
"""Apply a settings update and resend config if voice changed."""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
if "voice" in changed:
|
if "voice" in changed:
|
||||||
await self._send_config()
|
await self._send_config()
|
||||||
self._warn_unhandled_updated_settings(changed - {"voice"})
|
self._warn_unhandled_updated_settings(changed.keys() - {"voice"})
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
async def _connect(self):
|
async def _connect(self):
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Key concepts:
|
|||||||
service's current settings *and* for update objects. Fields set to
|
service's current settings *and* for update objects. Fields set to
|
||||||
``NOT_GIVEN`` are simply skipped when applying an update.
|
``NOT_GIVEN`` are simply skipped when applying an update.
|
||||||
- **apply_update**: Applies a delta onto a target settings object and returns
|
- **apply_update**: Applies a delta onto a target settings object and returns
|
||||||
the set of field names that actually changed.
|
a dict mapping each changed field name to its previous value.
|
||||||
- **from_mapping**: Constructs a settings object from a plain dict,
|
- **from_mapping**: Constructs a settings object from a plain dict,
|
||||||
supporting field aliases (e.g. ``"voice_id"`` → ``"voice"``).
|
supporting field aliases (e.g. ``"voice_id"`` → ``"voice"``).
|
||||||
- **Extras**: Unknown keys land in the ``extra`` dict so services that have
|
- **Extras**: Unknown keys land in the ``extra`` dict so services that have
|
||||||
@@ -30,7 +30,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
from dataclasses import dataclass, field, fields
|
from dataclasses import dataclass, field, fields
|
||||||
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Mapping, Optional, Set, Type, TypeVar
|
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Mapping, Optional, Type, TypeVar
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -140,8 +140,8 @@ class ServiceSettings:
|
|||||||
result.update(self.extra)
|
result.update(self.extra)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def apply_update(self: _S, update: _S) -> Set[str]:
|
def apply_update(self: _S, update: _S) -> Dict[str, Any]:
|
||||||
"""Apply *update* onto this settings object, returning changed field names.
|
"""Apply *update* onto this settings object, returning changed fields.
|
||||||
|
|
||||||
Only fields in *update* that are **given** (i.e. not ``NOT_GIVEN``)
|
Only fields in *update* that are **given** (i.e. not ``NOT_GIVEN``)
|
||||||
are considered. A field is "changed" if its new value differs from
|
are considered. A field is "changed" if its new value differs from
|
||||||
@@ -154,17 +154,19 @@ class ServiceSettings:
|
|||||||
update: A settings object of the same type containing the delta.
|
update: A settings object of the same type containing the delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The set of field names whose values actually changed.
|
A dict mapping each changed field name to its **pre-update** value.
|
||||||
|
Use ``changed.keys()`` for the set of names, or index with
|
||||||
|
``changed["field"]`` to inspect the old value.
|
||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
current = TTSSettings(voice="alice", language="en")
|
current = TTSSettings(voice="alice", language="en")
|
||||||
delta = TTSSettings(voice="bob")
|
delta = TTSSettings(voice="bob")
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
# changed == {"voice"}
|
# changed == {"voice": "alice"}
|
||||||
# current.voice == "bob", current.language == "en"
|
# current.voice == "bob", current.language == "en"
|
||||||
"""
|
"""
|
||||||
changed: Set[str] = set()
|
changed: Dict[str, Any] = {}
|
||||||
for f in fields(self):
|
for f in fields(self):
|
||||||
if f.name == "extra":
|
if f.name == "extra":
|
||||||
continue
|
continue
|
||||||
@@ -174,14 +176,14 @@ class ServiceSettings:
|
|||||||
old_val = getattr(self, f.name)
|
old_val = getattr(self, f.name)
|
||||||
if old_val != new_val:
|
if old_val != new_val:
|
||||||
setattr(self, f.name, new_val)
|
setattr(self, f.name, new_val)
|
||||||
changed.add(f.name)
|
changed[f.name] = old_val
|
||||||
|
|
||||||
# Merge extra
|
# Merge extra
|
||||||
for key, new_val in update.extra.items():
|
for key, new_val in update.extra.items():
|
||||||
old_val = self.extra.get(key, NOT_GIVEN)
|
old_val = self.extra.get(key, NOT_GIVEN)
|
||||||
if old_val != new_val:
|
if old_val != new_val:
|
||||||
self.extra[key] = new_val
|
self.extra[key] = new_val
|
||||||
changed.add(key)
|
changed[key] = old_val
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class SonioxSTTService(WebsocketSTTService):
|
|||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
await self._connect()
|
await self._connect()
|
||||||
|
|
||||||
async def _update_settings(self, update: SonioxSTTSettings) -> set[str]:
|
async def _update_settings(self, update: SonioxSTTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, keeping ``input_params`` in sync.
|
"""Apply a settings update, keeping ``input_params`` in sync.
|
||||||
|
|
||||||
Top-level ``model`` is the source of truth. When it is given in
|
Top-level ``model`` is the source of truth. When it is given in
|
||||||
@@ -231,7 +231,7 @@ class SonioxSTTService(WebsocketSTTService):
|
|||||||
update: A settings delta.
|
update: A settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
model_given = is_given(getattr(update, "model", NOT_GIVEN))
|
model_given = is_given(getattr(update, "model", NOT_GIVEN))
|
||||||
|
|
||||||
|
|||||||
@@ -480,7 +480,7 @@ class SpeechmaticsSTTService(STTService):
|
|||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
await self._connect()
|
await self._connect()
|
||||||
|
|
||||||
async def _update_settings(self, update: SpeechmaticsSTTSettings) -> set[str]:
|
async def _update_settings(self, update: SpeechmaticsSTTSettings) -> dict[str, Any]:
|
||||||
"""Apply settings update, reconnecting only when necessary.
|
"""Apply settings update, reconnecting only when necessary.
|
||||||
|
|
||||||
Fields are classified into three categories (see
|
Fields are classified into three categories (see
|
||||||
@@ -497,7 +497,7 @@ class SpeechmaticsSTTService(STTService):
|
|||||||
update: A settings delta.
|
update: A settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
|
|
||||||
@@ -505,7 +505,7 @@ class SpeechmaticsSTTService(STTService):
|
|||||||
return changed
|
return changed
|
||||||
|
|
||||||
no_reconnect = SpeechmaticsSTTSettings.HOT_FIELDS | SpeechmaticsSTTSettings.LOCAL_FIELDS
|
no_reconnect = SpeechmaticsSTTSettings.HOT_FIELDS | SpeechmaticsSTTSettings.LOCAL_FIELDS
|
||||||
needs_reconnect = bool(changed - no_reconnect)
|
needs_reconnect = bool(changed.keys() - no_reconnect)
|
||||||
|
|
||||||
if needs_reconnect:
|
if needs_reconnect:
|
||||||
# Connection-level fields changed — rebuild the SDK config
|
# Connection-level fields changed — rebuild the SDK config
|
||||||
|
|||||||
@@ -236,19 +236,19 @@ class STTService(AIService):
|
|||||||
await super().cleanup()
|
await super().cleanup()
|
||||||
await self._cancel_ttfb_timeout()
|
await self._cancel_ttfb_timeout()
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply an STT settings update.
|
"""Apply an STT settings update.
|
||||||
|
|
||||||
Handles ``model`` (via parent). Does **not** call ``set_language``
|
Handles ``model`` (via parent). Does **not** call ``set_language``
|
||||||
— concrete services should override this method and handle language
|
— concrete services should override this method and handle language
|
||||||
changes (including any reconnect logic) based on the returned
|
changes (including any reconnect logic) based on the returned
|
||||||
changed-field set.
|
changed-field dict.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: An STT settings delta.
|
update: An STT settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
return changed
|
return changed
|
||||||
|
|||||||
@@ -432,20 +432,20 @@ class TTSService(AIService):
|
|||||||
if not (agg_type == aggregation_type and func == transform_function)
|
if not (agg_type == aggregation_type and func == transform_function)
|
||||||
]
|
]
|
||||||
|
|
||||||
async def _update_settings(self, update: TTSSettings) -> set[str]:
|
async def _update_settings(self, update: TTSSettings) -> dict[str, Any]:
|
||||||
"""Apply a TTS settings update.
|
"""Apply a TTS settings update.
|
||||||
|
|
||||||
Handles ``model`` (via parent) and syncs ``_voice_id`` when voice
|
Handles ``model`` (via parent) and syncs ``_voice_id`` when voice
|
||||||
changes. Translates language values before applying. Does **not**
|
changes. Translates language values before applying. Does **not**
|
||||||
call ``set_voice`` or ``set_model`` directly — concrete services
|
call ``set_voice`` or ``set_model`` directly — concrete services
|
||||||
should override this method and handle reconnect logic based on the
|
should override this method and handle reconnect logic based on the
|
||||||
returned changed-field set.
|
returned changed-field dict.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update: A TTS settings delta.
|
update: A TTS settings delta.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set of field names whose values actually changed.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
# Translate language *before* applying so the stored value is canonical
|
# Translate language *before* applying so the stored value is canonical
|
||||||
if is_given(update.language) and update.language is not None:
|
if is_given(update.language) and update.language is not None:
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ class UltravoxRealtimeLLMService(LLMService):
|
|||||||
changed = await super()._update_settings(update)
|
changed = await super()._update_settings(update)
|
||||||
if "output_medium" in changed:
|
if "output_medium" in changed:
|
||||||
await self._update_output_medium(self._settings.output_medium)
|
await self._update_output_medium(self._settings.output_medium)
|
||||||
self._warn_unhandled_updated_settings(changed - {"output_medium"})
|
self._warn_unhandled_updated_settings(changed.keys() - {"output_medium"})
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ interface, including language mapping, metrics generation, and error handling.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import Any, AsyncGenerator, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from openai import AsyncOpenAI
|
from openai import AsyncOpenAI
|
||||||
@@ -174,7 +174,7 @@ class BaseWhisperSTTService(SegmentedSTTService):
|
|||||||
def _create_client(self, api_key: Optional[str], base_url: Optional[str]):
|
def _create_client(self, api_key: Optional[str], base_url: Optional[str]):
|
||||||
return AsyncOpenAI(api_key=api_key, base_url=base_url)
|
return AsyncOpenAI(api_key=api_key, base_url=base_url)
|
||||||
|
|
||||||
async def _update_settings(self, update: STTSettings) -> set[str]:
|
async def _update_settings(self, update: STTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings update, syncing instance variables.
|
"""Apply a settings update, syncing instance variables.
|
||||||
|
|
||||||
Keeps ``_language``, ``_prompt``, and ``_temperature`` in sync with
|
Keeps ``_language``, ``_prompt``, and ``_temperature`` in sync with
|
||||||
|
|||||||
@@ -100,7 +100,8 @@ class TestApplyUpdate:
|
|||||||
current = TTSSettings(voice="alice", language="en")
|
current = TTSSettings(voice="alice", language="en")
|
||||||
delta = TTSSettings(voice="bob")
|
delta = TTSSettings(voice="bob")
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == {"voice"}
|
assert changed.keys() == {"voice"}
|
||||||
|
assert changed["voice"] == "alice" # old value
|
||||||
assert current.voice == "bob"
|
assert current.voice == "bob"
|
||||||
assert current.language == "en"
|
assert current.language == "en"
|
||||||
|
|
||||||
@@ -108,14 +109,14 @@ class TestApplyUpdate:
|
|||||||
current = TTSSettings(voice="alice", language="en")
|
current = TTSSettings(voice="alice", language="en")
|
||||||
delta = TTSSettings(voice="alice")
|
delta = TTSSettings(voice="alice")
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == set()
|
assert changed == {}
|
||||||
assert current.voice == "alice"
|
assert current.voice == "alice"
|
||||||
|
|
||||||
def test_apply_update_not_given_skipped(self):
|
def test_apply_update_not_given_skipped(self):
|
||||||
current = TTSSettings(voice="alice", language="en")
|
current = TTSSettings(voice="alice", language="en")
|
||||||
delta = TTSSettings() # all NOT_GIVEN
|
delta = TTSSettings() # all NOT_GIVEN
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == set()
|
assert changed == {}
|
||||||
assert current.voice == "alice"
|
assert current.voice == "alice"
|
||||||
assert current.language == "en"
|
assert current.language == "en"
|
||||||
|
|
||||||
@@ -123,7 +124,9 @@ class TestApplyUpdate:
|
|||||||
current = LLMSettings(temperature=0.7, max_tokens=100)
|
current = LLMSettings(temperature=0.7, max_tokens=100)
|
||||||
delta = LLMSettings(temperature=0.9, max_tokens=200, top_p=0.95)
|
delta = LLMSettings(temperature=0.9, max_tokens=200, top_p=0.95)
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == {"temperature", "max_tokens", "top_p"}
|
assert changed.keys() == {"temperature", "max_tokens", "top_p"}
|
||||||
|
assert changed["temperature"] == 0.7
|
||||||
|
assert changed["max_tokens"] == 100
|
||||||
assert current.temperature == 0.9
|
assert current.temperature == 0.9
|
||||||
assert current.max_tokens == 200
|
assert current.max_tokens == 200
|
||||||
assert current.top_p == 0.95
|
assert current.top_p == 0.95
|
||||||
@@ -135,6 +138,7 @@ class TestApplyUpdate:
|
|||||||
delta.extra = {"speed": 1.2}
|
delta.extra = {"speed": 1.2}
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert "speed" in changed
|
assert "speed" in changed
|
||||||
|
assert changed["speed"] == 1.0 # old value
|
||||||
assert current.extra == {"speed": 1.2, "stability": 0.5}
|
assert current.extra == {"speed": 1.2, "stability": 0.5}
|
||||||
|
|
||||||
def test_apply_update_extra_no_change(self):
|
def test_apply_update_extra_no_change(self):
|
||||||
@@ -143,13 +147,14 @@ class TestApplyUpdate:
|
|||||||
delta = TTSSettings()
|
delta = TTSSettings()
|
||||||
delta.extra = {"speed": 1.0}
|
delta.extra = {"speed": 1.0}
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == set()
|
assert changed == {}
|
||||||
|
|
||||||
def test_apply_update_model_field(self):
|
def test_apply_update_model_field(self):
|
||||||
current = ServiceSettings(model="old-model")
|
current = ServiceSettings(model="old-model")
|
||||||
delta = ServiceSettings(model="new-model")
|
delta = ServiceSettings(model="new-model")
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == {"model"}
|
assert changed.keys() == {"model"}
|
||||||
|
assert changed["model"] == "old-model"
|
||||||
assert current.model == "new-model"
|
assert current.model == "new-model"
|
||||||
|
|
||||||
def test_apply_update_none_is_a_valid_value(self):
|
def test_apply_update_none_is_a_valid_value(self):
|
||||||
@@ -165,6 +170,7 @@ class TestApplyUpdate:
|
|||||||
delta = TTSSettings(language="en")
|
delta = TTSSettings(language="en")
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert "language" in changed
|
assert "language" in changed
|
||||||
|
assert changed["language"] is None # old value was None
|
||||||
assert current.language == "en"
|
assert current.language == "en"
|
||||||
|
|
||||||
|
|
||||||
@@ -293,7 +299,9 @@ class TestRoundtrip:
|
|||||||
delta = TTSSettings.from_mapping(raw)
|
delta = TTSSettings.from_mapping(raw)
|
||||||
|
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == {"voice", "speed"}
|
assert changed.keys() == {"voice", "speed"}
|
||||||
|
assert changed["voice"] == "alice"
|
||||||
|
assert changed["speed"] == 1.0
|
||||||
assert current.voice == "bob"
|
assert current.voice == "bob"
|
||||||
assert current.language == "en"
|
assert current.language == "en"
|
||||||
assert current.extra["speed"] == 1.2
|
assert current.extra["speed"] == 1.2
|
||||||
@@ -303,6 +311,7 @@ class TestRoundtrip:
|
|||||||
current = LLMSettings(model="gpt-4o", temperature=0.7)
|
current = LLMSettings(model="gpt-4o", temperature=0.7)
|
||||||
delta = LLMSettings.from_mapping({"model": "gpt-4o-mini", "temperature": 0.9})
|
delta = LLMSettings.from_mapping({"model": "gpt-4o-mini", "temperature": 0.9})
|
||||||
changed = current.apply_update(delta)
|
changed = current.apply_update(delta)
|
||||||
assert changed == {"model", "temperature"}
|
assert changed.keys() == {"model", "temperature"}
|
||||||
|
assert changed["model"] == "gpt-4o"
|
||||||
assert current.model == "gpt-4o-mini"
|
assert current.model == "gpt-4o-mini"
|
||||||
assert current.temperature == 0.9
|
assert current.temperature == 0.9
|
||||||
|
|||||||
Reference in New Issue
Block a user