Fix service settings init ordering and example bugs

- Speechmatics: move config build after super().__init__ and settings
  delta so turn_detection_mode (e.g. ADAPTIVE) takes effect
- Google STT: fix example passing bare Language enum instead of list
- Google TTS: add missing explicit defaults for all custom settings fields
- Soniox: fix accidental tuple wrapping of STT service in example
- Speechmatics examples: fix system->user role in kick-off messages
- Deepgram Flux: move tag from settings to __init__ (billing metadata)
- ElevenLabs STT: default tag_audio_events to None (use API default)
- Fal STT: simplify language default handling
- Google TTS: rename GoogleStreamTTSSettings to GoogleTTSSettings
This commit is contained in:
Mark Backman
2026-03-06 13:51:48 -05:00
parent c3794956ef
commit 6431ad8e2a
7 changed files with 54 additions and 43 deletions

View File

@@ -55,8 +55,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
stt = GoogleSTTService(
settings=GoogleSTTSettings(
languages=Language.EN_US,
model="chirp_3",
languages=[Language.EN_US],
# Add model to use a specific model
# model="chirp_3",
),
credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"),
location="us",

View File

@@ -51,13 +51,13 @@ transport_params = {
async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
logger.info(f"Starting bot")
stt = (
SonioxSTTService(
api_key=os.getenv("SONIOX_API_KEY"),
settings=SonioxSTTSettings(
language_hints=[Language.EN],
language_hints_strict=True,
),
stt = SonioxSTTService(
api_key=os.getenv("SONIOX_API_KEY"),
settings=SonioxSTTSettings(
# Add language hints to use a specific language
# Add strict mode to enforce the language hints
language_hints=[Language.EN],
language_hints_strict=True,
),
)

View File

@@ -81,7 +81,6 @@ class DeepgramFluxSTTSettings(STTSettings):
eot_timeout_ms: Time in ms after speech to finish a turn regardless of EOT
confidence (default 5000).
keyterm: Keyterms to boost recognition accuracy for specialized terminology.
tag: Tags to label requests for identification during usage reporting.
min_confidence: Minimum confidence required to create a TranscriptionFrame.
"""
@@ -89,7 +88,6 @@ class DeepgramFluxSTTSettings(STTSettings):
eot_threshold: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
eot_timeout_ms: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
keyterm: list | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
tag: list | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
min_confidence: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
@@ -157,6 +155,7 @@ class DeepgramFluxSTTService(WebsocketSTTService):
mip_opt_out: Optional[bool] = None,
model: Optional[str] = None,
flux_encoding: str = "linear16",
tag: Optional[list] = None,
params: Optional[InputParams] = None,
should_interrupt: bool = True,
settings: Optional[DeepgramFluxSTTSettings] = None,
@@ -177,6 +176,7 @@ class DeepgramFluxSTTService(WebsocketSTTService):
flux_encoding: Audio encoding format required by Flux API. Must be "linear16".
Raw signed little-endian 16-bit PCM encoding.
tag: Tags to label requests for identification during usage reporting.
params: InputParams instance containing detailed API configuration options.
.. deprecated:: 0.0.105
@@ -224,7 +224,6 @@ class DeepgramFluxSTTService(WebsocketSTTService):
eot_threshold=None,
eot_timeout_ms=None,
keyterm=[],
tag=[],
min_confidence=None,
)
@@ -241,7 +240,8 @@ class DeepgramFluxSTTService(WebsocketSTTService):
default_settings.eot_threshold = params.eot_threshold
default_settings.eot_timeout_ms = params.eot_timeout_ms
default_settings.keyterm = params.keyterm or []
default_settings.tag = params.tag or []
if params.tag and tag is None:
tag = params.tag
default_settings.min_confidence = params.min_confidence
if params.mip_opt_out is not None:
mip_opt_out = params.mip_opt_out
@@ -261,6 +261,7 @@ class DeepgramFluxSTTService(WebsocketSTTService):
self._should_interrupt = should_interrupt
self._encoding = flux_encoding
self._mip_opt_out = mip_opt_out
self._tag = tag or []
self._websocket_url = None
self._receive_task = None
@@ -469,7 +470,7 @@ class DeepgramFluxSTTService(WebsocketSTTService):
url_params.append(urlencode({"keyterm": keyterm}))
# Add tag parameters (can have multiple)
for tag_value in self._settings.tag:
for tag_value in self._tag:
url_params.append(urlencode({"tag": tag_value}))
self._websocket_url = f"{self._url}?{'&'.join(url_params)}"

View File

@@ -186,7 +186,7 @@ class ElevenLabsSTTSettings(STTSettings):
(coughing) in the transcription.
"""
tag_audio_events: bool | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
tag_audio_events: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
@dataclass
@@ -277,8 +277,8 @@ class ElevenLabsSTTService(SegmentedSTTService):
# 1. Initialize default_settings with hardcoded defaults
default_settings = ElevenLabsSTTSettings(
model="scribe_v2",
language="eng",
tag_audio_events=True,
language=language_to_elevenlabs_language(Language.EN),
tag_audio_events=None,
)
# 2. Apply direct init arg overrides (deprecated)
@@ -291,9 +291,7 @@ class ElevenLabsSTTService(SegmentedSTTService):
_warn_deprecated_param("params", ElevenLabsSTTSettings)
if not settings:
if params.language is not None:
default_settings.language = (
self.language_to_service_language(params.language) or "eng"
)
default_settings.language = language_to_elevenlabs_language(params.language)
default_settings.tag_audio_events = params.tag_audio_events
# 4. Apply settings delta (canonical API, always wins)
@@ -354,10 +352,11 @@ class ElevenLabsSTTService(SegmentedSTTService):
content_type="audio/x-wav",
)
# Add required model_id, language_code, and tag_audio_events
# Add required model_id and language_code
data.add_field("model_id", self._settings.model)
data.add_field("language_code", self._settings.language)
data.add_field("tag_audio_events", str(self._settings.tag_audio_events).lower())
if self._settings.tag_audio_events is not None:
data.add_field("tag_audio_events", str(self._settings.tag_audio_events).lower())
async with self._session.post(url, data=data, headers=headers) as response:
if response.status != 200:

View File

@@ -215,7 +215,7 @@ class FalSTTService(SegmentedSTTService):
# 1. Initialize default_settings with hardcoded defaults
default_settings = FalSTTSettings(
model=None,
language=language_to_fal_language(Language.EN) or "en",
language=language_to_fal_language(Language.EN),
)
# 2. (no deprecated direct args for this service)
@@ -224,9 +224,8 @@ class FalSTTService(SegmentedSTTService):
if params is not None:
_warn_deprecated_param("params", FalSTTSettings)
if not settings:
default_settings.language = (
language_to_fal_language(params.language) if params.language else "en"
)
if params.language is not None:
default_settings.language = language_to_fal_language(params.language)
if params.task != "transcribe":
task = params.task
if params.chunk_level != "segment":

View File

@@ -512,7 +512,7 @@ class GoogleHttpTTSSettings(TTSSettings):
@dataclass
class GoogleStreamTTSSettings(TTSSettings):
class GoogleTTSSettings(TTSSettings):
"""Settings for Google streaming TTS service.
Parameters:
@@ -619,6 +619,13 @@ class GoogleHttpTTSService(TTSService):
model=None,
voice="en-US-Chirp3-HD-Charon",
language="en-US",
pitch=None,
rate=None,
speaking_rate=None,
volume=None,
emphasis=None,
gender=None,
google_style=None,
)
# 2. Apply direct init arg overrides (deprecated)
@@ -1008,13 +1015,13 @@ class GoogleTTSService(GoogleBaseTTSService):
)
"""
_settings: GoogleStreamTTSSettings
_settings: GoogleTTSSettings
class InputParams(BaseModel):
"""Input parameters for Google streaming TTS configuration.
.. deprecated:: 0.0.105
Use ``GoogleStreamTTSSettings`` directly via the ``settings`` parameter instead.
Use ``GoogleTTSSettings`` directly via the ``settings`` parameter instead.
Parameters:
language: Language for synthesis. Defaults to English.
@@ -1034,7 +1041,7 @@ class GoogleTTSService(GoogleBaseTTSService):
voice_cloning_key: Optional[str] = None,
sample_rate: Optional[int] = None,
params: Optional[InputParams] = None,
settings: Optional[GoogleStreamTTSSettings] = None,
settings: Optional[GoogleTTSSettings] = None,
**kwargs,
):
"""Initializes the Google streaming TTS service.
@@ -1046,34 +1053,35 @@ class GoogleTTSService(GoogleBaseTTSService):
voice_id: Google TTS voice identifier (e.g., "en-US-Chirp3-HD-Charon").
.. deprecated:: 0.0.105
Use ``settings=GoogleStreamTTSSettings(voice=...)`` instead.
Use ``settings=GoogleTTSSettings(voice=...)`` instead.
voice_cloning_key: The voice cloning key for Chirp 3 custom voices.
sample_rate: Audio sample rate in Hz. If None, uses default.
params: Language configuration parameters.
.. deprecated:: 0.0.105
Use ``settings=GoogleStreamTTSSettings(...)`` instead.
Use ``settings=GoogleTTSSettings(...)`` instead.
settings: Runtime-updatable settings. When provided alongside deprecated
parameters, ``settings`` values take precedence.
**kwargs: Additional arguments passed to parent TTSService.
"""
# 1. Initialize default_settings with hardcoded defaults
default_settings = GoogleStreamTTSSettings(
default_settings = GoogleTTSSettings(
model=None,
voice="en-US-Chirp3-HD-Charon",
language="en-US",
speaking_rate=None,
)
# 2. Apply direct init arg overrides (deprecated)
if voice_id is not None:
_warn_deprecated_param("voice_id", GoogleStreamTTSSettings, "voice")
_warn_deprecated_param("voice_id", GoogleTTSSettings, "voice")
default_settings.voice = voice_id
# 3. Apply params overrides — only if settings not provided
if params is not None:
_warn_deprecated_param("params", GoogleStreamTTSSettings)
_warn_deprecated_param("params", GoogleTTSSettings)
if not settings:
if params.language is not None:
default_settings.language = self.language_to_service_language(params.language)
@@ -1104,7 +1112,7 @@ class GoogleTTSService(GoogleBaseTTSService):
Args:
delta: Settings delta. Can include 'speaking_rate' (float).
"""
if isinstance(delta, GoogleStreamTTSSettings) and is_given(delta.speaking_rate):
if isinstance(delta, GoogleTTSSettings) and is_given(delta.speaking_rate):
rate_value = float(delta.speaking_rate)
if not (0.25 <= rate_value <= 2.0):
logger.warning(
@@ -1308,6 +1316,9 @@ class GeminiTTSService(GoogleBaseTTSService):
model="gemini-2.5-flash-tts",
voice="Kore",
language="en-US",
prompt=None,
multi_speaker=False,
speaker_configs=None,
)
# 2. Apply direct init arg overrides (deprecated)

View File

@@ -90,7 +90,6 @@ class SpeechmaticsSTTSettings(STTSettings):
See ``SpeechmaticsSTTService.InputParams`` for detailed descriptions of each field.
Parameters:
model: The operating point / model name.
domain: Domain for Speechmatics API.
turn_detection_mode: Endpoint handling mode.
speaker_active_format: Formatter for active speaker ID.
@@ -490,12 +489,6 @@ class SpeechmaticsSTTService(STTService):
default_settings.prefer_current_speaker = _params.prefer_current_speaker
default_settings.extra_params = _params.extra_params
# Build SDK config from settings, then resolve model from operating_point
self._client: VoiceAgentClient | None = None
self._audio_encoding = encoding
self._config: VoiceAgentConfig = self._build_config(default_settings)
default_settings.model = self._config.operating_point.value
# --- 4. Settings delta (canonical API, always wins) ---
if settings is not None:
default_settings.apply_update(settings)
@@ -507,6 +500,13 @@ class SpeechmaticsSTTService(STTService):
**kwargs,
)
# Build SDK config from settings, then resolve model from operating_point
self._client: VoiceAgentClient | None = None
self._audio_encoding = encoding
self._config: VoiceAgentConfig = self._build_config(self._settings)
self._settings.model = self._config.operating_point.value
self._sync_model_name_to_metrics()
# Outbound frame queue
self._outbound_frames: asyncio.Queue[Frame] = asyncio.Queue()