1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Error types: one enum per pipeline stage plus the crate-wide umbrella
//! [`SkadooshError`] and the [`Result`] alias used throughout the crate.
use thiserror::Error;
/// Audio capture, playback, and resampling failures.
#[derive(Debug, Error)]
pub enum AudioError {
/// No input/output device matched the requested name (or no default
/// device exists, e.g. on a headless machine).
#[error("no suitable audio device found")]
NoDevice,
/// The device rejected or could not provide a usable stream configuration.
#[error("failed to negotiate audio stream config: {0}")]
StreamConfig(String),
/// Building or starting the cpal stream failed.
#[error("failed to build audio stream: {0}")]
StreamBuild(String),
/// Resampler setup or processing failed.
#[error("resampling failed: {0}")]
Resample(String),
}
/// VAD model loading and inference failures.
#[derive(Debug, Error)]
pub enum VadError {
/// The Silero ONNX model could not be loaded.
#[error("failed to load VAD model: {0}")]
ModelLoad(String),
/// An `ort` inference call failed.
#[error("VAD inference failed: {0}")]
Inference(String),
/// A frame with a length other than `FRAME_LEN` (512) was submitted.
#[error("VAD frames must be {expected} samples, got {actual}")]
BadFrameLen {
/// Required frame length in samples.
expected: usize,
/// Frame length that was submitted.
actual: usize,
},
}
/// Speech-to-text failures.
#[derive(Debug, Error)]
pub enum SttError {
/// The whisper.cpp model could not be loaded.
#[error("failed to load STT model: {0}")]
ModelLoad(String),
/// whisper-rs failed to transcribe a segment.
#[error("transcription failed: {0}")]
Transcribe(String),
/// The dedicated STT worker thread exited or its channel closed. During
/// shutdown drain this is benign and must not reach the fatal-error mpsc.
#[error("STT worker thread is gone")]
WorkerGone,
}
/// Streaming LLM client failures.
#[derive(Debug, Error)]
pub enum LlmError {
/// Transport-level HTTP failure (connect, read, TLS, ...).
#[error("LLM HTTP error: {0}")]
Http(#[from] reqwest::Error),
/// SSE framing/buffering failure while reading the stream.
#[error("LLM SSE stream error: {0}")]
Sse(String),
/// The server answered with a non-success status code.
#[error("LLM API returned status {status}: {body}")]
Api {
/// HTTP status code.
status: u16,
/// Response body (best effort, may be truncated).
body: String,
},
/// The turn's cancellation token fired (barge-in or shutdown). The
/// in-flight clause and the partial assistant reply are discarded.
#[error("LLM request cancelled")]
Cancelled,
}
/// Text-to-speech failures.
#[derive(Debug, Error)]
pub enum TtsError {
/// The Kokoro ONNX model could not be loaded.
#[error("failed to load TTS model: {0}")]
ModelLoad(String),
/// espeak-ng phonemization failed or the binary is missing.
#[error("phonemization failed: {0}")]
Phonemize(String),
/// An `ort` inference call failed.
#[error("TTS inference failed: {0}")]
Inference(String),
/// The voices bank is missing or does not contain the requested voice.
#[error("TTS voices unavailable: {0}")]
MissingVoices(String),
}
/// RAG (retrieval-augmented generation) failures: embedding model loading,
/// inference, and document ingestion.
#[derive(Debug, Error)]
pub enum RagError {
/// The embedding ONNX model or its vocab could not be loaded.
#[error("failed to load RAG embedding model: {0}")]
ModelLoad(String),
/// An `ort` embedding inference call failed.
#[error("RAG inference failed: {0}")]
Inference(String),
/// Reading or walking the `--rag-dir` document directory failed.
#[error("RAG document loading failed: {0}")]
Io(String),
}
/// Crate-wide umbrella error; every stage error converts into it.
#[derive(Debug, Error)]
pub enum SkadooshError {
/// Audio stage failure.
#[error(transparent)]
Audio(#[from] AudioError),
/// VAD stage failure.
#[error(transparent)]
Vad(#[from] VadError),
/// STT stage failure.
#[error(transparent)]
Stt(#[from] SttError),
/// LLM stage failure.
#[error(transparent)]
Llm(#[from] LlmError),
/// TTS stage failure.
#[error(transparent)]
Tts(#[from] TtsError),
/// RAG stage failure.
#[error(transparent)]
Rag(#[from] RagError),
/// Escape hatch for errors at task boundaries (I/O, wav decoding, ...).
#[error(transparent)]
Other(#[from] anyhow::Error),
}
/// Crate-wide result alias.
pub type Result<T> = std::result::Result<T, SkadooshError>;