Skip to main content

aither_openai/
error.rs

1use std::fmt;
2use std::time::Duration;
3use zenwave::{BodyError, Error as ZenwaveError, sse::ParseError as SseParseError};
4
5/// Errors that can arise when calling the `OpenAI` API.
6#[derive(Debug)]
7pub enum OpenAIError {
8    /// HTTP layer errors.
9    Http(ZenwaveError),
10    /// Response body parsing failures.
11    Body(BodyError),
12    /// SSE parsing failures.
13    Stream(SseParseError),
14    /// JSON serialization/deserialization errors.
15    Json(serde_json::Error),
16    /// Base64 decoding failures (e.g., image payloads).
17    Decode(base64::DecodeError),
18    /// API contract violations or unsupported operations.
19    Api(String),
20    /// Rate limit exceeded (HTTP 429).
21    RateLimit {
22        /// Message from the API.
23        message: String,
24        /// Suggested retry delay from Retry-After header.
25        retry_after: Option<Duration>,
26    },
27    /// Server error (HTTP 5xx).
28    ServerError {
29        /// HTTP status code.
30        status: u16,
31        /// Error message.
32        message: String,
33    },
34    /// Request timed out.
35    Timeout,
36}
37
38impl fmt::Display for OpenAIError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            Self::Http(err) => write!(f, "HTTP error: {err}"),
42            Self::Body(err) => write!(f, "Body error: {err}"),
43            Self::Stream(err) => write!(f, "SSE error: {err}"),
44            Self::Json(err) => write!(f, "JSON error: {err}"),
45            Self::Decode(err) => write!(f, "Base64 decode error: {err}"),
46            Self::Api(message) => write!(f, "{message}"),
47            Self::RateLimit {
48                message,
49                retry_after,
50            } => {
51                write!(f, "Rate limit exceeded: {message}")?;
52                if let Some(delay) = retry_after {
53                    write!(f, " (retry after {}s)", delay.as_secs())?;
54                }
55                Ok(())
56            }
57            Self::ServerError { status, message } => {
58                write!(f, "Server error {status}: {message}")
59            }
60            Self::Timeout => write!(f, "Request timed out"),
61        }
62    }
63}
64
65impl std::error::Error for OpenAIError {}
66
67impl OpenAIError {
68    /// Convert a zenwave error to an `OpenAIError`.
69    pub(crate) fn from_http(err: zenwave::Error) -> Self {
70        // Check if it's a rate limit error
71        if let zenwave::Error::Http { status, .. } = &err {
72            if status.as_u16() == 429 {
73                return Self::RateLimit {
74                    message: "Rate limit exceeded".to_string(),
75                    retry_after: None,
76                };
77            }
78            if status.as_u16() >= 500 {
79                return Self::ServerError {
80                    status: status.as_u16(),
81                    message: err.to_string(),
82                };
83            }
84        }
85        if err.is_timeout() {
86            return Self::Timeout;
87        }
88        Self::Http(err)
89    }
90}
91
92impl From<BodyError> for OpenAIError {
93    fn from(value: BodyError) -> Self {
94        Self::Body(value)
95    }
96}
97
98impl From<SseParseError> for OpenAIError {
99    fn from(value: SseParseError) -> Self {
100        Self::Stream(value)
101    }
102}
103
104impl From<serde_json::Error> for OpenAIError {
105    fn from(value: serde_json::Error) -> Self {
106        Self::Json(value)
107    }
108}
109
110impl From<base64::DecodeError> for OpenAIError {
111    fn from(value: base64::DecodeError) -> Self {
112        Self::Decode(value)
113    }
114}
115
116impl From<std::io::Error> for OpenAIError {
117    fn from(value: std::io::Error) -> Self {
118        Self::Api(format!("IO error: {value}"))
119    }
120}