Skip to main content

async_openai/
realtime.rs

1use crate::{
2    config::Config,
3    error::OpenAIError,
4    types::realtime::{
5        translation::{
6            RealtimeTranslationClientSecretCreateRequest,
7            RealtimeTranslationClientSecretCreateResponse,
8        },
9        RealtimeCallAcceptRequest, RealtimeCallCreateRequest, RealtimeCallCreateResponse,
10        RealtimeCallReferRequest, RealtimeCallRejectRequest, RealtimeCreateClientSecretRequest,
11        RealtimeCreateClientSecretResponse,
12    },
13    Client, RequestOptions,
14};
15
16/// Realtime API for creating sessions, managing calls, and handling WebRTC connections.
17/// Related guide: [Realtime API](https://platform.openai.com/docs/guides/realtime)
18pub struct Realtime<'c, C: Config> {
19    client: &'c Client<C>,
20    pub(crate) request_options: RequestOptions,
21}
22
23impl<'c, C: Config> Realtime<'c, C> {
24    pub fn new(client: &'c Client<C>) -> Self {
25        Self {
26            client,
27            request_options: RequestOptions::new(),
28        }
29    }
30
31    /// call [RealtimeTranslations] group APIs
32    pub fn translations(&self) -> RealtimeTranslations<'_, C> {
33        RealtimeTranslations::new(self.client)
34    }
35
36    /// Create a new Realtime API call over WebRTC and receive the SDP answer needed
37    /// to complete the peer connection.
38    ///
39    /// Returns the SDP answer in the response body and the call ID in the Location header.
40    pub async fn create_call(
41        &self,
42        request: RealtimeCallCreateRequest,
43    ) -> Result<RealtimeCallCreateResponse, OpenAIError> {
44        let (bytes, headers) = self
45            .client
46            .post_form_raw("/realtime/calls", request, &self.request_options)
47            .await?;
48
49        // Extract Location header
50        let location = headers
51            .get("location")
52            .and_then(|v| v.to_str().ok())
53            .map(|s| s.to_string());
54
55        if location.is_none() {
56            tracing::warn!("Location header not found in Realtime call creation response");
57        }
58
59        // Use from_utf8_lossy to handle any invalid UTF-8 bytes in SDP
60        let sdp = String::from_utf8_lossy(&bytes).into_owned();
61
62        Ok(RealtimeCallCreateResponse { sdp, location })
63    }
64
65    /// Accept an incoming SIP call and configure the realtime session that will
66    /// handle the call.
67    #[crate::byot(T0 = std::fmt::Display, T1 = serde::Serialize, R = serde::de::DeserializeOwned)]
68    pub async fn accept_call(
69        &self,
70        call_id: &str,
71        request: RealtimeCallAcceptRequest,
72    ) -> Result<(), OpenAIError> {
73        self.client
74            .post(
75                &format!("/realtime/calls/{}/accept", call_id),
76                request,
77                &self.request_options,
78            )
79            .await
80    }
81
82    /// End an active Realtime API call, whether it was initiated over SIP or WebRTC.
83    #[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
84    pub async fn hangup_call(&self, call_id: &str) -> Result<(), OpenAIError> {
85        self.client
86            .post(
87                &format!("/realtime/calls/{}/hangup", call_id),
88                (),
89                &self.request_options,
90            )
91            .await
92    }
93
94    /// Transfer a SIP call to a new destination using the Realtime API.
95    #[crate::byot(T0 = std::fmt::Display, T1 = serde::Serialize, R = serde::de::DeserializeOwned)]
96    pub async fn refer_call(
97        &self,
98        call_id: &str,
99        request: RealtimeCallReferRequest,
100    ) -> Result<(), OpenAIError> {
101        self.client
102            .post(
103                &format!("/realtime/calls/{}/refer", call_id),
104                request,
105                &self.request_options,
106            )
107            .await
108    }
109
110    /// Decline an incoming SIP call handled by the Realtime API.
111    #[crate::byot(T0 = std::fmt::Display, T1 = serde::Serialize, R = serde::de::DeserializeOwned)]
112    pub async fn reject_call(
113        &self,
114        call_id: &str,
115        request: RealtimeCallRejectRequest,
116    ) -> Result<(), OpenAIError> {
117        self.client
118            .post(
119                &format!("/realtime/calls/{}/reject", call_id),
120                request,
121                &self.request_options,
122            )
123            .await
124    }
125
126    /// Create a Realtime client secret with an associated session configuration.
127    #[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
128    pub async fn create_client_secret(
129        &self,
130        request: RealtimeCreateClientSecretRequest,
131    ) -> Result<RealtimeCreateClientSecretResponse, OpenAIError> {
132        self.client
133            .post("/realtime/client_secrets", request, &self.request_options)
134            .await
135    }
136}
137
138/// Realtime translations API. Translation sessions continuously translate input
139/// audio into the configured output language.
140pub struct RealtimeTranslations<'c, C: Config> {
141    client: &'c Client<C>,
142    pub(crate) request_options: RequestOptions,
143}
144
145impl<'c, C: Config> RealtimeTranslations<'c, C> {
146    pub fn new(client: &'c Client<C>) -> Self {
147        Self {
148            client,
149            request_options: RequestOptions::new(),
150        }
151    }
152
153    /// Create a Realtime translation client secret with an associated translation session configuration.
154    ///
155    /// Client secrets are short-lived tokens that can be passed to a client
156    /// app, such as a web frontend or mobile client, which grants access to the
157    /// Realtime Translation API without leaking your main API key. You can configure a
158    /// custom TTL for each client secret.
159    ///
160    /// Returns the created client secret and the effective translation session
161    /// object.
162    ///
163    /// The client secret is a string that looks like `ek_1234`.
164    #[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
165    pub async fn create_client_secret(
166        &self,
167        request: RealtimeTranslationClientSecretCreateRequest,
168    ) -> Result<RealtimeTranslationClientSecretCreateResponse, OpenAIError> {
169        self.client
170            .post(
171                "/realtime/translations/client_secrets",
172                request,
173                &self.request_options,
174            )
175            .await
176    }
177}