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