use std::sync::Arc;
use voxora_traits::{AsrEngine, AsrError, ModelCapabilities, TranscribeOptions, TranscriptionResult};
use crate::client::MiniMaxClient;
use crate::config::MiniMaxConfig;
use crate::language::known_languages_bcp47;
use crate::params;
use crate::wav;
const ASSUMED_SAMPLE_RATE: u32 = 16_000;
const ASSUMED_CHANNELS: u16 = 1;
#[derive(Clone)]
pub struct MiniMaxEngine {
client: Arc<MiniMaxClient>,
config: Arc<MiniMaxConfig>,
capabilities: ModelCapabilities,
}
impl std::fmt::Debug for MiniMaxEngine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MiniMaxEngine")
.field("client", &self.client)
.field("capabilities", &self.capabilities)
.finish_non_exhaustive()
}
}
impl MiniMaxEngine {
pub fn new(config: MiniMaxConfig) -> Result<Self, AsrError> {
let client = MiniMaxClient::new(&config);
let capabilities = build_capabilities();
Ok(Self {
client: Arc::new(client),
config: Arc::new(config),
capabilities,
})
}
pub fn config(&self) -> &MiniMaxConfig {
&self.config
}
#[cfg(feature = "engine-adapter")]
#[cfg_attr(docsrs, doc(cfg(feature = "engine-adapter")))]
pub fn adapter(self) -> crate::MiniMaxAdapter {
let arc = Arc::new(self);
crate::MiniMaxAdapter::new(arc)
}
}
impl AsrEngine for MiniMaxEngine {
fn capabilities(&self) -> ModelCapabilities {
self.capabilities.clone()
}
fn transcribe(
&self,
samples: &[f32],
opts: &TranscribeOptions,
) -> Result<TranscriptionResult, AsrError> {
let params = params::apply(opts)?;
let wav_bytes = wav::write_wav_pcm_f32(samples, ASSUMED_SAMPLE_RATE, ASSUMED_CHANNELS);
let resp = self.client.transcribe(&wav_bytes, ¶ms)?;
Ok(params::collect_result(resp, opts))
}
}
fn build_capabilities() -> ModelCapabilities {
ModelCapabilities::new(
true,
true,
false,
known_languages_bcp47()
.iter()
.map(|s| (*s).to_string())
.collect(),
)
}
#[cfg(feature = "engine-adapter")]
#[cfg_attr(docsrs, doc(cfg(feature = "engine-adapter")))]
pub struct MiniMaxAdapter {
engine: Arc<MiniMaxEngine>,
info: voxora_engine::EngineInfo,
backend: voxora_engine::BackendDescriptor,
}
#[cfg(feature = "engine-adapter")]
impl MiniMaxAdapter {
pub fn new(engine: Arc<MiniMaxEngine>) -> Self {
let capabilities = engine.capabilities();
let info =
voxora_engine::EngineInfo::new(voxora_engine::EngineFamily::MiniMax, capabilities)
.with_model_label(engine.config().model().to_string());
Self {
engine,
info,
backend: voxora_engine::BackendDescriptor::CPU,
}
}
pub fn engine(&self) -> &MiniMaxEngine {
&self.engine
}
}
#[cfg(feature = "engine-adapter")]
impl voxora_engine::EngineAdapter for MiniMaxAdapter {
fn family(&self) -> voxora_engine::EngineFamily {
voxora_engine::EngineFamily::MiniMax
}
fn info(&self) -> voxora_engine::EngineInfo {
self.info.clone()
}
fn backend(&self) -> voxora_engine::BackendDescriptor {
self.backend
}
fn as_asr_engine(&self) -> &dyn AsrEngine {
&*self.engine
}
}
#[cfg(feature = "engine-adapter")]
impl AsrEngine for MiniMaxAdapter {
fn capabilities(&self) -> voxora_traits::ModelCapabilities {
self.engine.capabilities()
}
fn transcribe(
&self,
samples: &[f32],
opts: &TranscribeOptions,
) -> Result<TranscriptionResult, AsrError> {
self.engine.transcribe(samples, opts)
}
}
#[cfg(feature = "engine-adapter")]
impl std::fmt::Debug for MiniMaxAdapter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MiniMaxAdapter")
.field("family", &voxora_engine::EngineFamily::MiniMax)
.field("backend", &self.backend)
.field("info", &self.info)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn capabilities_report_multilingual_with_known_languages() {
let caps = build_capabilities();
assert!(caps.multilingual, "MiniMax is multilingual");
assert!(caps.word_timestamps, "verbose_json returns segments[]");
assert!(!caps.streaming, "streaming is deferred to a future release");
assert_eq!(caps.languages.len(), known_languages_bcp47().len());
assert!(caps.languages.iter().any(|l| l == "en"));
assert!(caps.languages.iter().any(|l| l == "zh"));
}
#[test]
fn new_rejects_empty_token_via_config() {
let cfg_err = MiniMaxConfig::new("").expect_err("empty token rejected");
assert!(matches!(
cfg_err,
crate::config::MiniMaxConfigError::EmptyApiKey
));
}
#[test]
fn engine_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<MiniMaxEngine>();
}
#[test]
fn engine_is_clone() {
let cfg = MiniMaxConfig::new("sk-test").unwrap();
let engine = MiniMaxEngine::new(cfg).unwrap();
let _clone = engine.clone();
}
}