use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidState {
expected: &'static str,
actual: &'static str,
}
impl InvalidState {
#[inline(always)]
pub const fn new(expected: &'static str, actual: &'static str) -> Self {
Self { expected, actual }
}
#[inline(always)]
pub const fn expected(&self) -> &'static str {
self.expected
}
#[inline(always)]
pub const fn actual(&self) -> &'static str {
self.actual
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModelName {
name: String,
reason: &'static str,
}
impl ModelName {
#[inline(always)]
pub const fn new(name: String, reason: &'static str) -> Self {
Self { name, reason }
}
#[inline(always)]
pub fn name(&self) -> &str {
&self.name
}
#[inline(always)]
pub const fn reason(&self) -> &'static str {
self.reason
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ModelError {
#[error("model not found (searched {0:?})")]
NotFound(Vec<PathBuf>),
#[error("model is in state `{}`, expected `{}`", .0.actual(), .0.expected())]
InvalidState(InvalidState),
#[error("failed to load model: {0}")]
Load(#[from] crate::LoadError),
#[error("model info name must not be empty")]
EmptyName,
#[error("model name `{}` {}", .0.name(), .0.reason())]
ModelName(ModelName),
#[error("invalid support config: {0}")]
InvalidSupportConfig(String),
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum TokenizerError {
#[error("tokenizer file not found (searched {0:?})")]
FileNotFound(Vec<PathBuf>),
#[error("tokenizer backend failed: {0}")]
Backend(#[from] tokenizers::Error),
#[error("tokenizer vocabulary is missing required token `{0}`")]
MissingToken(&'static str),
}
#[derive(Debug, Clone, PartialEq)]
pub struct WindowTooLarge {
got: usize,
max: usize,
}
impl WindowTooLarge {
#[inline(always)]
pub const fn new(got: usize, max: usize) -> Self {
Self { got, max }
}
#[inline(always)]
pub const fn got(&self) -> usize {
self.got
}
#[inline(always)]
pub const fn max(&self) -> usize {
self.max
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct InvalidClipRange {
start: f32,
end: f32,
}
impl InvalidClipRange {
#[inline(always)]
pub const fn new(start: f32, end: f32) -> Self {
Self { start, end }
}
#[inline(always)]
pub const fn start(&self) -> f32 {
self.start
}
#[inline(always)]
pub const fn end(&self) -> f32 {
self.end
}
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum AudioError {
#[error("audio window of {} samples exceeds the maximum of {}", .0.got(), .0.max())]
WindowTooLarge(WindowTooLarge),
#[error("audio input is empty")]
EmptyInput,
#[error("invalid clip range: start {}, end {}", .0.start(), .0.end())]
InvalidClipRange(InvalidClipRange),
}
#[derive(Debug)]
pub struct LogitsShape {
expected: usize,
actual: usize,
}
impl LogitsShape {
#[inline(always)]
pub const fn new(expected: usize, actual: usize) -> Self {
Self { expected, actual }
}
#[inline(always)]
pub const fn expected(&self) -> usize {
self.expected
}
#[inline(always)]
pub const fn actual(&self) -> usize {
self.actual
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum DecodeError {
#[error("decoder prediction failed: {0}")]
Prediction(#[from] crate::PredictionError),
#[error("decoder tensor failed: {0}")]
Tensor(#[from] crate::TensorError),
#[error("logits shape mismatch: expected {}, got {}", .0.expected(), .0.actual())]
LogitsShape(LogitsShape),
#[error("decoder output is missing cross-attention alignment data")]
MissingAlignment,
#[error("backend failure: {0}")]
Backend(#[from] crate::audio::whisper::backend::BackendError),
#[error("tokenizer decode failed: {0}")]
Tokenizer(#[from] TokenizerError),
#[error(
"logits filter cannot mask token {} in a {}-wide vocabulary",
.0.token(), .0.vocab()
)]
UnmaskableToken(crate::audio::whisper::decode::filter::UnmaskableToken),
}
#[derive(Debug)]
pub struct InvalidAlignmentShape {
rows: usize,
cols: usize,
len: usize,
}
#[allow(clippy::len_without_is_empty)]
impl InvalidAlignmentShape {
#[inline(always)]
pub const fn new(rows: usize, cols: usize, len: usize) -> Self {
Self { rows, cols, len }
}
#[inline(always)]
pub const fn rows(&self) -> usize {
self.rows
}
#[inline(always)]
pub const fn cols(&self) -> usize {
self.cols
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.len
}
}
#[derive(Debug, thiserror::Error)]
#[error(
"cannot measure this host's CoreVideo Float16 row pitch for the {rows} x {cols} alignment \
gather, so the Swift-parity gather cannot be reproduced (select \
`AlignmentGather::Complete` to gather every row in full instead): {source}"
)]
pub struct AlignmentPitchUnavailable {
rows: usize,
cols: usize,
#[source]
source: crate::TensorError,
}
impl AlignmentPitchUnavailable {
#[inline(always)]
pub const fn new(rows: usize, cols: usize, source: crate::TensorError) -> Self {
Self { rows, cols, source }
}
#[inline(always)]
pub const fn rows(&self) -> usize {
self.rows
}
#[inline(always)]
pub const fn cols(&self) -> usize {
self.cols
}
#[inline(always)]
pub const fn source(&self) -> &crate::TensorError {
&self.source
}
}
#[derive(Debug)]
pub struct AlignmentPitchUnexpectedLayout {
rows: usize,
cols: usize,
strides: Vec<usize>,
}
impl AlignmentPitchUnexpectedLayout {
#[inline(always)]
pub const fn new(rows: usize, cols: usize, strides: Vec<usize>) -> Self {
Self {
rows,
cols,
strides,
}
}
#[inline(always)]
pub const fn rows(&self) -> usize {
self.rows
}
#[inline(always)]
pub const fn cols(&self) -> usize {
self.cols
}
#[inline(always)]
pub fn strides(&self) -> &[usize] {
&self.strides
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SegmentError {
#[error(
"invalid alignment matrix shape: {} rows x {} cols, but data has {} elements",
.0.rows(),
.0.cols(),
.0.len()
)]
InvalidAlignmentShape(InvalidAlignmentShape),
#[error(transparent)]
AlignmentPitchUnavailable(#[from] AlignmentPitchUnavailable),
#[error(
"this host's CoreVideo Float16 surface for the {} x {} alignment gather reports \
element strides {:?}, which is not the row-padded row-major layout the Swift-parity \
gather models (select `AlignmentGather::Complete` to gather every row in full instead)",
.0.rows(),
.0.cols(),
.0.strides()
)]
AlignmentPitchUnexpectedLayout(AlignmentPitchUnexpectedLayout),
#[error("tokenizer decode failed: {0}")]
Tokenizer(#[from] TokenizerError),
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum VadError {
#[error("voice-activity detection failed: {0}")]
Detection(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum TranscribeError {
#[error("model error: {0}")]
Model(#[from] ModelError),
#[error("tokenizer error: {0}")]
Tokenizer(#[from] TokenizerError),
#[error("audio error: {0}")]
Audio(#[from] AudioError),
#[error("decode error: {0}")]
Decode(#[from] DecodeError),
#[error("segment error: {0}")]
Segment(#[from] SegmentError),
#[error("vad error: {0}")]
Vad(#[from] VadError),
}
#[cfg(test)]
mod tests;