1use std::ffi::CString;
12use std::os::raw::c_void;
13use std::sync::atomic::AtomicBool;
14use std::sync::Arc;
15
16use transcribe_cpp_sys as sys;
17
18use crate::cancel::{abort_trampoline, CancelToken};
19use crate::error::{check, error_for_status, Error, Result};
20use crate::family::{RunExtRaw, RunExtension};
21use crate::model::{Model, ModelInner, SessionLimits, SessionOptions};
22use crate::result::{owned_opt_str, owned_str, Segment, Timings, Token, Transcript, Word};
23use crate::streaming::{build_stream_params, StreamOptions, StreamText, StreamUpdate};
24use crate::types::{Itn, Pnc, StreamState, Task, TimestampKind};
25
26#[derive(Debug, Clone, PartialEq)]
28pub struct RunOptions {
29 pub task: Task,
30 pub timestamps: TimestampKind,
31 pub pnc: Pnc,
32 pub itn: Itn,
33 pub language: Option<String>,
35 pub target_language: Option<String>,
37 pub keep_special_tags: bool,
39 pub spec_k_drafts: i32,
41 pub family: Option<RunExtension>,
43}
44
45impl Default for RunOptions {
46 fn default() -> Self {
47 RunOptions {
48 task: Task::Transcribe,
49 timestamps: TimestampKind::Auto,
50 pnc: Pnc::Default,
51 itn: Itn::Default,
52 language: None,
53 target_language: None,
54 keep_special_tags: false,
55 spec_k_drafts: -1,
56 family: None,
57 }
58 }
59}
60
61pub struct Session {
63 ptr: *mut sys::transcribe_session,
64 model: Arc<ModelInner>,
67 cancel: Option<Arc<AtomicBool>>,
70}
71
72impl std::fmt::Debug for Session {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 f.debug_struct("Session").finish_non_exhaustive()
75 }
76}
77
78unsafe impl Send for Session {}
82
83impl Drop for Session {
84 fn drop(&mut self) {
85 unsafe { sys::transcribe_session_free(self.ptr) };
86 }
88}
89
90impl Session {
91 pub(crate) fn new(model: &Model, options: &SessionOptions) -> Result<Session> {
92 let mut params: sys::transcribe_session_params = unsafe { std::mem::zeroed() };
93 unsafe { sys::transcribe_session_params_init(&mut params) };
94 params.n_threads = options.n_threads;
95 params.kv_type = options.kv_type.to_raw();
96 params.n_ctx = options.n_ctx;
97
98 let mut out: *mut sys::transcribe_session = std::ptr::null_mut();
99 let status = unsafe { sys::transcribe_session_init(model.inner.ptr, ¶ms, &mut out) };
100 check(status, "session init")?;
101 debug_assert!(!out.is_null());
102
103 Ok(Session {
104 ptr: out,
105 model: Arc::clone(&model.inner),
106 cancel: None,
107 })
108 }
109
110 pub fn set_cancel_token(&mut self, token: &CancelToken) {
116 let flag = Arc::clone(&token.flag);
117 let userdata = Arc::as_ptr(&flag) as *mut c_void;
118 unsafe { sys::transcribe_set_abort_callback(self.ptr, Some(abort_trampoline), userdata) };
122 self.cancel = Some(flag);
123 }
124
125 pub fn clear_cancel_token(&mut self) {
127 unsafe { sys::transcribe_set_abort_callback(self.ptr, None, std::ptr::null_mut()) };
128 self.cancel = None;
129 }
130
131 pub fn was_aborted(&self) -> bool {
133 unsafe { sys::transcribe_was_aborted(self.ptr) }
134 }
135
136 pub fn was_truncated(&self) -> bool {
139 unsafe { sys::transcribe_was_truncated(self.ptr) }
140 }
141
142 pub fn run(&mut self, pcm: &[f32], options: &RunOptions) -> Result<Transcript> {
147 let (params, _lang, _target, _family) = build_run_params(options)?;
148 let n = clamp_len(pcm.len())?;
149
150 let status = {
154 let guard = self
155 .model
156 .compute_lock
157 .lock()
158 .unwrap_or_else(|e| e.into_inner());
159 if *guard {
160 return Err(Error::Busy(
161 "a stream is active on this model; finish or drop it before run()".into(),
162 ));
163 }
164 unsafe { sys::transcribe_run(self.ptr, pcm.as_ptr(), n, ¶ms) }
165 };
166
167 match status {
168 s if s == sys::transcribe_status::TRANSCRIBE_OK => Ok(self.materialize_run()),
169 s if s == sys::transcribe_status::TRANSCRIBE_ERR_ABORTED
170 || s == sys::transcribe_status::TRANSCRIBE_ERR_OUTPUT_TRUNCATED =>
171 {
172 let partial = Box::new(self.materialize_run());
174 Err(attach_partial(error_for_status(s, "run"), partial))
175 }
176 s => Err(error_for_status(s, "run")),
177 }
178 }
179
180 pub fn run_batch(
186 &mut self,
187 pcms: &[&[f32]],
188 options: &RunOptions,
189 ) -> Result<Vec<Result<Transcript>>> {
190 let (params, _lang, _target, _family) = build_run_params(options)?;
191 let ptrs: Vec<*const f32> = pcms.iter().map(|p| p.as_ptr()).collect();
192 let lens: Vec<i32> = pcms
193 .iter()
194 .map(|p| clamp_len(p.len()))
195 .collect::<Result<_>>()?;
196 let n = clamp_len(pcms.len())?;
197
198 let status = {
199 let guard = self
200 .model
201 .compute_lock
202 .lock()
203 .unwrap_or_else(|e| e.into_inner());
204 if *guard {
205 return Err(Error::Busy(
206 "a stream is active on this model; finish or drop it before run_batch()".into(),
207 ));
208 }
209 unsafe { sys::transcribe_run_batch(self.ptr, ptrs.as_ptr(), lens.as_ptr(), n, ¶ms) }
210 };
211
212 let ok = status == sys::transcribe_status::TRANSCRIBE_OK
215 || status == sys::transcribe_status::TRANSCRIBE_ERR_ABORTED;
216 if !ok {
217 return Err(error_for_status(status, "run_batch"));
218 }
219
220 let count = unsafe { sys::transcribe_batch_n_results(self.ptr) };
221 let mut results = Vec::with_capacity(count.max(0) as usize);
222 for i in 0..count {
223 let st = unsafe { sys::transcribe_batch_status(self.ptr, i) };
224 if st == sys::transcribe_status::TRANSCRIBE_OK {
225 results.push(Ok(self.materialize_batch(i)));
226 } else if st == sys::transcribe_status::TRANSCRIBE_ERR_ABORTED
227 || st == sys::transcribe_status::TRANSCRIBE_ERR_OUTPUT_TRUNCATED
228 {
229 let partial = Box::new(self.materialize_batch(i));
230 let err = attach_partial(error_for_status(st, "run_batch utterance"), partial);
231 results.push(Err(err));
232 } else {
233 results.push(Err(error_for_status(st, "run_batch utterance")));
234 }
235 }
236 Ok(results)
237 }
238
239 pub fn limits(&self) -> Result<SessionLimits> {
241 let mut out: sys::transcribe_session_limits = unsafe { std::mem::zeroed() };
242 unsafe { sys::transcribe_session_limits_init(&mut out) };
243 let status = unsafe { sys::transcribe_session_get_limits(self.ptr, &mut out) };
244 check(status, "session limits")?;
245 Ok(SessionLimits {
246 effective_n_ctx: out.effective_n_ctx,
247 effective_max_audio_ms: out.effective_max_audio_ms,
248 max_kv_bytes: out.max_kv_bytes,
249 })
250 }
251
252 pub fn model(&self) -> Model {
254 Model {
255 inner: Arc::clone(&self.model),
256 }
257 }
258
259 pub fn stream(&mut self, run: &RunOptions, stream: &StreamOptions) -> Result<Stream<'_>> {
266 let (run_params, _lang, _target, _family) = build_run_params(run)?;
267 let (stream_params, _stream_family) = build_stream_params(stream);
268 {
269 let mut guard = self
273 .model
274 .compute_lock
275 .lock()
276 .unwrap_or_else(|e| e.into_inner());
277 if *guard {
278 return Err(Error::Busy(
279 "a stream is already active on this model".into(),
280 ));
281 }
282 check(
283 unsafe { sys::transcribe_stream_begin(self.ptr, &run_params, &stream_params) },
284 "stream begin",
285 )?;
286 *guard = true; }
288 Ok(Stream {
289 session: self,
290 holds_lease: true,
291 })
292 }
293
294 fn materialize_run(&self) -> Transcript {
297 let n_seg = unsafe { sys::transcribe_n_segments(self.ptr) };
298 let n_word = unsafe { sys::transcribe_n_words(self.ptr) };
299 let n_tok = unsafe { sys::transcribe_n_tokens(self.ptr) };
300
301 Transcript {
302 text: owned_str(unsafe { sys::transcribe_full_text(self.ptr) }),
303 language: owned_opt_str(unsafe { sys::transcribe_detected_language(self.ptr) }),
304 timestamp_kind: TimestampKind::from_raw(unsafe {
305 sys::transcribe_returned_timestamp_kind(self.ptr)
306 }),
307 segments: (0..n_seg).map(|i| self.segment(i)).collect(),
308 words: (0..n_word).map(|i| self.word(i)).collect(),
309 tokens: (0..n_tok).map(|i| self.token(i)).collect(),
310 timings: self.timings(),
311 }
312 }
313
314 fn segment(&self, i: i32) -> Segment {
315 let mut raw: sys::transcribe_segment = unsafe { std::mem::zeroed() };
316 unsafe { sys::transcribe_segment_init(&mut raw) };
317 let _ = unsafe { sys::transcribe_get_segment(self.ptr, i, &mut raw) };
318 Segment::from_raw(&raw)
319 }
320
321 fn word(&self, i: i32) -> Word {
322 let mut raw: sys::transcribe_word = unsafe { std::mem::zeroed() };
323 unsafe { sys::transcribe_word_init(&mut raw) };
324 let _ = unsafe { sys::transcribe_get_word(self.ptr, i, &mut raw) };
325 Word::from_raw(&raw)
326 }
327
328 fn token(&self, i: i32) -> Token {
329 let mut raw: sys::transcribe_token = unsafe { std::mem::zeroed() };
330 unsafe { sys::transcribe_token_init(&mut raw) };
331 let _ = unsafe { sys::transcribe_get_token(self.ptr, i, &mut raw) };
332 Token::from_raw(&raw)
333 }
334
335 fn timings(&self) -> Timings {
336 let mut raw: sys::transcribe_timings = unsafe { std::mem::zeroed() };
337 unsafe { sys::transcribe_timings_init(&mut raw) };
338 let _ = unsafe { sys::transcribe_get_timings(self.ptr, &mut raw) };
339 Timings::from_raw(&raw)
340 }
341
342 fn materialize_batch(&self, i: i32) -> Transcript {
345 let n_seg = unsafe { sys::transcribe_batch_n_segments(self.ptr, i) };
346 let n_word = unsafe { sys::transcribe_batch_n_words(self.ptr, i) };
347 let n_tok = unsafe { sys::transcribe_batch_n_tokens(self.ptr, i) };
348
349 Transcript {
350 text: owned_str(unsafe { sys::transcribe_batch_full_text(self.ptr, i) }),
351 language: owned_opt_str(unsafe {
352 sys::transcribe_batch_detected_language(self.ptr, i)
353 }),
354 timestamp_kind: TimestampKind::from_raw(unsafe {
355 sys::transcribe_batch_returned_timestamp_kind(self.ptr, i)
356 }),
357 segments: (0..n_seg).map(|j| self.batch_segment(i, j)).collect(),
358 words: (0..n_word).map(|j| self.batch_word(i, j)).collect(),
359 tokens: (0..n_tok).map(|j| self.batch_token(i, j)).collect(),
360 timings: self.batch_timings(i),
361 }
362 }
363
364 fn batch_segment(&self, i: i32, j: i32) -> Segment {
365 let mut raw: sys::transcribe_segment = unsafe { std::mem::zeroed() };
366 unsafe { sys::transcribe_segment_init(&mut raw) };
367 let _ = unsafe { sys::transcribe_batch_get_segment(self.ptr, i, j, &mut raw) };
368 Segment::from_raw(&raw)
369 }
370
371 fn batch_word(&self, i: i32, j: i32) -> Word {
372 let mut raw: sys::transcribe_word = unsafe { std::mem::zeroed() };
373 unsafe { sys::transcribe_word_init(&mut raw) };
374 let _ = unsafe { sys::transcribe_batch_get_word(self.ptr, i, j, &mut raw) };
375 Word::from_raw(&raw)
376 }
377
378 fn batch_token(&self, i: i32, j: i32) -> Token {
379 let mut raw: sys::transcribe_token = unsafe { std::mem::zeroed() };
380 unsafe { sys::transcribe_token_init(&mut raw) };
381 let _ = unsafe { sys::transcribe_batch_get_token(self.ptr, i, j, &mut raw) };
382 Token::from_raw(&raw)
383 }
384
385 fn batch_timings(&self, i: i32) -> Timings {
386 let mut raw: sys::transcribe_timings = unsafe { std::mem::zeroed() };
387 unsafe { sys::transcribe_timings_init(&mut raw) };
388 let _ = unsafe { sys::transcribe_batch_get_timings(self.ptr, i, &mut raw) };
389 Timings::from_raw(&raw)
390 }
391}
392
393type RunParamsBundle = (
396 sys::transcribe_run_params,
397 Option<CString>,
398 Option<CString>,
399 Option<RunExtRaw>,
400);
401
402fn build_run_params(o: &RunOptions) -> Result<RunParamsBundle> {
406 let mut params: sys::transcribe_run_params = unsafe { std::mem::zeroed() };
407 unsafe { sys::transcribe_run_params_init(&mut params) };
408
409 params.task = o.task.to_raw();
410 params.timestamps = o.timestamps.to_raw();
411 params.pnc = o.pnc.to_raw();
412 params.itn = o.itn.to_raw();
413 params.keep_special_tags = o.keep_special_tags;
414 params.spec_k_drafts = o.spec_k_drafts;
415
416 let lang = o.language.as_deref().map(CString::new).transpose()?;
417 let target = o.target_language.as_deref().map(CString::new).transpose()?;
418 params.language = lang.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
419 params.target_language = target.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
420
421 let family = o
422 .family
423 .as_ref()
424 .map(RunExtension::materialize)
425 .transpose()?;
426 params.family = family.as_ref().map_or(std::ptr::null(), |f| f.ext_ptr());
427
428 Ok((params, lang, target, family))
429}
430
431fn clamp_len(len: usize) -> Result<i32> {
433 i32::try_from(len).map_err(|_| Error::InvalidArgument(format!("length {len} exceeds i32::MAX")))
434}
435
436fn attach_partial(err: Error, partial: Box<Transcript>) -> Error {
439 match err {
440 Error::Aborted { message, .. } => Error::Aborted {
441 message,
442 partial: Some(partial),
443 },
444 Error::OutputTruncated { message, .. } => Error::OutputTruncated {
445 message,
446 partial: Some(partial),
447 },
448 other => other,
449 }
450}
451
452pub struct Stream<'a> {
459 session: &'a mut Session,
460 holds_lease: bool,
465}
466
467impl std::fmt::Debug for Stream<'_> {
468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469 f.debug_struct("Stream")
470 .field("state", &self.state())
471 .field("revision", &self.revision())
472 .finish()
473 }
474}
475
476impl Drop for Stream<'_> {
477 fn drop(&mut self) {
478 let mut guard = self
483 .session
484 .model
485 .compute_lock
486 .lock()
487 .unwrap_or_else(|e| e.into_inner());
488 unsafe { sys::transcribe_stream_reset(self.session.ptr) };
489 if self.holds_lease {
490 *guard = false;
491 }
492 }
493}
494
495impl Stream<'_> {
496 pub fn feed(&mut self, pcm: &[f32]) -> Result<StreamUpdate> {
498 let n = clamp_len(pcm.len())?;
499 let mut update: sys::transcribe_stream_update = unsafe { std::mem::zeroed() };
500 unsafe { sys::transcribe_stream_update_init(&mut update) };
501 let status = {
502 let _guard = self
505 .session
506 .model
507 .compute_lock
508 .lock()
509 .unwrap_or_else(|e| e.into_inner());
510 unsafe { sys::transcribe_stream_feed(self.session.ptr, pcm.as_ptr(), n, &mut update) }
511 };
512 check(status, "stream feed")?;
513 Ok(StreamUpdate::from_raw(&update))
514 }
515
516 pub fn finalize(&mut self) -> Result<StreamUpdate> {
519 let mut update: sys::transcribe_stream_update = unsafe { std::mem::zeroed() };
520 unsafe { sys::transcribe_stream_update_init(&mut update) };
521 let status = {
522 let mut guard = self
523 .session
524 .model
525 .compute_lock
526 .lock()
527 .unwrap_or_else(|e| e.into_inner());
528 let st = unsafe { sys::transcribe_stream_finalize(self.session.ptr, &mut update) };
529 if self.holds_lease {
533 *guard = false;
534 }
535 st
536 };
537 self.holds_lease = false;
538 check(status, "stream finalize")?;
539 Ok(StreamUpdate::from_raw(&update))
540 }
541
542 pub fn reset(&mut self) {
547 let mut guard = self
548 .session
549 .model
550 .compute_lock
551 .lock()
552 .unwrap_or_else(|e| e.into_inner());
553 unsafe { sys::transcribe_stream_reset(self.session.ptr) };
554 if self.holds_lease {
555 *guard = false;
556 }
557 self.holds_lease = false;
558 }
559
560 pub fn text(&self) -> StreamText {
562 let mut raw: sys::transcribe_stream_text = unsafe { std::mem::zeroed() };
563 unsafe { sys::transcribe_stream_text_init(&mut raw) };
564 let _ = unsafe { sys::transcribe_stream_get_text(self.session.ptr, &mut raw) };
565 StreamText::from_raw(&raw)
566 }
567
568 pub fn snapshot(&self) -> Transcript {
571 self.session.materialize_run()
572 }
573
574 pub fn state(&self) -> StreamState {
576 StreamState::from_raw(unsafe { sys::transcribe_stream_get_state(self.session.ptr) })
577 }
578
579 pub fn revision(&self) -> i32 {
581 unsafe { sys::transcribe_stream_revision(self.session.ptr) }
582 }
583
584 pub fn last_status(&self) -> Option<Error> {
590 let status = unsafe { sys::transcribe_stream_last_status(self.session.ptr) };
591 if status == sys::transcribe_status::TRANSCRIBE_OK {
592 None
593 } else {
594 Some(error_for_status(status, "stream"))
595 }
596 }
597
598 pub fn was_aborted(&self) -> bool {
600 self.session.was_aborted()
601 }
602}