use std::time::Duration;
use serde::Deserialize;
use thiserror::Error;
#[derive(Debug, Deserialize)]
pub struct ErrorResponse {
pub error: ErrorDetail,
}
#[derive(Debug, Deserialize)]
pub struct ErrorDetail {
pub message: String,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ClientError {
#[error("Network error: {0}")]
NetworkError(#[from] reqwest::Error),
#[error("Middleware error: {0}")]
MiddlewareError(#[from] reqwest_middleware::Error),
#[error("Serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("Authentication error: {0}")]
AuthenticationError(String),
#[error("EventSource error: {0}")]
EventSourceError(#[from] reqwest_eventsource::Error),
#[error("Rate limit exceeded: {retry_after:?}")]
RateLimitError {
retry_after: Option<Duration>,
},
#[error("Model error: {0}")]
ModelError(String),
#[error("Configuration error: {0}")]
ConfigurationError(String),
#[error("Timeout error")]
TimeoutError,
#[error("Invalid request: {0}")]
InvalidRequest(String),
#[error("Invalid response: {0}")]
InvalidResponse(String),
#[error("Tool execution not supported")]
ToolsNotSupported,
#[error("Streaming not supported")]
StreamingNotSupported,
#[error("Context length exceeded: {current_tokens} > {max_tokens}")]
ContextLengthExceeded {
current_tokens: usize,
max_tokens: usize,
},
#[error("Content filtered: {reason}")]
ContentFiltered {
reason: String,
},
#[error("Service unavailable: {0}")]
ServiceUnavailable(String),
#[error("Temperature must be between 0.0 & 2.0")]
InvalidTemperature,
#[error("TopP must be between 0.0 & 1.0")]
InvalidTopP,
#[error("FrequencyPenalty must be between -2.0 & 2.0")]
InvalidFrequencyPenalty,
}
impl ClientError {
pub fn is_retryable(&self) -> bool {
matches!(
self,
Self::NetworkError(_)
| Self::MiddlewareError(_)
| Self::TimeoutError
| Self::RateLimitError { .. }
| Self::ServiceUnavailable(_)
)
}
pub fn is_authentication_error(&self) -> bool {
matches!(self, Self::AuthenticationError(_))
}
pub fn is_rate_limit_error(&self) -> bool {
matches!(self, Self::RateLimitError { .. })
}
pub fn retry_after(&self) -> Option<Duration> {
match self {
Self::RateLimitError { retry_after } => *retry_after,
_ => None,
}
}
}