1use thiserror::Error;
4
5#[derive(Debug, Error)]
7pub enum SapientError {
8 #[error("Shape mismatch: expected {expected:?}, got {got:?}")]
10 ShapeMismatch {
11 expected: Vec<usize>,
12 got: Vec<usize>,
13 },
14
15 #[error("Rank mismatch: expected {expected}, got {got}")]
16 RankMismatch { expected: usize, got: usize },
17
18 #[error("Type mismatch: expected {expected}, got {got}")]
19 TypeMismatch { expected: String, got: String },
20
21 #[error("Incompatible shapes for broadcasting: {lhs:?} and {rhs:?}")]
22 BroadcastError { lhs: Vec<usize>, rhs: Vec<usize> },
23
24 #[error("Graph contains a cycle — execution is impossible")]
26 CyclicGraph,
27
28 #[error("Node {0:?} not found in graph")]
29 NodeNotFound(String),
30
31 #[error("Graph validation failed: {0}")]
32 InvalidGraph(String),
33
34 #[error("Shape inference failed for op '{op}': {reason}")]
35 ShapeInferenceFailed { op: String, reason: String },
36
37 #[error("Backend '{backend}' does not support op '{op}'")]
39 UnsupportedOp { backend: String, op: String },
40
41 #[error("Backend error from '{backend}': {message}")]
42 BackendError { backend: String, message: String },
43
44 #[error("No suitable backend found for execution")]
45 NoBackendAvailable,
46
47 #[error("Allocation failed: requested {bytes} bytes (alignment {align})")]
49 AllocationFailed { bytes: usize, align: usize },
50
51 #[error("Buffer size mismatch: expected {expected} bytes, got {got}")]
52 BufferSizeMismatch { expected: usize, got: usize },
53
54 #[error("Memory pool exhausted — consider increasing pool capacity")]
55 PoolExhausted,
56
57 #[error("ONNX parse error: {0}")]
59 OnnxParseError(String),
60
61 #[error("GGUF parse error: {0}")]
62 GgufParseError(String),
63
64 #[error("Safetensors parse error: {0}")]
65 SafetensorsParseError(String),
66
67 #[error("Unsupported model format: {0}")]
68 UnsupportedFormat(String),
69
70 #[error("Model not found at path '{0}'")]
71 ModelNotFound(String),
72
73 #[error("IO error: {0}")]
74 Io(#[from] std::io::Error),
75
76 #[error("Request timed out (deadline exceeded)")]
78 DeadlineExceeded,
79
80 #[error("Batch scheduler is shut down")]
81 SchedulerShutdown,
82
83 #[error("Runtime is not initialized — call Session::new() first")]
84 UninitializedRuntime,
85
86 #[error("Telemetry export failed: {0}")]
88 TelemetryError(String),
89
90 #[error("Internal error: {0}")]
92 Internal(String),
93}
94
95pub type Result<T> = std::result::Result<T, SapientError>;
97
98impl SapientError {
101 pub fn backend(backend: impl Into<String>, message: impl Into<String>) -> Self {
103 Self::BackendError {
104 backend: backend.into(),
105 message: message.into(),
106 }
107 }
108
109 pub fn unsupported_op(backend: impl Into<String>, op: impl Into<String>) -> Self {
111 Self::UnsupportedOp {
112 backend: backend.into(),
113 op: op.into(),
114 }
115 }
116
117 pub fn internal(msg: impl Into<String>) -> Self {
119 Self::Internal(msg.into())
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn error_display() {
129 let e = SapientError::ShapeMismatch {
130 expected: vec![2, 3],
131 got: vec![2, 4],
132 };
133 let s = e.to_string();
134 assert!(s.contains("Shape mismatch"));
135 assert!(s.contains("[2, 3]"));
136 }
137
138 #[test]
139 fn from_io_error() {
140 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
141 let e: SapientError = io_err.into();
142 assert!(matches!(e, SapientError::Io(_)));
143 }
144}