Flatten input_params into individual fields on SonioxSTTSettings and GladiaSTTSettings

This makes each service-specific field individually visible to the delta/update mechanism (`apply_update`, `given_fields`) and removes the need for complex sync logic between `input_params` and top-level fields like `model`.

- Soniox: replace `input_params: SonioxInputParams` with 8 individual fields, simplify `_update_settings` by removing model sync logic, remove unused `is_given` import
- Gladia: replace `input_params: GladiaInputParams` with 11 individual fields, resolve deprecated `language` → `language_config` at init time rather than at `_prepare_settings` time
This commit is contained in:
Paul Kompfner
2026-02-24 12:20:14 -05:00
parent 0a89d24f70
commit b78a293ffb
2 changed files with 120 additions and 65 deletions

View File

@@ -32,7 +32,13 @@ from pipecat.frames.frames import (
UserStartedSpeakingFrame,
UserStoppedSpeakingFrame,
)
from pipecat.services.gladia.config import GladiaInputParams
from pipecat.services.gladia.config import (
GladiaInputParams,
LanguageConfig,
MessagesConfig,
PreProcessingConfig,
RealtimeProcessingConfig,
)
from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven
from pipecat.services.stt_latency import GLADIA_TTFS_P99
from pipecat.services.stt_service import WebsocketSTTService
@@ -185,10 +191,36 @@ class GladiaSTTSettings(STTSettings):
"""Settings for Gladia STT service.
Parameters:
input_params: Gladia ``GladiaInputParams`` for detailed configuration.
encoding: Audio encoding format.
bit_depth: Audio bit depth.
channels: Number of audio channels.
custom_metadata: Additional metadata to include with requests.
endpointing: Silence duration in seconds to mark end of speech.
maximum_duration_without_endpointing: Maximum utterance duration without silence.
language_config: Detailed language configuration.
pre_processing: Audio pre-processing options.
realtime_processing: Real-time processing features.
messages_config: WebSocket message filtering options.
enable_vad: Enable VAD to trigger end of utterance detection.
"""
input_params: GladiaInputParams | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
encoding: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
bit_depth: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
channels: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
custom_metadata: Dict[str, Any] | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
endpointing: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
maximum_duration_without_endpointing: int | None | _NotGiven = field(
default_factory=lambda: NOT_GIVEN
)
language_config: LanguageConfig | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
pre_processing: PreProcessingConfig | None | _NotGiven = field(
default_factory=lambda: NOT_GIVEN
)
realtime_processing: RealtimeProcessingConfig | None | _NotGiven = field(
default_factory=lambda: NOT_GIVEN
)
messages_config: MessagesConfig | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
enable_vad: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
class GladiaSTTService(WebsocketSTTService):
@@ -280,7 +312,29 @@ class GladiaSTTService(WebsocketSTTService):
self._region = region
self._url = url
self._receive_task = None
self._settings = GladiaSTTSettings(model=model, language=None, input_params=params)
# Resolve deprecated language → language_config at init time
language_config = params.language_config
if not language_config and params.language:
language_code = self.language_to_service_language(params.language)
if language_code:
language_config = LanguageConfig(languages=[language_code], code_switching=False)
self._settings = GladiaSTTSettings(
model=model,
language=None,
encoding=params.encoding,
bit_depth=params.bit_depth,
channels=params.channels,
custom_metadata=params.custom_metadata,
endpointing=params.endpointing,
maximum_duration_without_endpointing=params.maximum_duration_without_endpointing,
language_config=language_config,
pre_processing=params.pre_processing,
realtime_processing=params.realtime_processing,
messages_config=params.messages_config,
enable_vad=params.enable_vad,
)
self._sync_model_name_to_metrics()
# Session management
@@ -321,52 +375,43 @@ class GladiaSTTService(WebsocketSTTService):
return language_to_gladia_language(language)
def _prepare_settings(self) -> Dict[str, Any]:
params = self._settings.input_params
s = self._settings
settings = {
"encoding": params.encoding or "wav/pcm",
"bit_depth": params.bit_depth or 16,
"encoding": s.encoding or "wav/pcm",
"bit_depth": s.bit_depth or 16,
"sample_rate": self.sample_rate,
"channels": params.channels or 1,
"model": self._settings.model,
"channels": s.channels or 1,
"model": s.model,
}
# Add custom_metadata if provided
settings["custom_metadata"] = dict(params.custom_metadata or {})
settings["custom_metadata"] = dict(s.custom_metadata or {})
settings["custom_metadata"]["pipecat"] = pipecat_version()
# Add endpointing parameters if provided
if params.endpointing is not None:
settings["endpointing"] = params.endpointing
if params.maximum_duration_without_endpointing is not None:
if s.endpointing is not None:
settings["endpointing"] = s.endpointing
if s.maximum_duration_without_endpointing is not None:
settings["maximum_duration_without_endpointing"] = (
params.maximum_duration_without_endpointing
s.maximum_duration_without_endpointing
)
# Add language configuration (prioritize language_config over deprecated language)
if params.language_config:
settings["language_config"] = params.language_config.model_dump(exclude_none=True)
elif params.language: # Backward compatibility for deprecated parameter
language_code = self.language_to_service_language(params.language)
if language_code:
settings["language_config"] = {
"languages": [language_code],
"code_switching": False,
}
# Add language configuration
if s.language_config:
settings["language_config"] = s.language_config.model_dump(exclude_none=True)
# Add pre_processing configuration if provided
if params.pre_processing:
settings["pre_processing"] = params.pre_processing.model_dump(exclude_none=True)
if s.pre_processing:
settings["pre_processing"] = s.pre_processing.model_dump(exclude_none=True)
# Add realtime_processing configuration if provided
if params.realtime_processing:
settings["realtime_processing"] = params.realtime_processing.model_dump(
exclude_none=True
)
if s.realtime_processing:
settings["realtime_processing"] = s.realtime_processing.model_dump(exclude_none=True)
# Add messages_config if provided
if params.messages_config:
settings["messages_config"] = params.messages_config.model_dump(exclude_none=True)
if s.messages_config:
settings["messages_config"] = s.messages_config.model_dump(exclude_none=True)
return settings
@@ -562,7 +607,7 @@ class GladiaSTTService(WebsocketSTTService):
Broadcasts UserStartedSpeakingFrame and optionally triggers interruption
when VAD is enabled.
"""
if not self._settings.input_params.enable_vad or self._is_speaking:
if not self._settings.enable_vad or self._is_speaking:
return
logger.debug(f"{self} User started speaking")
@@ -577,7 +622,7 @@ class GladiaSTTService(WebsocketSTTService):
Broadcasts UserStoppedSpeakingFrame when VAD is enabled.
"""
if not self._settings.input_params.enable_vad or not self._is_speaking:
if not self._settings.enable_vad or not self._is_speaking:
return
self._is_speaking = False
await self.broadcast_frame(UserStoppedSpeakingFrame)

View File

@@ -24,7 +24,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 SONIOX_TTFS_P99
from pipecat.services.stt_service import WebsocketSTTService
from pipecat.transcriptions.language import Language
@@ -141,10 +141,28 @@ class SonioxSTTSettings(STTSettings):
"""Settings for Soniox STT service.
Parameters:
input_params: Soniox ``SonioxInputParams`` for detailed configuration.
audio_format: Audio format to use for transcription.
num_channels: Number of channels to use for transcription.
language_hints: List of language hints to use for transcription.
language_hints_strict: If true, strictly enforce language hints.
context: Customization for transcription. String for models with
context_version 1 and SonioxContextObject for models with
context_version 2.
enable_speaker_diarization: Whether to enable speaker diarization.
enable_language_identification: Whether to enable language identification.
client_reference_id: Client reference ID to use for transcription.
"""
input_params: SonioxInputParams | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
audio_format: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
num_channels: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
language_hints: List[Language] | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
language_hints_strict: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
context: SonioxContextObject | str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
enable_speaker_diarization: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
enable_language_identification: bool | None | _NotGiven = field(
default_factory=lambda: NOT_GIVEN
)
client_reference_id: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
class SonioxSTTService(WebsocketSTTService):
@@ -199,7 +217,15 @@ class SonioxSTTService(WebsocketSTTService):
self._settings = SonioxSTTSettings(
model=params.model,
input_params=params,
language=None,
audio_format=params.audio_format,
num_channels=params.num_channels,
language_hints=params.language_hints,
language_hints_strict=params.language_hints_strict,
context=params.context,
enable_speaker_diarization=params.enable_speaker_diarization,
enable_language_identification=params.enable_language_identification,
client_reference_id=params.client_reference_id,
)
self._sync_model_name_to_metrics()
@@ -226,12 +252,7 @@ class SonioxSTTService(WebsocketSTTService):
await self._connect()
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
*delta* its value is propagated into ``input_params``. When only
``input_params`` is given, its ``model`` is propagated *up* to the
top-level field.
"""Apply settings delta.
Settings are stored but not applied to the active connection.
@@ -241,22 +262,11 @@ class SonioxSTTService(WebsocketSTTService):
Returns:
Dict mapping changed field names to their previous values.
"""
model_given = is_given(getattr(delta, "model", NOT_GIVEN))
changed = await super()._update_settings(delta)
if not changed:
return changed
# --- Sync model --------------------------------------------------
if model_given:
# Top-level model wins → push into input_params.
self._settings.input_params.model = self._settings.model
elif "input_params" in changed and self._settings.input_params.model is not None:
# Only input_params was given → pull model up.
self._settings.model = self._settings.input_params.model
self._sync_model_name_to_metrics()
# TODO: someday we could reconnect here to apply updated settings.
# Code might look something like the below:
# await self._disconnect()
@@ -377,26 +387,26 @@ class SonioxSTTService(WebsocketSTTService):
# Either one or the other is required.
enable_endpoint_detection = not self._vad_force_turn_endpoint
params = self._settings.input_params
s = self._settings
context = params.context
context = s.context
if isinstance(context, SonioxContextObject):
context = context.model_dump()
# Send the initial configuration message.
config = {
"api_key": self._api_key,
"model": self._settings.model,
"audio_format": params.audio_format,
"num_channels": params.num_channels or 1,
"model": s.model,
"audio_format": s.audio_format,
"num_channels": s.num_channels or 1,
"enable_endpoint_detection": enable_endpoint_detection,
"sample_rate": self.sample_rate,
"language_hints": _prepare_language_hints(params.language_hints),
"language_hints_strict": params.language_hints_strict,
"language_hints": _prepare_language_hints(s.language_hints),
"language_hints_strict": s.language_hints_strict,
"context": context,
"enable_speaker_diarization": params.enable_speaker_diarization,
"enable_language_identification": params.enable_language_identification,
"client_reference_id": params.client_reference_id,
"enable_speaker_diarization": s.enable_speaker_diarization,
"enable_language_identification": s.enable_language_identification,
"client_reference_id": s.client_reference_id,
}
# Send the configuration message.