use serde::{Deserialize, Serialize};
use crate::audio_generation::AudioGenerationResponse;
use crate::error::ProviderError;
pub const SPEECH_2_8_HD: &str = "speech-2.8-hd";
pub const SPEECH_2_8_TURBO: &str = "speech-2.8-turbo";
pub const SPEECH_2_6_HD: &str = "speech-2.6-hd";
pub const SPEECH_2_6_TURBO: &str = "speech-2.6-turbo";
pub const SPEECH_02_HD: &str = "speech-02-hd";
pub const SPEECH_02_TURBO: &str = "speech-02-turbo";
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct SpeechReply {
#[serde(default)]
pub data: Option<SpeechData>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_info: Option<serde_json::Value>,
#[serde(default)]
pub trace_id: Option<String>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct SpeechData {
#[serde(default)]
pub audio: String,
}
impl SpeechReply {
pub fn into_response(self) -> Result<AudioGenerationResponse, ProviderError> {
let encoded = self.data.map(|data| data.audio).unwrap_or_default();
let Some(audio) = decode_hex(&encoded).filter(|audio| !audio.is_empty()) else {
return Err(ProviderError::Response(
"MiniMax returned no hex audio; rig decodes only `output_format: hex`".to_owned(),
));
};
Ok(AudioGenerationResponse {
response_id: self.trace_id.filter(|id| !id.is_empty()),
..AudioGenerationResponse::new(audio)
})
}
}
fn decode_hex(hex: &str) -> Option<Vec<u8>> {
if !hex.len().is_multiple_of(2) {
return None;
}
let digit = |byte: &u8| char::from(*byte).to_digit(16);
hex.as_bytes()
.chunks_exact(2)
.map(|pair| match pair {
[high, low] => u8::try_from(digit(high)? * 16 + digit(low)?).ok(),
_ => None,
})
.collect()
}