diff --git a/sdk/voice/speechmatics/voice/_client.py b/sdk/voice/speechmatics/voice/_client.py index c0988dd3..97b246cb 100644 --- a/sdk/voice/speechmatics/voice/_client.py +++ b/sdk/voice/speechmatics/voice/_client.py @@ -400,6 +400,7 @@ def _prepare_config( language=config.language, domain=config.domain, output_locale=config.output_locale, + model=config.model, operating_point=config.operating_point, diarization="speaker" if config.enable_diarization else None, enable_partials=True, diff --git a/sdk/voice/speechmatics/voice/_models.py b/sdk/voice/speechmatics/voice/_models.py index b4a432c2..698d5dff 100644 --- a/sdk/voice/speechmatics/voice/_models.py +++ b/sdk/voice/speechmatics/voice/_models.py @@ -9,6 +9,7 @@ from typing import Any from typing import Literal from typing import Optional +from warnings import warn from pydantic import BaseModel as PydanticBaseModel from pydantic import ConfigDict @@ -17,6 +18,7 @@ from typing_extensions import Self from speechmatics.rt import AudioEncoding +from speechmatics.rt import Model from speechmatics.rt import OperatingPoint from speechmatics.rt import SpeakerIdentifier @@ -487,9 +489,7 @@ class VoiceAgentConfig(BaseModel): agent configuration for the `VoiceAgentClient`. Parameters: - operating_point: Operating point for transcription accuracy vs. latency tradeoff. It is - recommended to use `OperatingPoint.ENHANCED` for most use cases. Defaults to - `OperatingPoint.ENHANCED`. + model: Transcription model to use. Defaults to `Model.ENHANCED`. domain: Domain for Speechmatics API. Defaults to `None`. @@ -651,7 +651,7 @@ class VoiceAgentConfig(BaseModel): Complete example with multiple features: >>> config = VoiceAgentConfig( ... language="en", - ... operating_point=OperatingPoint.ENHANCED, + ... model=Model.ENHANCED, ... enable_diarization=True, ... speaker_sensitivity=0.7, ... max_speakers=3, @@ -670,7 +670,7 @@ class VoiceAgentConfig(BaseModel): """ # Service configuration - operating_point: OperatingPoint = OperatingPoint.ENHANCED + model: Model = Model.ENHANCED domain: Optional[str] = None language: str = "en" output_locale: Optional[str] = None @@ -711,6 +711,9 @@ class VoiceAgentConfig(BaseModel): audio_encoding: AudioEncoding = AudioEncoding.PCM_S16LE chunk_size: int = 160 + # Deprecated + operating_point: Optional[OperatingPoint] = None + # Validation @model_validator(mode="after") # type: ignore[misc] def validate_config(self) -> Self: @@ -751,6 +754,18 @@ def validate_config(self) -> Self: if self.sample_rate not in [8000, 16000]: errors.append("sample_rate must be 8000 or 16000") + # Deprecated `operating_point` - migrate to new `model` + if self.operating_point is not None: + if "model" in self.model_fields_set: + raise ValueError("Cannot specify both 'model' and 'operating_point'. Use 'model' instead.") + warn( + "'operating_point' is deprecated, use 'model' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.model = Model(self.operating_point.value) + self.operating_point = None + # Raise error if any validation errors if errors: raise ValueError(f"{len(errors)} config error(s): {'; '.join(errors)}") diff --git a/sdk/voice/speechmatics/voice/_presets.py b/sdk/voice/speechmatics/voice/_presets.py index 2bcb092f..013f727c 100644 --- a/sdk/voice/speechmatics/voice/_presets.py +++ b/sdk/voice/speechmatics/voice/_presets.py @@ -8,7 +8,7 @@ from ._models import EndOfTurnConfig from ._models import EndOfUtteranceMode -from ._models import OperatingPoint +from ._models import Model from ._models import SmartTurnConfig from ._models import SpeechSegmentConfig from ._models import VoiceActivityConfig @@ -26,12 +26,11 @@ def FAST(overlay: Optional[VoiceAgentConfig] = None) -> VoiceAgentConfig: # noq delay to finalizing the spoken sentences. It is not recommended for conversation, as it will not account for pauses, slow speech or disfluencies. - Note that this uses our standard operating point so will have marginally lower - accuracy that the enhanced operating point. + Note that this uses our standard model. """ return VoiceAgentConfigPreset._merge_configs( VoiceAgentConfig( - operating_point=OperatingPoint.STANDARD, + model=Model.STANDARD, enable_diarization=True, max_delay=2.0, end_of_utterance_silence_trigger=0.25, @@ -51,7 +50,7 @@ def FIXED(overlay: Optional[VoiceAgentConfig] = None) -> VoiceAgentConfig: # no """ return VoiceAgentConfigPreset._merge_configs( VoiceAgentConfig( - operating_point=OperatingPoint.ENHANCED, + model=Model.ENHANCED, enable_diarization=True, max_delay=2.0, end_of_utterance_silence_trigger=0.5, @@ -75,7 +74,7 @@ def ADAPTIVE(overlay: Optional[VoiceAgentConfig] = None) -> VoiceAgentConfig: # """ return VoiceAgentConfigPreset._merge_configs( VoiceAgentConfig( - operating_point=OperatingPoint.ENHANCED, + model=Model.ENHANCED, enable_diarization=True, max_delay=2.0, end_of_utterance_silence_trigger=0.7, @@ -104,7 +103,7 @@ def SMART_TURN(overlay: Optional[VoiceAgentConfig] = None) -> VoiceAgentConfig: """ return VoiceAgentConfigPreset._merge_configs( VoiceAgentConfig( - operating_point=OperatingPoint.ENHANCED, + model=Model.ENHANCED, enable_diarization=True, max_delay=2.0, end_of_utterance_silence_trigger=0.8, @@ -131,7 +130,7 @@ def SCRIBE(overlay: Optional[VoiceAgentConfig] = None) -> VoiceAgentConfig: # n """ return VoiceAgentConfigPreset._merge_configs( VoiceAgentConfig( - operating_point=OperatingPoint.ENHANCED, + model=Model.ENHANCED, enable_diarization=True, max_delay=2.0, end_of_utterance_silence_trigger=1.0, @@ -150,7 +149,7 @@ def CAPTIONS(overlay: Optional[VoiceAgentConfig] = None) -> VoiceAgentConfig: # """ return VoiceAgentConfigPreset._merge_configs( VoiceAgentConfig( - operating_point=OperatingPoint.ENHANCED, + model=Model.ENHANCED, enable_diarization=True, max_delay=0.7, end_of_utterance_silence_trigger=0.5, @@ -170,7 +169,7 @@ def EXTERNAL(overlay: Optional[VoiceAgentConfig] = None) -> VoiceAgentConfig: # """ return VoiceAgentConfigPreset._merge_configs( VoiceAgentConfig( - operating_point=OperatingPoint.ENHANCED, + model=Model.ENHANCED, enable_diarization=True, max_delay=2.0, end_of_utterance_mode=EndOfUtteranceMode.EXTERNAL, diff --git a/tests/voice/test_04_models.py b/tests/voice/test_04_models.py index 04c698eb..e8e8fa2f 100644 --- a/tests/voice/test_04_models.py +++ b/tests/voice/test_04_models.py @@ -7,6 +7,7 @@ from speechmatics.voice._models import AgentServerMessageType from speechmatics.voice._models import AnnotationFlags from speechmatics.voice._models import AnnotationResult +from speechmatics.voice._models import Model from speechmatics.voice._models import OperatingPoint from speechmatics.voice._models import SessionMetricsMessage from speechmatics.voice._models import SpeakerFocusConfig @@ -55,8 +56,27 @@ async def test_voice_agent_config(): assert config_from_json.known_speakers[0].label == "John" # From JSON - preset: VoiceAgentConfig = VoiceAgentConfig.from_json('{"operating_point": "enhanced"}') - assert preset.operating_point == OperatingPoint.ENHANCED + preset: VoiceAgentConfig = VoiceAgentConfig.from_json('{"model": "enhanced"}') + assert preset.model == Model.ENHANCED + + +@pytest.mark.asyncio +async def test_operating_point_deprecation(): + """Test that the deprecated `operating_point` field migrates to `model`.""" + + # `operating_point` only -> migrates to `model` with a DeprecationWarning + with pytest.warns(DeprecationWarning, match="operating_point"): + config = VoiceAgentConfig(operating_point=OperatingPoint.STANDARD) + assert config.model == Model.STANDARD + assert config.operating_point is None + + # `model` only -> used as-is, no warning + config = VoiceAgentConfig(model=Model.STANDARD) + assert config.model == Model.STANDARD + + # Both set -> error + with pytest.raises(ValueError, match="Cannot specify both 'model' and 'operating_point'"): + VoiceAgentConfig(model=Model.ENHANCED, operating_point=OperatingPoint.STANDARD) @pytest.mark.asyncio diff --git a/tests/voice/test_14_presets.py b/tests/voice/test_14_presets.py index a5cc898f..e397d473 100644 --- a/tests/voice/test_14_presets.py +++ b/tests/voice/test_14_presets.py @@ -1,7 +1,7 @@ import pytest from speechmatics.voice import VoiceAgentConfig -from speechmatics.voice._models import OperatingPoint +from speechmatics.voice._models import Model from speechmatics.voice._models import SpeechSegmentConfig from speechmatics.voice._presets import VoiceAgentConfigPreset @@ -42,10 +42,10 @@ async def test_presets(): async def test_json_presets(): """Test VoiceAgentConfigPreset JSON presets.""" - # With a JSON string overlay + # With a JSON string overlay (using deprecated `operating_point`, internally changed to `model`) preset: VoiceAgentConfig = VoiceAgentConfigPreset.load("fast", '{"operating_point": "enhanced"}') assert preset is not None - assert preset.operating_point == OperatingPoint.ENHANCED + assert preset.model == Model.ENHANCED # Check using incorrect preset name with pytest.raises(ValueError):