1use thiserror::Error;
7
8pub type SageResult<T> = Result<T, SageError>;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum ErrorKind {
17 Llm,
19 Agent,
21 Runtime,
23 Tool,
25}
26
27impl std::fmt::Display for ErrorKind {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 ErrorKind::Llm => write!(f, "Llm"),
31 ErrorKind::Agent => write!(f, "Agent"),
32 ErrorKind::Runtime => write!(f, "Runtime"),
33 ErrorKind::Tool => write!(f, "Tool"),
34 }
35 }
36}
37
38#[derive(Debug, Error)]
43pub enum SageError {
44 #[error("LLM error: {0}")]
46 Llm(String),
47
48 #[error("Agent error: {0}")]
50 Agent(String),
51
52 #[error("Type error: expected {expected}, got {got}")]
54 Type { expected: String, got: String },
55
56 #[error("HTTP error: {0}")]
58 Http(#[from] reqwest::Error),
59
60 #[error("JSON error: {0}")]
62 Json(#[from] serde_json::Error),
63
64 #[error("Agent task failed: {0}")]
66 JoinError(String),
67
68 #[error("Tool error: {0}")]
70 Tool(String),
71}
72
73impl SageError {
74 #[must_use]
78 pub fn message(&self) -> String {
79 self.to_string()
80 }
81
82 #[must_use]
86 pub fn kind(&self) -> ErrorKind {
87 match self {
88 SageError::Llm(_) | SageError::Json(_) => ErrorKind::Llm,
89 SageError::Agent(_) | SageError::JoinError(_) => ErrorKind::Agent,
90 SageError::Type { .. } => ErrorKind::Runtime,
91 SageError::Http(_) | SageError::Tool(_) => ErrorKind::Tool,
93 }
94 }
95
96 #[must_use]
98 pub fn llm(msg: impl Into<String>) -> Self {
99 SageError::Llm(msg.into())
100 }
101
102 #[must_use]
104 pub fn agent(msg: impl Into<String>) -> Self {
105 SageError::Agent(msg.into())
106 }
107
108 #[must_use]
110 pub fn type_error(expected: impl Into<String>, got: impl Into<String>) -> Self {
111 SageError::Type {
112 expected: expected.into(),
113 got: got.into(),
114 }
115 }
116
117 #[must_use]
119 pub fn tool(msg: impl Into<String>) -> Self {
120 SageError::Tool(msg.into())
121 }
122}
123
124impl From<tokio::task::JoinError> for SageError {
125 fn from(e: tokio::task::JoinError) -> Self {
126 SageError::JoinError(e.to_string())
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn error_kind_classification() {
136 assert_eq!(SageError::llm("test").kind(), ErrorKind::Llm);
137 assert_eq!(SageError::agent("test").kind(), ErrorKind::Agent);
138 assert_eq!(
139 SageError::type_error("Int", "String").kind(),
140 ErrorKind::Runtime
141 );
142 }
143
144 #[test]
145 fn error_message() {
146 let err = SageError::llm("inference failed");
147 assert_eq!(err.message(), "LLM error: inference failed");
148 }
149
150 #[test]
151 fn error_kind_display() {
152 assert_eq!(format!("{}", ErrorKind::Llm), "Llm");
153 assert_eq!(format!("{}", ErrorKind::Agent), "Agent");
154 assert_eq!(format!("{}", ErrorKind::Runtime), "Runtime");
155 assert_eq!(format!("{}", ErrorKind::Tool), "Tool");
156 }
157
158 #[test]
159 fn tool_error_classification() {
160 assert_eq!(SageError::tool("http failed").kind(), ErrorKind::Tool);
161 assert_eq!(SageError::tool("timeout").message(), "Tool error: timeout");
162 }
163}