Skip to main content

sapient_core/
error.rs

1//! `SapientError` — unified error type for the entire SAPIENT engine.
2
3use thiserror::Error;
4
5/// The central error enum for all SAPIENT operations.
6#[derive(Debug, Error)]
7pub enum SapientError {
8    // ── Shape / Type errors ──────────────────────────────────────────────────
9    #[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    // ── Graph / IR errors ────────────────────────────────────────────────────
25    #[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    // ── Backend errors ───────────────────────────────────────────────────────
38    #[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    // ── Memory / allocation errors ───────────────────────────────────────────
48    #[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    // ── Format / IO errors ───────────────────────────────────────────────────
58    #[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    // ── Scheduling / runtime errors ──────────────────────────────────────────
77    #[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    // ── Telemetry errors ─────────────────────────────────────────────────────
87    #[error("Telemetry export failed: {0}")]
88    TelemetryError(String),
89
90    // ── Catch-all ────────────────────────────────────────────────────────────
91    #[error("Internal error: {0}")]
92    Internal(String),
93}
94
95/// Convenience alias used throughout the codebase.
96pub type Result<T> = std::result::Result<T, SapientError>;
97
98// ── Helper constructors ──────────────────────────────────────────────────────
99
100impl SapientError {
101    /// Create a backend error with context.
102    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    /// Create an unsupported-op error.
110    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    /// Create an internal error.
118    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}