rlx_whisper/
bench_fixture.rs1use anyhow::{Context, Result, bail, ensure};
19use std::path::{Path, PathBuf};
20
21pub fn bench_cache_dir() -> PathBuf {
23 std::env::var("RLX_WHISPER_BENCH_DIR")
24 .map(PathBuf::from)
25 .unwrap_or_else(|_| default_bench_cache_dir())
26}
27
28fn default_bench_cache_dir() -> PathBuf {
29 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../.cache/whisper-bench")
30}
31
32pub fn jfk_wav_path() -> PathBuf {
34 std::env::var("RLX_WHISPER_WAV")
35 .map(PathBuf::from)
36 .unwrap_or_else(|_| bench_cache_dir().join("jfk_16k.wav"))
37}
38
39pub fn jfk_reference_path() -> PathBuf {
41 std::env::var("RLX_WHISPER_REFERENCE")
42 .map(PathBuf::from)
43 .unwrap_or_else(|_| bench_cache_dir().join("jfk.reference.txt"))
44}
45
46pub 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.";
48
49pub fn load_jfk_reference() -> Result<String> {
51 let path = jfk_reference_path();
52 if path.is_file() {
53 std::fs::read_to_string(&path)
54 .with_context(|| format!("read reference {path:?}"))
55 .map(|s| s.trim_end_matches(['\r', '\n']).to_string())
56 } else {
57 Ok(JFK_REFERENCE.to_string())
58 }
59}
60
61pub fn ensure_jfk_fixture() -> Result<(PathBuf, String)> {
63 let wav = jfk_wav_path();
64 ensure!(
65 wav.is_file(),
66 "missing JFK wav at {wav:?}; run `just fetch-whisper-bench`"
67 );
68 let reference = load_jfk_reference()?;
69 Ok((wav, reference))
70}
71
72pub fn normalize_transcript(text: &str) -> String {
74 text.split_whitespace().collect::<Vec<_>>().join(" ")
75}
76
77pub fn transcripts_match(got: &str, want: &str) -> bool {
79 normalize_transcript(got) == normalize_transcript(want)
80}
81
82pub fn assert_transcript_matches_reference(got: &str, want: &str) {
84 assert!(
85 transcripts_match(got, want),
86 "transcript mismatch.\n got: {got:?}\n want: {want:?}\n \
87 (normalized got: {:?})\n (normalized want: {:?})",
88 normalize_transcript(got),
89 normalize_transcript(want),
90 );
91}
92
93pub fn manifest_path() -> PathBuf {
95 bench_cache_dir().join("manifest.json")
96}
97
98pub fn clip_paths(id: &str) -> Result<(PathBuf, PathBuf)> {
100 if id == "jfk" {
101 return Ok((jfk_wav_path(), jfk_reference_path()));
102 }
103 bail!("unknown whisper bench clip {id:?}")
104}
105
106pub fn load_reference_for(id: &str) -> Result<String> {
108 match id {
109 "jfk" => load_jfk_reference(),
110 other => bail!("unknown whisper bench clip {other:?}"),
111 }
112}
113
114pub fn fixture_status(dir: &Path) -> Result<()> {
116 let wav = dir.join("jfk_16k.wav");
117 let reference = dir.join("jfk.reference.txt");
118 ensure!(wav.is_file(), "missing {wav:?}");
119 ensure!(reference.is_file(), "missing {reference:?}");
120 let _ = load_jfk_reference()?;
121 Ok(())
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn normalize_collapses_whitespace() {
130 assert_eq!(
131 normalize_transcript(" And so my fellow Americans "),
132 "And so my fellow Americans"
133 );
134 }
135
136 #[test]
137 fn embedded_jfk_reference_matches_openai_whisper() {
138 assert!(JFK_REFERENCE.starts_with(' '));
139 assert!(transcripts_match(
140 JFK_REFERENCE,
141 "And so my fellow Americans ask not what your country can do for you ask what you can do for your country."
142 ));
143 }
144}