Skip to main content

sage_runtime/
error.rs

1//! Error types for the Sage runtime.
2//!
3//! RFC-0007: This module provides the `SageError` type and `ErrorKind` enum
4//! that are exposed to Sage programs through the `Error` type.
5
6use thiserror::Error;
7
8/// Result type for Sage operations.
9pub type SageResult<T> = Result<T, SageError>;
10
11/// RFC-0007: Error kind classification for Sage errors.
12///
13/// This enum is exposed to Sage programs as `ErrorKind` and can be matched
14/// in `on error(e)` handlers via `e.kind`.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum ErrorKind {
17    /// Error from LLM inference (network, parsing, rate limits).
18    Llm,
19    /// Error from agent execution (panics, message failures).
20    Agent,
21    /// Runtime errors (type mismatches, I/O, etc.).
22    Runtime,
23}
24
25impl std::fmt::Display for ErrorKind {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        match self {
28            ErrorKind::Llm => write!(f, "Llm"),
29            ErrorKind::Agent => write!(f, "Agent"),
30            ErrorKind::Runtime => write!(f, "Runtime"),
31        }
32    }
33}
34
35/// Error type for Sage runtime errors.
36///
37/// RFC-0007: This is exposed to Sage programs as the `Error` type with
38/// `.message` and `.kind` field accessors.
39#[derive(Debug, Error)]
40pub enum SageError {
41    /// Error from LLM inference.
42    #[error("LLM error: {0}")]
43    Llm(String),
44
45    /// Error from agent execution.
46    #[error("Agent error: {0}")]
47    Agent(String),
48
49    /// Type mismatch at runtime.
50    #[error("Type error: expected {expected}, got {got}")]
51    Type { expected: String, got: String },
52
53    /// HTTP request error.
54    #[error("HTTP error: {0}")]
55    Http(#[from] reqwest::Error),
56
57    /// JSON parsing error.
58    #[error("JSON error: {0}")]
59    Json(#[from] serde_json::Error),
60
61    /// Agent task was cancelled or panicked.
62    #[error("Agent task failed: {0}")]
63    JoinError(String),
64}
65
66impl SageError {
67    /// RFC-0007: Get the error message as a String.
68    ///
69    /// This is exposed to Sage programs as `e.message`.
70    #[must_use]
71    pub fn message(&self) -> String {
72        self.to_string()
73    }
74
75    /// RFC-0007: Get the error kind classification.
76    ///
77    /// This is exposed to Sage programs as `e.kind`.
78    #[must_use]
79    pub fn kind(&self) -> ErrorKind {
80        match self {
81            SageError::Llm(_) | SageError::Http(_) | SageError::Json(_) => ErrorKind::Llm,
82            SageError::Agent(_) | SageError::JoinError(_) => ErrorKind::Agent,
83            SageError::Type { .. } => ErrorKind::Runtime,
84        }
85    }
86
87    /// Create an LLM error with a message.
88    #[must_use]
89    pub fn llm(msg: impl Into<String>) -> Self {
90        SageError::Llm(msg.into())
91    }
92
93    /// Create an agent error with a message.
94    #[must_use]
95    pub fn agent(msg: impl Into<String>) -> Self {
96        SageError::Agent(msg.into())
97    }
98
99    /// Create a type error.
100    #[must_use]
101    pub fn type_error(expected: impl Into<String>, got: impl Into<String>) -> Self {
102        SageError::Type {
103            expected: expected.into(),
104            got: got.into(),
105        }
106    }
107}
108
109impl From<tokio::task::JoinError> for SageError {
110    fn from(e: tokio::task::JoinError) -> Self {
111        SageError::JoinError(e.to_string())
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn error_kind_classification() {
121        assert_eq!(SageError::llm("test").kind(), ErrorKind::Llm);
122        assert_eq!(SageError::agent("test").kind(), ErrorKind::Agent);
123        assert_eq!(
124            SageError::type_error("Int", "String").kind(),
125            ErrorKind::Runtime
126        );
127    }
128
129    #[test]
130    fn error_message() {
131        let err = SageError::llm("inference failed");
132        assert_eq!(err.message(), "LLM error: inference failed");
133    }
134
135    #[test]
136    fn error_kind_display() {
137        assert_eq!(format!("{}", ErrorKind::Llm), "Llm");
138        assert_eq!(format!("{}", ErrorKind::Agent), "Agent");
139        assert_eq!(format!("{}", ErrorKind::Runtime), "Runtime");
140    }
141}