Skip to content

Commit 64703bd

Browse files
test(agentkit): add custom tests for v1.5.0 AgentKit behavior
Covers MLLM and avatar guardrails, generic avatar enrichment and token generation, vendor config serialization, turn pagination, and agent-level field overrides for both cascading and MLLM sessions.
1 parent 9f491c6 commit 64703bd

4 files changed

Lines changed: 815 additions & 0 deletions

File tree

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
from agora_agent.agentkit import (
2+
Agent,
3+
AvatarConfig,
4+
AvatarVendor,
5+
LlmConfig,
6+
LlmStyle,
7+
MllmConfig,
8+
MllmVendor,
9+
SttConfig,
10+
SttVendor,
11+
TtsConfig,
12+
)
13+
import pytest
14+
15+
from agora_agent.agentkit.vendors import (
16+
AkoolAvatar,
17+
ElevenLabsTTS,
18+
LiveAvatarAvatar,
19+
OpenAI,
20+
OpenAIRealtime,
21+
)
22+
23+
24+
def _parameter(config, key):
25+
parameters = config["parameters"]
26+
if isinstance(parameters, dict):
27+
return parameters[key]
28+
return getattr(parameters, key)
29+
30+
31+
class _CopyOnlyModel:
32+
def __init__(self, **values):
33+
self.values = values
34+
35+
def copy(self, update=None):
36+
return _CopyOnlyModel(**{**self.values, **(update or {})})
37+
38+
39+
def test_generated_core_aliases_are_public():
40+
assert LlmConfig is not None
41+
assert LlmStyle is not None
42+
assert SttConfig is not None
43+
assert SttVendor is not None
44+
assert TtsConfig is not None
45+
assert MllmConfig is not None
46+
assert MllmVendor is not None
47+
assert AvatarConfig is not None
48+
assert AvatarVendor is not None
49+
50+
51+
def test_model_copy_helper_supports_pydantic_v1_copy_api():
52+
copied = Agent._copy_model_update(_CopyOnlyModel(enable_rtm=True), {"data_channel": "rtm"}) # noqa: SLF001
53+
54+
assert copied.values == {"enable_rtm": True, "data_channel": "rtm"}
55+
56+
57+
def test_with_audio_scenario_sets_session_parameter():
58+
agent = Agent(name="test").with_audio_scenario("chorus")
59+
60+
assert _parameter(agent.config, "audio_scenario") == "chorus"
61+
62+
63+
def test_with_audio_scenario_preserves_existing_parameters():
64+
agent = Agent(name="test", parameters={"enable_metrics": True}).with_audio_scenario(
65+
"chorus"
66+
)
67+
68+
assert _parameter(agent.config, "enable_metrics") is True
69+
assert _parameter(agent.config, "audio_scenario") == "chorus"
70+
71+
72+
def test_enable_rtm_defaults_data_channel_to_rtm():
73+
properties = Agent(name="test", advanced_features={"enable_rtm": True}).to_properties(
74+
channel="room",
75+
agent_uid="1",
76+
remote_uids=["100"],
77+
token="token",
78+
skip_vendor_validation=True,
79+
)
80+
81+
assert properties.parameters.data_channel == "rtm"
82+
83+
84+
def test_enable_rtm_preserves_explicit_data_channel():
85+
properties = Agent(
86+
name="test",
87+
advanced_features={"enable_rtm": True},
88+
parameters={"data_channel": "datastream"},
89+
).to_properties(
90+
channel="room",
91+
agent_uid="1",
92+
remote_uids=["100"],
93+
token="token",
94+
skip_vendor_validation=True,
95+
)
96+
97+
assert properties.parameters.data_channel == "datastream"
98+
99+
100+
def test_agent_level_llm_fields_override_vendor_defaults():
101+
agent = (
102+
Agent(name="test")
103+
.with_llm(
104+
OpenAI(
105+
api_key="llm-key",
106+
greeting_message="vendor greeting",
107+
failure_message="vendor failure",
108+
max_history=1,
109+
)
110+
)
111+
.with_tts(ElevenLabsTTS(key="tts-key", model_id="model", voice_id="voice"))
112+
.with_greeting("agent greeting")
113+
.with_failure_message("agent failure")
114+
.with_max_history(2)
115+
)
116+
117+
properties = agent.to_properties(
118+
channel="room",
119+
agent_uid="1",
120+
remote_uids=["100"],
121+
token="token",
122+
)
123+
124+
assert properties.llm.greeting_message == "agent greeting"
125+
assert properties.llm.failure_message == "agent failure"
126+
assert properties.llm.max_history == 2
127+
128+
129+
def test_avatar_sample_rate_validation_works_when_tts_added_after_avatar():
130+
agent = Agent(name="test").with_avatar(
131+
LiveAvatarAvatar(api_key="avatar-key", quality="medium", agora_uid="2")
132+
)
133+
134+
with pytest.raises(ValueError, match="24000"):
135+
agent.with_tts(
136+
ElevenLabsTTS(key="tts-key", model_id="model", voice_id="voice", sample_rate=16000)
137+
)
138+
139+
140+
def test_avatar_sample_rate_validation_uses_wrapper_sample_rate():
141+
agent = (
142+
Agent(name="test")
143+
.with_avatar(AkoolAvatar(api_key="avatar-key"))
144+
.with_tts(
145+
ElevenLabsTTS(key="tts-key", model_id="model", voice_id="voice", sample_rate=16000)
146+
)
147+
)
148+
149+
assert agent.tts_sample_rate == 16000
150+
151+
152+
def test_with_mllm_removes_deprecated_advanced_features_enable_mllm():
153+
properties = (
154+
Agent(
155+
name="test",
156+
advanced_features={"enable_mllm": True, "enable_rtm": True},
157+
greeting="hello from agent",
158+
failure_message="try again",
159+
max_history=5,
160+
)
161+
.with_mllm(OpenAIRealtime(api_key="openai-key"))
162+
.to_properties(
163+
channel="room",
164+
agent_uid="1",
165+
remote_uids=["100"],
166+
token="rtc-token",
167+
)
168+
)
169+
170+
assert properties.mllm is not None
171+
assert properties.mllm.enable is True
172+
assert properties.mllm.greeting_message == "hello from agent"
173+
assert properties.mllm.failure_message == "try again"
174+
mllm_dump = properties.mllm.model_dump(exclude_none=True)
175+
assert "max_history" not in mllm_dump
176+
assert properties.advanced_features is not None
177+
af_dump = properties.advanced_features.model_dump(exclude_none=True)
178+
assert "enable_mllm" not in af_dump
179+
assert af_dump.get("enable_rtm") is True
180+
181+
182+
def test_to_properties_rejects_mllm_with_enabled_avatar():
183+
agent = (
184+
Agent(name="test")
185+
.with_mllm(OpenAIRealtime(api_key="mllm-key"))
186+
.with_avatar(
187+
LiveAvatarAvatar(
188+
api_key="avatar-key",
189+
quality="medium",
190+
agora_uid="2",
191+
agora_token="avatar-token",
192+
)
193+
)
194+
)
195+
196+
with pytest.raises(ValueError, match="cascading"):
197+
agent.to_properties(
198+
channel="room",
199+
agent_uid="1",
200+
remote_uids=["100"],
201+
token="rtc-token",
202+
)
203+
204+
205+
def test_to_properties_mllm_with_avatar_fires_before_token_generation():
206+
"""The guard must fire before the token-generation step so callers get a
207+
clear, actionable error even when app_id/app_certificate are empty.
208+
"""
209+
agent = (
210+
Agent(name="test")
211+
.with_mllm(OpenAIRealtime(api_key="mllm-key"))
212+
.with_avatar(
213+
LiveAvatarAvatar(
214+
api_key="avatar-key",
215+
quality="medium",
216+
agora_uid="2",
217+
agora_token="avatar-token",
218+
)
219+
)
220+
)
221+
222+
with pytest.raises(ValueError, match="cascading"):
223+
agent.to_properties(
224+
channel="room",
225+
agent_uid="1",
226+
remote_uids=["100"],
227+
app_id="",
228+
app_certificate="",
229+
)
230+
231+
232+
def test_to_properties_rejects_mllm_with_default_enabled_avatar():
233+
"""Avatar with no `enable` field should be treated as enabled."""
234+
agent = Agent(name="test").with_mllm(OpenAIRealtime(api_key="mllm-key"))
235+
agent._avatar = { # noqa: SLF001
236+
"vendor": "liveavatar",
237+
"params": {
238+
"api_key": "avatar-key",
239+
"quality": "high",
240+
"agora_uid": "200",
241+
"agora_token": "avatar-token",
242+
},
243+
}
244+
245+
with pytest.raises(ValueError, match="cascading"):
246+
agent.to_properties(
247+
channel="room",
248+
agent_uid="1",
249+
remote_uids=["100"],
250+
token="rtc-token",
251+
)
252+
253+
254+
def test_to_properties_allows_mllm_with_disabled_avatar_and_no_tts():
255+
properties = (
256+
Agent(name="test")
257+
.with_mllm(OpenAIRealtime(api_key="mllm-key"))
258+
.with_avatar(
259+
LiveAvatarAvatar(
260+
api_key="avatar-key",
261+
quality="medium",
262+
agora_uid="2",
263+
agora_token="avatar-token",
264+
enable=False,
265+
)
266+
)
267+
.to_properties(
268+
channel="room",
269+
agent_uid="1",
270+
remote_uids=["100"],
271+
token="rtc-token",
272+
)
273+
)
274+
275+
assert properties.mllm is not None and properties.mllm.enable is True
276+
assert properties.tts is None
277+
assert properties.llm is None
278+
assert properties.asr is None
279+
assert properties.avatar is not None and properties.avatar.enable is False
280+
281+
282+
def test_to_properties_mllm_without_tts_or_llm_succeeds():
283+
properties = (
284+
Agent(name="test")
285+
.with_mllm(OpenAIRealtime(api_key="mllm-key"))
286+
.to_properties(
287+
channel="room",
288+
agent_uid="1",
289+
remote_uids=["100"],
290+
token="rtc-token",
291+
)
292+
)
293+
294+
assert properties.mllm is not None and properties.mllm.enable is True
295+
assert properties.tts is None
296+
assert properties.llm is None
297+
assert properties.asr is None
298+
assert properties.avatar is None

0 commit comments

Comments
 (0)