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:
@@ -32,7 +32,13 @@ from pipecat.frames.frames import (
|
|||||||
UserStartedSpeakingFrame,
|
UserStartedSpeakingFrame,
|
||||||
UserStoppedSpeakingFrame,
|
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.settings import NOT_GIVEN, STTSettings, _NotGiven
|
||||||
from pipecat.services.stt_latency import GLADIA_TTFS_P99
|
from pipecat.services.stt_latency import GLADIA_TTFS_P99
|
||||||
from pipecat.services.stt_service import WebsocketSTTService
|
from pipecat.services.stt_service import WebsocketSTTService
|
||||||
@@ -185,10 +191,36 @@ class GladiaSTTSettings(STTSettings):
|
|||||||
"""Settings for Gladia STT service.
|
"""Settings for Gladia STT service.
|
||||||
|
|
||||||
Parameters:
|
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):
|
class GladiaSTTService(WebsocketSTTService):
|
||||||
@@ -280,7 +312,29 @@ class GladiaSTTService(WebsocketSTTService):
|
|||||||
self._region = region
|
self._region = region
|
||||||
self._url = url
|
self._url = url
|
||||||
self._receive_task = None
|
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()
|
self._sync_model_name_to_metrics()
|
||||||
|
|
||||||
# Session management
|
# Session management
|
||||||
@@ -321,52 +375,43 @@ class GladiaSTTService(WebsocketSTTService):
|
|||||||
return language_to_gladia_language(language)
|
return language_to_gladia_language(language)
|
||||||
|
|
||||||
def _prepare_settings(self) -> Dict[str, Any]:
|
def _prepare_settings(self) -> Dict[str, Any]:
|
||||||
params = self._settings.input_params
|
s = self._settings
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
"encoding": params.encoding or "wav/pcm",
|
"encoding": s.encoding or "wav/pcm",
|
||||||
"bit_depth": params.bit_depth or 16,
|
"bit_depth": s.bit_depth or 16,
|
||||||
"sample_rate": self.sample_rate,
|
"sample_rate": self.sample_rate,
|
||||||
"channels": params.channels or 1,
|
"channels": s.channels or 1,
|
||||||
"model": self._settings.model,
|
"model": s.model,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add custom_metadata if provided
|
# 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()
|
settings["custom_metadata"]["pipecat"] = pipecat_version()
|
||||||
|
|
||||||
# Add endpointing parameters if provided
|
# Add endpointing parameters if provided
|
||||||
if params.endpointing is not None:
|
if s.endpointing is not None:
|
||||||
settings["endpointing"] = params.endpointing
|
settings["endpointing"] = s.endpointing
|
||||||
if params.maximum_duration_without_endpointing is not None:
|
if s.maximum_duration_without_endpointing is not None:
|
||||||
settings["maximum_duration_without_endpointing"] = (
|
settings["maximum_duration_without_endpointing"] = (
|
||||||
params.maximum_duration_without_endpointing
|
s.maximum_duration_without_endpointing
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add language configuration (prioritize language_config over deprecated language)
|
# Add language configuration
|
||||||
if params.language_config:
|
if s.language_config:
|
||||||
settings["language_config"] = params.language_config.model_dump(exclude_none=True)
|
settings["language_config"] = s.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 pre_processing configuration if provided
|
# Add pre_processing configuration if provided
|
||||||
if params.pre_processing:
|
if s.pre_processing:
|
||||||
settings["pre_processing"] = params.pre_processing.model_dump(exclude_none=True)
|
settings["pre_processing"] = s.pre_processing.model_dump(exclude_none=True)
|
||||||
|
|
||||||
# Add realtime_processing configuration if provided
|
# Add realtime_processing configuration if provided
|
||||||
if params.realtime_processing:
|
if s.realtime_processing:
|
||||||
settings["realtime_processing"] = params.realtime_processing.model_dump(
|
settings["realtime_processing"] = s.realtime_processing.model_dump(exclude_none=True)
|
||||||
exclude_none=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add messages_config if provided
|
# Add messages_config if provided
|
||||||
if params.messages_config:
|
if s.messages_config:
|
||||||
settings["messages_config"] = params.messages_config.model_dump(exclude_none=True)
|
settings["messages_config"] = s.messages_config.model_dump(exclude_none=True)
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
@@ -562,7 +607,7 @@ class GladiaSTTService(WebsocketSTTService):
|
|||||||
Broadcasts UserStartedSpeakingFrame and optionally triggers interruption
|
Broadcasts UserStartedSpeakingFrame and optionally triggers interruption
|
||||||
when VAD is enabled.
|
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
|
return
|
||||||
|
|
||||||
logger.debug(f"{self} User started speaking")
|
logger.debug(f"{self} User started speaking")
|
||||||
@@ -577,7 +622,7 @@ class GladiaSTTService(WebsocketSTTService):
|
|||||||
|
|
||||||
Broadcasts UserStoppedSpeakingFrame when VAD is enabled.
|
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
|
return
|
||||||
self._is_speaking = False
|
self._is_speaking = False
|
||||||
await self.broadcast_frame(UserStoppedSpeakingFrame)
|
await self.broadcast_frame(UserStoppedSpeakingFrame)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from pipecat.frames.frames import (
|
|||||||
VADUserStoppedSpeakingFrame,
|
VADUserStoppedSpeakingFrame,
|
||||||
)
|
)
|
||||||
from pipecat.processors.frame_processor import FrameDirection
|
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_latency import SONIOX_TTFS_P99
|
||||||
from pipecat.services.stt_service import WebsocketSTTService
|
from pipecat.services.stt_service import WebsocketSTTService
|
||||||
from pipecat.transcriptions.language import Language
|
from pipecat.transcriptions.language import Language
|
||||||
@@ -141,10 +141,28 @@ class SonioxSTTSettings(STTSettings):
|
|||||||
"""Settings for Soniox STT service.
|
"""Settings for Soniox STT service.
|
||||||
|
|
||||||
Parameters:
|
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):
|
class SonioxSTTService(WebsocketSTTService):
|
||||||
@@ -199,7 +217,15 @@ class SonioxSTTService(WebsocketSTTService):
|
|||||||
|
|
||||||
self._settings = SonioxSTTSettings(
|
self._settings = SonioxSTTSettings(
|
||||||
model=params.model,
|
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()
|
self._sync_model_name_to_metrics()
|
||||||
|
|
||||||
@@ -226,12 +252,7 @@ class SonioxSTTService(WebsocketSTTService):
|
|||||||
await self._connect()
|
await self._connect()
|
||||||
|
|
||||||
async def _update_settings(self, delta: SonioxSTTSettings) -> dict[str, Any]:
|
async def _update_settings(self, delta: SonioxSTTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings delta, keeping ``input_params`` in sync.
|
"""Apply settings delta.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Settings are stored but not applied to the active connection.
|
||||||
|
|
||||||
@@ -241,22 +262,11 @@ class SonioxSTTService(WebsocketSTTService):
|
|||||||
Returns:
|
Returns:
|
||||||
Dict mapping changed field names to their previous values.
|
Dict mapping changed field names to their previous values.
|
||||||
"""
|
"""
|
||||||
model_given = is_given(getattr(delta, "model", NOT_GIVEN))
|
|
||||||
|
|
||||||
changed = await super()._update_settings(delta)
|
changed = await super()._update_settings(delta)
|
||||||
|
|
||||||
if not changed:
|
if not changed:
|
||||||
return 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.
|
# TODO: someday we could reconnect here to apply updated settings.
|
||||||
# Code might look something like the below:
|
# Code might look something like the below:
|
||||||
# await self._disconnect()
|
# await self._disconnect()
|
||||||
@@ -377,26 +387,26 @@ class SonioxSTTService(WebsocketSTTService):
|
|||||||
# Either one or the other is required.
|
# Either one or the other is required.
|
||||||
enable_endpoint_detection = not self._vad_force_turn_endpoint
|
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):
|
if isinstance(context, SonioxContextObject):
|
||||||
context = context.model_dump()
|
context = context.model_dump()
|
||||||
|
|
||||||
# Send the initial configuration message.
|
# Send the initial configuration message.
|
||||||
config = {
|
config = {
|
||||||
"api_key": self._api_key,
|
"api_key": self._api_key,
|
||||||
"model": self._settings.model,
|
"model": s.model,
|
||||||
"audio_format": params.audio_format,
|
"audio_format": s.audio_format,
|
||||||
"num_channels": params.num_channels or 1,
|
"num_channels": s.num_channels or 1,
|
||||||
"enable_endpoint_detection": enable_endpoint_detection,
|
"enable_endpoint_detection": enable_endpoint_detection,
|
||||||
"sample_rate": self.sample_rate,
|
"sample_rate": self.sample_rate,
|
||||||
"language_hints": _prepare_language_hints(params.language_hints),
|
"language_hints": _prepare_language_hints(s.language_hints),
|
||||||
"language_hints_strict": params.language_hints_strict,
|
"language_hints_strict": s.language_hints_strict,
|
||||||
"context": context,
|
"context": context,
|
||||||
"enable_speaker_diarization": params.enable_speaker_diarization,
|
"enable_speaker_diarization": s.enable_speaker_diarization,
|
||||||
"enable_language_identification": params.enable_language_identification,
|
"enable_language_identification": s.enable_language_identification,
|
||||||
"client_reference_id": params.client_reference_id,
|
"client_reference_id": s.client_reference_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Send the configuration message.
|
# Send the configuration message.
|
||||||
|
|||||||
Reference in New Issue
Block a user