use crate::PcmFormat;
use bytes::Bytes;
use serde_json::{Map, Value};
use std::{collections::BTreeMap, fmt, time::Duration};
pub type Labels = BTreeMap<String, String>;
#[derive(Clone, Copy, Debug)]
pub struct RealtimeTimeouts {
pub connect: Duration,
pub ready: Duration,
pub request: Duration,
}
impl Default for RealtimeTimeouts {
fn default() -> Self {
Self {
connect: Duration::from_secs(10),
ready: Duration::from_secs(30),
request: Duration::from_secs(10),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TurnDetection {
pub eagerness: Option<String>,
pub end_of_turn_timeout_ms: Option<u32>,
}
#[derive(Clone, Debug)]
pub enum PrismFiller {
None,
Text(String),
Auto(Option<String>),
}
#[derive(Clone, Debug)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub parameters: Map<String, Value>,
pub prism_filler: Option<PrismFiller>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Truncation {
Auto,
Disabled,
RetentionRatio(f64),
}
#[derive(Clone, Debug)]
pub struct RealtimeConnectOptions {
pub(crate) endpoint: String,
pub(crate) voice: Option<String>,
pub(crate) instructions: Option<String>,
pub(crate) tools: Vec<ToolDefinition>,
pub(crate) interrupt: bool,
pub(crate) detection: TurnDetection,
pub(crate) truncation: Option<Truncation>,
pub(crate) timeouts: RealtimeTimeouts,
pub(crate) request_id: Option<String>,
pub(crate) response_metadata: Option<Labels>,
}
impl Default for RealtimeConnectOptions {
fn default() -> Self {
Self {
endpoint: "wss://prism.api.rime.ai/v1/realtime".into(),
voice: None,
instructions: None,
tools: vec![],
interrupt: true,
detection: TurnDetection::default(),
truncation: None,
timeouts: RealtimeTimeouts::default(),
request_id: None,
response_metadata: None,
}
}
}
impl RealtimeConnectOptions {
pub fn truncation(mut self, value: Truncation) -> Self {
self.truncation = Some(value);
self
}
pub fn endpoint(mut self, value: impl Into<String>) -> Self {
self.endpoint = value.into();
self
}
pub fn voice(mut self, value: impl Into<String>) -> Self {
self.voice = Some(value.into());
self
}
pub fn instructions(mut self, value: impl Into<String>) -> Self {
self.instructions = Some(value.into());
self
}
pub fn tools(mut self, value: Vec<ToolDefinition>) -> Self {
self.tools = value;
self
}
pub fn interrupt_on_speech(mut self, value: bool) -> Self {
self.interrupt = value;
self
}
pub fn turn_detection(mut self, value: TurnDetection) -> Self {
self.detection = value;
self
}
pub fn timeouts(mut self, value: RealtimeTimeouts) -> Self {
self.timeouts = value;
self
}
pub fn request_id(mut self, value: impl Into<String>) -> Self {
self.request_id = Some(value.into());
self
}
pub fn response_metadata(mut self, value: Labels) -> Self {
self.response_metadata = Some(value);
self
}
}
#[derive(Clone, Debug, Default)]
pub struct RealtimeUpdateOptions {
pub(crate) instructions: Option<String>,
pub(crate) tools: Option<Vec<ToolDefinition>>,
pub(crate) voice: Option<String>,
pub(crate) interrupt: Option<bool>,
pub(crate) truncation: Option<Truncation>,
}
impl RealtimeUpdateOptions {
pub fn truncation(mut self, value: Truncation) -> Self {
self.truncation = Some(value);
self
}
pub fn instructions(mut self, value: impl Into<String>) -> Self {
self.instructions = Some(value.into());
self
}
pub fn tools(mut self, value: Vec<ToolDefinition>) -> Self {
self.tools = Some(value);
self
}
pub fn voice(mut self, value: impl Into<String>) -> Self {
self.voice = Some(value.into());
self
}
pub fn interrupt_on_speech(mut self, value: bool) -> Self {
self.interrupt = Some(value);
self
}
}
#[derive(Clone, Debug, Default)]
pub struct ReplyOptions {
pub(crate) instruction: Option<String>,
pub(crate) tool_call: Option<ToolCallRef>,
}
impl ReplyOptions {
pub fn instruction(mut self, value: impl Into<String>) -> Self {
self.instruction = Some(value.into());
self
}
pub fn tool_call(mut self, value: ToolCallRef) -> Self {
self.tool_call = Some(value);
self
}
}
#[derive(Clone, Debug)]
pub struct AudioChunk {
pub data: Bytes,
pub format: PcmFormat,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ResponseRef {
pub(crate) session_id: String,
pub(crate) response_id: String,
}
impl ResponseRef {
pub fn session_id(&self) -> &str {
&self.session_id
}
pub fn response_id(&self) -> &str {
&self.response_id
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ItemRef {
pub(crate) session_id: String,
pub(crate) item_id: String,
}
impl ItemRef {
pub fn session_id(&self) -> &str {
&self.session_id
}
pub fn item_id(&self) -> &str {
&self.item_id
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ToolCallRef {
pub(crate) response: ResponseRef,
pub(crate) call_id: String,
}
impl ToolCallRef {
pub fn response(&self) -> &ResponseRef {
&self.response
}
pub fn call_id(&self) -> &str {
&self.call_id
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct OutputRef {
pub(crate) response: ResponseRef,
pub(crate) item_id: String,
pub(crate) output_index: u64,
pub(crate) content_index: u64,
}
impl OutputRef {
pub fn response(&self) -> &ResponseRef {
&self.response
}
pub fn item_id(&self) -> &str {
&self.item_id
}
pub fn output_index(&self) -> u64 {
self.output_index
}
pub fn content_index(&self) -> u64 {
self.content_index
}
}
#[derive(Clone, Debug)]
pub struct SessionInfo {
pub truncation: Option<Truncation>,
pub settings_revision: Option<u64>,
pub model: &'static str,
pub session_id: String,
pub voice: String,
pub interrupt_on_speech: bool,
pub tool_result_timeout_s: Option<f64>,
pub tool_continuation_timeout_s: Option<f64>,
pub eagerness: Option<String>,
pub end_of_turn_timeout_ms: Option<u64>,
pub request_id: Option<String>,
pub response_metadata: Option<Labels>,
}
#[derive(Clone, Debug)]
pub struct RealtimeFault {
pub code: String,
pub message: String,
pub scope: String,
pub request_id: Option<String>,
pub response_id: Option<String>,
pub item_id: Option<String>,
pub call_ids: Vec<String>,
pub correlation_id: Option<String>,
pub parameter: Option<String>,
}
impl RealtimeFault {
pub fn retryable(&self) -> bool {
matches!(
self.code.as_str(),
"capacity_exceeded" | "unavailable" | "response_timeout"
)
}
}
impl fmt::Display for RealtimeFault {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code, self.message)
}
}
impl std::error::Error for RealtimeFault {}
#[derive(Clone, Debug)]
pub struct SessionEvent {
pub session_id: String,
pub event_id: String,
pub request_id: Option<String>,
pub payload: RealtimeEvent,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum RealtimeEvent {
InputReady,
SpeechHint {
audio_start_ms: u64,
},
SpeechStarted {
item_id: String,
audio_start_ms: u64,
},
SpeechStopped {
item_id: String,
audio_end_ms: u64,
end_reason: Option<String>,
},
InputCommitted {
item_id: String,
},
TranscriptDelta {
item_id: String,
delta: String,
},
TranscriptFinal {
item_id: String,
text: String,
},
TranscriptFailed {
item_id: String,
fault: RealtimeFault,
},
ResponseStarted {
settings_revision: Option<u64>,
response: ResponseRef,
cause: String,
parent: Option<ResponseRef>,
input_item_id: Option<String>,
metadata: Labels,
input_item_ids: Vec<String>,
},
ResponseAbandoned {
response: ResponseRef,
},
MessageStarted {
output: OutputRef,
},
TextDelta {
output: OutputRef,
delta: String,
},
TextDone {
output: OutputRef,
text: String,
},
AudioDelta {
output: OutputRef,
audio: AudioChunk,
},
AudioDone {
output: OutputRef,
},
ToolCall {
call: ToolCallRef,
item_id: String,
name: String,
arguments: Map<String, Value>,
},
ResponseEnded {
settings_revision: Option<u64>,
response: ResponseRef,
status: String,
reason: Option<String>,
},
Fault(RealtimeFault),
}
#[derive(Clone, Debug)]
pub enum PlaybackReport {
Interrupted {
output: OutputRef,
audio_end_ms: u32,
},
Finished {
response: ResponseRef,
played_ms: Option<f64>,
audible_tail_ms: Option<f64>,
},
}