1use std::time::Duration;
4
5use thiserror::Error;
6
7use crate::id::ProviderId;
8
9pub type Result<T, E = Error> = std::result::Result<T, E>;
11
12#[derive(Debug, Error)]
14#[non_exhaustive]
15pub enum Error {
16 #[error(transparent)]
18 Provider(#[from] ProviderError),
19
20 #[error(transparent)]
22 Tool(#[from] ToolError),
23
24 #[error(transparent)]
26 Context(#[from] ContextError),
27
28 #[error(transparent)]
30 Storage(#[from] StorageError),
31
32 #[error("configuration error: {0}")]
34 Config(String),
35}
36
37#[derive(Debug, Error)]
39#[non_exhaustive]
40pub enum ProviderError {
41 #[error("authentication failed for provider `{provider}`")]
43 Authentication {
44 provider: ProviderId,
46 },
47
48 #[error("provider `{provider}` rejected request: {message}")]
50 BadRequest {
51 provider: ProviderId,
53 message: String,
55 },
56
57 #[error("provider `{provider}` rate limited request")]
59 RateLimited {
60 provider: ProviderId,
62 retry_after: Option<Duration>,
64 },
65
66 #[error("provider `{provider}` timed out")]
68 Timeout {
69 provider: ProviderId,
71 },
72
73 #[error("provider `{provider}` is overloaded")]
75 Overloaded {
76 provider: ProviderId,
78 },
79
80 #[error("provider `{provider}` does not support feature `{feature}`")]
82 Unsupported {
83 provider: ProviderId,
85 feature: String,
87 },
88
89 #[error("transport error for provider `{provider}`: {source}")]
91 Transport {
92 provider: ProviderId,
94 #[source]
96 source: TransportError,
97 },
98
99 #[error("failed to decode response from provider `{provider}`: {message}")]
101 Decode {
102 provider: ProviderId,
104 message: String,
106 },
107
108 #[error("provider `{provider}` returned error: {message}")]
110 Provider {
111 provider: ProviderId,
113 status: Option<u16>,
115 message: String,
117 },
118}
119
120impl ProviderError {
121 #[must_use]
123 pub const fn is_retryable(&self) -> bool {
124 matches!(
125 self,
126 Self::RateLimited { .. }
127 | Self::Timeout { .. }
128 | Self::Overloaded { .. }
129 | Self::Transport { .. }
130 )
131 }
132
133 #[must_use]
136 pub fn is_context_overflow(&self) -> bool {
137 let message = match self {
138 Self::BadRequest { message, .. } | Self::Provider { message, .. } => message.as_str(),
139 _ => return false,
140 };
141
142 message.contains("context_length_exceeded")
143 || message.contains("maximum context length")
144 || message.contains("too many tokens")
145 || message.contains("reduce the length")
146 || message.contains("token limit")
147 || message.contains("prompt is too long")
148 || message.contains("input length exceeds")
149 || message.contains("context window")
150 }
151}
152
153#[derive(Debug)]
158pub struct TransportError(Box<dyn std::error::Error + Send + Sync>);
159
160impl TransportError {
161 #[must_use]
163 pub fn new(error: impl std::error::Error + Send + Sync + 'static) -> Self {
164 Self(Box::new(error))
165 }
166}
167
168impl std::fmt::Display for TransportError {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 self.0.fmt(f)
171 }
172}
173
174impl std::error::Error for TransportError {
175 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
176 Some(self.0.as_ref())
177 }
178}
179
180#[derive(Debug, Error)]
182#[non_exhaustive]
183pub enum ToolError {
184 #[error("tool `{name}` is not registered")]
186 NotFound {
187 name: String,
189 },
190
191 #[error("tool `{name}` failed: {message}")]
193 Execution {
194 name: String,
196 message: String,
198 },
199
200 #[error("tool `{name}` received invalid arguments: {message}")]
202 InvalidArguments {
203 name: String,
205 message: String,
207 },
208
209 #[error("tool `{name}` is not implemented")]
211 NotImplemented {
212 name: String,
214 },
215}
216
217#[derive(Debug, Error)]
219#[non_exhaustive]
220pub enum ContextError {
221 #[error("context adapter `{adapter}` failed: {message}")]
223 AdapterFailed {
224 adapter: String,
226 message: String,
228 },
229
230 #[error("invalid context input: {message}")]
232 InvalidInput {
233 message: String,
235 },
236
237 #[error("context adapter `{adapter}` is not registered")]
239 AdapterNotFound {
240 adapter: String,
242 },
243}
244
245#[derive(Debug, Error)]
247#[non_exhaustive]
248pub enum StorageError {
249 #[error("storage entity not found: `{id}`")]
251 NotFound {
252 id: String,
254 },
255
256 #[error("storage connection failed ({backend}): {message}")]
258 ConnectionFailed {
259 backend: String,
261 message: String,
263 #[source]
265 source: Option<Box<dyn std::error::Error + Send + Sync>>,
266 },
267
268 #[error("storage serialization failed: {message}")]
270 SerializationFailed {
271 message: String,
273 #[source]
275 source: Option<Box<dyn std::error::Error + Send + Sync>>,
276 },
277
278 #[error("storage backend error ({backend}): {message}")]
280 BackendError {
281 backend: String,
283 message: String,
285 #[source]
287 source: Option<Box<dyn std::error::Error + Send + Sync>>,
288 },
289
290 #[error("storage migration failed ({backend}): {message}")]
292 MigrationFailed {
293 backend: String,
295 message: String,
297 #[source]
299 source: Option<Box<dyn std::error::Error + Send + Sync>>,
300 },
301
302 #[error("data corruption in `{field}`: {message}")]
304 DataCorruption {
305 field: String,
307 message: String,
309 #[source]
311 source: Option<Box<dyn std::error::Error + Send + Sync>>,
312 },
313}