Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/voice/speechmatics/voice/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
25 changes: 20 additions & 5 deletions sdk/voice/speechmatics/voice/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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`.

Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)}")
Expand Down
19 changes: 9 additions & 10 deletions sdk/voice/speechmatics/voice/_presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
24 changes: 22 additions & 2 deletions tests/voice/test_04_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions tests/voice/test_14_presets.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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):
Expand Down
Loading