mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Route WebRTC sideband joins to the Realtime API (#35830)
## What changed - Use `https://api.openai.com/v1` for WebRTC sideband websocket joins instead of deriving the URL from the model provider. - Keep `experimental_realtime_ws_base_url` as an explicit sideband override for local development and tests. - Exclude provider query parameters when building sideband URLs. ## Testing - Add coverage that a frameless sideband join uses `wss://api.openai.com/v1/live/rtc_test` even when the provider points at the ChatGPT backend. - Update the sideband retry end-to-end test to inject its mock server URL through the new override. GitOrigin-RevId: 5d3d2cd69905ba04143010ced36a22712825b563
This commit is contained in:
@@ -55,6 +55,7 @@ use tungstenite::protocol::WebSocketConfig;
|
||||
use url::Url;
|
||||
|
||||
const REALTIME_WIRE_LOG_TARGET: &str = "codex_api::realtime_websocket::wire";
|
||||
const OPENAI_REALTIME_API_BASE_URL: &str = "https://api.openai.com/v1";
|
||||
|
||||
struct WsStream {
|
||||
tx_command: mpsc::Sender<WsCommand>,
|
||||
@@ -696,11 +697,21 @@ fn contains_transcript_entry(entries: &[RealtimeTranscriptEntry], role: &str, te
|
||||
|
||||
pub struct RealtimeWebsocketClient {
|
||||
provider: Provider,
|
||||
webrtc_sideband_base_url: String,
|
||||
}
|
||||
|
||||
impl RealtimeWebsocketClient {
|
||||
pub fn new(provider: Provider) -> Self {
|
||||
Self { provider }
|
||||
Self {
|
||||
provider,
|
||||
webrtc_sideband_base_url: OPENAI_REALTIME_API_BASE_URL.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the direct WebRTC sideband URL for local development and tests.
|
||||
pub fn with_webrtc_sideband_base_url(mut self, base_url: String) -> Self {
|
||||
self.webrtc_sideband_base_url = base_url;
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn connect(
|
||||
@@ -775,13 +786,7 @@ impl RealtimeWebsocketClient {
|
||||
) -> Result<RealtimeWebsocketConnection, ApiError> {
|
||||
// Keep the parser/session query shaping from standalone realtime while replacing the model
|
||||
// query with a call_id join onto an existing WebRTC session.
|
||||
let ws_url = websocket_url_from_api_url_for_call(
|
||||
self.provider.base_url.as_str(),
|
||||
self.provider.query_params.as_ref(),
|
||||
config.event_parser,
|
||||
config.session_mode,
|
||||
call_id,
|
||||
)?;
|
||||
let ws_url = self.webrtc_sideband_url(config.event_parser, config.session_mode, call_id)?;
|
||||
self.connect_realtime_websocket_url(
|
||||
ws_url,
|
||||
config,
|
||||
@@ -792,6 +797,21 @@ impl RealtimeWebsocketClient {
|
||||
.await
|
||||
}
|
||||
|
||||
fn webrtc_sideband_url(
|
||||
&self,
|
||||
event_parser: RealtimeEventParser,
|
||||
session_mode: RealtimeSessionMode,
|
||||
call_id: &str,
|
||||
) -> Result<Url, ApiError> {
|
||||
websocket_url_from_api_url_for_call(
|
||||
self.webrtc_sideband_base_url.as_str(),
|
||||
/*query_params*/ None,
|
||||
event_parser,
|
||||
session_mode,
|
||||
call_id,
|
||||
)
|
||||
}
|
||||
|
||||
async fn connect_realtime_websocket_url(
|
||||
&self,
|
||||
ws_url: Url,
|
||||
@@ -1013,6 +1033,7 @@ fn normalize_realtime_path(url: &mut Url, event_parser: RealtimeEventParser) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeTranscriptEntry;
|
||||
use crate::provider::RetryConfig;
|
||||
use codex_protocol::protocol::RealtimeHandoffRequested;
|
||||
use codex_protocol::protocol::RealtimeInputAudioSpeechStarted;
|
||||
use codex_protocol::protocol::RealtimeNoopRequested;
|
||||
@@ -1780,6 +1801,34 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn webrtc_frameless_sideband_ignores_provider_base_url() {
|
||||
let client = RealtimeWebsocketClient::new(Provider {
|
||||
name: "chatgpt".to_string(),
|
||||
base_url: "https://chatgpt.com/backend-api/codex".to_string(),
|
||||
query_params: None,
|
||||
headers: HeaderMap::new(),
|
||||
retry: RetryConfig {
|
||||
max_attempts: 0,
|
||||
base_delay: Duration::ZERO,
|
||||
retry_429: false,
|
||||
retry_5xx: false,
|
||||
retry_transport: false,
|
||||
},
|
||||
stream_idle_timeout: Duration::from_secs(5),
|
||||
});
|
||||
|
||||
let url = client
|
||||
.webrtc_sideband_url(
|
||||
RealtimeEventParser::FramelessBidi,
|
||||
RealtimeSessionMode::Conversational,
|
||||
"rtc_test",
|
||||
)
|
||||
.expect("build ws url");
|
||||
|
||||
assert_eq!(url.as_str(), "wss://api.openai.com/v1/live/rtc_test");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn e2e_connect_and_exchange_events_against_mock_ws_server() {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
|
||||
|
||||
Reference in New Issue
Block a user