use std::fmt;
use std::time::Duration;
use zenwave::{BodyError, Error as ZenwaveError, sse::ParseError as SseParseError};
#[derive(Debug)]
pub enum OpenAIError {
Http(ZenwaveError),
Body(BodyError),
Stream(SseParseError),
Json(serde_json::Error),
Decode(base64::DecodeError),
Api(String),
RateLimit {
message: String,
retry_after: Option<Duration>,
},
ServerError {
status: u16,
message: String,
},
Timeout,
}
impl fmt::Display for OpenAIError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Http(err) => write!(f, "HTTP error: {err}"),
Self::Body(err) => write!(f, "Body error: {err}"),
Self::Stream(err) => write!(f, "SSE error: {err}"),
Self::Json(err) => write!(f, "JSON error: {err}"),
Self::Decode(err) => write!(f, "Base64 decode error: {err}"),
Self::Api(message) => write!(f, "{message}"),
Self::RateLimit {
message,
retry_after,
} => {
write!(f, "Rate limit exceeded: {message}")?;
if let Some(delay) = retry_after {
write!(f, " (retry after {}s)", delay.as_secs())?;
}
Ok(())
}
Self::ServerError { status, message } => {
write!(f, "Server error {status}: {message}")
}
Self::Timeout => write!(f, "Request timed out"),
}
}
}
impl std::error::Error for OpenAIError {}
impl OpenAIError {
pub(crate) fn from_http(err: zenwave::Error) -> Self {
if let zenwave::Error::Http { status, .. } = &err {
if status.as_u16() == 429 {
return Self::RateLimit {
message: "Rate limit exceeded".to_string(),
retry_after: None,
};
}
if status.as_u16() >= 500 {
return Self::ServerError {
status: status.as_u16(),
message: err.to_string(),
};
}
}
if err.is_timeout() {
return Self::Timeout;
}
Self::Http(err)
}
}
impl From<BodyError> for OpenAIError {
fn from(value: BodyError) -> Self {
Self::Body(value)
}
}
impl From<SseParseError> for OpenAIError {
fn from(value: SseParseError) -> Self {
Self::Stream(value)
}
}
impl From<serde_json::Error> for OpenAIError {
fn from(value: serde_json::Error) -> Self {
Self::Json(value)
}
}
impl From<base64::DecodeError> for OpenAIError {
fn from(value: base64::DecodeError) -> Self {
Self::Decode(value)
}
}
impl From<std::io::Error> for OpenAIError {
fn from(value: std::io::Error) -> Self {
Self::Api(format!("IO error: {value}"))
}
}