use super::session::RealtimeSessionCreateRequest;
use crate::{
WireRequest, WireResponse,
connection::{HttpBody, MultipartPart},
};
#[derive(Debug, gproxy_protocol_macros::WireBuilder)]
#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
#[derive(gproxy_protocol_macros::DeclaredFields)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CreateRealtimeCallMultipartForm {
pub sdp: MultipartPart,
pub session: Option<RealtimeSessionCreateRequest>,
}
pub type CreateRealtimeCallRequest = WireRequest<CreateRealtimeCallMultipartForm>;
pub type CreateRealtimeCallResponse = WireResponse<HttpBody>;
pub type RealtimeCallSessionJson = RealtimeSessionCreateRequest;
#[derive(
Debug,
Clone,
PartialEq,
serde::Serialize,
serde::Deserialize,
gproxy_protocol_macros::WireBuilder,
gproxy_protocol_macros::DeclaredFields,
)]
#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct LiveSessionCreateRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub instructions: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio: Option<super::RealtimeAudioConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegation: Option<LiveDelegation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub initial_items: Option<Vec<LiveInitialMessage>>,
#[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
pub rest: crate::Rest,
}
#[derive(
Debug,
Clone,
PartialEq,
serde::Serialize,
serde::Deserialize,
gproxy_protocol_macros::WireBuilder,
gproxy_protocol_macros::DeclaredFields,
)]
#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct LiveDelegation {
#[serde(rename = "type")]
pub type_: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub ack_filler: Option<bool>,
#[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
pub rest: crate::Rest,
}
#[derive(
Debug,
Clone,
PartialEq,
serde::Serialize,
serde::Deserialize,
gproxy_protocol_macros::WireBuilder,
gproxy_protocol_macros::DeclaredFields,
)]
#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct LiveInitialMessage {
#[serde(rename = "type")]
pub type_: String,
pub role: String,
pub content: Vec<LiveInitialText>,
#[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
pub rest: crate::Rest,
}
#[derive(
Debug,
Clone,
PartialEq,
serde::Serialize,
serde::Deserialize,
gproxy_protocol_macros::WireBuilder,
gproxy_protocol_macros::DeclaredFields,
)]
#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct LiveInitialText {
#[serde(rename = "type")]
pub type_: String,
pub text: String,
#[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
pub rest: crate::Rest,
}
#[derive(Debug, gproxy_protocol_macros::WireBuilder, gproxy_protocol_macros::DeclaredFields)]
#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CreateLiveCallMultipartForm {
pub sdp: MultipartPart,
pub session: LiveSessionCreateRequest,
}
pub type CreateLiveCallRequest = WireRequest<CreateLiveCallMultipartForm>;
pub type CreateLiveCallResponse = WireResponse<HttpBody>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum RealtimeRoute<'a> {
CreateCall,
CreateLiveCall,
Connect,
Live { call_id: Option<&'a str> },
HangupCall { call_id: &'a str },
CreateLiveSession,
LiveSession { session_id: Option<&'a str> },
HangupLiveSession { session_id: &'a str },
}
impl<'a> RealtimeRoute<'a> {
pub fn from_request(method: &http::Method, path: &'a str) -> Option<Self> {
let route = Self::from_path(path)?;
match (method, route) {
(&http::Method::POST, Self::Live { call_id: None }) => Some(Self::CreateLiveCall),
(&http::Method::POST, Self::CreateCall) => Some(route),
(&http::Method::POST, Self::LiveSession { session_id: None }) => {
Some(Self::CreateLiveSession)
}
(&http::Method::POST, Self::HangupCall { .. } | Self::HangupLiveSession { .. }) => {
Some(route)
}
(&http::Method::GET, Self::Connect | Self::Live { .. } | Self::LiveSession { .. }) => {
Some(route)
}
_ => None,
}
}
pub fn from_path(path: &'a str) -> Option<Self> {
let path = path.strip_prefix("/v1/").unwrap_or(path);
let path = path.trim_start_matches('/').trim_end_matches('/');
match path {
"realtime/calls" => Some(Self::CreateCall),
"realtime" => Some(Self::Connect),
"live" => Some(Self::Live { call_id: None }),
"live/sessions" => Some(Self::LiveSession { session_id: None }),
_ if path.starts_with("realtime/calls/") => segment(path, "realtime/calls/", "/hangup")
.map(|call_id| Self::HangupCall { call_id }),
_ if path.starts_with("live/sessions/") => segment(path, "live/sessions/", "/attach")
.map(|session_id| Self::LiveSession {
session_id: Some(session_id),
})
.or_else(|| {
segment(path, "live/sessions/", "/hangup")
.map(|session_id| Self::HangupLiveSession { session_id })
}),
_ => path
.strip_prefix("live/")
.filter(|id| !id.is_empty() && !id.contains('/') && !matches!(*id, "." | ".."))
.map(|id| Self::Live { call_id: Some(id) }),
}
}
pub fn operation(self) -> crate::OperationKey {
crate::OperationKey {
operation: match self {
Self::CreateCall | Self::CreateLiveCall => crate::Operation::CreateRealtimeCall,
Self::HangupCall { .. } => crate::Operation::HangupRealtimeCall,
Self::CreateLiveSession => crate::Operation::CreateLiveSession,
Self::LiveSession { .. } => crate::Operation::ConnectLive,
Self::HangupLiveSession { .. } => crate::Operation::HangupLiveSession,
_ => crate::Operation::ConnectRealtime,
},
dialect: crate::Dialect::OpenAi,
}
}
}
fn segment<'a>(path: &'a str, prefix: &str, suffix: &str) -> Option<&'a str> {
path.strip_prefix(prefix)?
.strip_suffix(suffix)
.filter(|id| !id.is_empty() && !id.contains('/') && !matches!(*id, "." | ".."))
}