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/// A family extension for the run slot (offline `run`/`run_batch`).
64#[derive(Debug, Clone, PartialEq)]
65#[non_exhaustive]
66pub enum RunExtension {
67    Whisper(WhisperRunOptions),
68}
69
70/// A family extension for the stream slot.
71#[derive(Debug, Clone, PartialEq, Eq)]
72#[non_exhaustive]
73pub enum StreamExtension {
74    ParakeetStream(ParakeetStreamOptions),
75    ParakeetBuffered(ParakeetBufferedStreamOptions),
76    MoonshineStreaming(MoonshineStreamingOptions),
77    VoxtralRealtime(VoxtralRealtimeStreamOptions),
78}
79
80/// Owns a materialized run-slot C extension struct (and any strings it points
81/// at) so its address — and the `transcribe_ext` pointer handed to the C API —
82/// stays valid for the duration of the native call. Boxed for a stable address
83/// across moves of the holder.
84pub(crate) enum RunExtRaw {
85    Whisper {
86        ext: Box<sys::transcribe_whisper_run_ext>,
87        _prompt: Option<CString>,
88    },
89}
90
91impl RunExtRaw {
92    pub(crate) fn ext_ptr(&self) -> *const sys::transcribe_ext {
93        match self {
94            // `ext` is field 0, so &ext == &the family struct.
95            RunExtRaw::Whisper { ext, .. } => {
96                (&**ext) as *const sys::transcribe_whisper_run_ext as *const sys::transcribe_ext
97            }
98        }
99    }
100}
101
102impl RunExtension {
103    pub(crate) fn materialize(&self) -> Result<RunExtRaw> {
104        match self {
105            RunExtension::Whisper(o) => {
106                let mut ext: sys::transcribe_whisper_run_ext = unsafe { std::mem::zeroed() };
107                unsafe { sys::transcribe_whisper_run_ext_init(&mut ext) };
108                // Propagate an interior NUL as Error::Nul (like language /
109                // target_language) instead of silently dropping the prompt.
110                let prompt = o.initial_prompt.as_deref().map(CString::new).transpose()?;
111                if let Some(c) = prompt.as_ref() {
112                    ext.initial_prompt = c.as_ptr();
113                }
114                set(
115                    &mut ext.condition_on_prev_tokens,
116                    o.condition_on_prev_tokens,
117                );
118                set(&mut ext.temperature, o.temperature);
119                set(&mut ext.temperature_inc, o.temperature_inc);
120                set(&mut ext.compression_ratio_thold, o.compression_ratio_thold);
121                set(&mut ext.logprob_thold, o.logprob_thold);
122                set(&mut ext.no_speech_thold, o.no_speech_thold);
123                set(&mut ext.max_prev_context_tokens, o.max_prev_context_tokens);
124                set(&mut ext.seed, o.seed);
125                set(&mut ext.max_initial_timestamp, o.max_initial_timestamp);
126                Ok(RunExtRaw::Whisper {
127                    ext: Box::new(ext),
128                    _prompt: prompt,
129                })
130            }
131        }
132    }
133}
134
135/// Owns a materialized stream-slot C extension struct (none carry strings).
136pub(crate) enum StreamExtRaw {
137    ParakeetStream(Box<sys::transcribe_parakeet_stream_ext>),
138    ParakeetBuffered(Box<sys::transcribe_parakeet_buffered_stream_ext>),
139    MoonshineStreaming(Box<sys::transcribe_moonshine_streaming_stream_ext>),
140    VoxtralRealtime(Box<sys::transcribe_voxtral_realtime_stream_ext>),
141}
142
143impl StreamExtRaw {
144    pub(crate) fn ext_ptr(&self) -> *const sys::transcribe_ext {
145        match self {
146            StreamExtRaw::ParakeetStream(e) => {
147                (&**e) as *const sys::transcribe_parakeet_stream_ext as *const sys::transcribe_ext
148            }
149            StreamExtRaw::ParakeetBuffered(e) => {
150                (&**e) as *const sys::transcribe_parakeet_buffered_stream_ext
151                    as *const sys::transcribe_ext
152            }
153            StreamExtRaw::MoonshineStreaming(e) => {
154                (&**e) as *const sys::transcribe_moonshine_streaming_stream_ext
155                    as *const sys::transcribe_ext
156            }
157            StreamExtRaw::VoxtralRealtime(e) => {
158                (&**e) as *const sys::transcribe_voxtral_realtime_stream_ext
159                    as *const sys::transcribe_ext
160            }
161        }
162    }
163}
164
165impl StreamExtension {
166    pub(crate) fn materialize(&self) -> StreamExtRaw {
167        match self {
168            StreamExtension::ParakeetStream(o) => {
169                let mut e: sys::transcribe_parakeet_stream_ext = unsafe { std::mem::zeroed() };
170                unsafe { sys::transcribe_parakeet_stream_ext_init(&mut e) };
171                set(&mut e.att_context_right, o.att_context_right);
172                StreamExtRaw::ParakeetStream(Box::new(e))
173            }
174            StreamExtension::ParakeetBuffered(o) => {
175                let mut e: sys::transcribe_parakeet_buffered_stream_ext =
176                    unsafe { std::mem::zeroed() };
177                unsafe { sys::transcribe_parakeet_buffered_stream_ext_init(&mut e) };
178                set(&mut e.left_ms, o.left_ms);
179                set(&mut e.chunk_ms, o.chunk_ms);
180                set(&mut e.right_ms, o.right_ms);
181                StreamExtRaw::ParakeetBuffered(Box::new(e))
182            }
183            StreamExtension::MoonshineStreaming(o) => {
184                let mut e: sys::transcribe_moonshine_streaming_stream_ext =
185                    unsafe { std::mem::zeroed() };
186                unsafe { sys::transcribe_moonshine_streaming_stream_ext_init(&mut e) };
187                set(&mut e.min_decode_interval_ms, o.min_decode_interval_ms);
188                StreamExtRaw::MoonshineStreaming(Box::new(e))
189            }
190            StreamExtension::VoxtralRealtime(o) => {
191                let mut e: sys::transcribe_voxtral_realtime_stream_ext =
192                    unsafe { std::mem::zeroed() };
193                unsafe { sys::transcribe_voxtral_realtime_stream_ext_init(&mut e) };
194                set(&mut e.num_delay_tokens, o.num_delay_tokens);
195                set(&mut e.min_decode_interval_ms, o.min_decode_interval_ms);
196                StreamExtRaw::VoxtralRealtime(Box::new(e))
197            }
198        }
199    }
200}
201
202/// Overwrite `slot` only when the caller provided a value.
203fn set<T>(slot: &mut T, value: Option<T>) {
204    if let Some(v) = value {
205        *slot = v;
206    }
207}