use std::fmt;
#[derive(Debug)]
pub enum AgentError {
Tool {
tool_name: String,
message: String,
},
Stream(String),
State(String),
Config(String),
Model {
model_id: String,
message: String,
},
MaxIterations {
iterations: usize,
},
RateLimited {
retry_after_secs: u64,
},
RetriesExhausted {
attempts: usize,
last_error: String,
},
FallbackFailed {
primary_model: String,
primary_error: String,
fallback_model: String,
fallback_error: String,
},
}
impl fmt::Display for AgentError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Tool { tool_name, message } => {
write!(f, "Tool '{}' failed: {}", tool_name, message)
}
Self::Stream(msg) => write!(f, "Stream error: {}", msg),
Self::State(msg) => write!(f, "State error: {}", msg),
Self::Config(msg) => write!(f, "Config error: {}", msg),
Self::Model { model_id, message } => {
write!(f, "Model '{}' error: {}", model_id, message)
}
Self::MaxIterations { iterations } => {
write!(f, "Maximum iterations reached ({})", iterations)
}
Self::RateLimited { retry_after_secs } => {
write!(f, "Rate limited – retry after {}s", retry_after_secs)
}
Self::RetriesExhausted { attempts, last_error } => {
write!(f, "Failed after {} retries: {}", attempts, last_error)
}
Self::FallbackFailed {
primary_model,
primary_error,
fallback_model,
fallback_error,
} => {
write!(
f,
"Both models failed – {} ({}) and {} ({})",
primary_model, primary_error, fallback_model, fallback_error
)
}
}
}
}
impl std::error::Error for AgentError {}
impl AgentError {
pub fn is_retryable(&self) -> bool {
matches!(
self,
Self::RateLimited { .. } | Self::Stream(_) | Self::RetriesExhausted { .. }
)
}
pub fn user_friendly(&self) -> String {
match self {
Self::Tool { tool_name, message } => {
format!("Tool '{}' failed: {}", tool_name, message)
}
Self::Stream(msg) => format!("Connection error: {}", msg),
Self::State(msg) => format!("Internal error: {}", msg),
Self::Config(msg) => format!("Configuration error: {}", msg),
Self::Model { model_id, message } => {
format!("Model '{}' error: {}", model_id, message)
}
Self::MaxIterations { iterations } => {
format!("Reached the iteration limit ({}). Try simplifying your request.", iterations)
}
Self::RateLimited { retry_after_secs } => {
format!("Rate limited – will retry in {}s", retry_after_secs)
}
Self::RetriesExhausted { attempts, last_error } => {
format!("Failed after {} attempts: {}", attempts, last_error)
}
Self::FallbackFailed {
primary_model,
primary_error,
fallback_model,
fallback_error: _,
} => {
format!(
"Primary model ({}) failed: {}. Fallback ({}) also failed.",
primary_model, primary_error, fallback_model
)
}
}
}
}
impl From<anyhow::Error> for AgentError {
fn from(err: anyhow::Error) -> Self {
AgentError::Stream(err.to_string())
}
}
pub type Result<T> = std::result::Result<T, AgentError>;