use serde::{Deserialize, Serialize};
use super::audio::{InputAudioFormat, OutputAudioFormat};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TurnDetectionType {
#[default]
ClientVad,
ServerVad,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TurnDetection {
#[serde(rename = "type")]
pub type_: TurnDetectionType,
}
impl TurnDetection {
pub fn new(type_: TurnDetectionType) -> Self {
Self { type_ }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ChatMode {
VideoPassive,
#[default]
Audio,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BetaFields {
#[serde(skip_serializing_if = "Option::is_none")]
pub chat_mode: Option<ChatMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tts_source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auto_search: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RealtimeTool {
#[serde(rename = "type")]
pub type_: String,
pub name: String,
pub description: String,
pub parameters: serde_json::Value,
}
impl RealtimeTool {
pub fn function(
name: impl Into<String>,
description: impl Into<String>,
parameters: serde_json::Value,
) -> Self {
Self {
type_: "function".to_string(),
name: name.into(),
description: description.into(),
parameters,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionConfig {
pub input_audio_format: InputAudioFormat,
pub output_audio_format: OutputAudioFormat,
#[serde(skip_serializing_if = "Option::is_none")]
pub instructions: Option<String>,
pub turn_detection: TurnDetection,
pub beta_fields: BetaFields,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<RealtimeTool>,
}
impl Default for SessionConfig {
fn default() -> Self {
Self {
input_audio_format: InputAudioFormat::default(),
output_audio_format: OutputAudioFormat::default(),
instructions: None,
turn_detection: TurnDetection::new(TurnDetectionType::default()),
beta_fields: BetaFields::default(),
tools: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ItemType {
Message,
FunctionCall,
FunctionCallOutput,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItemContent {
#[serde(rename = "type")]
pub type_: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transcript: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RealtimeConversationItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(rename = "type")]
pub type_: ItemType,
pub object: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub content: Vec<ItemContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output: Option<String>,
}
impl RealtimeConversationItem {
pub fn user_text(text: impl Into<String>) -> Self {
Self {
id: None,
type_: ItemType::Message,
object: "realtime.item".to_string(),
status: Some("completed".to_string()),
role: Some("user".to_string()),
content: vec![ItemContent {
type_: "input_text".to_string(),
text: Some(text.into()),
audio: None,
transcript: None,
}],
name: None,
arguments: None,
output: None,
}
}
pub fn function_output(call_name: impl Into<String>, output: impl Into<String>) -> Self {
Self {
id: None,
type_: ItemType::FunctionCallOutput,
object: "realtime.item".to_string(),
status: None,
role: None,
content: Vec::new(),
name: Some(call_name.into()),
arguments: None,
output: Some(output.into()),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TokenDetails {
#[serde(skip_serializing_if = "Option::is_none")]
pub text_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_tokens: Option<u64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RealtimeUsage {
#[serde(default)]
pub total_tokens: u64,
#[serde(default)]
pub input_tokens: u64,
#[serde(default)]
pub output_tokens: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_token_details: Option<TokenDetails>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_token_details: Option<TokenDetails>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RealtimeResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub object: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<RealtimeUsage>,
}