9c54eb7fef
Co-authored-by: Douwe Osinga <douwe@squareup.com>
154 lines
4.9 KiB
Rust
154 lines
4.9 KiB
Rust
use reqwest::StatusCode;
|
|
use std::time::Duration;
|
|
use thiserror::Error;
|
|
|
|
#[derive(Error, Debug, Clone, PartialEq)]
|
|
pub enum ProviderError {
|
|
#[error("Authentication error: {0}")]
|
|
Authentication(String),
|
|
|
|
#[error("Context length exceeded: {0}")]
|
|
ContextLengthExceeded(String),
|
|
|
|
#[error("Rate limit exceeded: {details}")]
|
|
RateLimitExceeded {
|
|
details: String,
|
|
retry_delay: Option<Duration>,
|
|
},
|
|
|
|
#[error("Server error: {0}")]
|
|
ServerError(String),
|
|
|
|
#[error("Network error: {0}")]
|
|
NetworkError(String),
|
|
|
|
#[error("Request failed: {0}")]
|
|
RequestFailed(String),
|
|
|
|
#[error("Execution error: {0}")]
|
|
ExecutionError(String),
|
|
|
|
#[error("Usage data error: {0}")]
|
|
UsageError(String),
|
|
|
|
#[error("Unsupported operation: {0}")]
|
|
NotImplemented(String),
|
|
|
|
#[error("Credits exhausted: {details}")]
|
|
CreditsExhausted {
|
|
details: String,
|
|
top_up_url: Option<String>,
|
|
},
|
|
}
|
|
|
|
impl ProviderError {
|
|
pub fn telemetry_type(&self) -> &'static str {
|
|
match self {
|
|
ProviderError::Authentication(_) => "auth",
|
|
ProviderError::ContextLengthExceeded(_) => "context_length",
|
|
ProviderError::RateLimitExceeded { .. } => "rate_limit",
|
|
ProviderError::ServerError(_) => "server",
|
|
ProviderError::NetworkError(_) => "network",
|
|
ProviderError::RequestFailed(_) => "request",
|
|
ProviderError::ExecutionError(_) => "execution",
|
|
ProviderError::UsageError(_) => "usage",
|
|
ProviderError::NotImplemented(_) => "not_implemented",
|
|
ProviderError::CreditsExhausted { .. } => "credits_exhausted",
|
|
}
|
|
}
|
|
}
|
|
|
|
fn is_network_error(err: &reqwest::Error) -> bool {
|
|
err.is_connect() || err.is_timeout() || (err.status().is_none() && err.is_request())
|
|
}
|
|
|
|
fn provider_error_from_reqwest(error: &reqwest::Error) -> ProviderError {
|
|
if is_network_error(error) {
|
|
let msg = if error.is_timeout() {
|
|
"Request timed out — check your network connection and try again.".to_string()
|
|
} else if error.is_connect() {
|
|
if let Some(url) = error.url() {
|
|
if let Some(host) = url.host_str() {
|
|
let port_info = url.port().map(|p| format!(":{}", p)).unwrap_or_default();
|
|
format!(
|
|
"Could not connect to {}{} — check your network connection and try again.",
|
|
host, port_info
|
|
)
|
|
} else {
|
|
"Could not connect to the provider — check your network connection and try again.".to_string()
|
|
}
|
|
} else {
|
|
"Could not connect to the provider — check your network connection and try again."
|
|
.to_string()
|
|
}
|
|
} else {
|
|
"Network error — check your network connection and try again.".to_string()
|
|
};
|
|
return ProviderError::NetworkError(msg);
|
|
}
|
|
|
|
let mut details = vec![];
|
|
if let Some(status) = error.status() {
|
|
details.push(format!("status: {}", status));
|
|
}
|
|
let msg = if details.is_empty() {
|
|
error.to_string()
|
|
} else {
|
|
format!("{} ({})", error, details.join(", "))
|
|
};
|
|
ProviderError::RequestFailed(msg)
|
|
}
|
|
|
|
impl From<anyhow::Error> for ProviderError {
|
|
fn from(error: anyhow::Error) -> Self {
|
|
if let Some(reqwest_err) = error.downcast_ref::<reqwest::Error>() {
|
|
return provider_error_from_reqwest(reqwest_err);
|
|
}
|
|
ProviderError::ExecutionError(error.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<reqwest::Error> for ProviderError {
|
|
fn from(error: reqwest::Error) -> Self {
|
|
provider_error_from_reqwest(&error)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum GoogleErrorCode {
|
|
BadRequest = 400,
|
|
Unauthorized = 401,
|
|
Forbidden = 403,
|
|
NotFound = 404,
|
|
TooManyRequests = 429,
|
|
InternalServerError = 500,
|
|
ServiceUnavailable = 503,
|
|
}
|
|
|
|
impl GoogleErrorCode {
|
|
pub fn to_status_code(&self) -> StatusCode {
|
|
match self {
|
|
Self::BadRequest => StatusCode::BAD_REQUEST,
|
|
Self::Unauthorized => StatusCode::UNAUTHORIZED,
|
|
Self::Forbidden => StatusCode::FORBIDDEN,
|
|
Self::NotFound => StatusCode::NOT_FOUND,
|
|
Self::TooManyRequests => StatusCode::TOO_MANY_REQUESTS,
|
|
Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
|
Self::ServiceUnavailable => StatusCode::SERVICE_UNAVAILABLE,
|
|
}
|
|
}
|
|
|
|
pub fn from_code(code: u64) -> Option<Self> {
|
|
match code {
|
|
400 => Some(Self::BadRequest),
|
|
401 => Some(Self::Unauthorized),
|
|
403 => Some(Self::Forbidden),
|
|
404 => Some(Self::NotFound),
|
|
429 => Some(Self::TooManyRequests),
|
|
500 => Some(Self::InternalServerError),
|
|
503 => Some(Self::ServiceUnavailable),
|
|
_ => Some(Self::InternalServerError),
|
|
}
|
|
}
|
|
}
|