1use std::fmt;
2
3#[derive(Debug)]
5pub enum LLMError {
6 HttpError(String),
8 AuthError(String),
10 InvalidRequest(String),
12 ProviderError(String),
14 ResponseFormatError {
16 message: String,
17 raw_response: String,
18 },
19 Generic(String),
21 JsonError(String),
23 ToolConfigError(String),
25 NoToolSupport(String),
27}
28
29impl fmt::Display for LLMError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 LLMError::HttpError(e) => write!(f, "HTTP Error: {e}"),
33 LLMError::AuthError(e) => write!(f, "Auth Error: {e}"),
34 LLMError::InvalidRequest(e) => write!(f, "Invalid Request: {e}"),
35 LLMError::ProviderError(e) => write!(f, "Provider Error: {e}"),
36 LLMError::Generic(e) => write!(f, "Generic Error : {e}"),
37 LLMError::ResponseFormatError {
38 message,
39 raw_response,
40 } => {
41 write!(
42 f,
43 "Response Format Error: {message}. Raw response: {raw_response}"
44 )
45 }
46 LLMError::JsonError(e) => write!(f, "JSON Parse Error: {e}"),
47 LLMError::ToolConfigError(e) => write!(f, "Tool Configuration Error: {e}"),
48 LLMError::NoToolSupport(e) => write!(f, "No Tool Support: {e}"),
49 }
50 }
51}
52
53impl std::error::Error for LLMError {}
54
55#[cfg(not(target_arch = "wasm32"))]
57impl From<reqwest::Error> for LLMError {
58 fn from(err: reqwest::Error) -> Self {
59 LLMError::HttpError(err.to_string())
60 }
61}
62
63impl From<serde_json::Error> for LLMError {
64 fn from(err: serde_json::Error) -> Self {
65 LLMError::JsonError(format!(
66 "{} at line {} column {}",
67 err,
68 err.line(),
69 err.column()
70 ))
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use serde_json::Error as JsonError;
78 use std::error::Error;
79
80 #[test]
81 fn test_llm_error_display_http_error() {
82 let error = LLMError::HttpError("Connection failed".to_string());
83 assert_eq!(error.to_string(), "HTTP Error: Connection failed");
84 }
85
86 #[test]
87 fn test_llm_error_display_auth_error() {
88 let error = LLMError::AuthError("Invalid API key".to_string());
89 assert_eq!(error.to_string(), "Auth Error: Invalid API key");
90 }
91
92 #[test]
93 fn test_llm_error_display_invalid_request() {
94 let error = LLMError::InvalidRequest("Missing required parameter".to_string());
95 assert_eq!(
96 error.to_string(),
97 "Invalid Request: Missing required parameter"
98 );
99 }
100
101 #[test]
102 fn test_llm_error_display_provider_error() {
103 let error = LLMError::ProviderError("Model not found".to_string());
104 assert_eq!(error.to_string(), "Provider Error: Model not found");
105 }
106
107 #[test]
108 fn test_llm_error_display_generic_error() {
109 let error = LLMError::Generic("Something went wrong".to_string());
110 assert_eq!(error.to_string(), "Generic Error : Something went wrong");
111 }
112
113 #[test]
114 fn test_llm_error_display_response_format_error() {
115 let error = LLMError::ResponseFormatError {
116 message: "Invalid JSON".to_string(),
117 raw_response: "{invalid json}".to_string(),
118 };
119 assert_eq!(
120 error.to_string(),
121 "Response Format Error: Invalid JSON. Raw response: {invalid json}"
122 );
123 }
124
125 #[test]
126 fn test_llm_error_display_json_error() {
127 let error = LLMError::JsonError("Parse error at line 5 column 10".to_string());
128 assert_eq!(
129 error.to_string(),
130 "JSON Parse Error: Parse error at line 5 column 10"
131 );
132 }
133
134 #[test]
135 fn test_llm_error_display_tool_config_error() {
136 let error = LLMError::ToolConfigError("Invalid tool configuration".to_string());
137 assert_eq!(
138 error.to_string(),
139 "Tool Configuration Error: Invalid tool configuration"
140 );
141 }
142
143 #[test]
144 fn test_llm_error_is_error_trait() {
145 let error = LLMError::Generic("test error".to_string());
146 assert!(error.source().is_none());
147 }
148
149 #[test]
150 fn test_llm_error_debug_format() {
151 let error = LLMError::HttpError("test".to_string());
152 let debug_str = format!("{error:?}");
153 assert!(debug_str.contains("HttpError"));
154 assert!(debug_str.contains("test"));
155 }
156
157 #[test]
158 fn test_from_reqwest_error() {
159 let client = reqwest::Client::new();
161 let rt = tokio::runtime::Runtime::new().unwrap();
162 let reqwest_error = rt
163 .block_on(async {
164 client
165 .get("http://invalid-url-that-does-not-exist-12345.com/")
166 .timeout(std::time::Duration::from_millis(100))
167 .send()
168 .await
169 })
170 .unwrap_err();
171
172 let llm_error: LLMError = reqwest_error.into();
173
174 match llm_error {
175 LLMError::HttpError(msg) => {
176 assert!(!msg.is_empty());
177 }
178 _ => panic!("Expected HttpError"),
179 }
180 }
181
182 #[test]
183 fn test_from_serde_json_error() {
184 let json_str = r#"{"invalid": json}"#;
185 let json_error: JsonError =
186 serde_json::from_str::<serde_json::Value>(json_str).unwrap_err();
187
188 let llm_error: LLMError = json_error.into();
189
190 match llm_error {
191 LLMError::JsonError(msg) => {
192 assert!(msg.contains("line"));
193 assert!(msg.contains("column"));
194 }
195 _ => panic!("Expected JsonError"),
196 }
197 }
198
199 #[test]
200 fn test_error_variants_equality() {
201 let error1 = LLMError::HttpError("test".to_string());
202 let error2 = LLMError::HttpError("test".to_string());
203 let error3 = LLMError::HttpError("different".to_string());
204 let error4 = LLMError::AuthError("test".to_string());
205
206 assert_eq!(error1.to_string(), error2.to_string());
208 assert_ne!(error1.to_string(), error3.to_string());
209 assert_ne!(error1.to_string(), error4.to_string());
210 }
211
212 #[test]
213 fn test_response_format_error_fields() {
214 let error = LLMError::ResponseFormatError {
215 message: "Parse failed".to_string(),
216 raw_response: "raw content".to_string(),
217 };
218
219 let display_str = error.to_string();
220 assert!(display_str.contains("Parse failed"));
221 assert!(display_str.contains("raw content"));
222 }
223
224 #[test]
225 fn test_all_error_variants_have_display() {
226 let errors = vec![
227 LLMError::HttpError("http".to_string()),
228 LLMError::AuthError("auth".to_string()),
229 LLMError::InvalidRequest("invalid".to_string()),
230 LLMError::ProviderError("provider".to_string()),
231 LLMError::Generic("generic".to_string()),
232 LLMError::ResponseFormatError {
233 message: "format".to_string(),
234 raw_response: "raw".to_string(),
235 },
236 LLMError::JsonError("json".to_string()),
237 LLMError::ToolConfigError("tool".to_string()),
238 ];
239
240 for error in errors {
241 let display_str = error.to_string();
242 assert!(!display_str.is_empty());
243 }
244 }
245
246 #[test]
247 fn test_error_type_classification() {
248 let http_error = LLMError::HttpError("test".to_string());
250 match http_error {
251 LLMError::HttpError(_) => {}
252 _ => panic!("Expected HttpError"),
253 }
254
255 let auth_error = LLMError::AuthError("test".to_string());
256 match auth_error {
257 LLMError::AuthError(_) => {}
258 _ => panic!("Expected AuthError"),
259 }
260
261 let response_error = LLMError::ResponseFormatError {
262 message: "test".to_string(),
263 raw_response: "test".to_string(),
264 };
265 match response_error {
266 LLMError::ResponseFormatError { .. } => {}
267 _ => panic!("Expected ResponseFormatError"),
268 }
269 }
270}