use crate::audio::whisper::{
constants::DEFAULT_LANGUAGE_CODE,
options::DecodingOptions,
task_facts::{SpanKnowledge, TaskFacts, TaskFactsAccumulator},
transcribe::LanguageObservation,
};
pub mod writer;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WordTiming {
word: String,
tokens: Vec<u32>,
start: f32,
end: f32,
probability: f32,
}
impl WordTiming {
pub fn new(
word: impl Into<String>,
tokens: impl Into<Vec<u32>>,
start: f32,
end: f32,
probability: f32,
) -> Self {
Self {
word: word.into(),
tokens: tokens.into(),
start,
end,
probability,
}
}
#[inline(always)]
pub fn word(&self) -> &str {
self.word.as_str()
}
#[inline(always)]
pub const fn tokens_slice(&self) -> &[u32] {
self.tokens.as_slice()
}
#[inline(always)]
pub const fn start(&self) -> f32 {
self.start
}
#[inline(always)]
pub const fn end(&self) -> f32 {
self.end
}
#[inline(always)]
pub const fn set_start(&mut self, start: f32) -> &mut Self {
self.start = start;
self
}
#[inline(always)]
pub const fn set_end(&mut self, end: f32) -> &mut Self {
self.end = end;
self
}
#[inline(always)]
pub const fn probability(&self) -> f32 {
self.probability
}
#[inline(always)]
pub const fn duration(&self) -> f32 {
self.end - self.start
}
}
pub const DEFAULT_SEGMENT_TEMPERATURE: f32 = 1.0;
pub const DEFAULT_SEGMENT_COMPRESSION_RATIO: f32 = 1.0;
#[cfg(feature = "serde")]
fn default_segment_temperature() -> f32 {
DEFAULT_SEGMENT_TEMPERATURE
}
#[cfg(feature = "serde")]
fn default_segment_compression_ratio() -> f32 {
DEFAULT_SEGMENT_COMPRESSION_RATIO
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TranscriptionSegment {
#[cfg_attr(feature = "serde", serde(default))]
id: usize,
#[cfg_attr(feature = "serde", serde(default))]
seek: usize,
#[cfg_attr(feature = "serde", serde(default))]
start: f32,
#[cfg_attr(feature = "serde", serde(default))]
end: f32,
#[cfg_attr(feature = "serde", serde(default))]
text: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
tokens: Vec<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
token_log_probs: Vec<(u32, f32)>,
#[cfg_attr(
feature = "serde",
serde(
default = "default_segment_temperature",
with = "crate::audio::whisper::options::finite_f32"
)
)]
temperature: f32,
#[cfg_attr(feature = "serde", serde(default))]
avg_logprob: f32,
#[cfg_attr(
feature = "serde",
serde(default = "default_segment_compression_ratio")
)]
compression_ratio: f32,
#[cfg_attr(feature = "serde", serde(default))]
no_speech_prob: f32,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
words: Vec<WordTiming>,
}
impl Default for TranscriptionSegment {
fn default() -> Self {
Self::new()
}
}
impl TranscriptionSegment {
pub const fn new() -> Self {
Self {
id: 0,
seek: 0,
start: 0.0,
end: 0.0,
text: String::new(),
tokens: Vec::new(),
token_log_probs: Vec::new(),
temperature: DEFAULT_SEGMENT_TEMPERATURE,
avg_logprob: 0.0,
compression_ratio: DEFAULT_SEGMENT_COMPRESSION_RATIO,
no_speech_prob: 0.0,
words: Vec::new(),
}
}
#[inline(always)]
pub const fn id(&self) -> usize {
self.id
}
#[must_use]
#[inline(always)]
pub const fn with_id(mut self, id: usize) -> Self {
self.set_id(id);
self
}
#[inline(always)]
pub const fn set_id(&mut self, id: usize) -> &mut Self {
self.id = id;
self
}
#[inline(always)]
pub const fn seek(&self) -> usize {
self.seek
}
#[must_use]
#[inline(always)]
pub const fn with_seek(mut self, seek: usize) -> Self {
self.set_seek(seek);
self
}
#[inline(always)]
pub const fn set_seek(&mut self, seek: usize) -> &mut Self {
self.seek = seek;
self
}
#[inline(always)]
pub const fn start(&self) -> f32 {
self.start
}
#[must_use]
#[inline(always)]
pub const fn with_start(mut self, start: f32) -> Self {
self.set_start(start);
self
}
#[inline(always)]
pub const fn set_start(&mut self, start: f32) -> &mut Self {
self.start = start;
self
}
#[inline(always)]
pub const fn end(&self) -> f32 {
self.end
}
#[must_use]
#[inline(always)]
pub const fn with_end(mut self, end: f32) -> Self {
self.set_end(end);
self
}
#[inline(always)]
pub const fn set_end(&mut self, end: f32) -> &mut Self {
self.end = end;
self
}
#[inline(always)]
pub fn text(&self) -> &str {
self.text.as_str()
}
#[must_use]
#[inline(always)]
pub fn with_text(mut self, text: impl Into<String>) -> Self {
self.set_text(text);
self
}
#[inline(always)]
pub fn set_text(&mut self, text: impl Into<String>) -> &mut Self {
self.text = text.into();
self
}
#[inline(always)]
pub const fn tokens_slice(&self) -> &[u32] {
self.tokens.as_slice()
}
#[must_use]
#[inline(always)]
pub fn with_tokens(mut self, tokens: impl Into<Vec<u32>>) -> Self {
self.set_tokens(tokens);
self
}
#[inline(always)]
pub fn set_tokens(&mut self, tokens: impl Into<Vec<u32>>) -> &mut Self {
self.tokens = tokens.into();
self
}
#[inline(always)]
pub const fn token_log_probs_slice(&self) -> &[(u32, f32)] {
self.token_log_probs.as_slice()
}
#[must_use]
#[inline(always)]
pub fn with_token_log_probs(mut self, token_log_probs: impl Into<Vec<(u32, f32)>>) -> Self {
self.set_token_log_probs(token_log_probs);
self
}
#[inline(always)]
pub fn set_token_log_probs(&mut self, token_log_probs: impl Into<Vec<(u32, f32)>>) -> &mut Self {
self.token_log_probs = token_log_probs.into();
self
}
#[inline(always)]
pub const fn temperature(&self) -> f32 {
self.temperature
}
#[must_use]
#[inline(always)]
pub const fn with_temperature(mut self, temperature: f32) -> Self {
self.set_temperature(temperature);
self
}
#[inline(always)]
pub const fn set_temperature(&mut self, temperature: f32) -> &mut Self {
self.temperature = temperature;
self
}
#[inline(always)]
pub const fn avg_logprob(&self) -> f32 {
self.avg_logprob
}
#[must_use]
#[inline(always)]
pub const fn with_avg_logprob(mut self, avg_logprob: f32) -> Self {
self.set_avg_logprob(avg_logprob);
self
}
#[inline(always)]
pub const fn set_avg_logprob(&mut self, avg_logprob: f32) -> &mut Self {
self.avg_logprob = avg_logprob;
self
}
#[inline(always)]
pub const fn compression_ratio(&self) -> f32 {
self.compression_ratio
}
#[must_use]
#[inline(always)]
pub const fn with_compression_ratio(mut self, compression_ratio: f32) -> Self {
self.set_compression_ratio(compression_ratio);
self
}
#[inline(always)]
pub const fn set_compression_ratio(&mut self, compression_ratio: f32) -> &mut Self {
self.compression_ratio = compression_ratio;
self
}
#[inline(always)]
pub const fn no_speech_prob(&self) -> f32 {
self.no_speech_prob
}
#[must_use]
#[inline(always)]
pub const fn with_no_speech_prob(mut self, no_speech_prob: f32) -> Self {
self.set_no_speech_prob(no_speech_prob);
self
}
#[inline(always)]
pub const fn set_no_speech_prob(&mut self, no_speech_prob: f32) -> &mut Self {
self.no_speech_prob = no_speech_prob;
self
}
#[inline(always)]
pub const fn words_slice(&self) -> &[WordTiming] {
self.words.as_slice()
}
#[inline(always)]
pub const fn words_slice_mut(&mut self) -> &mut [WordTiming] {
self.words.as_mut_slice()
}
#[must_use]
#[inline(always)]
pub fn with_words(mut self, words: impl Into<Vec<WordTiming>>) -> Self {
self.set_words(words);
self
}
#[inline(always)]
pub fn set_words(&mut self, words: impl Into<Vec<WordTiming>>) -> &mut Self {
self.words = words.into();
self
}
#[inline(always)]
pub const fn duration(&self) -> f32 {
self.end - self.start
}
}
pub const DEFAULT_PIPELINE_TIME_SENTINEL: f64 = f64::MAX;
pub const DEFAULT_INPUT_AUDIO_SECONDS: f64 = 0.001;
#[cfg(feature = "serde")]
fn default_pipeline_time_sentinel() -> f64 {
DEFAULT_PIPELINE_TIME_SENTINEL
}
#[cfg(feature = "serde")]
fn default_input_audio_seconds() -> f64 {
DEFAULT_INPUT_AUDIO_SECONDS
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TranscriptionTimings {
#[cfg_attr(feature = "serde", serde(default = "default_pipeline_time_sentinel"))]
pipeline_start: f64,
#[cfg_attr(feature = "serde", serde(default = "default_pipeline_time_sentinel"))]
first_token_time: f64,
#[cfg_attr(feature = "serde", serde(default = "default_input_audio_seconds"))]
input_audio_seconds: f64,
#[cfg_attr(feature = "serde", serde(default))]
model_loading: f64,
#[cfg_attr(feature = "serde", serde(default))]
prewarm_load_time: f64,
#[cfg_attr(feature = "serde", serde(default))]
encoder_load_time: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoder_load_time: f64,
#[cfg_attr(feature = "serde", serde(default))]
encoder_specialization_time: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoder_specialization_time: f64,
#[cfg_attr(feature = "serde", serde(default))]
tokenizer_load_time: f64,
#[cfg_attr(feature = "serde", serde(default))]
audio_loading: f64,
#[cfg_attr(feature = "serde", serde(default))]
audio_processing: f64,
#[cfg_attr(feature = "serde", serde(default))]
logmels: f64,
#[cfg_attr(feature = "serde", serde(default))]
encoding: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_init: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_loop: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_predictions: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_filtering: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_sampling: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_fallback: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_windowing: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_kv_caching: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_word_timestamps: f64,
#[cfg_attr(feature = "serde", serde(default))]
decoding_non_prediction: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_audio_processing_runs: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_logmel_runs: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_encoding_runs: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_decoding_loops: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_kv_update_runs: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_timestamp_alignment_runs: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_decoding_fallbacks: f64,
#[cfg_attr(feature = "serde", serde(default))]
total_decoding_windows: f64,
#[cfg_attr(feature = "serde", serde(default))]
full_pipeline: f64,
}
impl Default for TranscriptionTimings {
fn default() -> Self {
Self::new()
}
}
impl TranscriptionTimings {
pub const fn new() -> Self {
Self {
pipeline_start: DEFAULT_PIPELINE_TIME_SENTINEL,
first_token_time: DEFAULT_PIPELINE_TIME_SENTINEL,
input_audio_seconds: DEFAULT_INPUT_AUDIO_SECONDS,
model_loading: 0.0,
prewarm_load_time: 0.0,
encoder_load_time: 0.0,
decoder_load_time: 0.0,
encoder_specialization_time: 0.0,
decoder_specialization_time: 0.0,
tokenizer_load_time: 0.0,
audio_loading: 0.0,
audio_processing: 0.0,
logmels: 0.0,
encoding: 0.0,
decoding_init: 0.0,
decoding_loop: 0.0,
decoding_predictions: 0.0,
decoding_filtering: 0.0,
decoding_sampling: 0.0,
decoding_fallback: 0.0,
decoding_windowing: 0.0,
decoding_kv_caching: 0.0,
decoding_word_timestamps: 0.0,
decoding_non_prediction: 0.0,
total_audio_processing_runs: 0.0,
total_logmel_runs: 0.0,
total_encoding_runs: 0.0,
total_decoding_loops: 0.0,
total_kv_update_runs: 0.0,
total_timestamp_alignment_runs: 0.0,
total_decoding_fallbacks: 0.0,
total_decoding_windows: 0.0,
full_pipeline: 0.0,
}
}
#[inline(always)]
pub const fn pipeline_start(&self) -> f64 {
self.pipeline_start
}
#[inline(always)]
pub const fn set_pipeline_start(&mut self, pipeline_start: f64) -> &mut Self {
self.pipeline_start = pipeline_start;
self
}
#[inline(always)]
pub const fn first_token_time(&self) -> f64 {
self.first_token_time
}
#[inline(always)]
pub const fn set_first_token_time(&mut self, first_token_time: f64) -> &mut Self {
self.first_token_time = first_token_time;
self
}
#[inline(always)]
pub const fn input_audio_seconds(&self) -> f64 {
self.input_audio_seconds
}
#[inline(always)]
pub const fn set_input_audio_seconds(&mut self, input_audio_seconds: f64) -> &mut Self {
self.input_audio_seconds = input_audio_seconds;
self
}
#[inline(always)]
pub const fn model_loading(&self) -> f64 {
self.model_loading
}
#[inline(always)]
pub const fn set_model_loading(&mut self, model_loading: f64) -> &mut Self {
self.model_loading = model_loading;
self
}
#[inline(always)]
pub const fn prewarm_load_time(&self) -> f64 {
self.prewarm_load_time
}
#[inline(always)]
pub const fn set_prewarm_load_time(&mut self, prewarm_load_time: f64) -> &mut Self {
self.prewarm_load_time = prewarm_load_time;
self
}
#[inline(always)]
pub const fn encoder_load_time(&self) -> f64 {
self.encoder_load_time
}
#[inline(always)]
pub const fn set_encoder_load_time(&mut self, encoder_load_time: f64) -> &mut Self {
self.encoder_load_time = encoder_load_time;
self
}
#[inline(always)]
pub const fn decoder_load_time(&self) -> f64 {
self.decoder_load_time
}
#[inline(always)]
pub const fn set_decoder_load_time(&mut self, decoder_load_time: f64) -> &mut Self {
self.decoder_load_time = decoder_load_time;
self
}
#[inline(always)]
pub const fn encoder_specialization_time(&self) -> f64 {
self.encoder_specialization_time
}
#[inline(always)]
pub const fn set_encoder_specialization_time(
&mut self,
encoder_specialization_time: f64,
) -> &mut Self {
self.encoder_specialization_time = encoder_specialization_time;
self
}
#[inline(always)]
pub const fn decoder_specialization_time(&self) -> f64 {
self.decoder_specialization_time
}
#[inline(always)]
pub const fn set_decoder_specialization_time(
&mut self,
decoder_specialization_time: f64,
) -> &mut Self {
self.decoder_specialization_time = decoder_specialization_time;
self
}
#[inline(always)]
pub const fn tokenizer_load_time(&self) -> f64 {
self.tokenizer_load_time
}
#[inline(always)]
pub const fn set_tokenizer_load_time(&mut self, tokenizer_load_time: f64) -> &mut Self {
self.tokenizer_load_time = tokenizer_load_time;
self
}
#[inline(always)]
pub const fn audio_loading(&self) -> f64 {
self.audio_loading
}
#[inline(always)]
pub const fn set_audio_loading(&mut self, audio_loading: f64) -> &mut Self {
self.audio_loading = audio_loading;
self
}
#[inline(always)]
pub const fn audio_processing(&self) -> f64 {
self.audio_processing
}
#[inline(always)]
pub const fn set_audio_processing(&mut self, audio_processing: f64) -> &mut Self {
self.audio_processing = audio_processing;
self
}
#[inline(always)]
pub const fn logmels(&self) -> f64 {
self.logmels
}
#[inline(always)]
pub const fn set_logmels(&mut self, logmels: f64) -> &mut Self {
self.logmels = logmels;
self
}
#[inline(always)]
pub const fn encoding(&self) -> f64 {
self.encoding
}
#[inline(always)]
pub const fn set_encoding(&mut self, encoding: f64) -> &mut Self {
self.encoding = encoding;
self
}
#[inline(always)]
pub const fn decoding_init(&self) -> f64 {
self.decoding_init
}
#[inline(always)]
pub const fn set_decoding_init(&mut self, decoding_init: f64) -> &mut Self {
self.decoding_init = decoding_init;
self
}
#[inline(always)]
pub const fn decoding_loop(&self) -> f64 {
self.decoding_loop
}
#[inline(always)]
pub const fn set_decoding_loop(&mut self, decoding_loop: f64) -> &mut Self {
self.decoding_loop = decoding_loop;
self
}
#[inline(always)]
pub const fn decoding_predictions(&self) -> f64 {
self.decoding_predictions
}
#[inline(always)]
pub const fn set_decoding_predictions(&mut self, decoding_predictions: f64) -> &mut Self {
self.decoding_predictions = decoding_predictions;
self
}
#[inline(always)]
pub const fn decoding_filtering(&self) -> f64 {
self.decoding_filtering
}
#[inline(always)]
pub const fn set_decoding_filtering(&mut self, decoding_filtering: f64) -> &mut Self {
self.decoding_filtering = decoding_filtering;
self
}
#[inline(always)]
pub const fn decoding_sampling(&self) -> f64 {
self.decoding_sampling
}
#[inline(always)]
pub const fn set_decoding_sampling(&mut self, decoding_sampling: f64) -> &mut Self {
self.decoding_sampling = decoding_sampling;
self
}
#[inline(always)]
pub const fn decoding_fallback(&self) -> f64 {
self.decoding_fallback
}
#[inline(always)]
pub const fn set_decoding_fallback(&mut self, decoding_fallback: f64) -> &mut Self {
self.decoding_fallback = decoding_fallback;
self
}
#[inline(always)]
pub const fn decoding_windowing(&self) -> f64 {
self.decoding_windowing
}
#[inline(always)]
pub const fn set_decoding_windowing(&mut self, decoding_windowing: f64) -> &mut Self {
self.decoding_windowing = decoding_windowing;
self
}
#[inline(always)]
pub const fn decoding_kv_caching(&self) -> f64 {
self.decoding_kv_caching
}
#[inline(always)]
pub const fn set_decoding_kv_caching(&mut self, decoding_kv_caching: f64) -> &mut Self {
self.decoding_kv_caching = decoding_kv_caching;
self
}
#[inline(always)]
pub const fn decoding_word_timestamps(&self) -> f64 {
self.decoding_word_timestamps
}
#[inline(always)]
pub const fn set_decoding_word_timestamps(&mut self, decoding_word_timestamps: f64) -> &mut Self {
self.decoding_word_timestamps = decoding_word_timestamps;
self
}
#[inline(always)]
pub const fn decoding_non_prediction(&self) -> f64 {
self.decoding_non_prediction
}
#[inline(always)]
pub const fn set_decoding_non_prediction(&mut self, decoding_non_prediction: f64) -> &mut Self {
self.decoding_non_prediction = decoding_non_prediction;
self
}
#[inline(always)]
pub const fn total_audio_processing_runs(&self) -> f64 {
self.total_audio_processing_runs
}
#[inline(always)]
pub const fn set_total_audio_processing_runs(
&mut self,
total_audio_processing_runs: f64,
) -> &mut Self {
self.total_audio_processing_runs = total_audio_processing_runs;
self
}
#[inline(always)]
pub const fn total_logmel_runs(&self) -> f64 {
self.total_logmel_runs
}
#[inline(always)]
pub const fn set_total_logmel_runs(&mut self, total_logmel_runs: f64) -> &mut Self {
self.total_logmel_runs = total_logmel_runs;
self
}
#[inline(always)]
pub const fn total_encoding_runs(&self) -> f64 {
self.total_encoding_runs
}
#[inline(always)]
pub const fn set_total_encoding_runs(&mut self, total_encoding_runs: f64) -> &mut Self {
self.total_encoding_runs = total_encoding_runs;
self
}
#[inline(always)]
pub const fn total_decoding_loops(&self) -> f64 {
self.total_decoding_loops
}
#[inline(always)]
pub const fn set_total_decoding_loops(&mut self, total_decoding_loops: f64) -> &mut Self {
self.total_decoding_loops = total_decoding_loops;
self
}
#[inline(always)]
pub const fn total_kv_update_runs(&self) -> f64 {
self.total_kv_update_runs
}
#[inline(always)]
pub const fn set_total_kv_update_runs(&mut self, total_kv_update_runs: f64) -> &mut Self {
self.total_kv_update_runs = total_kv_update_runs;
self
}
#[inline(always)]
pub const fn total_timestamp_alignment_runs(&self) -> f64 {
self.total_timestamp_alignment_runs
}
#[inline(always)]
pub const fn set_total_timestamp_alignment_runs(
&mut self,
total_timestamp_alignment_runs: f64,
) -> &mut Self {
self.total_timestamp_alignment_runs = total_timestamp_alignment_runs;
self
}
#[inline(always)]
pub const fn total_decoding_fallbacks(&self) -> f64 {
self.total_decoding_fallbacks
}
#[inline(always)]
pub const fn set_total_decoding_fallbacks(&mut self, total_decoding_fallbacks: f64) -> &mut Self {
self.total_decoding_fallbacks = total_decoding_fallbacks;
self
}
#[inline(always)]
pub const fn total_decoding_windows(&self) -> f64 {
self.total_decoding_windows
}
#[inline(always)]
pub const fn set_total_decoding_windows(&mut self, total_decoding_windows: f64) -> &mut Self {
self.total_decoding_windows = total_decoding_windows;
self
}
#[inline(always)]
pub const fn full_pipeline(&self) -> f64 {
self.full_pipeline
}
#[inline(always)]
pub const fn set_full_pipeline(&mut self, full_pipeline: f64) -> &mut Self {
self.full_pipeline = full_pipeline;
self
}
#[inline(always)]
pub const fn tokens_per_second(&self) -> f64 {
if self.full_pipeline == 0.0 {
0.0
} else {
self.total_decoding_loops / self.full_pipeline
}
}
#[inline(always)]
pub const fn real_time_factor(&self) -> f64 {
if self.input_audio_seconds == 0.0 {
0.0
} else {
self.full_pipeline / self.input_audio_seconds
}
}
#[inline(always)]
pub const fn speed_factor(&self) -> f64 {
if self.full_pipeline == 0.0 {
0.0
} else {
self.input_audio_seconds / self.full_pipeline
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TranscriptionResult {
#[cfg_attr(feature = "serde", serde(default))]
text: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
segments: Vec<TranscriptionSegment>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "String::is_empty")
)]
language: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
language_observations: Vec<LanguageObservation>,
#[cfg_attr(feature = "serde", serde(default))]
timings: TranscriptionTimings,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
seek_time: Option<f32>,
task_facts: TaskFacts,
}
impl TranscriptionResult {
pub fn new(
text: impl Into<String>,
segments: impl Into<Vec<TranscriptionSegment>>,
language: impl Into<String>,
timings: TranscriptionTimings,
) -> Self {
Self {
text: text.into(),
segments: segments.into(),
language: language.into(),
language_observations: Vec::new(),
timings,
seek_time: None,
task_facts: TaskFacts::unknown(),
}
}
#[inline(always)]
pub fn text(&self) -> &str {
self.text.as_str()
}
#[must_use]
#[inline(always)]
pub fn with_text(mut self, text: impl Into<String>) -> Self {
self.set_text(text);
self
}
#[inline(always)]
pub fn set_text(&mut self, text: impl Into<String>) -> &mut Self {
self.text = text.into();
self
}
#[inline(always)]
pub const fn segments_slice(&self) -> &[TranscriptionSegment] {
self.segments.as_slice()
}
#[inline(always)]
pub const fn segments_slice_mut(&mut self) -> &mut [TranscriptionSegment] {
self.segments.as_mut_slice()
}
#[must_use]
#[inline(always)]
pub fn with_segments(mut self, segments: impl Into<Vec<TranscriptionSegment>>) -> Self {
self.set_segments(segments);
self
}
#[inline(always)]
pub fn set_segments(&mut self, segments: impl Into<Vec<TranscriptionSegment>>) -> &mut Self {
self.segments = segments.into();
self
}
#[inline(always)]
pub fn language(&self) -> &str {
self.language.as_str()
}
#[must_use]
#[inline(always)]
pub fn with_language(mut self, language: impl Into<String>) -> Self {
self.set_language(language);
self
}
#[inline(always)]
pub fn set_language(&mut self, language: impl Into<String>) -> &mut Self {
self.language = language.into();
self
}
#[inline(always)]
pub const fn language_observations_slice(&self) -> &[LanguageObservation] {
self.language_observations.as_slice()
}
#[inline(always)]
pub const fn language_observations_slice_mut(&mut self) -> &mut [LanguageObservation] {
self.language_observations.as_mut_slice()
}
#[must_use]
#[inline(always)]
pub fn with_language_observations(
mut self,
language_observations: impl Into<Vec<LanguageObservation>>,
) -> Self {
self.set_language_observations(language_observations);
self
}
#[inline(always)]
pub fn set_language_observations(
&mut self,
language_observations: impl Into<Vec<LanguageObservation>>,
) -> &mut Self {
self.language_observations = language_observations.into();
self
}
#[inline(always)]
pub const fn task_facts(&self) -> &TaskFacts {
&self.task_facts
}
#[inline(always)]
pub const fn task_facts_mut(&mut self) -> &mut TaskFacts {
&mut self.task_facts
}
#[must_use]
#[inline(always)]
pub fn with_task_facts(mut self, task_facts: TaskFacts) -> Self {
self.set_task_facts(task_facts);
self
}
#[inline(always)]
pub fn set_task_facts(&mut self, task_facts: TaskFacts) -> &mut Self {
self.task_facts = task_facts;
self
}
#[inline(always)]
pub const fn timings(&self) -> &TranscriptionTimings {
&self.timings
}
#[must_use]
#[inline(always)]
pub fn with_timings(mut self, timings: TranscriptionTimings) -> Self {
self.set_timings(timings);
self
}
#[inline(always)]
pub fn set_timings(&mut self, timings: TranscriptionTimings) -> &mut Self {
self.timings = timings;
self
}
#[inline(always)]
pub const fn seek_time(&self) -> Option<f32> {
self.seek_time
}
#[must_use]
#[inline(always)]
pub const fn with_seek_time(mut self, seek_time: f32) -> Self {
self.set_seek_time(seek_time);
self
}
#[inline(always)]
pub const fn set_seek_time(&mut self, seek_time: f32) -> &mut Self {
self.seek_time = Some(seek_time);
self
}
#[must_use]
#[inline(always)]
pub const fn maybe_seek_time(mut self, seek_time: Option<f32>) -> Self {
self.update_seek_time(seek_time);
self
}
#[inline(always)]
pub const fn update_seek_time(&mut self, seek_time: Option<f32>) -> &mut Self {
self.seek_time = seek_time;
self
}
#[inline(always)]
pub const fn clear_seek_time(&mut self) -> &mut Self {
self.seek_time = None;
self
}
pub fn all_words(&self) -> Vec<WordTiming> {
self
.segments
.iter()
.flat_map(TranscriptionSegment::words_slice)
.cloned()
.collect()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DecodingResult {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "String::is_empty")
)]
language: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
language_probs: Vec<(String, f32)>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
observed_language: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
tokens: Vec<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
token_log_probs: Vec<(u32, f32)>,
#[cfg_attr(feature = "serde", serde(default))]
text: String,
#[cfg_attr(feature = "serde", serde(default))]
avg_logprob: f32,
#[cfg_attr(feature = "serde", serde(default))]
no_speech_prob: f32,
#[cfg_attr(feature = "serde", serde(default))]
temperature: f32,
#[cfg_attr(feature = "serde", serde(default))]
compression_ratio: f32,
#[cfg_attr(feature = "serde", serde(default))]
first_token_log_prob: f32,
#[cfg_attr(feature = "serde", serde(default))]
early_stopped: bool,
}
impl Default for DecodingResult {
fn default() -> Self {
Self::new()
}
}
impl DecodingResult {
pub const fn new() -> Self {
Self {
language: String::new(),
language_probs: Vec::new(),
observed_language: None,
tokens: Vec::new(),
token_log_probs: Vec::new(),
text: String::new(),
avg_logprob: 0.0,
no_speech_prob: 0.0,
temperature: 0.0,
compression_ratio: 0.0,
first_token_log_prob: 0.0,
early_stopped: false,
}
}
#[inline(always)]
pub fn language(&self) -> &str {
self.language.as_str()
}
#[must_use]
#[inline(always)]
pub fn with_language(mut self, language: impl Into<String>) -> Self {
self.set_language(language);
self
}
#[inline(always)]
pub fn set_language(&mut self, language: impl Into<String>) -> &mut Self {
self.language = language.into();
self
}
#[inline(always)]
pub const fn language_probs_slice(&self) -> &[(String, f32)] {
self.language_probs.as_slice()
}
#[must_use]
#[inline(always)]
pub fn with_language_probs(mut self, language_probs: impl Into<Vec<(String, f32)>>) -> Self {
self.set_language_probs(language_probs);
self
}
#[inline(always)]
pub fn set_language_probs(&mut self, language_probs: impl Into<Vec<(String, f32)>>) -> &mut Self {
self.language_probs = language_probs.into();
self
}
#[inline(always)]
pub fn observed_language(&self) -> Option<&str> {
self.observed_language.as_deref()
}
#[must_use]
#[inline(always)]
pub fn maybe_observed_language(mut self, observed_language: Option<String>) -> Self {
self.update_observed_language(observed_language);
self
}
#[inline(always)]
pub fn update_observed_language(&mut self, observed_language: Option<String>) -> &mut Self {
self.observed_language = observed_language;
self
}
#[inline(always)]
pub const fn tokens_slice(&self) -> &[u32] {
self.tokens.as_slice()
}
#[must_use]
#[inline(always)]
pub fn with_tokens(mut self, tokens: impl Into<Vec<u32>>) -> Self {
self.set_tokens(tokens);
self
}
#[inline(always)]
pub fn set_tokens(&mut self, tokens: impl Into<Vec<u32>>) -> &mut Self {
self.tokens = tokens.into();
self
}
#[inline(always)]
pub const fn token_log_probs_slice(&self) -> &[(u32, f32)] {
self.token_log_probs.as_slice()
}
#[must_use]
#[inline(always)]
pub fn with_token_log_probs(mut self, token_log_probs: impl Into<Vec<(u32, f32)>>) -> Self {
self.set_token_log_probs(token_log_probs);
self
}
#[inline(always)]
pub fn set_token_log_probs(&mut self, token_log_probs: impl Into<Vec<(u32, f32)>>) -> &mut Self {
self.token_log_probs = token_log_probs.into();
self
}
#[inline(always)]
pub fn text(&self) -> &str {
self.text.as_str()
}
#[must_use]
#[inline(always)]
pub fn with_text(mut self, text: impl Into<String>) -> Self {
self.set_text(text);
self
}
#[inline(always)]
pub fn set_text(&mut self, text: impl Into<String>) -> &mut Self {
self.text = text.into();
self
}
#[inline(always)]
pub const fn avg_logprob(&self) -> f32 {
self.avg_logprob
}
#[must_use]
#[inline(always)]
pub const fn with_avg_logprob(mut self, avg_logprob: f32) -> Self {
self.set_avg_logprob(avg_logprob);
self
}
#[inline(always)]
pub const fn set_avg_logprob(&mut self, avg_logprob: f32) -> &mut Self {
self.avg_logprob = avg_logprob;
self
}
#[inline(always)]
pub const fn no_speech_prob(&self) -> f32 {
self.no_speech_prob
}
#[must_use]
#[inline(always)]
pub const fn with_no_speech_prob(mut self, no_speech_prob: f32) -> Self {
self.set_no_speech_prob(no_speech_prob);
self
}
#[inline(always)]
pub const fn set_no_speech_prob(&mut self, no_speech_prob: f32) -> &mut Self {
self.no_speech_prob = no_speech_prob;
self
}
#[inline(always)]
pub const fn temperature(&self) -> f32 {
self.temperature
}
#[must_use]
#[inline(always)]
pub const fn with_temperature(mut self, temperature: f32) -> Self {
self.set_temperature(temperature);
self
}
#[inline(always)]
pub const fn set_temperature(&mut self, temperature: f32) -> &mut Self {
self.temperature = temperature;
self
}
#[inline(always)]
pub const fn compression_ratio(&self) -> f32 {
self.compression_ratio
}
#[must_use]
#[inline(always)]
pub const fn with_compression_ratio(mut self, compression_ratio: f32) -> Self {
self.set_compression_ratio(compression_ratio);
self
}
#[inline(always)]
pub const fn set_compression_ratio(&mut self, compression_ratio: f32) -> &mut Self {
self.compression_ratio = compression_ratio;
self
}
#[inline(always)]
pub const fn first_token_log_prob(&self) -> f32 {
self.first_token_log_prob
}
#[must_use]
#[inline(always)]
pub const fn with_first_token_log_prob(mut self, first_token_log_prob: f32) -> Self {
self.set_first_token_log_prob(first_token_log_prob);
self
}
#[inline(always)]
pub const fn set_first_token_log_prob(&mut self, first_token_log_prob: f32) -> &mut Self {
self.first_token_log_prob = first_token_log_prob;
self
}
#[inline(always)]
pub const fn early_stopped(&self) -> bool {
self.early_stopped
}
#[must_use]
#[inline(always)]
pub const fn maybe_early_stopped(mut self, early_stopped: bool) -> Self {
self.update_early_stopped(early_stopped);
self
}
#[inline(always)]
pub const fn update_early_stopped(&mut self, early_stopped: bool) -> &mut Self {
self.early_stopped = early_stopped;
self
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TranscriptionProgress {
#[cfg_attr(feature = "serde", serde(default))]
timings: TranscriptionTimings,
#[cfg_attr(feature = "serde", serde(default))]
text: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
tokens: Vec<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
temperature: Option<f32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
avg_logprob: Option<f32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
compression_ratio: Option<f32>,
#[cfg_attr(feature = "serde", serde(default))]
window_id: usize,
}
impl TranscriptionProgress {
pub fn new(
timings: TranscriptionTimings,
text: impl Into<String>,
tokens: impl Into<Vec<u32>>,
) -> Self {
Self {
timings,
text: text.into(),
tokens: tokens.into(),
temperature: None,
avg_logprob: None,
compression_ratio: None,
window_id: 0,
}
}
#[inline(always)]
pub const fn timings(&self) -> &TranscriptionTimings {
&self.timings
}
#[must_use]
#[inline(always)]
pub fn with_timings(mut self, timings: TranscriptionTimings) -> Self {
self.set_timings(timings);
self
}
#[inline(always)]
pub fn set_timings(&mut self, timings: TranscriptionTimings) -> &mut Self {
self.timings = timings;
self
}
#[inline(always)]
pub fn text(&self) -> &str {
self.text.as_str()
}
#[must_use]
#[inline(always)]
pub fn with_text(mut self, text: impl Into<String>) -> Self {
self.set_text(text);
self
}
#[inline(always)]
pub fn set_text(&mut self, text: impl Into<String>) -> &mut Self {
self.text = text.into();
self
}
#[inline(always)]
pub const fn tokens_slice(&self) -> &[u32] {
self.tokens.as_slice()
}
#[must_use]
#[inline(always)]
pub fn with_tokens(mut self, tokens: impl Into<Vec<u32>>) -> Self {
self.set_tokens(tokens);
self
}
#[inline(always)]
pub fn set_tokens(&mut self, tokens: impl Into<Vec<u32>>) -> &mut Self {
self.tokens = tokens.into();
self
}
#[inline(always)]
pub const fn temperature(&self) -> Option<f32> {
self.temperature
}
#[must_use]
#[inline(always)]
pub const fn with_temperature(mut self, temperature: f32) -> Self {
self.set_temperature(temperature);
self
}
#[inline(always)]
pub const fn set_temperature(&mut self, temperature: f32) -> &mut Self {
self.temperature = Some(temperature);
self
}
#[must_use]
#[inline(always)]
pub const fn maybe_temperature(mut self, temperature: Option<f32>) -> Self {
self.update_temperature(temperature);
self
}
#[inline(always)]
pub const fn update_temperature(&mut self, temperature: Option<f32>) -> &mut Self {
self.temperature = temperature;
self
}
#[inline(always)]
pub const fn clear_temperature(&mut self) -> &mut Self {
self.temperature = None;
self
}
#[inline(always)]
pub const fn avg_logprob(&self) -> Option<f32> {
self.avg_logprob
}
#[must_use]
#[inline(always)]
pub const fn with_avg_logprob(mut self, avg_logprob: f32) -> Self {
self.set_avg_logprob(avg_logprob);
self
}
#[inline(always)]
pub const fn set_avg_logprob(&mut self, avg_logprob: f32) -> &mut Self {
self.avg_logprob = Some(avg_logprob);
self
}
#[must_use]
#[inline(always)]
pub const fn maybe_avg_logprob(mut self, avg_logprob: Option<f32>) -> Self {
self.update_avg_logprob(avg_logprob);
self
}
#[inline(always)]
pub const fn update_avg_logprob(&mut self, avg_logprob: Option<f32>) -> &mut Self {
self.avg_logprob = avg_logprob;
self
}
#[inline(always)]
pub const fn clear_avg_logprob(&mut self) -> &mut Self {
self.avg_logprob = None;
self
}
#[inline(always)]
pub const fn compression_ratio(&self) -> Option<f32> {
self.compression_ratio
}
#[must_use]
#[inline(always)]
pub const fn with_compression_ratio(mut self, compression_ratio: f32) -> Self {
self.set_compression_ratio(compression_ratio);
self
}
#[inline(always)]
pub const fn set_compression_ratio(&mut self, compression_ratio: f32) -> &mut Self {
self.compression_ratio = Some(compression_ratio);
self
}
#[must_use]
#[inline(always)]
pub const fn maybe_compression_ratio(mut self, compression_ratio: Option<f32>) -> Self {
self.update_compression_ratio(compression_ratio);
self
}
#[inline(always)]
pub const fn update_compression_ratio(&mut self, compression_ratio: Option<f32>) -> &mut Self {
self.compression_ratio = compression_ratio;
self
}
#[inline(always)]
pub const fn clear_compression_ratio(&mut self) -> &mut Self {
self.compression_ratio = None;
self
}
#[inline(always)]
pub const fn window_id(&self) -> usize {
self.window_id
}
#[must_use]
#[inline(always)]
pub const fn with_window_id(mut self, window_id: usize) -> Self {
self.set_window_id(window_id);
self
}
#[inline(always)]
pub const fn set_window_id(&mut self, window_id: usize) -> &mut Self {
self.window_id = window_id;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::Display, derive_more::IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum FallbackReason {
FirstTokenLogProbThreshold,
CompressionRatioThreshold,
LogProbThreshold,
}
impl FallbackReason {
#[inline(always)]
pub const fn as_str(&self) -> &'static str {
match self {
Self::FirstTokenLogProbThreshold => "firstTokenLogProbThreshold",
Self::CompressionRatioThreshold => "compressionRatioThreshold",
Self::LogProbThreshold => "logProbThreshold",
}
}
}
pub fn needs_fallback(
first_token_log_prob_too_low: bool,
decoding: &DecodingResult,
options: &DecodingOptions,
) -> Option<FallbackReason> {
if first_token_log_prob_too_low {
return Some(FallbackReason::FirstTokenLogProbThreshold);
}
if let Some(threshold) = options.no_speech_threshold()
&& decoding.no_speech_prob() > threshold
{
return None;
}
if let Some(threshold) = options.compression_ratio_threshold()
&& decoding.compression_ratio() > threshold
{
return Some(FallbackReason::CompressionRatioThreshold);
}
if let Some(threshold) = options.logprob_threshold()
&& decoding.avg_logprob() < threshold
{
return Some(FallbackReason::LogProbThreshold);
}
None
}
pub fn format_segments(segments: &[TranscriptionSegment], with_timestamps: bool) -> Vec<String> {
segments
.iter()
.map(|segment| {
if with_timestamps {
format!(
"[{:.2} --> {:.2}] {}",
segment.start(),
segment.end(),
segment.text()
)
} else {
segment.text().to_string()
}
})
.collect()
}
fn sum_timing(results: &[TranscriptionResult], f: impl Fn(&TranscriptionTimings) -> f64) -> f64 {
results.iter().map(|r| f(r.timings())).sum()
}
fn max_timing(results: &[TranscriptionResult], f: impl Fn(&TranscriptionTimings) -> f64) -> f64 {
results
.iter()
.map(|r| f(r.timings()))
.max_by(f64::total_cmp)
.unwrap_or(0.0)
}
fn min_timing(results: &[TranscriptionResult], f: impl Fn(&TranscriptionTimings) -> f64) -> f64 {
results
.iter()
.map(|r| f(r.timings()))
.min_by(f64::total_cmp)
.unwrap_or(0.0)
}
pub fn merge_transcription_results(results: &[TranscriptionResult]) -> TranscriptionResult {
merge_results(results, false)
}
pub fn merge_transcription_results_with_options(
results: &[TranscriptionResult],
options: &DecodingOptions,
) -> TranscriptionResult {
merge_results(results, options.drop_blank_audio())
}
fn effective_span_knowledge(result: &TranscriptionResult) -> Option<SpanKnowledge> {
let survivor_extent = match result
.segments_slice()
.iter()
.map(TranscriptionSegment::id)
.max()
{
None => 0,
Some(max_local_id) => max_local_id.checked_add(1)?,
};
let carried = result.task_facts().decoded_span();
let floored = carried.lower_bound().max(survivor_extent);
Some(if carried.is_exact() && floored == carried.lower_bound() {
SpanKnowledge::Exact(floored)
} else {
SpanKnowledge::AtLeast(floored)
})
}
fn merge_results(results: &[TranscriptionResult], skip_empty_texts: bool) -> TranscriptionResult {
let text = results
.iter()
.map(TranscriptionResult::text)
.filter(|text| !(skip_empty_texts && text.is_empty()))
.collect::<Vec<_>>()
.join(" ");
let mut segments = Vec::new();
let mut id_base = 0usize;
for (result_index, result) in results.iter().enumerate() {
for (segment_index, segment) in result.segments_slice().iter().enumerate() {
let id = if skip_empty_texts {
id_base.checked_add(segment.id()).expect(
"drop_blank_audio segment-id mapping overflowed usize (a segment id near usize::MAX)",
)
} else {
result_index + segment_index
};
segments.push(segment.clone().with_id(id));
}
if skip_empty_texts {
id_base = effective_span_knowledge(result)
.and_then(|span| id_base.checked_add(span.lower_bound()))
.expect("drop_blank_audio segment-id base overflowed usize (a segment id near usize::MAX)");
}
}
let language = results.first().map_or_else(
|| DEFAULT_LANGUAGE_CODE.to_string(),
|first| first.language().to_string(),
);
let language_observations: Vec<LanguageObservation> = results
.iter()
.flat_map(|result| result.language_observations_slice().iter().cloned())
.collect();
let earliest_pipeline_start = min_timing(results, TranscriptionTimings::pipeline_start);
let latest_pipeline_end = results
.iter()
.map(|r| r.timings().pipeline_start() + r.timings().full_pipeline())
.max_by(f64::total_cmp)
.unwrap_or(0.0);
let user_pipeline_duration = if earliest_pipeline_start == DEFAULT_PIPELINE_TIME_SENTINEL {
f64::INFINITY
} else {
latest_pipeline_end - earliest_pipeline_start
};
let system_pipeline_duration = sum_timing(results, TranscriptionTimings::full_pipeline);
let mut timings = TranscriptionTimings::new();
timings
.set_model_loading(max_timing(results, TranscriptionTimings::model_loading))
.set_prewarm_load_time(max_timing(results, TranscriptionTimings::prewarm_load_time))
.set_encoder_load_time(max_timing(results, TranscriptionTimings::encoder_load_time))
.set_decoder_load_time(max_timing(results, TranscriptionTimings::decoder_load_time))
.set_tokenizer_load_time(max_timing(
results,
TranscriptionTimings::tokenizer_load_time,
))
.set_audio_loading(sum_timing(results, TranscriptionTimings::audio_loading))
.set_audio_processing(sum_timing(results, TranscriptionTimings::audio_processing))
.set_logmels(sum_timing(results, TranscriptionTimings::logmels))
.set_encoding(sum_timing(results, TranscriptionTimings::encoding))
.set_decoding_init(sum_timing(results, TranscriptionTimings::decoding_init))
.set_decoding_loop(sum_timing(results, TranscriptionTimings::decoding_loop))
.set_decoding_predictions(sum_timing(
results,
TranscriptionTimings::decoding_predictions,
))
.set_decoding_filtering(sum_timing(
results,
TranscriptionTimings::decoding_filtering,
))
.set_decoding_sampling(sum_timing(results, TranscriptionTimings::decoding_sampling))
.set_decoding_fallback(sum_timing(results, TranscriptionTimings::decoding_fallback))
.set_decoding_windowing(sum_timing(
results,
TranscriptionTimings::decoding_windowing,
))
.set_decoding_kv_caching(sum_timing(
results,
TranscriptionTimings::decoding_kv_caching,
))
.set_decoding_word_timestamps(sum_timing(
results,
TranscriptionTimings::decoding_word_timestamps,
))
.set_decoding_non_prediction(sum_timing(
results,
TranscriptionTimings::decoding_non_prediction,
))
.set_total_audio_processing_runs(sum_timing(
results,
TranscriptionTimings::total_audio_processing_runs,
))
.set_total_logmel_runs(sum_timing(results, TranscriptionTimings::total_logmel_runs))
.set_total_encoding_runs(sum_timing(
results,
TranscriptionTimings::total_encoding_runs,
))
.set_total_decoding_loops(sum_timing(
results,
TranscriptionTimings::total_decoding_loops,
))
.set_total_kv_update_runs(sum_timing(
results,
TranscriptionTimings::total_kv_update_runs,
))
.set_total_timestamp_alignment_runs(sum_timing(
results,
TranscriptionTimings::total_timestamp_alignment_runs,
))
.set_total_decoding_fallbacks(sum_timing(
results,
TranscriptionTimings::total_decoding_fallbacks,
))
.set_total_decoding_windows(sum_timing(
results,
TranscriptionTimings::total_decoding_windows,
))
.set_input_audio_seconds(sum_timing(
results,
TranscriptionTimings::input_audio_seconds,
))
.set_full_pipeline(user_pipeline_duration.min(system_pipeline_duration))
.set_pipeline_start(earliest_pipeline_start)
.set_first_token_time(min_timing(results, TranscriptionTimings::first_token_time));
let mut task_facts = TaskFactsAccumulator::new();
for result in results {
if skip_empty_texts {
let effective =
effective_span_knowledge(result).unwrap_or(SpanKnowledge::AtLeast(usize::MAX));
task_facts.merge(&result.task_facts().clone().with_decoded_span(effective));
} else {
task_facts.merge(result.task_facts());
}
}
let task_facts = task_facts.into_facts();
TranscriptionResult::new(text, segments, language, timings)
.with_language_observations(language_observations)
.with_task_facts(task_facts)
}
pub fn merge_transcription_results_with_words(
results: &[TranscriptionResult],
confirmed_words: &[WordTiming],
options: &DecodingOptions,
) -> TranscriptionResult {
let mut merged = merge_results(results, options.drop_blank_audio());
let text: String = confirmed_words.iter().map(WordTiming::word).collect();
merged.set_text(text);
merged
}
#[cfg(test)]
mod tests;