Add realtime v2 event parser behind feature flag (#14537)

- Add a feature-flagged realtime v2 parser on the existing
websocket/session pipeline.
- Wire parser selection from core feature flags and map the codex
handoff tool-call path into existing handoff events.

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Ahmed Ibrahim
2026-03-12 21:12:40 -07:00
committed by GitHub
Unverified
parent 650beb177e
commit 3e8f47169e
9 changed files with 352 additions and 10 deletions
+8
View File
@@ -180,6 +180,8 @@ pub enum Feature {
VoiceTranscription,
/// Enable experimental realtime voice conversation mode in the TUI.
RealtimeConversation,
/// Route realtime conversations through the v2 event parser.
RealtimeConversationV2,
/// Prevent idle system sleep while a turn is actively running.
PreventIdleSleep,
/// Use the Responses API WebSocket transport for OpenAI by default.
@@ -823,6 +825,12 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::RealtimeConversationV2,
key: "realtime_conversation_v2",
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::PreventIdleSleep,
key: "prevent_idle_sleep",
@@ -5,6 +5,7 @@ use crate::codex::Session;
use crate::default_client::default_headers;
use crate::error::CodexErr;
use crate::error::Result as CodexResult;
use crate::features::Feature;
use crate::realtime_context::build_realtime_startup_context;
use async_channel::Receiver;
use async_channel::Sender;
@@ -12,6 +13,7 @@ use async_channel::TrySendError;
use codex_api::Provider as ApiProvider;
use codex_api::RealtimeAudioFrame;
use codex_api::RealtimeEvent;
use codex_api::RealtimeEventParser;
use codex_api::RealtimeSessionConfig;
use codex_api::RealtimeWebsocketClient;
use codex_api::endpoint::realtime_websocket::RealtimeWebsocketEvents;
@@ -117,6 +119,7 @@ impl RealtimeConversationManager {
prompt: String,
model: Option<String>,
session_id: Option<String>,
event_parser: RealtimeEventParser,
) -> CodexResult<(Receiver<RealtimeEvent>, Arc<AtomicBool>)> {
let previous_state = {
let mut guard = self.state.lock().await;
@@ -132,6 +135,7 @@ impl RealtimeConversationManager {
instructions: prompt,
model,
session_id,
event_parser,
};
let client = RealtimeWebsocketClient::new(api_provider);
let connection = client
@@ -298,6 +302,11 @@ pub(crate) async fn handle_start(
format!("{prompt}\n\n{startup_context}")
};
let model = config.experimental_realtime_ws_model.clone();
let event_parser = if config.features.enabled(Feature::RealtimeConversationV2) {
RealtimeEventParser::RealtimeV2
} else {
RealtimeEventParser::V1
};
let requested_session_id = params
.session_id
@@ -313,6 +322,7 @@ pub(crate) async fn handle_start(
prompt,
model,
requested_session_id.clone(),
event_parser,
)
.await
{