use std::path::Path;
pub async fn transcribe(audio: Vec<u8>, model_dir: std::path::PathBuf) -> anyhow::Result<String> {
#[cfg(feature = "voice-parakeet")]
{
tokio::task::spawn_blocking(move || engine::transcribe_wav_bytes(&audio, &model_dir))
.await
.map_err(|e| anyhow::anyhow!("parakeet transcribe task panicked: {e}"))?
}
#[cfg(not(feature = "voice-parakeet"))]
{
let _ = (audio, model_dir);
anyhow::bail!(
"parakeet inference is not built into this build. Rebuild with \
`--features voice-parakeet` (pulls ONNX Runtime via transcribe-rs), or use the \
whisper.cpp voice engine instead."
)
}
}
pub fn preload(model_dir: &Path) -> anyhow::Result<()> {
#[cfg(feature = "voice-parakeet")]
{
engine::preload(model_dir)
}
#[cfg(not(feature = "voice-parakeet"))]
{
let _ = model_dir;
Ok(())
}
}
pub fn unload() {
#[cfg(feature = "voice-parakeet")]
engine::unload();
}
#[cfg(feature = "voice-parakeet")]
mod engine {
use std::io::Write;
use std::path::Path;
use std::sync::Mutex;
use anyhow::{Context, Result};
use once_cell::sync::Lazy;
use transcribe_rs::onnx::parakeet::ParakeetModel;
use transcribe_rs::onnx::Quantization;
use transcribe_rs::{SpeechModel, TranscribeOptions};
static MODEL: Lazy<Mutex<Option<ParakeetModel>>> = Lazy::new(|| Mutex::new(None));
pub fn preload(model_dir: &Path) -> Result<()> {
let mut guard = MODEL.lock().expect("parakeet model mutex");
if guard.is_some() {
return Ok(());
}
let model = ParakeetModel::load(model_dir, &Quantization::Int8)
.map_err(|e| anyhow::anyhow!("{e}"))
.context("loading parakeet ONNX model")?;
*guard = Some(model);
Ok(())
}
pub fn unload() {
let mut guard = MODEL.lock().expect("parakeet model mutex");
*guard = None;
}
pub fn transcribe_wav_bytes(audio: &[u8], model_dir: &Path) -> Result<String> {
preload(model_dir)?;
let mut guard = MODEL.lock().expect("parakeet model mutex");
let model = guard.as_mut().context("parakeet model not loaded")?;
let mut tmp = tempfile::Builder::new()
.suffix(".wav")
.tempfile()
.context("creating temp wav for parakeet")?;
tmp.write_all(audio).context("writing temp wav")?;
let path = tmp.path().to_path_buf();
let result = model
.transcribe_file(&path, &TranscribeOptions::default())
.map_err(|e| anyhow::anyhow!("{e}"))
.context("parakeet transcription failed")?;
Ok(result.text.trim().to_string())
}
}