Skip to main content

transcribe_cpp/
family.rs

1//! Typed family-extension options.
2//!
3//! Each family exposes model-specific knobs via a kind-tagged extension struct
4//! (`include/transcribe/<family>.h`). These typed options mirror those structs:
5//! every field is an `Option`, and only the fields you set override the
6//! defaults the C `*_init()` stamps. A [`RunExtension`] attaches to
7//! [`RunOptions`](crate::RunOptions); a [`StreamExtension`] attaches to
8//! [`StreamOptions`](crate::StreamOptions).
9//!
10//! Probe [`Model::accepts_ext`](crate::Model::accepts_ext) to learn whether a
11//! loaded model accepts a given kind on a slot; an unaccepted extension is
12//! rejected by `run`/`stream` with [`Error::InvalidArgument`](crate::Error).
13
14use std::ffi::CString;
15
16use transcribe_cpp_sys as sys;
17
18use crate::error::Result;
19
20/// Whisper run-extension knobs (run slot): initial prompt, temperature
21/// fallback, and decode thresholds. `None` keeps the family default.
22#[derive(Debug, Clone, Default, PartialEq)]
23pub struct WhisperRunOptions {
24    pub initial_prompt: Option<String>,
25    pub condition_on_prev_tokens: Option<bool>,
26    pub temperature: Option<f32>,
27    pub temperature_inc: Option<f32>,
28    pub compression_ratio_thold: Option<f32>,
29    pub logprob_thold: Option<f32>,
30    pub no_speech_thold: Option<f32>,
31    pub max_prev_context_tokens: Option<i32>,
32    pub seed: Option<u32>,
33    pub max_initial_timestamp: Option<f32>,
34}
35
36/// Moonshine-streaming stream-extension knobs (stream slot).
37#[derive(Debug, Clone, Default, PartialEq, Eq)]
38pub struct MoonshineStreamingOptions {
39    pub min_decode_interval_ms: Option<i32>,
40}
41
42/// Parakeet cache-aware streaming knobs (stream slot).
43#[derive(Debug, Clone, Default, PartialEq, Eq)]
44pub struct ParakeetStreamOptions {
45    pub att_context_right: Option<i32>,
46}
47
48/// Parakeet chunked-attention buffered streaming knobs (stream slot).
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
50pub struct ParakeetBufferedStreamOptions {
51    pub left_ms: Option<i32>,
52    pub chunk_ms: Option<i32>,
53    pub right_ms: Option<i32>,
54}
55
56/// Voxtral-realtime streaming knobs (stream slot).
57#[derive(Debug, Clone, Default, PartialEq, Eq)]
58pub struct VoxtralRealtimeStreamOptions {
59    pub num_delay_tokens: Option<i32>,
60    pub min_decode_interval_ms: Option<i32>,
61}
62
63/// Sortformer streaming operating point (latency / accuracy trade-off).
64/// The menu is discrete (jointly-tuned bundles), not a latency dial;
65/// `Default` keeps the GGUF-shipped checkpoint configuration.
66#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
67pub enum SortformerPreset {
68    #[default]
69    Default,
70    /// ~30.4 s algorithmic lookahead; the offline-file operating point.
71    VeryHighLatency,
72    /// ~10.0 s lookahead.
73    HighLatency,
74    /// ~1.04 s lookahead; the real-time point (compute-heavy per audio
75    /// second — many small windows).
76    LowLatency,
77}
78
79impl SortformerPreset {
80    fn to_sys(self) -> sys::transcribe_sortformer_preset {
81        match self {
82            SortformerPreset::Default => {
83                sys::transcribe_sortformer_preset::TRANSCRIBE_SORTFORMER_PRESET_DEFAULT
84            }
85            SortformerPreset::VeryHighLatency => {
86                sys::transcribe_sortformer_preset::TRANSCRIBE_SORTFORMER_PRESET_VERY_HIGH_LATENCY
87            }
88            SortformerPreset::HighLatency => {
89                sys::transcribe_sortformer_preset::TRANSCRIBE_SORTFORMER_PRESET_HIGH_LATENCY
90            }
91            SortformerPreset::LowLatency => {
92                sys::transcribe_sortformer_preset::TRANSCRIBE_SORTFORMER_PRESET_LOW_LATENCY
93            }
94        }
95    }
96}
97
98/// Sortformer diarizer run-extension knobs (run slot). Sortformer produces
99/// speaker segments, no text; read results via the speaker-segment
100/// accessors. `None` keeps the family default (the GGUF-shipped cfg).
101#[derive(Debug, Clone, Default, PartialEq, Eq)]
102pub struct SortformerStreamOptions {
103    pub preset: Option<SortformerPreset>,
104}
105
106/// A family extension for the run slot (offline `run`/`run_batch`).
107#[derive(Debug, Clone, PartialEq)]
108#[non_exhaustive]
109pub enum RunExtension {
110    Whisper(WhisperRunOptions),
111    Sortformer(SortformerStreamOptions),
112}
113
114/// A family extension for the stream slot.
115#[derive(Debug, Clone, PartialEq, Eq)]
116#[non_exhaustive]
117pub enum StreamExtension {
118    ParakeetStream(ParakeetStreamOptions),
119    ParakeetBuffered(ParakeetBufferedStreamOptions),
120    MoonshineStreaming(MoonshineStreamingOptions),
121    VoxtralRealtime(VoxtralRealtimeStreamOptions),
122}
123
124/// Owns a materialized run-slot C extension struct (and any strings it points
125/// at) so its address — and the `transcribe_ext` pointer handed to the C API —
126/// stays valid for the duration of the native call. Boxed for a stable address
127/// across moves of the holder.
128pub(crate) enum RunExtRaw {
129    Whisper {
130        ext: Box<sys::transcribe_whisper_run_ext>,
131        _prompt: Option<CString>,
132    },
133    Sortformer(Box<sys::transcribe_sortformer_stream_ext>),
134}
135
136impl RunExtRaw {
137    pub(crate) fn ext_ptr(&self) -> *const sys::transcribe_ext {
138        match self {
139            // `ext` is field 0, so &ext == &the family struct.
140            RunExtRaw::Whisper { ext, .. } => {
141                (&**ext) as *const sys::transcribe_whisper_run_ext as *const sys::transcribe_ext
142            }
143            RunExtRaw::Sortformer(e) => {
144                (&**e) as *const sys::transcribe_sortformer_stream_ext as *const sys::transcribe_ext
145            }
146        }
147    }
148}
149
150impl RunExtension {
151    pub(crate) fn materialize(&self) -> Result<RunExtRaw> {
152        match self {
153            RunExtension::Whisper(o) => {
154                let mut ext: sys::transcribe_whisper_run_ext = unsafe { std::mem::zeroed() };
155                unsafe { sys::transcribe_whisper_run_ext_init(&mut ext) };
156                // Propagate an interior NUL as Error::Nul (like language /
157                // target_language) instead of silently dropping the prompt.
158                let prompt = o.initial_prompt.as_deref().map(CString::new).transpose()?;
159                if let Some(c) = prompt.as_ref() {
160                    ext.initial_prompt = c.as_ptr();
161                }
162                set(
163                    &mut ext.condition_on_prev_tokens,
164                    o.condition_on_prev_tokens,
165                );
166                set(&mut ext.temperature, o.temperature);
167                set(&mut ext.temperature_inc, o.temperature_inc);
168                set(&mut ext.compression_ratio_thold, o.compression_ratio_thold);
169                set(&mut ext.logprob_thold, o.logprob_thold);
170                set(&mut ext.no_speech_thold, o.no_speech_thold);
171                set(&mut ext.max_prev_context_tokens, o.max_prev_context_tokens);
172                set(&mut ext.seed, o.seed);
173                set(&mut ext.max_initial_timestamp, o.max_initial_timestamp);
174                Ok(RunExtRaw::Whisper {
175                    ext: Box::new(ext),
176                    _prompt: prompt,
177                })
178            }
179            RunExtension::Sortformer(o) => {
180                let mut ext: sys::transcribe_sortformer_stream_ext = unsafe { std::mem::zeroed() };
181                unsafe { sys::transcribe_sortformer_stream_ext_init(&mut ext) };
182                set(&mut ext.preset, o.preset.map(SortformerPreset::to_sys));
183                Ok(RunExtRaw::Sortformer(Box::new(ext)))
184            }
185        }
186    }
187}
188
189/// Owns a materialized stream-slot C extension struct (none carry strings).
190pub(crate) enum StreamExtRaw {
191    ParakeetStream(Box<sys::transcribe_parakeet_stream_ext>),
192    ParakeetBuffered(Box<sys::transcribe_parakeet_buffered_stream_ext>),
193    MoonshineStreaming(Box<sys::transcribe_moonshine_streaming_stream_ext>),
194    VoxtralRealtime(Box<sys::transcribe_voxtral_realtime_stream_ext>),
195}
196
197impl StreamExtRaw {
198    pub(crate) fn ext_ptr(&self) -> *const sys::transcribe_ext {
199        match self {
200            StreamExtRaw::ParakeetStream(e) => {
201                (&**e) as *const sys::transcribe_parakeet_stream_ext as *const sys::transcribe_ext
202            }
203            StreamExtRaw::ParakeetBuffered(e) => {
204                (&**e) as *const sys::transcribe_parakeet_buffered_stream_ext
205                    as *const sys::transcribe_ext
206            }
207            StreamExtRaw::MoonshineStreaming(e) => {
208                (&**e) as *const sys::transcribe_moonshine_streaming_stream_ext
209                    as *const sys::transcribe_ext
210            }
211            StreamExtRaw::VoxtralRealtime(e) => {
212                (&**e) as *const sys::transcribe_voxtral_realtime_stream_ext
213                    as *const sys::transcribe_ext
214            }
215        }
216    }
217}
218
219impl StreamExtension {
220    pub(crate) fn materialize(&self) -> StreamExtRaw {
221        match self {
222            StreamExtension::ParakeetStream(o) => {
223                let mut e: sys::transcribe_parakeet_stream_ext = unsafe { std::mem::zeroed() };
224                unsafe { sys::transcribe_parakeet_stream_ext_init(&mut e) };
225                set(&mut e.att_context_right, o.att_context_right);
226                StreamExtRaw::ParakeetStream(Box::new(e))
227            }
228            StreamExtension::ParakeetBuffered(o) => {
229                let mut e: sys::transcribe_parakeet_buffered_stream_ext =
230                    unsafe { std::mem::zeroed() };
231                unsafe { sys::transcribe_parakeet_buffered_stream_ext_init(&mut e) };
232                set(&mut e.left_ms, o.left_ms);
233                set(&mut e.chunk_ms, o.chunk_ms);
234                set(&mut e.right_ms, o.right_ms);
235                StreamExtRaw::ParakeetBuffered(Box::new(e))
236            }
237            StreamExtension::MoonshineStreaming(o) => {
238                let mut e: sys::transcribe_moonshine_streaming_stream_ext =
239                    unsafe { std::mem::zeroed() };
240                unsafe { sys::transcribe_moonshine_streaming_stream_ext_init(&mut e) };
241                set(&mut e.min_decode_interval_ms, o.min_decode_interval_ms);
242                StreamExtRaw::MoonshineStreaming(Box::new(e))
243            }
244            StreamExtension::VoxtralRealtime(o) => {
245                let mut e: sys::transcribe_voxtral_realtime_stream_ext =
246                    unsafe { std::mem::zeroed() };
247                unsafe { sys::transcribe_voxtral_realtime_stream_ext_init(&mut e) };
248                set(&mut e.num_delay_tokens, o.num_delay_tokens);
249                set(&mut e.min_decode_interval_ms, o.min_decode_interval_ms);
250                StreamExtRaw::VoxtralRealtime(Box::new(e))
251            }
252        }
253    }
254}
255
256/// Overwrite `slot` only when the caller provided a value.
257fn set<T>(slot: &mut T, value: Option<T>) {
258    if let Some(v) = value {
259        *slot = v;
260    }
261}