Skip to main content

openai_compat/resources/
audio.rs

1//! `/audio` resources, mirroring `resources/audio/speech.py` and
2//! `resources/audio/transcriptions.py`.
3
4use bytes::Bytes;
5use reqwest::Method;
6
7use crate::client::Client;
8use crate::error::OpenAIError;
9use crate::request::{MultipartField, RequestOptions};
10use crate::types::audio::{SpeechRequest, Transcription, TranscriptionRequest};
11use crate::types::files::FileUpload;
12
13/// Accessor for audio endpoints: `client.audio().speech(...)`.
14#[derive(Debug, Clone)]
15pub struct Audio {
16    client: Client,
17}
18
19impl Audio {
20    pub(crate) fn new(client: Client) -> Self {
21        Self { client }
22    }
23
24    /// Text-to-speech: returns the raw audio bytes.
25    pub async fn speech(&self, request: SpeechRequest) -> Result<Bytes, OpenAIError> {
26        let body = serde_json::to_value(&request)?;
27        self.client
28            .execute_bytes(Method::POST, "/audio/speech", RequestOptions::json(body))
29            .await
30    }
31
32    /// Speech-to-text: transcribe an audio file (multipart upload).
33    ///
34    /// Parses the JSON response shapes (`response_format` of `json`, the
35    /// default, or `verbose_json`). For the plain-text formats (`text`,
36    /// `srt`, `vtt`) use [`Audio::transcriptions_raw`].
37    pub async fn transcriptions(
38        &self,
39        file: FileUpload,
40        request: TranscriptionRequest,
41    ) -> Result<Transcription, OpenAIError> {
42        if matches!(
43            request.response_format.as_deref(),
44            Some("text") | Some("srt") | Some("vtt")
45        ) {
46            return Err(OpenAIError::Config(format!(
47                "response_format {:?} returns plain text; use transcriptions_raw",
48                request.response_format.as_deref().unwrap_or_default()
49            )));
50        }
51        let fields = transcription_fields(file, request);
52        self.client
53            .execute(
54                Method::POST,
55                "/audio/transcriptions",
56                RequestOptions::multipart(fields),
57            )
58            .await
59    }
60
61    /// Speech-to-text returning the raw response body as text — for the
62    /// non-JSON `response_format`s (`text`, `srt`, `vtt`).
63    pub async fn transcriptions_raw(
64        &self,
65        file: FileUpload,
66        request: TranscriptionRequest,
67    ) -> Result<String, OpenAIError> {
68        let fields = transcription_fields(file, request);
69        let bytes = self
70            .client
71            .execute_bytes(
72                Method::POST,
73                "/audio/transcriptions",
74                RequestOptions::multipart(fields),
75            )
76            .await?;
77        Ok(String::from_utf8_lossy(&bytes).into_owned())
78    }
79}
80
81fn transcription_fields(file: FileUpload, request: TranscriptionRequest) -> Vec<MultipartField> {
82    {
83        let mut fields = vec![
84            MultipartField::File {
85                name: "file".into(),
86                filename: file.filename,
87                bytes: file.bytes,
88                content_type: file.content_type,
89            },
90            MultipartField::Text {
91                name: "model".into(),
92                value: request.model,
93            },
94        ];
95        if let Some(language) = request.language {
96            fields.push(MultipartField::Text {
97                name: "language".into(),
98                value: language,
99            });
100        }
101        if let Some(prompt) = request.prompt {
102            fields.push(MultipartField::Text {
103                name: "prompt".into(),
104                value: prompt,
105            });
106        }
107        if let Some(response_format) = request.response_format {
108            fields.push(MultipartField::Text {
109                name: "response_format".into(),
110                value: response_format,
111            });
112        }
113        if let Some(temperature) = request.temperature {
114            fields.push(MultipartField::Text {
115                name: "temperature".into(),
116                value: temperature.to_string(),
117            });
118        }
119        fields
120    }
121}