#[derive(thiserror::Error, Debug)]
pub enum LlmConnectorError {
#[error("Authentication failed: {0}")]
AuthenticationError(String),
#[error("Rate limit exceeded: {0}")]
RateLimitError(String),
#[error("Network error: {0}")]
NetworkError(String),
#[error("Invalid request: {0}")]
InvalidRequest(String),
#[error("Unsupported model: {0}")]
UnsupportedModel(String),
#[error("Provider error: {0}")]
ProviderError(String),
#[error("Permission denied: {0}")]
PermissionError(String),
#[error("Not found: {0}")]
NotFoundError(String),
#[error("Server error: {0}")]
ServerError(String),
#[error("Timeout error: {0}")]
TimeoutError(String),
#[error("Connection error: {0}")]
ConnectionError(String),
#[error("Max retries exceeded: {0}")]
MaxRetriesExceeded(String),
#[error("Parse error: {0}")]
ParseError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Streaming error: {0}")]
StreamingError(String),
#[error("Unsupported operation: {0}")]
UnsupportedOperation(String),
#[error("JSON parsing error: {0}")]
JsonError(#[from] serde_json::Error),
#[cfg(feature = "reqwest")]
#[error("HTTP error: {0}")]
HttpError(#[from] reqwest::Error),
}
impl LlmConnectorError {
pub fn is_retryable(&self) -> bool {
matches!(
self,
LlmConnectorError::NetworkError(_)
| LlmConnectorError::RateLimitError(_)
| LlmConnectorError::ProviderError(_)
)
}
pub fn status_code(&self) -> u16 {
match self {
LlmConnectorError::AuthenticationError(_) => 401,
LlmConnectorError::RateLimitError(_) => 429,
LlmConnectorError::InvalidRequest(_) => 400,
LlmConnectorError::UnsupportedModel(_) => 400,
LlmConnectorError::ConfigError(_) => 500,
LlmConnectorError::JsonError(_) => 400,
LlmConnectorError::NetworkError(_) => 502,
LlmConnectorError::ProviderError(_) => 502,
LlmConnectorError::StreamingError(_) => 500,
LlmConnectorError::PermissionError(_) => 403,
LlmConnectorError::NotFoundError(_) => 404,
LlmConnectorError::ServerError(_) => 500,
LlmConnectorError::TimeoutError(_) => 408,
LlmConnectorError::ConnectionError(_) => 502,
LlmConnectorError::ParseError(_) => 400,
LlmConnectorError::UnsupportedOperation(_) => 501,
LlmConnectorError::HttpError(_) => 502,
LlmConnectorError::MaxRetriesExceeded(_) => 503,
}
}
pub fn from_status_code(status: u16, message: String) -> Self {
match status {
401 | 403 => LlmConnectorError::AuthenticationError(message),
429 => LlmConnectorError::RateLimitError(message),
400 => LlmConnectorError::InvalidRequest(message),
_ if status >= 500 => LlmConnectorError::ProviderError(message),
_ => LlmConnectorError::NetworkError(message),
}
}
}
#[cfg(test)]
mod tests {
use super::LlmConnectorError;
#[test]
fn test_status_codes() {
assert_eq!(
LlmConnectorError::AuthenticationError("test".to_string()).status_code(),
401
);
assert_eq!(
LlmConnectorError::RateLimitError("test".to_string()).status_code(),
429
);
assert_eq!(
LlmConnectorError::InvalidRequest("test".to_string()).status_code(),
400
);
assert_eq!(
LlmConnectorError::UnsupportedModel("test".to_string()).status_code(),
400
);
assert_eq!(
LlmConnectorError::NetworkError("test".to_string()).status_code(),
502
);
}
#[test]
fn test_retryable() {
assert!(LlmConnectorError::NetworkError("test".to_string()).is_retryable());
assert!(LlmConnectorError::RateLimitError("test".to_string()).is_retryable());
assert!(LlmConnectorError::ProviderError("test".to_string()).is_retryable());
assert!(!LlmConnectorError::AuthenticationError("test".to_string()).is_retryable());
assert!(!LlmConnectorError::InvalidRequest("test".to_string()).is_retryable());
assert!(!LlmConnectorError::UnsupportedModel("test".to_string()).is_retryable());
}
#[test]
fn test_from_status_code() {
assert!(matches!(
LlmConnectorError::from_status_code(401, "test".to_string()),
LlmConnectorError::AuthenticationError(_)
));
assert!(matches!(
LlmConnectorError::from_status_code(429, "test".to_string()),
LlmConnectorError::RateLimitError(_)
));
assert!(matches!(
LlmConnectorError::from_status_code(400, "test".to_string()),
LlmConnectorError::InvalidRequest(_)
));
}
}