mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Split realtime transcript notifications
This commit is contained in:
@@ -16,7 +16,6 @@ pub use realtime_websocket::RealtimeEventParser;
|
||||
pub use realtime_websocket::RealtimeOutputModality;
|
||||
pub use realtime_websocket::RealtimeSessionConfig;
|
||||
pub use realtime_websocket::RealtimeSessionMode;
|
||||
pub use realtime_websocket::RealtimeTranscriptUpdateKind;
|
||||
pub use realtime_websocket::RealtimeWebsocketClient;
|
||||
pub use realtime_websocket::RealtimeWebsocketConnection;
|
||||
pub use realtime_websocket::RealtimeWebsocketEvents;
|
||||
|
||||
@@ -10,9 +10,7 @@ use crate::endpoint::realtime_websocket::protocol::RealtimeOutboundMessage;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeOutputModality;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeSessionConfig;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeSessionMode;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeTranscriptDelta;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeTranscriptEntry;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeTranscriptUpdateKind;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeVoice;
|
||||
use crate::endpoint::realtime_websocket::protocol::parse_realtime_event;
|
||||
use crate::error::ApiError;
|
||||
@@ -416,10 +414,20 @@ impl RealtimeWebsocketEvents {
|
||||
match event {
|
||||
RealtimeEvent::InputAudioSpeechStarted(_) => {}
|
||||
RealtimeEvent::InputTranscriptDelta(update) => {
|
||||
update_active_transcript_entry(&mut active_transcript.entries, "user", update);
|
||||
append_transcript_delta(&mut active_transcript.entries, "user", &update.delta);
|
||||
}
|
||||
RealtimeEvent::InputTranscriptDone(update) => {
|
||||
complete_transcript_entry(&mut active_transcript.entries, "user", &update.text);
|
||||
}
|
||||
RealtimeEvent::OutputTranscriptDelta(update) => {
|
||||
update_active_transcript_entry(&mut active_transcript.entries, "assistant", update);
|
||||
append_transcript_delta(&mut active_transcript.entries, "assistant", &update.delta);
|
||||
}
|
||||
RealtimeEvent::OutputTranscriptDone(update) => {
|
||||
complete_transcript_entry(
|
||||
&mut active_transcript.entries,
|
||||
"assistant",
|
||||
&update.text,
|
||||
);
|
||||
}
|
||||
RealtimeEvent::HandoffRequested(handoff) => {
|
||||
if self.event_parser == RealtimeEventParser::V1 {
|
||||
@@ -456,21 +464,6 @@ fn append_transcript_delta(entries: &mut Vec<RealtimeTranscriptEntry>, role: &st
|
||||
});
|
||||
}
|
||||
|
||||
fn update_active_transcript_entry(
|
||||
entries: &mut Vec<RealtimeTranscriptEntry>,
|
||||
role: &str,
|
||||
update: &RealtimeTranscriptDelta,
|
||||
) {
|
||||
match update.update_kind {
|
||||
RealtimeTranscriptUpdateKind::Delta => {
|
||||
append_transcript_delta(entries, role, &update.delta)
|
||||
}
|
||||
RealtimeTranscriptUpdateKind::Done => {
|
||||
complete_transcript_entry(entries, role, &update.delta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_transcript_entry(entries: &mut Vec<RealtimeTranscriptEntry>, role: &str, text: &str) {
|
||||
if text.is_empty() {
|
||||
return;
|
||||
@@ -770,13 +763,14 @@ fn normalize_realtime_path(url: &mut Url) {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeTranscriptDelta;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeTranscriptEntry;
|
||||
use codex_protocol::protocol::RealtimeHandoffRequested;
|
||||
use codex_protocol::protocol::RealtimeInputAudioSpeechStarted;
|
||||
use codex_protocol::protocol::RealtimeResponseCancelled;
|
||||
use codex_protocol::protocol::RealtimeResponseCreated;
|
||||
use codex_protocol::protocol::RealtimeResponseDone;
|
||||
use codex_protocol::protocol::RealtimeTranscriptDelta;
|
||||
use codex_protocol::protocol::RealtimeTranscriptDone;
|
||||
use codex_protocol::protocol::RealtimeVoice;
|
||||
use http::HeaderValue;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -891,7 +885,6 @@ mod tests {
|
||||
Some(RealtimeEvent::InputTranscriptDelta(
|
||||
RealtimeTranscriptDelta {
|
||||
delta: "hello ".to_string(),
|
||||
update_kind: RealtimeTranscriptUpdateKind::Delta,
|
||||
}
|
||||
))
|
||||
);
|
||||
@@ -910,7 +903,6 @@ mod tests {
|
||||
Some(RealtimeEvent::OutputTranscriptDelta(
|
||||
RealtimeTranscriptDelta {
|
||||
delta: "hi".to_string(),
|
||||
update_kind: RealtimeTranscriptUpdateKind::Delta,
|
||||
}
|
||||
))
|
||||
);
|
||||
@@ -954,7 +946,6 @@ mod tests {
|
||||
Some(RealtimeEvent::InputTranscriptDelta(
|
||||
RealtimeTranscriptDelta {
|
||||
delta: "hello".to_string(),
|
||||
update_kind: RealtimeTranscriptUpdateKind::Delta,
|
||||
}
|
||||
))
|
||||
);
|
||||
@@ -970,10 +961,9 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
parse_realtime_event(payload.as_str(), RealtimeEventParser::RealtimeV2),
|
||||
Some(RealtimeEvent::OutputTranscriptDelta(
|
||||
RealtimeTranscriptDelta {
|
||||
delta: "all done".to_string(),
|
||||
update_kind: RealtimeTranscriptUpdateKind::Done,
|
||||
Some(RealtimeEvent::OutputTranscriptDone(
|
||||
RealtimeTranscriptDone {
|
||||
text: "all done".to_string(),
|
||||
}
|
||||
))
|
||||
);
|
||||
@@ -1514,7 +1504,6 @@ mod tests {
|
||||
input_delta_event,
|
||||
RealtimeEvent::InputTranscriptDelta(RealtimeTranscriptDelta {
|
||||
delta: "delegate ".to_string(),
|
||||
update_kind: RealtimeTranscriptUpdateKind::Delta,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1527,7 +1516,6 @@ mod tests {
|
||||
input_delta_event,
|
||||
RealtimeEvent::InputTranscriptDelta(RealtimeTranscriptDelta {
|
||||
delta: "now".to_string(),
|
||||
update_kind: RealtimeTranscriptUpdateKind::Delta,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1540,7 +1528,6 @@ mod tests {
|
||||
output_delta_event,
|
||||
RealtimeEvent::OutputTranscriptDelta(RealtimeTranscriptDelta {
|
||||
delta: "working".to_string(),
|
||||
update_kind: RealtimeTranscriptUpdateKind::Delta,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -16,4 +16,3 @@ pub use protocol::RealtimeEventParser;
|
||||
pub use protocol::RealtimeOutputModality;
|
||||
pub use protocol::RealtimeSessionConfig;
|
||||
pub use protocol::RealtimeSessionMode;
|
||||
pub use protocol::RealtimeTranscriptUpdateKind;
|
||||
|
||||
@@ -3,9 +3,7 @@ use crate::endpoint::realtime_websocket::protocol_v2::parse_realtime_event_v2;
|
||||
pub use codex_protocol::protocol::RealtimeAudioFrame;
|
||||
pub use codex_protocol::protocol::RealtimeEvent;
|
||||
pub use codex_protocol::protocol::RealtimeOutputModality;
|
||||
pub use codex_protocol::protocol::RealtimeTranscriptDelta;
|
||||
pub use codex_protocol::protocol::RealtimeTranscriptEntry;
|
||||
pub use codex_protocol::protocol::RealtimeTranscriptUpdateKind;
|
||||
pub use codex_protocol::protocol::RealtimeVoice;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use codex_protocol::protocol::RealtimeEvent;
|
||||
use codex_protocol::protocol::RealtimeTranscriptDelta;
|
||||
use codex_protocol::protocol::RealtimeTranscriptUpdateKind;
|
||||
use codex_protocol::protocol::RealtimeTranscriptDone;
|
||||
use serde_json::Value;
|
||||
use tracing::debug;
|
||||
|
||||
@@ -46,20 +46,23 @@ pub(super) fn parse_session_updated_event(parsed: &Value) -> Option<RealtimeEven
|
||||
pub(super) fn parse_transcript_delta_event(
|
||||
parsed: &Value,
|
||||
field: &str,
|
||||
) -> Option<RealtimeTranscriptDelta> {
|
||||
parse_transcript_update_event(parsed, field, RealtimeTranscriptUpdateKind::Delta)
|
||||
}
|
||||
|
||||
pub(super) fn parse_transcript_update_event(
|
||||
parsed: &Value,
|
||||
field: &str,
|
||||
update_kind: RealtimeTranscriptUpdateKind,
|
||||
) -> Option<RealtimeTranscriptDelta> {
|
||||
parsed
|
||||
.get(field)
|
||||
.and_then(Value::as_str)
|
||||
.map(str::to_string)
|
||||
.map(|delta| RealtimeTranscriptDelta { delta, update_kind })
|
||||
.map(|delta| RealtimeTranscriptDelta { delta })
|
||||
}
|
||||
|
||||
pub(super) fn parse_transcript_done_event(
|
||||
parsed: &Value,
|
||||
field: &str,
|
||||
) -> Option<RealtimeTranscriptDone> {
|
||||
parsed
|
||||
.get(field)
|
||||
.and_then(Value::as_str)
|
||||
.map(str::to_string)
|
||||
.map(|text| RealtimeTranscriptDone { text })
|
||||
}
|
||||
|
||||
pub(super) fn parse_error_event(parsed: &Value) -> Option<RealtimeEvent> {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::endpoint::realtime_websocket::protocol_common::parse_error_event;
|
||||
use crate::endpoint::realtime_websocket::protocol_common::parse_realtime_payload;
|
||||
use crate::endpoint::realtime_websocket::protocol_common::parse_session_updated_event;
|
||||
use crate::endpoint::realtime_websocket::protocol_common::parse_transcript_delta_event;
|
||||
use crate::endpoint::realtime_websocket::protocol_common::parse_transcript_update_event;
|
||||
use crate::endpoint::realtime_websocket::protocol_common::parse_transcript_done_event;
|
||||
use codex_protocol::protocol::RealtimeAudioFrame;
|
||||
use codex_protocol::protocol::RealtimeEvent;
|
||||
use codex_protocol::protocol::RealtimeHandoffRequested;
|
||||
@@ -10,7 +10,6 @@ use codex_protocol::protocol::RealtimeInputAudioSpeechStarted;
|
||||
use codex_protocol::protocol::RealtimeResponseCancelled;
|
||||
use codex_protocol::protocol::RealtimeResponseCreated;
|
||||
use codex_protocol::protocol::RealtimeResponseDone;
|
||||
use codex_protocol::protocol::RealtimeTranscriptUpdateKind;
|
||||
use serde_json::Map as JsonMap;
|
||||
use serde_json::Value;
|
||||
use tracing::debug;
|
||||
@@ -32,19 +31,18 @@ pub(super) fn parse_realtime_event_v2(payload: &str) -> Option<RealtimeEvent> {
|
||||
parse_transcript_delta_event(&parsed, "delta").map(RealtimeEvent::InputTranscriptDelta)
|
||||
}
|
||||
"conversation.item.input_audio_transcription.completed" => {
|
||||
parse_transcript_update_event(&parsed, "transcript", RealtimeTranscriptUpdateKind::Done)
|
||||
.map(RealtimeEvent::InputTranscriptDelta)
|
||||
parse_transcript_done_event(&parsed, "transcript")
|
||||
.map(RealtimeEvent::InputTranscriptDone)
|
||||
}
|
||||
"response.output_text.delta" | "response.output_audio_transcript.delta" => {
|
||||
parse_transcript_delta_event(&parsed, "delta").map(RealtimeEvent::OutputTranscriptDelta)
|
||||
}
|
||||
"response.output_text.done" => {
|
||||
parse_transcript_update_event(&parsed, "text", RealtimeTranscriptUpdateKind::Done)
|
||||
.map(RealtimeEvent::OutputTranscriptDelta)
|
||||
parse_transcript_done_event(&parsed, "text").map(RealtimeEvent::OutputTranscriptDone)
|
||||
}
|
||||
"response.output_audio_transcript.done" => {
|
||||
parse_transcript_update_event(&parsed, "transcript", RealtimeTranscriptUpdateKind::Done)
|
||||
.map(RealtimeEvent::OutputTranscriptDelta)
|
||||
parse_transcript_done_event(&parsed, "transcript")
|
||||
.map(RealtimeEvent::OutputTranscriptDone)
|
||||
}
|
||||
"input_audio_buffer.speech_started" => Some(RealtimeEvent::InputAudioSpeechStarted(
|
||||
RealtimeInputAudioSpeechStarted {
|
||||
|
||||
@@ -44,7 +44,6 @@ pub use crate::endpoint::RealtimeEventParser;
|
||||
pub use crate::endpoint::RealtimeOutputModality;
|
||||
pub use crate::endpoint::RealtimeSessionConfig;
|
||||
pub use crate::endpoint::RealtimeSessionMode;
|
||||
pub use crate::endpoint::RealtimeTranscriptUpdateKind;
|
||||
pub use crate::endpoint::RealtimeWebsocketClient;
|
||||
pub use crate::endpoint::RealtimeWebsocketConnection;
|
||||
pub use crate::endpoint::RealtimeWebsocketEvents;
|
||||
|
||||
Reference in New Issue
Block a user