From bf658f61c9139ca06602d718ff0d3ea8c6f55bb1 Mon Sep 17 00:00:00 2001 From: seymourtang Date: Thu, 23 Jul 2026 11:10:43 +0800 Subject: [PATCH] feat: update OpenAIRealtime WebSocket URL and add validation tests - Changed the default WebSocket URL for OpenAIRealtime to `wss://api.openai.com/v1/realtime`. - Updated the OpenAIRealtime class to enforce the new default URL. - Added tests to verify the default URL usage, custom URL preservation, and rejection of None values for the URL parameter. --- docs/reference/vendors.md | 2 +- src/agora_agent/agentkit/vendors/mllm.py | 8 +++++--- tests/custom/test_agentkit_vendors.py | 20 ++++++++++++++++++++ tests/custom/test_request_body.py | 1 + 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/docs/reference/vendors.md b/docs/reference/vendors.md index ac61d9a..8741453 100644 --- a/docs/reference/vendors.md +++ b/docs/reference/vendors.md @@ -780,7 +780,7 @@ No constructor parameters. Use `FengmingSTT()`. |---|---|---|---|---| | `api_key` | `str` | Yes | — | OpenAI API key | | `model` | `str` | No | `None` | Model (e.g., `gpt-4o-realtime-preview`) | -| `url` | `str` | No | `None` | Custom WebSocket URL | +| `url` | `str` | No | `wss://api.openai.com/v1/realtime` | OpenAI Realtime WebSocket URL | | `greeting_message` | `str` | No | `None` | Greeting message | | `failure_message` | `str` | No | `None` | Message played when the model call fails | | `input_modalities` | `List[str]` | No | `None` | Input modalities | diff --git a/src/agora_agent/agentkit/vendors/mllm.py b/src/agora_agent/agentkit/vendors/mllm.py index 71d94fe..4bc74cd 100644 --- a/src/agora_agent/agentkit/vendors/mllm.py +++ b/src/agora_agent/agentkit/vendors/mllm.py @@ -16,7 +16,10 @@ class OpenAIRealtime(BaseMLLM): voice: Optional[str] = Field(default=None, description="Voice identifier") instructions: Optional[str] = Field(default=None, description="System instructions") input_audio_transcription: Optional[Dict[str, Any]] = Field(default=None, description="Audio transcription settings") - url: Optional[str] = Field(default=None, description="WebSocket URL") + url: str = Field( + default="wss://api.openai.com/v1/realtime", + description="OpenAI Realtime WebSocket URL", + ) greeting_message: Optional[str] = Field(default=None, description="Agent greeting message") input_modalities: Optional[List[str]] = Field(default=None, description="Input modalities") output_modalities: Optional[List[str]] = Field(default=None, description="Output modalities") @@ -29,10 +32,9 @@ def to_config(self) -> Dict[str, Any]: config: Dict[str, Any] = { "vendor": "openai", "api_key": self.api_key, + "url": self.url, } - if self.url is not None: - config["url"] = self.url if ( self.model is not None or self.params is not None diff --git a/tests/custom/test_agentkit_vendors.py b/tests/custom/test_agentkit_vendors.py index fb1bdd0..3585dda 100644 --- a/tests/custom/test_agentkit_vendors.py +++ b/tests/custom/test_agentkit_vendors.py @@ -38,6 +38,26 @@ def test_xai_grok_serializes_v27_shape_without_style(): assert "style" not in config +def test_openai_realtime_uses_default_url_when_omitted(): + config = OpenAIRealtime(api_key="openai-key").to_config() + + assert config["url"] == "wss://api.openai.com/v1/realtime" + + +def test_openai_realtime_preserves_custom_url(): + config = OpenAIRealtime( + api_key="openai-key", + url="wss://openai.example.com/v1/realtime", + ).to_config() + + assert config["url"] == "wss://openai.example.com/v1/realtime" + + +def test_openai_realtime_rejects_none_url(): + with pytest.raises(ValidationError): + OpenAIRealtime(api_key="openai-key", url=None) + + def test_xai_grok_emits_params_even_when_empty(): assert XaiGrok(api_key="xai-key").to_config()["params"] == {} diff --git a/tests/custom/test_request_body.py b/tests/custom/test_request_body.py index f662f84..e3117e5 100644 --- a/tests/custom/test_request_body.py +++ b/tests/custom/test_request_body.py @@ -1265,6 +1265,7 @@ def test_byok_openai_realtime_mllm_params() -> None: props = build_properties(agent) assert props["mllm"]["vendor"] == "openai" assert props["mllm"]["api_key"] == "realtime-key" + assert props["mllm"]["url"] == "wss://api.openai.com/v1/realtime" assert props["mllm"]["params"]["model"] == "gpt-4o-realtime-preview" assert props["mllm"]["params"]["voice"] == "coral"