rag_toolchain/clients/open_ai/model/
errors.rs

1use serde::Deserialize;
2use thiserror::Error;
3
4// This is what is returned from OpenAI
5// when an error occurs
6#[derive(Debug, Deserialize, PartialEq, Clone)]
7pub struct OpenAIErrorBody {
8    pub error: OpenAIErrorData,
9}
10
11#[derive(Debug, Deserialize, PartialEq, Clone)]
12pub struct OpenAIErrorData {
13    pub message: String,
14    #[serde(rename = "type")]
15    pub error_type: String,
16    pub param: Option<String>,
17    pub code: String,
18}
19// --------------------------------------------------------------------------------
20
21/// # [`OpenAIError`]
22///
23/// This error type largely mirrors the error codes list here
24/// <https://platform.openai.com/docs/guides/error-codes>
25#[derive(Error, Debug, PartialEq, Clone)]
26pub enum OpenAIError {
27    /// # Invalid Authentication or Incorrect API Key provided
28    #[error("Bad Request: {0:?}")]
29    CODE400(OpenAIErrorBody),
30    /// # Invalid Authentication or Incorrect API Key provided
31    #[error("Invalid Authentication or Incorrect API Key provided: {0:?}")]
32    CODE401(OpenAIErrorBody),
33    /// # Rate limit reached or Monthly quota exceeded
34    #[error("Rate limit reached or monthly quota exceeded: {0:?}")]
35    CODE429(OpenAIErrorBody),
36    /// # Server Error
37    #[error("Server error: {0:?}")]
38    CODE500(OpenAIErrorBody),
39    /// # The engine is currently overloaded
40    #[error("The engine is currently overloaded: {0:?}")]
41    CODE503(OpenAIErrorBody),
42    /// # Missed cases for error codes, includes Status Code and Error Body as a string
43    #[error("Undefined Error. This should not happen, if this is a missed error please report it: https://github.com/JackMatthewRimmer/rust-rag-toolchain: status code = {0}, error = {1}")]
44    Undefined(u16, String),
45    #[error("Error sending request: {0}")]
46    ErrorSendingRequest(String),
47    /// # Carries underlying error
48    #[error("Error getting response body: {0}")]
49    ErrorGettingResponseBody(String),
50    /// # Carries underlying error and the status code
51    #[error("Error deserializining response body: status code = {0}, error = {1}")]
52    ErrorDeserializingResponseBody(u16, String),
53    /// # Carries underlying error if something went wrong when reading from a stream
54    #[error("Error reading stream: {0}")]
55    ErrorReadingStream(String),
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_code_400_display() {
64        let error_body = OpenAIErrorBody {
65            error: OpenAIErrorData {
66                message: "Invalid request".to_string(),
67                error_type: "Bad Request".to_string(),
68                param: None,
69                code: "400".to_string(),
70            },
71        };
72        let error = OpenAIError::CODE400(error_body.clone());
73        let expected = format!("Bad Request: {:?}", error_body);
74        assert_eq!(format!("{}", error), expected);
75    }
76
77    #[test]
78    fn test_code_401_display() {
79        let error_body = OpenAIErrorBody {
80            error: OpenAIErrorData {
81                message: "Invalid API key".to_string(),
82                error_type: "Unauthorized".to_string(),
83                param: None,
84                code: "401".to_string(),
85            },
86        };
87        let error = OpenAIError::CODE401(error_body.clone());
88        let expected = format!(
89            "Invalid Authentication or Incorrect API Key provided: {:?}",
90            error_body
91        );
92        assert_eq!(format!("{}", error), expected);
93    }
94
95    #[test]
96    fn test_code_429_display() {
97        let error_body = OpenAIErrorBody {
98            error: OpenAIErrorData {
99                message: "Rate limit exceeded".to_string(),
100                error_type: "Rate Limit Exceeded".to_string(),
101                param: None,
102                code: "429".to_string(),
103            },
104        };
105        let error = OpenAIError::CODE429(error_body.clone());
106        let expected = format!(
107            "Rate limit reached or monthly quota exceeded: {:?}",
108            error_body
109        );
110        assert_eq!(format!("{}", error), expected);
111    }
112
113    #[test]
114    fn test_code_500_display() {
115        let error_body = OpenAIErrorBody {
116            error: OpenAIErrorData {
117                message: "Internal server error".to_string(),
118                error_type: "Internal Server Error".to_string(),
119                param: None,
120                code: "500".to_string(),
121            },
122        };
123        let error = OpenAIError::CODE500(error_body.clone());
124        let expected = format!("Server error: {:?}", error_body);
125        assert_eq!(format!("{}", error), expected);
126    }
127
128    #[test]
129    fn test_code_503_display() {
130        let error_body = OpenAIErrorBody {
131            error: OpenAIErrorData {
132                message: "Engine overloaded".to_string(),
133                error_type: "Service Unavailable".to_string(),
134                param: None,
135                code: "503".to_string(),
136            },
137        };
138        let error = OpenAIError::CODE503(error_body.clone());
139        let expected = format!("The engine is currently overloaded: {:?}", error_body);
140        assert_eq!(format!("{}", error), expected);
141    }
142
143    #[test]
144    fn test_undefined_display() {
145        let status_code = 404;
146        let error_message = "Not Found".to_string();
147        let error = OpenAIError::Undefined(status_code, error_message.clone());
148        let expected = format!("Undefined Error. This should not happen, if this is a missed error please report it: https://github.com/JackMatthewRimmer/rust-rag-toolchain: status code = {}, error = {}", status_code, error_message);
149        assert_eq!(format!("{}", error), expected);
150    }
151
152    #[test]
153    fn test_error_sending_request_display() {
154        let error_message = "Connection timed out".to_string();
155        let error = OpenAIError::ErrorSendingRequest(error_message.clone());
156        let expected = format!("Error sending request: {}", error_message);
157        assert_eq!(format!("{}", error), expected);
158    }
159
160    #[test]
161    fn test_error_getting_response_body_display() {
162        let error_message = "Failed to read response body".to_string();
163        let error = OpenAIError::ErrorGettingResponseBody(error_message.clone());
164        let expected = format!("Error getting response body: {}", error_message);
165        assert_eq!(format!("{}", error), expected);
166    }
167
168    #[test]
169    fn test_error_deserializing_response_body_display() {
170        let status_code = 400;
171        let error_message = "Invalid JSON format".to_string();
172        let error = OpenAIError::ErrorDeserializingResponseBody(status_code, error_message.clone());
173        let expected = format!(
174            "Error deserializining response body: status code = {}, error = {}",
175            status_code, error_message
176        );
177        assert_eq!(format!("{}", error), expected);
178    }
179
180    #[test]
181    fn test_error_reading_stream_display() {
182        let error_message = "Failed to read from stream".to_string();
183        let error = OpenAIError::ErrorReadingStream(error_message.clone());
184        let expected = format!("Error reading stream: {}", error_message);
185        assert_eq!(format!("{}", error), expected);
186    }
187}