use super::conversation_function_call_output_message; use super::conversation_handoff_append_message; use super::session_update_message; use super::standalone_handoff_message; use crate::endpoint::realtime_websocket::protocol::RealtimeContextAppendChannel; use crate::endpoint::realtime_websocket::protocol::RealtimeOutputModality; use crate::endpoint::realtime_websocket::protocol::RealtimeSessionMode; use crate::endpoint::realtime_websocket::protocol::RealtimeVoice; use crate::endpoint::realtime_websocket::protocol::RealtimeWireAdapter; use pretty_assertions::assert_eq; use serde_json::Value; use serde_json::json; use serde_json::to_value; #[test] fn frameless_session_update_encodes_explicit_delegation_ack_filler() { for delegation_ack_filler in [false, true] { let message = session_update_message( RealtimeWireAdapter::FramelessBidi, "instructions".to_string(), Vec::new(), RealtimeSessionMode::Conversational, RealtimeOutputModality::Audio, RealtimeVoice::Marin, Some(delegation_ack_filler), ); assert_eq!( to_value(message).expect("frameless session update should serialize"), json!({ "type": "session.update", "session": { "instructions": "instructions", "audio": { "output": { "voice": "marin", }, }, "delegation": { "type": "client", "ack_filler": delegation_ack_filler, }, }, }) ); } } #[test] fn context_append_channel_only_encodes_for_frameless_handoff_output() { let legacy = conversation_handoff_append_message( RealtimeWireAdapter::V1, "handoff-123".to_string(), "The result".to_string(), Some(RealtimeContextAppendChannel::Commentary), ); let frameless = conversation_handoff_append_message( RealtimeWireAdapter::FramelessBidi, "handoff-123".to_string(), "The result".to_string(), Some(RealtimeContextAppendChannel::Commentary), ); assert_eq!( to_value(legacy).expect("legacy handoff should serialize"), json!({ "type": "conversation.handoff.append", "handoff_id": "handoff-123", "output_text": "The result", }) ); assert_eq!( to_value(frameless).expect("frameless handoff should serialize"), json!({ "type": "delegation.context.append", "delegation_item_id": "handoff-123", "channel": "commentary", "content": [{"type": "input_text", "text": "The result"}], }) ); } #[test] fn standalone_handoff_uses_session_context_for_frameless() { let legacy = standalone_handoff_message( RealtimeWireAdapter::V1, "codex".to_string(), "Speak this".to_string(), Some(RealtimeContextAppendChannel::Speakable), ); let frameless = standalone_handoff_message( RealtimeWireAdapter::FramelessBidi, "codex".to_string(), "Speak this".to_string(), Some(RealtimeContextAppendChannel::Speakable), ); assert_eq!( to_value(legacy).expect("legacy standalone handoff should serialize"), json!({ "type": "conversation.handoff.append", "handoff_id": "codex", "output_text": "Speak this", }) ); assert_eq!( to_value(frameless).expect("frameless standalone handoff should serialize"), json!({ "type": "session.context.append", "channel": "speakable", "content": [{"type": "input_text", "text": "Speak this"}], }) ); } #[test] fn completed_handoff_only_prefixes_v1_payload_text() { for wire_adapter in [RealtimeWireAdapter::V1, RealtimeWireAdapter::FramelessBidi] { let encoded = to_value(conversation_function_call_output_message( wire_adapter, "handoff-123".to_string(), "Done".to_string(), Some(RealtimeContextAppendChannel::Speakable), )) .expect("handoff output should serialize"); let text = match wire_adapter { RealtimeWireAdapter::V1 => &encoded["output_text"], RealtimeWireAdapter::FramelessBidi => &encoded["content"][0]["text"], RealtimeWireAdapter::RealtimeV2 => unreachable!(), }; assert_eq!( text, &Value::String(match wire_adapter { RealtimeWireAdapter::V1 => "\"Agent Final Message\":\n\nDone".to_string(), RealtimeWireAdapter::FramelessBidi => "Done".to_string(), RealtimeWireAdapter::RealtimeV2 => unreachable!(), }) ); assert_eq!( encoded.get("channel").cloned(), match wire_adapter { RealtimeWireAdapter::V1 => None, RealtimeWireAdapter::FramelessBidi => { Some(Value::String("speakable".to_string())) } RealtimeWireAdapter::RealtimeV2 => unreachable!(), } ); } }