1use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6use text_transcripts::TranscriptionContract;
7
8use super::defaults::{default_gating, default_true};
9use super::{
10 AlignmentConfig, AsrConfig, DiarizationConfig, ExternalWhisperxConfig, NativeWhisperxReport,
11 OutputConfig, OutputFormat, TranslationConfig, VadConfig,
12};
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct ExpectedOutputFile {
17 pub format: OutputFormat,
18 pub path: PathBuf,
19 #[serde(default)]
20 pub comparison: OutputComparisonMode,
21 #[serde(default = "default_true")]
22 pub gating: bool,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub enum OutputComparisonMode {
28 #[default]
29 Exact,
30 JsonSemantic,
31 SubtitleSemantic,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub struct ExpectedOutputComparison {
37 pub format: OutputFormat,
38 #[serde(default)]
39 pub comparison: OutputComparisonMode,
40 #[serde(default = "default_true")]
41 pub gating: bool,
42 pub expected_path: PathBuf,
43 pub actual_path: Option<PathBuf>,
44 pub passed: bool,
45 pub difference: Option<String>,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
49#[serde(rename_all = "camelCase")]
50pub enum ExpectedTranscriptTarget {
51 #[default]
52 Native,
53 Whisperx,
54}
55
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57#[serde(rename_all = "camelCase")]
58pub struct ParityConfig {
59 pub input: PathBuf,
60 #[serde(default)]
61 pub expected_json: Option<PathBuf>,
62 #[serde(default)]
63 pub expected_target: ExpectedTranscriptTarget,
64 #[serde(default)]
65 pub comparison: ParityComparisonConfig,
66 #[serde(default)]
67 pub native_asr: AsrConfig,
68 #[serde(default)]
69 pub translation: TranslationConfig,
70 #[serde(default)]
71 pub vad: VadConfig,
72 #[serde(default)]
73 pub alignment: AlignmentConfig,
74 #[serde(default)]
75 pub diarization: DiarizationConfig,
76 #[serde(default)]
77 pub whisperx_diarization: Option<DiarizationConfig>,
78 #[serde(default)]
79 pub whisperx: ExternalWhisperxConfig,
80 #[serde(default)]
81 pub language: Option<String>,
82 #[serde(default)]
83 pub output: OutputConfig,
84}
85
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
87#[serde(rename_all = "camelCase")]
88pub struct ParityFixtureSuite {
89 #[serde(default)]
90 pub fixtures: Vec<ParityFixtureCase>,
91 #[serde(default)]
92 pub multi_input_fixtures: Vec<ParityMultiInputFixtureCase>,
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct ParityFixtureCase {
98 pub name: String,
99 #[serde(default = "default_gating")]
100 pub gating: bool,
101 pub input: PathBuf,
102 #[serde(default)]
103 pub clip_seconds: Option<f64>,
104 #[serde(default)]
105 pub timeout_seconds: Option<u64>,
106 #[serde(default)]
107 pub expected_json: Option<PathBuf>,
108 #[serde(default)]
109 pub expected_target: ExpectedTranscriptTarget,
110 #[serde(default)]
111 pub comparison: ParityComparisonConfig,
112 #[serde(default)]
113 pub expected_outputs: Vec<ExpectedOutputFile>,
114 #[serde(default)]
115 pub native_asr: AsrConfig,
116 #[serde(default)]
117 pub translation: TranslationConfig,
118 #[serde(default)]
119 pub vad: VadConfig,
120 #[serde(default)]
121 pub alignment: AlignmentConfig,
122 #[serde(default)]
123 pub diarization: DiarizationConfig,
124 #[serde(default)]
125 pub whisperx_diarization: Option<DiarizationConfig>,
126 #[serde(default)]
127 pub whisperx: ExternalWhisperxConfig,
128 #[serde(default)]
129 pub language: Option<String>,
130 #[serde(default)]
131 pub output: OutputConfig,
132 #[serde(default)]
133 pub required_diagnostics: Vec<String>,
134}
135
136#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct ParityMultiInputFixtureCase {
139 pub name: String,
140 #[serde(default = "default_gating")]
141 pub gating: bool,
142 pub inputs: Vec<PathBuf>,
143 #[serde(default)]
144 pub clip_seconds_per_input: Option<f64>,
145 #[serde(default)]
146 pub timeout_seconds: Option<u64>,
147 #[serde(default)]
148 pub native_asr: AsrConfig,
149 #[serde(default)]
150 pub translation: TranslationConfig,
151 #[serde(default)]
152 pub vad: VadConfig,
153 #[serde(default)]
154 pub alignment: AlignmentConfig,
155 #[serde(default)]
156 pub diarization: DiarizationConfig,
157 #[serde(default)]
158 pub whisperx_diarization: Option<DiarizationConfig>,
159 #[serde(default)]
160 pub whisperx: ExternalWhisperxConfig,
161 #[serde(default)]
162 pub language: Option<String>,
163 #[serde(default)]
164 pub output: OutputConfig,
165 #[serde(default)]
166 pub required_diagnostics: Vec<String>,
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
170#[serde(rename_all = "camelCase")]
171pub struct ParityComparisonConfig {
172 #[serde(default = "default_true")]
173 pub text: bool,
174 #[serde(default = "default_true")]
175 pub language: bool,
176 #[serde(default = "default_true")]
177 pub segment_text: bool,
178 #[serde(default = "default_true")]
179 pub word_text: bool,
180 #[serde(default = "default_true")]
181 pub char_count: bool,
182 #[serde(default = "default_true")]
183 pub char_content: bool,
184 #[serde(default = "default_true")]
185 pub segment_count: bool,
186 #[serde(default = "default_true")]
187 pub word_count: bool,
188 #[serde(default = "default_true")]
189 pub segment_timing: bool,
190 #[serde(default = "default_true")]
191 pub word_timing: bool,
192 #[serde(default = "default_true")]
193 pub speaker_turns: bool,
194 #[serde(default = "default_true")]
195 pub vad_segments: bool,
196 #[serde(default = "default_true")]
197 pub vad_segment_timing: bool,
198 #[serde(default = "default_true")]
199 pub vad_segment_count: bool,
200}
201
202impl Default for ParityComparisonConfig {
203 fn default() -> Self {
204 Self {
205 text: true,
206 language: true,
207 segment_text: true,
208 word_text: true,
209 char_count: true,
210 char_content: true,
211 segment_count: true,
212 word_count: true,
213 segment_timing: true,
214 word_timing: true,
215 speaker_turns: true,
216 vad_segments: true,
217 vad_segment_timing: true,
218 vad_segment_count: true,
219 }
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
224#[serde(rename_all = "camelCase")]
225pub struct ParityFixtureSuiteReport {
226 pub passed: bool,
227 pub cases: Vec<ParityFixtureCaseReport>,
228}
229
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct ParityFixtureCaseReport {
233 pub name: String,
234 #[serde(default)]
235 pub gating: bool,
236 pub passed: bool,
237 #[serde(default, skip_serializing_if = "Option::is_none")]
238 pub started_at: Option<String>,
239 #[serde(default, skip_serializing_if = "Option::is_none")]
240 pub elapsed_seconds: Option<f64>,
241 #[serde(default)]
242 pub timed_out: bool,
243 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub report: Option<ParityReport>,
245 #[serde(default)]
246 pub missing_required_diagnostics: Vec<String>,
247 #[serde(default)]
248 pub expected_output_matches: Vec<ExpectedOutputComparison>,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub error: Option<String>,
251 #[serde(default)]
252 pub failure_summary: Vec<String>,
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct ParityPreflightReport {
258 pub passed: bool,
259 pub manifest: PathBuf,
260 pub root: PathBuf,
261 pub whisperx_command: PathBuf,
262 pub model_dir: PathBuf,
263 pub source_checkout_tag: Option<String>,
264 pub cases: Vec<ParityPreflightCaseReport>,
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
268#[serde(rename_all = "camelCase")]
269pub struct ParityPreflightCaseReport {
270 pub name: String,
271 pub gating: bool,
272 pub passed: bool,
273 pub missing: Vec<String>,
274 pub warnings: Vec<String>,
275}
276
277#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
278#[serde(rename_all = "camelCase")]
279pub struct ParityReport {
280 pub native_report: NativeWhisperxReport,
281 pub whisperx_report: NativeWhisperxReport,
282 #[serde(default)]
283 pub expected: Option<TranscriptionContract>,
284 #[serde(default)]
285 pub expected_target: ExpectedTranscriptTarget,
286 pub comparison: ParityComparison,
287 pub expected_segment_count_matches: Option<bool>,
288 pub expected_text_matches: Option<bool>,
289}
290
291#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
292#[serde(rename_all = "camelCase")]
293pub struct ParityComparison {
294 pub text_matches: bool,
295 #[serde(default)]
296 pub language_matches: Option<bool>,
297 #[serde(default)]
298 pub segment_text_matches: Option<bool>,
299 #[serde(default)]
300 pub word_text_matches: Option<bool>,
301 #[serde(default)]
302 pub char_count_matches: Option<bool>,
303 #[serde(default)]
304 pub char_content_matches: Option<bool>,
305 pub segment_count_matches: bool,
306 pub word_count_matches: bool,
307 pub segment_timing_matches: bool,
308 pub word_timing_matches: bool,
309 pub speaker_turns_match: bool,
310 #[serde(default)]
311 pub vad_segment_count_matches: Option<bool>,
312 #[serde(default)]
313 pub vad_segment_timing_matches: Option<bool>,
314 pub confidence_compared: bool,
315 pub passed: bool,
316 pub tolerance: ParityTolerance,
317 #[serde(default)]
318 pub differences: Vec<String>,
319 #[serde(default)]
320 pub diagnostic_differences: Vec<String>,
321}
322
323#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
324#[serde(rename_all = "camelCase")]
325pub struct ParityTolerance {
326 pub segment_seconds: f64,
327 pub word_seconds: f64,
328}
329
330impl Default for ParityTolerance {
331 fn default() -> Self {
332 Self {
333 segment_seconds: 0.100,
334 word_seconds: 0.050,
335 }
336 }
337}