Skip to main content

native_whisperx/workflow/
progress.rs

1//! Observer types for the native Transcription Progress Stream.
2
3use std::path::PathBuf;
4use std::sync::{
5    atomic::{AtomicBool, Ordering},
6    Arc,
7};
8
9use crate::config::NativeWhisperxReport;
10use crate::translation::{CuratedLanguage, TranslationPlanProvenance};
11
12/// Cloneable cooperative cancellation shared by finite and live workflows.
13///
14/// Cancellation is sticky: after [`cancel`](Self::cancel) is called, every
15/// clone observes the request. Workflows stop at the next safe composition
16/// boundary rather than interrupting a model invocation or output write.
17#[derive(Debug, Clone, Default)]
18pub struct CancellationHandle {
19    requested: Arc<AtomicBool>,
20}
21
22impl CancellationHandle {
23    /// Creates an uncancelled handle.
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Requests cooperative cancellation from this or another thread.
29    pub fn cancel(&self) {
30        self.requested.store(true, Ordering::Release);
31    }
32
33    /// Returns whether cancellation has been requested.
34    pub fn is_cancelled(&self) -> bool {
35        self.requested.load(Ordering::Acquire)
36    }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum TranscriptionProgressTask {
41    Decode,
42    Vad,
43    Asr,
44    Alignment,
45    Diarization,
46    Translation,
47    Output,
48}
49
50/// Details about the safe finite-workflow boundary where cancellation won.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct FiniteCancellation {
53    file_index: usize,
54    input: PathBuf,
55    task: Option<TranscriptionProgressTask>,
56}
57
58impl FiniteCancellation {
59    pub(crate) fn new(
60        file_index: usize,
61        input: PathBuf,
62        task: Option<TranscriptionProgressTask>,
63    ) -> Self {
64        Self {
65            file_index,
66            input,
67            task,
68        }
69    }
70
71    /// Zero-based index of the file active when cancellation was observed.
72    pub const fn file_index(&self) -> usize {
73        self.file_index
74    }
75
76    /// Input associated with the cancelled file.
77    pub fn input(&self) -> &std::path::Path {
78        &self.input
79    }
80
81    /// Phase that was active, or `None` when cancellation preceded all phases.
82    pub const fn task(&self) -> Option<TranscriptionProgressTask> {
83        self.task
84    }
85}
86
87/// Typed result of a cancellable single-input finite workflow.
88#[derive(Debug)]
89pub enum FiniteTranscriptionOutcome {
90    /// The workflow and output writing completed.
91    Completed(Box<NativeWhisperxReport>),
92    /// The workflow stopped at a safe boundary without reporting a failure.
93    Cancelled(FiniteCancellation),
94}
95
96/// Input not completed by a cancelled Multi-Input Transcription Run.
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct UnfinishedTranscription {
99    file_index: usize,
100    input: PathBuf,
101}
102
103impl UnfinishedTranscription {
104    pub(crate) fn new(file_index: usize, input: PathBuf) -> Self {
105        Self { file_index, input }
106    }
107
108    /// Zero-based position in the original Multi-Input Transcription Run.
109    pub const fn file_index(&self) -> usize {
110        self.file_index
111    }
112
113    /// Input that did not complete.
114    pub fn input(&self) -> &std::path::Path {
115        &self.input
116    }
117}
118
119/// Typed result of a cancellable Multi-Input Transcription Run.
120#[derive(Debug)]
121pub enum MultiInputTranscriptionOutcome {
122    /// Every file completed successfully.
123    Completed(Vec<NativeWhisperxReport>),
124    /// Cancellation retained completed files and identified all unfinished work.
125    Cancelled {
126        completed: Vec<NativeWhisperxReport>,
127        cancellation: FiniteCancellation,
128        unfinished: Vec<UnfinishedTranscription>,
129    },
130}
131
132/// One ordered observation in the finite Transcription Progress Stream.
133///
134/// This enum is non-exhaustive so embedding applications can remain source
135/// compatible as new model/provider facts are added. Consumers should retain
136/// a wildcard match arm when formatting events.
137#[non_exhaustive]
138#[derive(Debug, Clone, PartialEq)]
139pub enum TranscriptionProgressEvent {
140    RunStart {
141        total_files: usize,
142    },
143    RunEnd {
144        total_files: usize,
145        duration_seconds: f64,
146    },
147    FileStart {
148        file_index: usize,
149        total_files: usize,
150        input: PathBuf,
151    },
152    FileEnd {
153        file_index: usize,
154        total_files: usize,
155        input: PathBuf,
156        duration_seconds: f64,
157    },
158    TaskStart {
159        file_index: usize,
160        task: TranscriptionProgressTask,
161    },
162    TaskEnd {
163        file_index: usize,
164        task: TranscriptionProgressTask,
165        duration_seconds: f64,
166    },
167    ModelResolutionStart {
168        file_index: usize,
169        task: TranscriptionProgressTask,
170        provider: String,
171        model_id: String,
172    },
173    ModelResolutionEnd {
174        file_index: usize,
175        task: TranscriptionProgressTask,
176        provider: String,
177        model_id: String,
178        source: String,
179    },
180    ModelDownloadStart {
181        file_index: usize,
182        task: TranscriptionProgressTask,
183        provider: String,
184        model_id: String,
185    },
186    ModelDownloadEnd {
187        file_index: usize,
188        task: TranscriptionProgressTask,
189        provider: String,
190        model_id: String,
191        duration_seconds: f64,
192    },
193    ModelLoadStart {
194        file_index: usize,
195        task: TranscriptionProgressTask,
196        provider: String,
197        model_id: String,
198    },
199    ModelLoadEnd {
200        file_index: usize,
201        task: TranscriptionProgressTask,
202        provider: String,
203        model_id: String,
204        duration_seconds: f64,
205    },
206    ModelReuse {
207        file_index: usize,
208        task: TranscriptionProgressTask,
209        provider: String,
210        model_id: String,
211    },
212    TranslationLegStart {
213        file_index: usize,
214        leg_index: usize,
215        total_legs: usize,
216        provenance: TranslationPlanProvenance,
217        source: CuratedLanguage,
218        target: CuratedLanguage,
219        provider: String,
220        model_id: String,
221    },
222    TranslationLegEnd {
223        file_index: usize,
224        leg_index: usize,
225        total_legs: usize,
226        provenance: TranslationPlanProvenance,
227        source: CuratedLanguage,
228        target: CuratedLanguage,
229        provider: String,
230        model_id: String,
231        duration_seconds: f64,
232    },
233    Failure {
234        file_index: usize,
235        input: PathBuf,
236        task: Option<TranscriptionProgressTask>,
237        duration_seconds: f64,
238        message: String,
239    },
240    Cancelled {
241        file_index: usize,
242        input: PathBuf,
243        task: Option<TranscriptionProgressTask>,
244        duration_seconds: f64,
245    },
246}
247
248pub trait TranscriptionProgressObserver {
249    fn observe(&mut self, event: TranscriptionProgressEvent);
250}
251
252#[derive(Debug, Default)]
253pub struct NoopTranscriptionProgressObserver;
254
255impl TranscriptionProgressObserver for NoopTranscriptionProgressObserver {
256    fn observe(&mut self, _event: TranscriptionProgressEvent) {}
257}
258
259#[derive(Debug, Default)]
260pub(crate) struct ProgressTaskTracker {
261    current: Option<TranscriptionProgressTask>,
262}
263
264impl ProgressTaskTracker {
265    pub(crate) fn set_current(&mut self, task: Option<TranscriptionProgressTask>) {
266        self.current = task;
267    }
268
269    pub(crate) fn current(&self) -> Option<TranscriptionProgressTask> {
270        self.current
271    }
272}
273
274pub(crate) struct NativeProgressContext<'a> {
275    pub(crate) observer: &'a mut dyn TranscriptionProgressObserver,
276    pub(crate) file_index: usize,
277    pub(crate) task_tracker: &'a mut ProgressTaskTracker,
278    pub(crate) cancellation: &'a CancellationHandle,
279}