use reqwest::StatusCode;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, ApiError>;
#[derive(Error, Debug)]
pub enum ApiError {
#[error("HTTP {status}: {text}")]
Http { status: StatusCode, text: String },
#[error("LLM Provider error: {0}")]
Llm(String),
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Tool error: {0}")]
Tool(String),
#[error("MCP error: {0}")]
Mcp(String),
#[error("Stream error: {0}")]
Stream(String),
#[error("Configuration error: {0}")]
Config(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Other(String),
}
impl ApiError {
pub fn is_retriable(&self) -> bool {
match self {
ApiError::Http { status, .. } => {
matches!(
*status,
StatusCode::TOO_MANY_REQUESTS | StatusCode::INTERNAL_SERVER_ERROR | StatusCode::BAD_GATEWAY | StatusCode::SERVICE_UNAVAILABLE | StatusCode::GATEWAY_TIMEOUT )
}
ApiError::Network(e) => {
e.is_connect() || e.is_timeout()
}
_ => false,
}
}
pub fn http(status: StatusCode, text: impl Into<String>) -> Self {
Self::Http {
status,
text: text.into(),
}
}
}
impl From<&str> for ApiError {
fn from(s: &str) -> Self {
ApiError::Other(s.to_string())
}
}
impl From<String> for ApiError {
fn from(s: String) -> Self {
ApiError::Other(s)
}
}