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 User,
27}
28
29impl std::fmt::Display for ErrorKind {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 ErrorKind::Llm => write!(f, "Llm"),
33 ErrorKind::Agent => write!(f, "Agent"),
34 ErrorKind::Runtime => write!(f, "Runtime"),
35 ErrorKind::Tool => write!(f, "Tool"),
36 ErrorKind::User => write!(f, "User"),
37 }
38 }
39}
40
41#[derive(Debug, Error)]
46pub enum SageError {
47 #[error("LLM error: {0}")]
49 Llm(String),
50
51 #[error("Agent error: {0}")]
53 Agent(String),
54
55 #[error("Type error: expected {expected}, got {got}")]
57 Type { expected: String, got: String },
58
59 #[error("HTTP error: {0}")]
61 Http(#[from] reqwest::Error),
62
63 #[error("JSON error: {0}")]
65 Json(#[from] serde_json::Error),
66
67 #[error("Agent task failed: {0}")]
69 JoinError(String),
70
71 #[error("Tool error: {0}")]
73 Tool(String),
74
75 #[error("{0}")]
77 User(String),
78}
79
80impl SageError {
81 #[must_use]
85 pub fn message(&self) -> String {
86 self.to_string()
87 }
88
89 #[must_use]
93 pub fn kind(&self) -> ErrorKind {
94 match self {
95 SageError::Llm(_) | SageError::Json(_) => ErrorKind::Llm,
96 SageError::Agent(_) | SageError::JoinError(_) => ErrorKind::Agent,
97 SageError::Type { .. } => ErrorKind::Runtime,
98 SageError::Http(_) | SageError::Tool(_) => ErrorKind::Tool,
100 SageError::User(_) => ErrorKind::User,
101 }
102 }
103
104 #[must_use]
106 pub fn llm(msg: impl Into<String>) -> Self {
107 SageError::Llm(msg.into())
108 }
109
110 #[must_use]
112 pub fn agent(msg: impl Into<String>) -> Self {
113 SageError::Agent(msg.into())
114 }
115
116 #[must_use]
118 pub fn type_error(expected: impl Into<String>, got: impl Into<String>) -> Self {
119 SageError::Type {
120 expected: expected.into(),
121 got: got.into(),
122 }
123 }
124
125 #[must_use]
127 pub fn tool(msg: impl Into<String>) -> Self {
128 SageError::Tool(msg.into())
129 }
130
131 #[must_use]
133 pub fn user(msg: impl Into<String>) -> Self {
134 SageError::User(msg.into())
135 }
136}
137
138impl From<tokio::task::JoinError> for SageError {
139 fn from(e: tokio::task::JoinError) -> Self {
140 SageError::JoinError(e.to_string())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn error_kind_classification() {
150 assert_eq!(SageError::llm("test").kind(), ErrorKind::Llm);
151 assert_eq!(SageError::agent("test").kind(), ErrorKind::Agent);
152 assert_eq!(
153 SageError::type_error("Int", "String").kind(),
154 ErrorKind::Runtime
155 );
156 }
157
158 #[test]
159 fn error_message() {
160 let err = SageError::llm("inference failed");
161 assert_eq!(err.message(), "LLM error: inference failed");
162 }
163
164 #[test]
165 fn error_kind_display() {
166 assert_eq!(format!("{}", ErrorKind::Llm), "Llm");
167 assert_eq!(format!("{}", ErrorKind::Agent), "Agent");
168 assert_eq!(format!("{}", ErrorKind::Runtime), "Runtime");
169 assert_eq!(format!("{}", ErrorKind::Tool), "Tool");
170 }
171
172 #[test]
173 fn tool_error_classification() {
174 assert_eq!(SageError::tool("http failed").kind(), ErrorKind::Tool);
175 assert_eq!(SageError::tool("timeout").message(), "Tool error: timeout");
176 }
177}