use anyhow::{Context, Result, bail, ensure};
use std::path::{Path, PathBuf};
pub fn bench_cache_dir() -> PathBuf {
std::env::var("RLX_WHISPER_BENCH_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| default_bench_cache_dir())
}
fn default_bench_cache_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../.cache/whisper-bench")
}
pub fn jfk_wav_path() -> PathBuf {
std::env::var("RLX_WHISPER_WAV")
.map(PathBuf::from)
.unwrap_or_else(|_| bench_cache_dir().join("jfk_16k.wav"))
}
pub fn jfk_reference_path() -> PathBuf {
std::env::var("RLX_WHISPER_REFERENCE")
.map(PathBuf::from)
.unwrap_or_else(|_| bench_cache_dir().join("jfk.reference.txt"))
}
pub const JFK_REFERENCE: &str = " And so my fellow Americans ask not what your country can do for you ask what you can do for your country.";
pub fn load_jfk_reference() -> Result<String> {
let path = jfk_reference_path();
if path.is_file() {
std::fs::read_to_string(&path)
.with_context(|| format!("read reference {path:?}"))
.map(|s| s.trim_end_matches(['\r', '\n']).to_string())
} else {
Ok(JFK_REFERENCE.to_string())
}
}
pub fn ensure_jfk_fixture() -> Result<(PathBuf, String)> {
let wav = jfk_wav_path();
ensure!(
wav.is_file(),
"missing JFK wav at {wav:?}; run `just fetch-whisper-bench`"
);
let reference = load_jfk_reference()?;
Ok((wav, reference))
}
pub fn normalize_transcript(text: &str) -> String {
text.split_whitespace().collect::<Vec<_>>().join(" ")
}
pub fn transcripts_match(got: &str, want: &str) -> bool {
normalize_transcript(got) == normalize_transcript(want)
}
pub fn assert_transcript_matches_reference(got: &str, want: &str) {
assert!(
transcripts_match(got, want),
"transcript mismatch.\n got: {got:?}\n want: {want:?}\n \
(normalized got: {:?})\n (normalized want: {:?})",
normalize_transcript(got),
normalize_transcript(want),
);
}
pub fn manifest_path() -> PathBuf {
bench_cache_dir().join("manifest.json")
}
pub fn clip_paths(id: &str) -> Result<(PathBuf, PathBuf)> {
if id == "jfk" {
return Ok((jfk_wav_path(), jfk_reference_path()));
}
bail!("unknown whisper bench clip {id:?}")
}
pub fn load_reference_for(id: &str) -> Result<String> {
match id {
"jfk" => load_jfk_reference(),
other => bail!("unknown whisper bench clip {other:?}"),
}
}
pub fn fixture_status(dir: &Path) -> Result<()> {
let wav = dir.join("jfk_16k.wav");
let reference = dir.join("jfk.reference.txt");
ensure!(wav.is_file(), "missing {wav:?}");
ensure!(reference.is_file(), "missing {reference:?}");
let _ = load_jfk_reference()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize_collapses_whitespace() {
assert_eq!(
normalize_transcript(" And so my fellow Americans "),
"And so my fellow Americans"
);
}
#[test]
fn embedded_jfk_reference_matches_openai_whisper() {
assert!(JFK_REFERENCE.starts_with(' '));
assert!(transcripts_match(
JFK_REFERENCE,
"And so my fellow Americans ask not what your country can do for you ask what you can do for your country."
));
}
}