use serde::Deserialize;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, OpenAiError>;
#[derive(Debug, Error)]
pub enum OpenAiError {
#[error("OpenAI API error ({status}): {message}{}{}",
code.as_ref().map(|c| format!(" [code={}]", c)).unwrap_or_default(),
type_.as_ref().map(|t| format!(" [type={}]", t)).unwrap_or_default()
)]
Api {
status: u16,
message: String,
code: Option<String>,
#[allow(non_snake_case)]
type_: Option<String>,
param: Option<String>,
},
#[error("HTTP error: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("JSON decode error: {0}")]
Decode(#[from] serde_json::Error),
#[error("Stream error: {0}")]
Stream(String),
#[error("Config error: {0}")]
Config(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
impl OpenAiError {
pub fn config(msg: impl Into<String>) -> Self {
OpenAiError::Config(msg.into())
}
pub fn stream(msg: impl Into<String>) -> Self {
OpenAiError::Stream(msg.into())
}
pub(crate) fn from_response_body(status: u16, body: &str) -> Self {
if let Ok(envelope) = serde_json::from_str::<ApiErrorEnvelope>(body) {
let e = envelope.error;
return OpenAiError::Api {
status,
message: e.message,
code: e.code,
type_: e.r#type,
param: e.param,
};
}
OpenAiError::Api {
status,
message: body.to_string(),
code: None,
type_: None,
param: None,
}
}
}
impl From<OpenAiError> for String {
fn from(value: OpenAiError) -> Self {
value.to_string()
}
}
#[derive(Deserialize)]
struct ApiErrorEnvelope {
error: ApiErrorBody,
}
#[derive(Deserialize)]
struct ApiErrorBody {
message: String,
#[serde(default)]
r#type: Option<String>,
#[serde(default)]
code: Option<String>,
#[serde(default)]
param: Option<String>,
}