use crate::IntoRequest;
use derive_builder::Builder;
use reqwest::{Client, RequestBuilder};
use serde::Serialize;
#[derive(Debug, Clone, Serialize, Builder)]
#[builder(pattern = "mutable")]
pub struct SpeechRequest {
#[builder(default)]
model: SpeechModel,
#[builder(setter(into))]
input: String,
#[builder(default)]
voice: SpeechVoice,
#[builder(default)]
response_format: SpeechResponseFormat,
#[builder(default, setter(strip_option))]
#[serde(skip_serializing_if = "Option::is_none")]
speed: Option<f32>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
pub enum SpeechModel {
#[default]
#[serde(rename = "tts-1")]
Tts1,
#[serde(rename = "tts-1-hd")]
Tts1Hd,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SpeechVoice {
Alloy,
Echo,
Fable,
Onyx,
#[default]
Nova,
Shimmer,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SpeechResponseFormat {
#[default]
Mp3,
Opus,
Aac,
Flac,
}
impl IntoRequest for SpeechRequest {
fn into_request(self, base_url: &str, client: Client) -> RequestBuilder {
let url = format!("{}/audio/speech", base_url);
client.post(url).json(&self)
}
}
impl SpeechRequest {
pub fn new(input: impl Into<String>) -> Self {
SpeechRequestBuilder::default()
.input(input)
.build()
.unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SDK;
use anyhow::Result;
use std::fs;
#[tokio::test]
async fn speech_should_work() -> Result<()> {
let req = SpeechRequest::new("The quick brown fox jumped over the lazy dog.");
let res = SDK.speech(req).await?;
fs::write("/tmp/llm-sdk/test.mp3", res)?;
Ok(())
}
}