Skip to content

Commit ee564d1

Browse files
hotfix for missing url parameter for mllm
1 parent c1d644b commit ee564d1

5 files changed

Lines changed: 86 additions & 5 deletions

File tree

src/agora_agent/agentkit/agent.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ def to_properties(
536536
mllm_config = dict(self._mllm)
537537
if self._greeting:
538538
mllm_config.setdefault("greeting_message", self._greeting)
539+
if self._failure_message:
540+
mllm_config.setdefault("failure_message", self._failure_message)
541+
if self._max_history is not None:
542+
mllm_config.setdefault("max_history", self._max_history)
539543
base_kwargs["mllm"] = mllm_config
540544
return StartAgentsRequestProperties(**base_kwargs)
541545

src/agora_agent/agentkit/vendors/mllm.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class OpenAIRealtimeOptions(BaseModel):
1616
output_modalities: Optional[List[str]] = Field(default=None, description="Output modalities")
1717
messages: Optional[List[Dict[str, Any]]] = Field(default=None, description="Conversation messages")
1818
params: Optional[Dict[str, Any]] = Field(default=None, description="Additional parameters")
19+
predefined_tools: Optional[List[str]] = Field(default=None, description="Predefined tools")
20+
failure_message: Optional[str] = Field(default=None, description="Message played on failure")
21+
max_history: Optional[int] = Field(default=None, description="Maximum conversation history length")
1922

2023
class OpenAIRealtime(BaseMLLM):
2124
def __init__(self, **kwargs: Any):
@@ -45,6 +48,12 @@ def to_config(self) -> Dict[str, Any]:
4548
config["output_modalities"] = self.options.output_modalities
4649
if self.options.messages is not None:
4750
config["messages"] = self.options.messages
51+
if self.options.predefined_tools is not None:
52+
config["predefined_tools"] = self.options.predefined_tools
53+
if self.options.failure_message is not None:
54+
config["failure_message"] = self.options.failure_message
55+
if self.options.max_history is not None:
56+
config["max_history"] = self.options.max_history
4857

4958
return config
5059

@@ -53,6 +62,7 @@ class VertexAIOptions(BaseModel):
5362
model_config = ConfigDict(extra="forbid")
5463

5564
model: str = Field(..., description="Model name")
65+
url: Optional[str] = Field(default=None, description="WebSocket URL")
5666
project_id: str = Field(..., description="Google Cloud project ID")
5767
location: str = Field(..., description="Google Cloud location/region")
5868
adc_credentials_string: str = Field(..., description="Application Default Credentials JSON string")
@@ -63,6 +73,9 @@ class VertexAIOptions(BaseModel):
6373
output_modalities: Optional[List[str]] = Field(default=None, description="Output modalities")
6474
messages: Optional[List[Dict[str, Any]]] = Field(default=None, description="Conversation messages")
6575
additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional parameters")
76+
predefined_tools: Optional[List[str]] = Field(default=None, description="Predefined tools")
77+
failure_message: Optional[str] = Field(default=None, description="Message played on failure")
78+
max_history: Optional[int] = Field(default=None, description="Maximum conversation history length")
6679

6780
class VertexAI(BaseMLLM):
6881
def __init__(self, **kwargs: Any):
@@ -89,6 +102,8 @@ def to_config(self) -> Dict[str, Any]:
89102
"params": params,
90103
}
91104

105+
if self.options.url is not None:
106+
config["url"] = self.options.url
92107
if self.options.greeting_message is not None:
93108
config["greeting_message"] = self.options.greeting_message
94109
if self.options.input_modalities is not None:
@@ -97,6 +112,12 @@ def to_config(self) -> Dict[str, Any]:
97112
config["output_modalities"] = self.options.output_modalities
98113
if self.options.messages is not None:
99114
config["messages"] = self.options.messages
115+
if self.options.predefined_tools is not None:
116+
config["predefined_tools"] = self.options.predefined_tools
117+
if self.options.failure_message is not None:
118+
config["failure_message"] = self.options.failure_message
119+
if self.options.max_history is not None:
120+
config["max_history"] = self.options.max_history
100121

101122
return config
102123

@@ -106,13 +127,17 @@ class GeminiLiveOptions(BaseModel):
106127

107128
api_key: str = Field(..., description="Google API key")
108129
model: str = Field(..., description="Gemini Live model name")
130+
url: Optional[str] = Field(default=None, description="WebSocket URL")
109131
instructions: Optional[str] = Field(default=None, description="System instructions")
110132
voice: Optional[str] = Field(default=None, description="Voice name")
111133
greeting_message: Optional[str] = Field(default=None, description="Agent greeting message")
112134
input_modalities: Optional[List[str]] = Field(default=None, description="Input modalities")
113135
output_modalities: Optional[List[str]] = Field(default=None, description="Output modalities")
114136
messages: Optional[List[Dict[str, Any]]] = Field(default=None, description="Conversation messages")
115137
additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional parameters")
138+
predefined_tools: Optional[List[str]] = Field(default=None, description="Predefined tools")
139+
failure_message: Optional[str] = Field(default=None, description="Message played on failure")
140+
max_history: Optional[int] = Field(default=None, description="Maximum conversation history length")
116141

117142
class GeminiLive(BaseMLLM):
118143
def __init__(self, **kwargs: Any):
@@ -135,6 +160,8 @@ def to_config(self) -> Dict[str, Any]:
135160
"params": params,
136161
}
137162

163+
if self.options.url is not None:
164+
config["url"] = self.options.url
138165
if self.options.greeting_message is not None:
139166
config["greeting_message"] = self.options.greeting_message
140167
if self.options.input_modalities is not None:
@@ -143,5 +170,11 @@ def to_config(self) -> Dict[str, Any]:
143170
config["output_modalities"] = self.options.output_modalities
144171
if self.options.messages is not None:
145172
config["messages"] = self.options.messages
173+
if self.options.predefined_tools is not None:
174+
config["predefined_tools"] = self.options.predefined_tools
175+
if self.options.failure_message is not None:
176+
config["failure_message"] = self.options.failure_message
177+
if self.options.max_history is not None:
178+
config["max_history"] = self.options.max_history
146179

147180
return config

tests/agentkit/test_agent.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def test_to_properties_generates_token_and_respects_mllm_vendor_precedence():
108108
agent = Agent(greeting="top hello", failure_message="top fail", max_history=9).with_mllm(
109109
OpenAIRealtime(
110110
api_key="key",
111+
url="wss://openai.example.com/realtime",
111112
greeting_message="vendor hello",
112113
)
113114
).with_advanced_features({"enable_mllm": True})
@@ -123,6 +124,7 @@ def test_to_properties_generates_token_and_respects_mllm_vendor_precedence():
123124
)
124125

125126
assert props["mllm"]["greeting_message"] == "vendor hello"
126-
assert "failure_message" not in props["mllm"]
127-
assert "max_history" not in props["mllm"]
127+
assert props["mllm"]["failure_message"] == "top fail"
128+
assert props["mllm"]["max_history"] == 9
129+
assert props["mllm"]["url"] == "wss://openai.example.com/realtime"
128130
assert isinstance(props["token"], str) and props["token"]

tests/agentkit/test_agentkit_parity.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,13 @@ def test_gemini_live_matches_low_level_shape(self):
137137
config = GeminiLive(
138138
api_key="google-key",
139139
model="gemini-live-2.5-flash",
140+
url="wss://generativelanguage.googleapis.com/ws",
140141
instructions="You are concise.",
141142
voice="Aoede",
142143
greeting_message="Hello",
144+
predefined_tools=["_publish_message"],
145+
failure_message="Please try again.",
146+
max_history=8,
143147
additional_params={"temperature": 0.2},
144148
messages=[{"role": "user", "content": "Hi"}],
145149
).to_config()
@@ -150,6 +154,7 @@ def test_gemini_live_matches_low_level_shape(self):
150154
"vendor": "gemini",
151155
"style": "openai",
152156
"api_key": "google-key",
157+
"url": "wss://generativelanguage.googleapis.com/ws",
153158
"params": {
154159
"temperature": 0.2,
155160
"model": "gemini-live-2.5-flash",
@@ -158,6 +163,9 @@ def test_gemini_live_matches_low_level_shape(self):
158163
},
159164
"messages": [{"role": "user", "content": "Hi"}],
160165
"greeting_message": "Hello",
166+
"predefined_tools": ["_publish_message"],
167+
"failure_message": "Please try again.",
168+
"max_history": 8,
161169
},
162170
)
163171

tests/agentkit/test_vendors.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,56 @@ def test_llm_vendor_mappings_cover_core_shapes_and_defaults():
5656

5757

5858
def test_mllm_vendor_mappings_cover_optional_branches():
59-
realtime = OpenAIRealtime(api_key="key").to_config()
60-
assert realtime == {"vendor": "openai", "style": "openai", "api_key": "key"}
59+
realtime = OpenAIRealtime(
60+
api_key="key",
61+
url="wss://openai.example.com/realtime",
62+
predefined_tools=["_publish_message"],
63+
failure_message="Retry",
64+
max_history=3,
65+
).to_config()
66+
assert realtime == {
67+
"vendor": "openai",
68+
"style": "openai",
69+
"api_key": "key",
70+
"url": "wss://openai.example.com/realtime",
71+
"predefined_tools": ["_publish_message"],
72+
"failure_message": "Retry",
73+
"max_history": 3,
74+
}
6175

6276
vertex = VertexAI(
6377
model="gemini-live",
78+
url="wss://vertex.example.com/realtime",
6479
project_id="project",
6580
location="us-central1",
6681
adc_credentials_string="creds",
6782
additional_params={"temperature": 0.2},
83+
predefined_tools=["_publish_message"],
84+
failure_message="Try again",
85+
max_history=5,
6886
).to_config()
6987
assert vertex["vendor"] == "vertexai"
88+
assert vertex["url"] == "wss://vertex.example.com/realtime"
7089
assert vertex["params"]["temperature"] == 0.2
90+
assert vertex["predefined_tools"] == ["_publish_message"]
91+
assert vertex["failure_message"] == "Try again"
92+
assert vertex["max_history"] == 5
7193

72-
gemini_live = GeminiLive(api_key="key", model="gemini-live", voice="Aoede").to_config()
94+
gemini_live = GeminiLive(
95+
api_key="key",
96+
model="gemini-live",
97+
url="wss://gemini.example.com/realtime",
98+
voice="Aoede",
99+
predefined_tools=["_publish_message"],
100+
failure_message="Please try again.",
101+
max_history=8,
102+
).to_config()
73103
assert gemini_live["vendor"] == "gemini"
104+
assert gemini_live["url"] == "wss://gemini.example.com/realtime"
74105
assert gemini_live["params"]["voice"] == "Aoede"
106+
assert gemini_live["predefined_tools"] == ["_publish_message"]
107+
assert gemini_live["failure_message"] == "Please try again."
108+
assert gemini_live["max_history"] == 8
75109

76110

77111
def test_stt_vendor_mappings_cover_all_wrappers():

0 commit comments

Comments
 (0)