Codex Handbook
codex-api/src/endpoint/realtime_websocket/protocol_common.rs 83 lines
use codex_protocol::protocol::RealtimeEvent;use codex_protocol::protocol::RealtimeTranscriptDelta;use codex_protocol::protocol::RealtimeTranscriptDone;use serde_json::Value;use tracing::debug;pub(super) fn parse_realtime_payload(payload: &str, parser_name: &str) -> Option<(Value, String)> {    let parsed: Value = match serde_json::from_str(payload) {        Ok(message) => message,        Err(err) => {            debug!("failed to parse {parser_name} event: {err}, data: {payload}");            return None;        }    };    let message_type = match parsed.get("type").and_then(Value::as_str) {        Some(message_type) => message_type.to_string(),        None => {            debug!("received {parser_name} event without type field: {payload}");            return None;        }    };    Some((parsed, message_type))}pub(super) fn parse_session_updated_event(parsed: &Value) -> Option<RealtimeEvent> {    let session_id = parsed        .get("session")        .and_then(Value::as_object)        .and_then(|session| session.get("id"))        .and_then(Value::as_str)        .map(str::to_string)?;    let instructions = parsed        .get("session")        .and_then(Value::as_object)        .and_then(|session| session.get("instructions"))        .and_then(Value::as_str)        .map(str::to_string);    Some(RealtimeEvent::SessionUpdated {        realtime_session_id: session_id,        instructions,    })}pub(super) fn parse_transcript_delta_event(    parsed: &Value,    field: &str,) -> Option<RealtimeTranscriptDelta> {    parsed        .get(field)        .and_then(Value::as_str)        .map(str::to_string)        .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> {    parsed        .get("message")        .and_then(Value::as_str)        .map(str::to_string)        .or_else(|| {            parsed                .get("error")                .and_then(Value::as_object)                .and_then(|error| error.get("message"))                .and_then(Value::as_str)                .map(str::to_string)        })        .or_else(|| parsed.get("error").map(ToString::to_string))        .map(RealtimeEvent::Error)}