Skip to main content

oxi_agent/
error.rs

1//! Error types for oxi-agent
2
3/// Agent-specific errors
4#[derive(Debug, thiserror::Error)]
5pub enum AgentError {
6    /// Error originating from a tool.
7    #[error("Tool '{tool_name}' failed: {message}")]
8    Tool {
9        /// Name of the tool that failed.
10        tool_name: String,
11        /// Human-readable error message.
12        message: String,
13    },
14    /// Stream / provider communication error
15    #[error("Stream error: {0}")]
16    Stream(String),
17    /// State management error
18    #[error("State error: {0}")]
19    State(String),
20    /// Configuration error
21    #[error("Config error: {0}")]
22    Config(String),
23    /// Model not found or unavailable.
24    #[error("Model '{model_id}' error: {message}")]
25    Model {
26        /// Identifier of the model.
27        model_id: String,
28        /// Error detail.
29        message: String,
30    },
31    /// Maximum iterations reached.
32    #[error("Maximum iterations reached ({iterations})")]
33    MaxIterations {
34        /// Number of iterations that were executed.
35        iterations: usize,
36    },
37    /// Rate limited – retry after N seconds.
38    #[error("Rate limited – retry after {retry_after_secs}s")]
39    RateLimited {
40        /// Seconds to wait before retrying.
41        retry_after_secs: u64,
42    },
43    /// A retriable error that failed after exhausting retries.
44    #[error("Failed after {attempts} retries: {last_error}")]
45    RetriesExhausted {
46        /// Number of retry attempts made.
47        attempts: usize,
48        /// Error from the final attempt.
49        last_error: String,
50    },
51    /// Fallback failed – both primary and fallback model errored
52    #[error(
53        "Both models failed – {primary_model} ({primary_error}) and {fallback_model} ({fallback_error})"
54    )]
55    FallbackFailed {
56        /// Name of the primary model.
57        primary_model: String,
58        /// Error from the primary model.
59        primary_error: String,
60        /// Name of the fallback model.
61        fallback_model: String,
62        /// Error from the fallback model.
63        fallback_error: String,
64    },
65}
66
67impl AgentError {
68    /// Check if this error is retryable.
69    pub fn is_retryable(&self) -> bool {
70        matches!(
71            self,
72            Self::RateLimited { .. } | Self::Stream(_) | Self::RetriesExhausted { .. }
73        )
74    }
75
76    /// Produce a short, user-friendly message suitable for TUI display.
77    pub fn user_friendly(&self) -> String {
78        match self {
79            Self::Tool { tool_name, message } => {
80                format!("Tool '{}' failed: {}", tool_name, message)
81            }
82            Self::Stream(msg) => format!("Connection error: {}", msg),
83            Self::State(msg) => format!("Internal error: {}", msg),
84            Self::Config(msg) => format!("Configuration error: {}", msg),
85            Self::Model { model_id, message } => {
86                format!("Model '{}' error: {}", model_id, message)
87            }
88            Self::MaxIterations { iterations } => {
89                format!(
90                    "Reached the iteration limit ({}). Try simplifying your request.",
91                    iterations
92                )
93            }
94            Self::RateLimited { retry_after_secs } => {
95                format!("Rate limited – will retry in {}s", retry_after_secs)
96            }
97            Self::RetriesExhausted {
98                attempts,
99                last_error,
100            } => {
101                format!("Failed after {} attempts: {}", attempts, last_error)
102            }
103            Self::FallbackFailed {
104                primary_model,
105                primary_error,
106                fallback_model,
107                fallback_error: _,
108            } => {
109                format!(
110                    "Primary model ({}) failed: {}. Fallback ({}) also failed.",
111                    primary_model, primary_error, fallback_model
112                )
113            }
114        }
115    }
116}
117
118impl From<anyhow::Error> for AgentError {
119    fn from(err: anyhow::Error) -> Self {
120        AgentError::Stream(err.to_string())
121    }
122}
123
124/// Result type alias for agent operations.
125pub type Result<T> = std::result::Result<T, AgentError>;