Skip to content
Open
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
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/).

## [v2.6.1] — 2026-08-19

### Fixed

- **Speechmatics credential field** — `SpeechmaticsSTT` now emits the REST-compatible `asr.params.key`. The `key` field is preferred; deprecated `api_key` remains supported, warns, and is normalized to `key`.

## [v2.6.0] — 2026-08-10

### Added
Expand All @@ -20,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
- **AssemblyAI STT WebSocket URL** — `AssemblyAISTT.uri` and `AssemblyAiAsrParams.uri` are renamed to `ws_url`, and the field is serialized as `asr.params.ws_url`. This is a breaking rename for callers that set `uri`.
- **Generated model aliasing** — Wire-key aliases (`VoiceSelectionParams`, `AudioConfig`, `voiceId`, `modelId`, `appId`, `sceneList`) now use native pydantic field aliases with population by field name, instead of annotation-metadata conversion on every request and response.


## [v2.4.0] — 2026-06-30

### Added
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/vendors.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Use `turn_detection.language` for Agora interaction language; it defaults to `en

| Class | Provider | Required Parameters |
|---|---|---|
| `SpeechmaticsSTT` | Speechmatics | `api_key`, `language` |
| `SpeechmaticsSTT` | Speechmatics | `key`, `language`; deprecated `api_key` remains supported |
| `DeepgramSTT` | Deepgram | `model` for Agora-managed `nova-2`/`nova-3`; `api_key` for BYOK; `language?`, `keyterm?` |
| `MicrosoftSTT` | Microsoft Azure | `key`, `region`, `language` |
| `OpenAISTT` | OpenAI | `api_key` |
Expand Down
5 changes: 4 additions & 1 deletion docs/reference/vendors.md
Original file line number Diff line number Diff line change
Expand Up @@ -488,11 +488,14 @@ Use `turn_detection.language` for Agora interaction language; it defaults to `en

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `api_key` | `str` | Yes | — | Speechmatics API key |
| `key` | `str` | Yes | `None` | Speechmatics API key |
| `api_key` | `str` | No | `None` | Deprecated alias for `key`; retained for backward compatibility |
| `language` | `str` | Yes | — | Language code (e.g., `en`) |
| `uri` | `str` | No | `None` | Speechmatics streaming WebSocket URL |
| `additional_params` | `Dict[str, Any]` | No | `None` | Additional parameters |

`SpeechmaticsSTT` always serializes its credential as `asr.params.key`. Passing `api_key` emits a `DeprecationWarning` and is normalized to `key`; when both are provided, `key` takes precedence.

### `DeepgramSTT`

| Parameter | Type | Required | Default | Description |
Expand Down
27 changes: 25 additions & 2 deletions src/agora_agent/agentkit/vendors/stt.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import warnings
from typing import Any, Dict, List, Optional

from .base import BaseSTT
Expand All @@ -9,18 +10,40 @@
class SpeechmaticsSTTOptions(BaseModel):
model_config = ConfigDict(extra="forbid")

api_key: str = Field(..., description="Speechmatics API key")
key: Optional[str] = Field(default=None, description="Speechmatics API key")
api_key: Optional[str] = Field(
default=None,
description="Deprecated alias for key; normalized to the REST API key field",
deprecated="Use key instead.",
)
language: str = Field(..., description="Language code (e.g., en, es, fr)")
model: Optional[str] = Field(default=None, description="Model name")
uri: Optional[str] = Field(default=None, description="Speechmatics streaming WebSocket URL")
additional_params: Optional[Dict[str, Any]] = Field(default=None)

@model_validator(mode="before")
@classmethod
def _warn_deprecated_api_key(cls, values: Any) -> Any:
if isinstance(values, dict) and "api_key" in values:
warnings.warn(
"SpeechmaticsSTT.api_key is deprecated; use key instead.",
DeprecationWarning,
stacklevel=2,
)
return values

@model_validator(mode="after")
def _validate_key(self) -> "SpeechmaticsSTTOptions":
if self.key is None and self.__dict__.get("api_key") is None:
raise ValueError("SpeechmaticsSTT requires key")
return self

class SpeechmaticsSTT(SpeechmaticsSTTOptions, BaseSTT):
def to_config(self) -> Dict[str, Any]:
params: Dict[str, Any] = dict(self.additional_params or {})
params.pop("api_key", None)
params.update({
"api_key": self.api_key,
"key": self.key if self.key is not None else self.__dict__.get("api_key"),
"language": self.language,
})
if self.model is not None:
Expand Down
37 changes: 36 additions & 1 deletion src/agora_agent/types/speechmatics_asr_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ class SpeechmaticsAsrParams(UncheckedBaseModel):
Speechmatics ASR configuration parameters.
"""

api_key: str = pydantic.Field()
key: typing.Optional[str] = pydantic.Field(default=None)
"""
Speechmatics API key
"""

api_key: typing.Optional[str] = pydantic.Field(
default=None,
deprecated="Use key instead.",
)
"""
Deprecated alias for key. The SDK normalizes it to key during validation.
"""

language: str = pydantic.Field()
"""
Language code to use for transcription
Expand All @@ -27,6 +35,33 @@ class SpeechmaticsAsrParams(UncheckedBaseModel):
WebSocket URL for the Speechmatics streaming API
"""

if IS_PYDANTIC_V2:

@pydantic.model_validator(mode="before")
@classmethod
def _normalize_api_key(cls, values: typing.Any) -> typing.Any:
if not isinstance(values, typing.Mapping):
return values
normalized = dict(values)
legacy_key = normalized.pop("api_key", None)
if legacy_key is not None:
normalized.setdefault("key", legacy_key)
if normalized.get("key") is None:
raise ValueError("SpeechmaticsAsrParams requires key")
return normalized

else:

@pydantic.root_validator(pre=True)
def _normalize_api_key(cls, values: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]:
normalized = dict(values)
legacy_key = normalized.pop("api_key", None)
if legacy_key is not None:
normalized.setdefault("key", legacy_key)
if normalized.get("key") is None:
raise ValueError("SpeechmaticsAsrParams requires key")
return normalized

if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
else:
Expand Down
26 changes: 24 additions & 2 deletions tests/custom/test_request_body.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
from agora_agent.agentkit import AgentSession
from agora_agent.agentkit.presets import resolve_session_presets
from agora_agent.cn import QwenOmni
from agora_agent.types.speechmatics_asr_params import SpeechmaticsAsrParams
from test_helpers import test_client


Expand Down Expand Up @@ -836,13 +837,34 @@ def test_byok_ares_stt_no_params() -> None:


def test_byok_speechmatics_stt_params() -> None:
agent = Agent(test_client()).with_stt(SpeechmaticsSTT(api_key="sm-key", language="en"))
with pytest.warns(DeprecationWarning, match="use key instead"):
agent = Agent(test_client()).with_stt(SpeechmaticsSTT(api_key="sm-key", language="en"))
props = build_properties(agent, allow_missing={"llm", "tts"})
assert props["asr"]["vendor"] == "speechmatics"
assert props["asr"]["params"]["api_key"] == "sm-key"
assert props["asr"]["params"]["key"] == "sm-key"
assert "api_key" not in props["asr"]["params"]
assert props["asr"]["params"]["language"] == "en"


def test_byok_speechmatics_stt_key_takes_precedence() -> None:
assert SpeechmaticsSTT(key="new-key", language="en").to_config()["params"]["key"] == "new-key"

with pytest.warns(DeprecationWarning, match="use key instead"):
config = SpeechmaticsSTT(key="new-key", api_key="legacy-key", language="en").to_config()
assert config["params"]["key"] == "new-key"
assert "api_key" not in config["params"]


def test_generated_speechmatics_params_normalizes_deprecated_api_key() -> None:
params = SpeechmaticsAsrParams(api_key="legacy-key", language="en")
assert dump(params) == {"key": "legacy-key", "language": "en"}


def test_generated_speechmatics_params_requires_a_key() -> None:
with pytest.raises(ValueError, match="requires key"):
SpeechmaticsAsrParams(language="en")


def test_byok_sarvam_stt_params() -> None:
agent = Agent(test_client()).with_stt(SarvamSTT(api_key="sarvam-key", language="en-IN"))
props = build_properties(agent, allow_missing={"llm", "tts"})
Expand Down
2 changes: 1 addition & 1 deletion tests/custom/test_stt_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def test_stt_vendor_params_match_documented_shapes() -> None:
}

assert SpeechmaticsSTT(api_key="sm-key", language="en").to_config()["params"] == {
"api_key": "sm-key",
"key": "sm-key",
"language": "en",
}

Expand Down
Loading