1#[derive(thiserror::Error, Debug)]
5pub enum LlmConnectorError {
6 #[error("Authentication failed: {0}")]
8 AuthenticationError(String),
9
10 #[error("Rate limit exceeded: {0}")]
12 RateLimitError(String),
13
14 #[error("Network error: {0}")]
16 NetworkError(String),
17
18 #[error("Invalid request: {0}")]
20 InvalidRequest(String),
21
22 #[error("Unsupported model: {0}")]
24 UnsupportedModel(String),
25
26 #[error("Provider error: {0}")]
28 ProviderError(String),
29
30 #[error("Permission denied: {0}")]
32 PermissionError(String),
33
34 #[error("Not found: {0}")]
36 NotFoundError(String),
37
38 #[error("Server error: {0}")]
40 ServerError(String),
41
42 #[error("Timeout error: {0}")]
44 TimeoutError(String),
45
46 #[error("Connection error: {0}")]
48 ConnectionError(String),
49
50 #[error("Max retries exceeded: {0}")]
52 MaxRetriesExceeded(String),
53
54 #[error("Parse error: {0}")]
56 ParseError(String),
57
58 #[error("Configuration error: {0}")]
60 ConfigError(String),
61
62 #[error("Streaming error: {0}")]
64 StreamingError(String),
65
66 #[error("Streaming not supported: {0}")]
68 StreamingNotSupported(String),
69
70 #[error("Unsupported operation: {0}")]
72 UnsupportedOperation(String),
73
74 #[error("API error: {0}")]
76 ApiError(String),
77
78 #[error("JSON parsing error: {0}")]
80 JsonError(#[from] serde_json::Error),
81
82 #[cfg(feature = "reqwest")]
84 #[error("HTTP error: {0}")]
85 HttpError(#[from] reqwest::Error),
86}
87
88impl LlmConnectorError {
89 pub fn is_retryable(&self) -> bool {
91 matches!(
92 self,
93 LlmConnectorError::NetworkError(_)
94 | LlmConnectorError::RateLimitError(_)
95 | LlmConnectorError::ProviderError(_)
96 )
97 }
98
99 pub fn status_code(&self) -> u16 {
101 match self {
102 LlmConnectorError::AuthenticationError(_) => 401,
103 LlmConnectorError::RateLimitError(_) => 429,
104 LlmConnectorError::InvalidRequest(_) => 400,
105 LlmConnectorError::UnsupportedModel(_) => 400,
106 LlmConnectorError::ConfigError(_) => 500,
107 LlmConnectorError::JsonError(_) => 400,
108 LlmConnectorError::NetworkError(_) => 502,
109 LlmConnectorError::ProviderError(_) => 502,
110 LlmConnectorError::StreamingError(_) => 500,
111 LlmConnectorError::StreamingNotSupported(_) => 501,
112 LlmConnectorError::PermissionError(_) => 403,
113 LlmConnectorError::NotFoundError(_) => 404,
114 LlmConnectorError::ServerError(_) => 500,
115 LlmConnectorError::TimeoutError(_) => 408,
116 LlmConnectorError::ConnectionError(_) => 502,
117 LlmConnectorError::ParseError(_) => 400,
118 LlmConnectorError::UnsupportedOperation(_) => 501,
119 LlmConnectorError::ApiError(_) => 500,
120 LlmConnectorError::HttpError(_) => 502,
121 LlmConnectorError::MaxRetriesExceeded(_) => 503,
122 }
123 }
124
125 pub fn from_status_code(status: u16, message: String) -> Self {
127 match status {
128 401 | 403 => LlmConnectorError::AuthenticationError(message),
129 429 => LlmConnectorError::RateLimitError(message),
130 400 => LlmConnectorError::InvalidRequest(message),
131 _ if status >= 500 => LlmConnectorError::ProviderError(message),
132 _ => LlmConnectorError::NetworkError(message),
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::LlmConnectorError;
140
141 #[test]
142 fn test_status_codes() {
143 assert_eq!(
144 LlmConnectorError::AuthenticationError("test".to_string()).status_code(),
145 401
146 );
147 assert_eq!(
148 LlmConnectorError::RateLimitError("test".to_string()).status_code(),
149 429
150 );
151 assert_eq!(
152 LlmConnectorError::InvalidRequest("test".to_string()).status_code(),
153 400
154 );
155 assert_eq!(
156 LlmConnectorError::UnsupportedModel("test".to_string()).status_code(),
157 400
158 );
159 assert_eq!(
160 LlmConnectorError::NetworkError("test".to_string()).status_code(),
161 502
162 );
163 }
164
165 #[test]
166 fn test_retryable() {
167 assert!(LlmConnectorError::NetworkError("test".to_string()).is_retryable());
168 assert!(LlmConnectorError::RateLimitError("test".to_string()).is_retryable());
169 assert!(LlmConnectorError::ProviderError("test".to_string()).is_retryable());
170
171 assert!(!LlmConnectorError::AuthenticationError("test".to_string()).is_retryable());
172 assert!(!LlmConnectorError::InvalidRequest("test".to_string()).is_retryable());
173 assert!(!LlmConnectorError::UnsupportedModel("test".to_string()).is_retryable());
174 }
175
176 #[test]
177 fn test_from_status_code() {
178 assert!(matches!(
179 LlmConnectorError::from_status_code(401, "test".to_string()),
180 LlmConnectorError::AuthenticationError(_)
181 ));
182 assert!(matches!(
183 LlmConnectorError::from_status_code(429, "test".to_string()),
184 LlmConnectorError::RateLimitError(_)
185 ));
186 assert!(matches!(
187 LlmConnectorError::from_status_code(400, "test".to_string()),
188 LlmConnectorError::InvalidRequest(_)
189 ));
190 }
191}