1use std::fmt;
7
8#[derive(Debug)]
10pub enum ModelError {
11 Backend(BackendError),
13
14 Config(ConfigError),
16
17 ModelNotFound { model: String, searched: Vec<String> },
19
20 Timeout { operation: String, duration_secs: u64 },
22
23 RateLimit { retry_after: Option<u64> },
25
26 InvalidRequest(String),
28
29 ParseError { message: String, raw: Option<String> },
31
32 StreamError(String),
34
35 Authentication(String),
37}
38
39impl fmt::Display for ModelError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 ModelError::Backend(e) => write!(f, "Backend error: {}", e),
43 ModelError::Config(e) => write!(f, "Configuration error: {}", e),
44 ModelError::ModelNotFound { model, searched } => {
45 write!(f, "Model '{}' not found. Searched: {}", model, searched.join(", "))
46 }
47 ModelError::Timeout { operation, duration_secs } => {
48 write!(f, "Operation '{}' timed out after {} seconds", operation, duration_secs)
49 }
50 ModelError::RateLimit { retry_after } => {
51 if let Some(secs) = retry_after {
52 write!(f, "Rate limit exceeded. Retry after {} seconds", secs)
53 } else {
54 write!(f, "Rate limit exceeded")
55 }
56 }
57 ModelError::InvalidRequest(msg) => write!(f, "Invalid request: {}", msg),
58 ModelError::ParseError { message, raw } => {
59 if let Some(r) = raw {
60 write!(f, "Parse error: {} (raw: {})", message, r)
61 } else {
62 write!(f, "Parse error: {}", message)
63 }
64 }
65 ModelError::StreamError(msg) => write!(f, "Stream error: {}", msg),
66 ModelError::Authentication(msg) => write!(f, "Authentication error: {}", msg),
67 }
68 }
69}
70
71impl std::error::Error for ModelError {}
72
73#[derive(Debug)]
75pub enum BackendError {
76 ConnectionFailed { backend: String, url: String, reason: String },
78
79 NotAvailable { backend: String, reason: String },
81
82 HttpError { status: u16, message: String },
84
85 UnexpectedResponse { backend: String, message: String },
87
88 ProviderError { provider: String, code: Option<String>, message: String },
90}
91
92impl fmt::Display for BackendError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 BackendError::ConnectionFailed { backend, url, reason } => {
96 write!(f, "Failed to connect to {} at {}: {}", backend, url, reason)
97 }
98 BackendError::NotAvailable { backend, reason } => {
99 write!(f, "Backend '{}' not available: {}", backend, reason)
100 }
101 BackendError::HttpError { status, message } => {
102 write!(f, "HTTP error {}: {}", status, message)
103 }
104 BackendError::UnexpectedResponse { backend, message } => {
105 write!(f, "Unexpected response from {}: {}", backend, message)
106 }
107 BackendError::ProviderError { provider, code, message } => {
108 if let Some(c) = code {
109 write!(f, "{} error {}: {}", provider, c, message)
110 } else {
111 write!(f, "{} error: {}", provider, message)
112 }
113 }
114 }
115 }
116}
117
118impl std::error::Error for BackendError {}
119
120#[derive(Debug)]
122pub enum ConfigError {
123 MissingRequired(String),
125
126 InvalidValue { field: String, value: String, reason: String },
128
129 FileError { path: String, reason: String },
131}
132
133impl fmt::Display for ConfigError {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 match self {
136 ConfigError::MissingRequired(field) => {
137 write!(f, "Missing required configuration: {}", field)
138 }
139 ConfigError::InvalidValue { field, value, reason } => {
140 write!(f, "Invalid value for '{}': '{}' ({})", field, value, reason)
141 }
142 ConfigError::FileError { path, reason } => {
143 write!(f, "Error reading config file '{}': {}", path, reason)
144 }
145 }
146 }
147}
148
149impl std::error::Error for ConfigError {}
150
151pub type Result<T> = std::result::Result<T, ModelError>;
153
154impl From<anyhow::Error> for ModelError {
156 fn from(err: anyhow::Error) -> Self {
157 ModelError::InvalidRequest(err.to_string())
158 }
159}
160
161impl From<reqwest::Error> for ModelError {
163 fn from(err: reqwest::Error) -> Self {
164 if err.is_timeout() {
165 ModelError::Timeout {
166 operation: "HTTP request".to_string(),
167 duration_secs: 120,
168 }
169 } else if err.is_connect() {
170 ModelError::Backend(BackendError::ConnectionFailed {
171 backend: "unknown".to_string(),
172 url: err.url().map(|u| u.to_string()).unwrap_or_else(|| "unknown".to_string()),
173 reason: err.to_string(),
174 })
175 } else if err.is_status() {
176 let status = err.status().map(|s| s.as_u16()).unwrap_or(500);
177 ModelError::Backend(BackendError::HttpError {
178 status,
179 message: err.to_string(),
180 })
181 } else {
182 ModelError::Backend(BackendError::UnexpectedResponse {
183 backend: "unknown".to_string(),
184 message: err.to_string(),
185 })
186 }
187 }
188}
189
190impl From<serde_json::Error> for ModelError {
192 fn from(err: serde_json::Error) -> Self {
193 ModelError::ParseError {
194 message: err.to_string(),
195 raw: None,
196 }
197 }
198}