mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Route realtime V3 handoffs by response channel (#33903)
## What changed - Add `codexResponseHandoffMode` to `thread/realtime/start` for V3 sessions, with `thinking` as the default and `commentary` and `bemTags` routing modes. - Map BEM `analysis` and `commentary` output to the commentary channel and `final` output to the speakable channel, while preserving the original BEM envelope. Unrecognized output falls back to speakable. - Mark explicit speech appends as speakable and stop adding the legacy `"Agent Final Message"` prefix to V3 handoffs. V1 and V2 continue to ignore the new setting. ## Testing - Cover channel selection for every routing mode, streamed BEM header parsing, fallback behavior, explicit speech, and V1 compatibility. GitOrigin-RevId: 1974578257488f2914b252c9c1990ba38831c96b
This commit is contained in:
1 parent
161748a68e
commit
025db22058
26 files changed
+851
-167
No files matched your search
@@ -9,6 +9,7 @@ use crate::endpoint::realtime_websocket::methods_frameless_bidi::context_append_
|
||||
use crate::endpoint::realtime_websocket::methods_frameless_bidi::delegation_context_append_message as frameless_delegation_context_append_message;
|
||||
use crate::endpoint::realtime_websocket::methods_frameless_bidi::session_context_append_message as frameless_session_context_append_message;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeAudioFrame;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeContextAppendChannel;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeEvent;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeEventParser;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeOutboundMessage;
|
||||
@@ -211,6 +212,7 @@ pub struct RealtimeWebsocketWriter {
|
||||
stream: Arc<WsStream>,
|
||||
is_closed: Arc<AtomicBool>,
|
||||
event_parser: RealtimeEventParser,
|
||||
context_append_channel: Option<RealtimeContextAppendChannel>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -281,6 +283,7 @@ impl RealtimeWebsocketConnection {
|
||||
stream: Arc::clone(&stream),
|
||||
is_closed: Arc::clone(&is_closed),
|
||||
event_parser,
|
||||
context_append_channel: None,
|
||||
},
|
||||
events: RealtimeWebsocketEvents {
|
||||
rx_message,
|
||||
@@ -294,6 +297,11 @@ impl RealtimeWebsocketConnection {
|
||||
}
|
||||
|
||||
impl RealtimeWebsocketWriter {
|
||||
pub fn with_context_append_channel(mut self, channel: RealtimeContextAppendChannel) -> Self {
|
||||
self.context_append_channel = Some(channel);
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn send_audio_frame(&self, frame: RealtimeAudioFrame) -> Result<(), ApiError> {
|
||||
let message = match self.event_parser {
|
||||
RealtimeEventParser::V1 | RealtimeEventParser::RealtimeV2 => {
|
||||
@@ -315,6 +323,7 @@ impl RealtimeWebsocketWriter {
|
||||
self.event_parser,
|
||||
text,
|
||||
role,
|
||||
self.context_append_channel,
|
||||
))
|
||||
.await
|
||||
}
|
||||
@@ -328,6 +337,7 @@ impl RealtimeWebsocketWriter {
|
||||
self.event_parser,
|
||||
handoff_id,
|
||||
output_text,
|
||||
self.context_append_channel,
|
||||
))
|
||||
.await
|
||||
}
|
||||
@@ -341,6 +351,7 @@ impl RealtimeWebsocketWriter {
|
||||
self.event_parser,
|
||||
handoff_id,
|
||||
output_text,
|
||||
self.context_append_channel,
|
||||
))
|
||||
.await
|
||||
}
|
||||
@@ -354,6 +365,7 @@ impl RealtimeWebsocketWriter {
|
||||
self.event_parser,
|
||||
call_id,
|
||||
output_text,
|
||||
self.context_append_channel,
|
||||
))
|
||||
.await
|
||||
}
|
||||
@@ -413,6 +425,7 @@ impl RealtimeWebsocketWriter {
|
||||
match message {
|
||||
RealtimeOutboundMessage::DelegationContextAppend {
|
||||
delegation_item_id,
|
||||
channel,
|
||||
content,
|
||||
} => {
|
||||
if let Some(content) = content.first() {
|
||||
@@ -420,17 +433,20 @@ impl RealtimeWebsocketWriter {
|
||||
self.send_json_frame(&frameless_delegation_context_append_message(
|
||||
delegation_item_id.clone(),
|
||||
chunk,
|
||||
*channel,
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
RealtimeOutboundMessage::SessionContextAppend { content } => {
|
||||
RealtimeOutboundMessage::SessionContextAppend { channel, content } => {
|
||||
if let Some(content) = content.first() {
|
||||
for chunk in context_append_chunks(&content.text) {
|
||||
self.send_json_frame(&frameless_session_context_append_message(chunk))
|
||||
.await?;
|
||||
self.send_json_frame(&frameless_session_context_append_message(
|
||||
chunk, *channel,
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user