Skip to content

Commit 434c8af

Browse files
Align AgentKit LLM and ASR vendor validation
1 parent 96afe78 commit 434c8af

7 files changed

Lines changed: 71 additions & 25 deletions

File tree

docs/concepts/vendors.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ Used with `agent.with_llm()` for the cascading flow (ASR → LLM → TTS).
2121

2222
| Class | Provider | Required Parameters |
2323
|---|---|---|
24-
| `OpenAI` | OpenAI | `api_key` |
24+
| `OpenAI` | OpenAI | `model` for Agora-managed models; `api_key`, `base_url`, `model` for BYOK |
2525
| `AzureOpenAI` | Azure OpenAI | `api_key`, `endpoint`, `deployment_name` |
26-
| `Anthropic` | Anthropic | `api_key`, `url`, `headers`, `max_tokens` |
27-
| `Gemini` | Google Gemini | `api_key` |
28-
| `Groq` | Groq | `api_key` |
29-
| `VertexAILLM` | Google Vertex AI | `api_key`, `project_id`, `location` |
30-
| `AmazonBedrock` | Amazon Bedrock | `api_key`, `url`, `model` |
31-
| `Dify` | Dify | `api_key`, `url` |
26+
| `Anthropic` | Anthropic | `api_key`, `model`, `url`, `headers`, `max_tokens` |
27+
| `Gemini` | Google Gemini | `api_key`, `model` |
28+
| `Groq` | Groq | `api_key`, `model`, `base_url` |
29+
| `VertexAILLM` | Google Vertex AI | `api_key`, `model`, `project_id`, `location` |
30+
| `AmazonBedrock` | Amazon Bedrock | `access_key`, `secret_key`, `region`, `model` |
31+
| `Dify` | Dify | `api_key`, `url`, `model` |
3232
| `CustomLLM` | OpenAI-compatible LLM | `api_key`, `base_url`, `model` |
3333

3434
<!-- snippet: executable -->

docs/reference/vendors.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ from agora_agent import OpenAI, ElevenLabsTTS, DeepgramTTS, DeepgramSTT, OpenAIR
2323

2424
| Parameter | Type | Required | Default | Description |
2525
|---|---|---|---|---|
26-
| `api_key` | `str` | Yes | | OpenAI API key |
27-
| `model` | `str` | No | `gpt-4o-mini` | Model name |
28-
| `base_url` | `str` | No | `None` | Custom base URL (overrides default OpenAI endpoint) |
26+
| `api_key` | `str` | BYOK only | `None` | OpenAI API key. Optional for supported Agora-managed OpenAI models. |
27+
| `model` | `str` | Yes | | Model name |
28+
| `base_url` | `str` | BYOK only | `None` | OpenAI Chat Completions endpoint URL. Required when `api_key` is set. |
2929
| `temperature` | `float` | No | `None` | Sampling temperature (0.0–2.0) |
3030
| `top_p` | `float` | No | `None` | Nucleus sampling (0.0–1.0) |
3131
| `max_tokens` | `int` | No | `None` | Maximum tokens to generate |
@@ -83,7 +83,7 @@ llm = AzureOpenAI(
8383
| Parameter | Type | Required | Default | Description |
8484
|---|---|---|---|---|
8585
| `api_key` | `str` | Yes || Anthropic API key |
86-
| `model` | `str` | No | `claude-3-5-sonnet-20241022` | Model name |
86+
| `model` | `str` | Yes | | Model name |
8787
| `url` | `str` | Yes || Anthropic messages endpoint URL |
8888
| `headers` | `Dict[str, str]` | Yes || Request headers, including Anthropic API version |
8989
| `max_tokens` | `int` | Yes || Maximum tokens |
@@ -116,7 +116,7 @@ llm = Anthropic(
116116
| Parameter | Type | Required | Default | Description |
117117
|---|---|---|---|---|
118118
| `api_key` | `str` | Yes || Google AI API key |
119-
| `model` | `str` | No | `gemini-2.0-flash-exp` | Model name |
119+
| `model` | `str` | Yes | | Model name |
120120
| `temperature` | `float` | No | `None` | Sampling temperature (0.0–2.0) |
121121
| `top_p` | `float` | No | `None` | Nucleus sampling (0.0–1.0) |
122122
| `top_k` | `int` | No | `None` | Top-k sampling |
@@ -146,8 +146,8 @@ The SDK also includes named helpers for the remaining Agora-supported LLM provid
146146
|---|---|---|
147147
| `Groq` | Groq | `api_key`, `model`, `base_url?` |
148148
| `VertexAILLM` | Google Vertex AI | `api_key`, `model`, `project_id`, `location`, `url?` |
149-
| `AmazonBedrock` | Amazon Bedrock | `api_key`, `url`, `model` |
150-
| `Dify` | Dify | `api_key`, `url`, `user?`, `conversation_id?` |
149+
| `AmazonBedrock` | Amazon Bedrock | `access_key`, `secret_key`, `region`, `model` |
150+
| `Dify` | Dify | `api_key`, `url`, `model`, `user?`, `conversation_id?` |
151151
| `CustomLLM` | OpenAI-compatible LLM | `api_key`, `model`, `base_url` |
152152

153153
---
@@ -285,7 +285,6 @@ The SDK also includes named helpers for the remaining Agora-supported LLM provid
285285
| `key` | `str` | Yes || Murf API key |
286286
| `voice_id` | `str` | No | `None` | Voice ID (e.g., `Ariana`, `Natalie`) |
287287
| `base_url` | `str` | No | `None` | WebSocket endpoint |
288-
| `style` | `str` | No | `None` | Voice style (e.g., `Conversational`) |
289288
| `locale` | `str` | No | `None` | Voice locale |
290289
| `rate` | `float` | No | `None` | Speech rate |
291290
| `pitch` | `float` | No | `None` | Pitch adjustment |

src/agora_agent/agentkit/agent.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ def _is_interaction_language(value: typing.Any) -> bool:
261261
return isinstance(value, str) and value in _INTERACTION_LANGUAGES
262262

263263

264+
def _validate_interaction_language(value: typing.Any) -> InteractionLanguage:
265+
if not _is_interaction_language(value):
266+
raise ValueError(f"Invalid interaction language: {value}")
267+
return value # type: ignore[return-value]
268+
269+
264270
class Agent:
265271
"""A reusable agent definition.
266272
@@ -322,7 +328,11 @@ def __init__(
322328
self._sal = sal
323329
self._advanced_features = advanced_features
324330
self._parameters = parameters
325-
self._interaction_language = interaction_language
331+
self._interaction_language = (
332+
_validate_interaction_language(interaction_language)
333+
if interaction_language is not None
334+
else None
335+
)
326336
self._geofence = geofence
327337
self._labels = labels
328338
self._rtc = rtc
@@ -363,7 +373,7 @@ def with_interaction_language(self, language: InteractionLanguage) -> "Agent":
363373
remain under ``asr.params``, for example ``asr.params.language``.
364374
"""
365375
new_agent = self._clone()
366-
new_agent._interaction_language = language
376+
new_agent._interaction_language = _validate_interaction_language(language)
367377
return new_agent
368378

369379
def with_mllm(self, vendor: BaseMLLM) -> "Agent":

src/agora_agent/agentkit/vendors/llm.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class OpenAIOptions(BaseModel):
2929
model_config = ConfigDict(extra="forbid")
3030

3131
api_key: Optional[str] = Field(default=None, description="OpenAI API key")
32-
model: str = Field(default="gpt-4o-mini", description="Model name")
32+
model: str = Field(..., description="Model name")
3333
base_url: Optional[str] = Field(default=None, description="Custom base URL")
3434
temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0)
3535
top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0)
@@ -49,6 +49,8 @@ class OpenAIOptions(BaseModel):
4949

5050
@model_validator(mode="after")
5151
def _validate_byok_params(self) -> "OpenAIOptions":
52+
if not self.model:
53+
raise ValueError("OpenAI requires model")
5254
if self.api_key is not None and self.base_url is None:
5355
raise ValueError("OpenAI requires base_url when api_key is set")
5456
if self.api_key is None and self.base_url is not None:
@@ -184,7 +186,7 @@ class AnthropicOptions(BaseModel):
184186
model_config = ConfigDict(extra="forbid")
185187

186188
api_key: str = Field(..., description="Anthropic API key")
187-
model: str = Field(default="claude-3-5-sonnet-20241022", description="Model name")
189+
model: str = Field(..., description="Model name")
188190
url: str = Field(..., description="Anthropic messages endpoint URL")
189191
max_tokens: int = Field(..., gt=0)
190192
temperature: Optional[float] = Field(default=None, ge=0.0, le=1.0)
@@ -251,7 +253,7 @@ class GeminiOptions(BaseModel):
251253
model_config = ConfigDict(extra="forbid")
252254

253255
api_key: str = Field(..., description="Google AI API key")
254-
model: str = Field(default="gemini-2.0-flash-exp", description="Model name")
256+
model: str = Field(..., description="Model name")
255257
url: Optional[str] = Field(default=None, description="Custom API endpoint URL")
256258
temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0)
257259
top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0)
@@ -322,7 +324,7 @@ class GroqOptions(OpenAIOptions):
322324
model_config = ConfigDict(extra="forbid")
323325

324326
api_key: str = Field(..., description="Groq API key")
325-
model: str = Field(default="llama-3.3-70b-versatile", description="Model name")
327+
model: str = Field(..., description="Model name")
326328
base_url: str = Field(..., description="Groq-compatible endpoint")
327329

328330

@@ -383,6 +385,7 @@ class AmazonBedrockOptions(AnthropicOptions):
383385
access_key: str = Field(..., description="AWS access key ID")
384386
secret_key: str = Field(..., description="AWS secret access key")
385387
region: str = Field(..., description="AWS region")
388+
model: str = Field(..., description="Amazon Bedrock model identifier")
386389
max_tokens: Optional[int] = Field(default=None, gt=0)
387390
api_key: Optional[str] = Field(default=None, description="Unused; kept for AnthropicOptions compatibility")
388391
url: Optional[str] = Field(default=None, description="Unused; kept for AnthropicOptions compatibility")

src/agora_agent/agentkit/vendors/tts.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,6 @@ class MurfTTSOptions(BaseModel):
497497
key: str = Field(..., description="Murf API key")
498498
voice_id: Optional[str] = Field(default=None, description="Voice ID (e.g., 'Ariana', 'Natalie', 'Ken')")
499499
base_url: Optional[str] = Field(default=None, description="WebSocket endpoint")
500-
style: Optional[str] = Field(default=None, description="Voice style (e.g., 'Angry', 'Sad', 'Conversational', 'Newscast')")
501500
locale: Optional[str] = Field(default=None, description="Voice locale")
502501
rate: Optional[float] = Field(default=None, description="Speech rate")
503502
pitch: Optional[float] = Field(default=None, description="Pitch adjustment")
@@ -520,8 +519,6 @@ def to_config(self) -> Dict[str, Any]:
520519
params["base_url"] = self.options.base_url
521520
if self.options.voice_id is not None:
522521
params["voiceId"] = self.options.voice_id
523-
if self.options.style is not None:
524-
params["style"] = self.options.style
525522
if self.options.locale is not None:
526523
params["locale"] = self.options.locale
527524
if self.options.rate is not None:

tests/custom/test_llm_vendors.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from agora_agent import AmazonBedrock, Anthropic, AzureOpenAI, CustomLLM, Dify, Groq, VertexAILLM
1+
import pytest
2+
3+
from agora_agent import AmazonBedrock, Anthropic, AzureOpenAI, CustomLLM, Dify, Gemini, Groq, OpenAI, VertexAILLM
24

35

46
def test_groq_serializes_as_openai_compatible() -> None:
@@ -95,3 +97,28 @@ def test_dify_serializes_conversation_fields() -> None:
9597
assert config["params"]["model"] == "default"
9698
assert config["params"]["user"] == "user-1"
9799
assert config["params"]["conversation_id"] == "conversation-1"
100+
101+
102+
def test_llm_vendors_reject_missing_required_models() -> None:
103+
with pytest.raises(Exception, match="model"):
104+
OpenAI(api_key="openai-key", base_url="https://api.openai.com/v1/chat/completions")
105+
106+
with pytest.raises(Exception, match="model"):
107+
Anthropic(
108+
api_key="anthropic-key",
109+
url="https://api.anthropic.com/v1/messages",
110+
headers={"anthropic-version": "2023-06-01"},
111+
max_tokens=1024,
112+
)
113+
114+
with pytest.raises(Exception, match="model"):
115+
Gemini(api_key="google-key")
116+
117+
with pytest.raises(Exception, match="model"):
118+
Groq(api_key="groq-key", base_url="https://api.groq.com/openai/v1/chat/completions")
119+
120+
with pytest.raises(Exception, match="model"):
121+
VertexAILLM(api_key="vertex-token", project_id="project", location="us-central1")
122+
123+
with pytest.raises(Exception, match="model"):
124+
AmazonBedrock(access_key="aws-access", secret_key="aws-secret", region="us-east-1")

tests/custom/test_stt_language.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import pytest
2+
13
from agora_agent import (
24
Agent,
35
AmazonSTT,
@@ -64,6 +66,14 @@ def test_explicit_interaction_language_can_differ_from_provider_language() -> No
6466
assert props["asr"]["params"]["language"] == "en"
6567

6668

69+
def test_invalid_explicit_interaction_language_is_rejected() -> None:
70+
with pytest.raises(ValueError, match="Invalid interaction language: en"):
71+
Agent(interaction_language="en") # type: ignore[arg-type]
72+
73+
with pytest.raises(ValueError, match="Invalid interaction language: xx-YY"):
74+
base_agent().with_interaction_language("xx-YY") # type: ignore[arg-type]
75+
76+
6777
def test_default_interaction_language_is_sent_without_stt() -> None:
6878
props = properties(base_agent())
6979

0 commit comments

Comments
 (0)