1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::fmt;
9use std::time::Duration;
10use thiserror::Error;
11
12#[derive(Error, Debug)]
14pub enum SerdesAiError {
15 #[error(transparent)]
17 AgentRun(#[from] AgentRunError),
18
19 #[error(transparent)]
21 ModelRetry(#[from] ModelRetry),
22
23 #[error(transparent)]
25 ModelApi(#[from] ModelApiError),
26
27 #[error(transparent)]
29 ModelHttp(#[from] ModelHttpError),
30
31 #[error(transparent)]
33 User(#[from] UserError),
34
35 #[error(transparent)]
37 UsageLimit(#[from] UsageLimitExceeded),
38
39 #[error(transparent)]
41 UnexpectedBehavior(#[from] UnexpectedModelBehavior),
42
43 #[error(transparent)]
45 ToolRetry(#[from] ToolRetryError),
46
47 #[error(transparent)]
49 ApprovalRequired(#[from] ApprovalRequired),
50
51 #[error(transparent)]
53 CallDeferred(#[from] CallDeferred),
54
55 #[error(transparent)]
57 IncompleteToolCall(#[from] IncompleteToolCall),
58
59 #[error(transparent)]
61 FallbackGroup(#[from] FallbackExceptionGroup),
62
63 #[error("Serialization error: {0}")]
65 Serialization(#[from] serde_json::Error),
66
67 #[error("Configuration error: {0}")]
69 Configuration(String),
70
71 #[error("Internal error: {0}")]
73 Internal(String),
74}
75
76pub type Result<T> = std::result::Result<T, SerdesAiError>;
78
79#[derive(Error, Debug, Clone)]
81pub struct AgentRunError {
82 pub message: String,
84 pub run_id: Option<String>,
86 pub attempts: u32,
88 pub cause: Option<String>,
90}
91
92impl fmt::Display for AgentRunError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "Agent run error: {}", self.message)?;
95 if let Some(ref run_id) = self.run_id {
96 write!(f, " (run_id: {})", run_id)?;
97 }
98 if self.attempts > 1 {
99 write!(f, " after {} attempts", self.attempts)?;
100 }
101 Ok(())
102 }
103}
104
105impl AgentRunError {
106 pub fn new(message: impl Into<String>) -> Self {
108 Self {
109 message: message.into(),
110 run_id: None,
111 attempts: 1,
112 cause: None,
113 }
114 }
115
116 pub fn with_run_id(mut self, run_id: impl Into<String>) -> Self {
118 self.run_id = Some(run_id.into());
119 self
120 }
121
122 pub fn with_attempts(mut self, attempts: u32) -> Self {
124 self.attempts = attempts;
125 self
126 }
127
128 pub fn with_cause(mut self, cause: impl Into<String>) -> Self {
130 self.cause = Some(cause.into());
131 self
132 }
133}
134
135#[derive(Error, Debug, Clone, Serialize, Deserialize)]
137pub struct ModelRetry {
138 pub message: String,
140}
141
142impl fmt::Display for ModelRetry {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 write!(f, "Model retry requested: {}", self.message)
145 }
146}
147
148impl ModelRetry {
149 pub fn new(message: impl Into<String>) -> Self {
151 Self {
152 message: message.into(),
153 }
154 }
155}
156
157#[derive(Error, Debug, Clone)]
159pub struct ModelApiError {
160 pub status_code: u16,
162 pub body: String,
164 pub headers: HashMap<String, String>,
166 pub message: Option<String>,
168 pub error_code: Option<String>,
170 pub retryable: bool,
172 pub retry_after: Option<u64>,
174}
175
176impl fmt::Display for ModelApiError {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(f, "Model API error (status {})", self.status_code)?;
179 if let Some(ref msg) = self.message {
180 write!(f, ": {}", msg)?;
181 }
182 if let Some(ref code) = self.error_code {
183 write!(f, " [{}]", code)?;
184 }
185 Ok(())
186 }
187}
188
189impl ModelApiError {
190 pub fn new(status_code: u16, body: impl Into<String>) -> Self {
192 Self {
193 status_code,
194 body: body.into(),
195 headers: HashMap::new(),
196 message: None,
197 error_code: None,
198 retryable: status_code == 429 || status_code >= 500,
199 retry_after: None,
200 }
201 }
202
203 pub fn with_message(mut self, message: impl Into<String>) -> Self {
205 self.message = Some(message.into());
206 self
207 }
208
209 pub fn with_error_code(mut self, code: impl Into<String>) -> Self {
211 self.error_code = Some(code.into());
212 self
213 }
214
215 pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
217 self.headers = headers;
218 if let Some(retry_after) = self.headers.get("retry-after") {
220 self.retry_after = retry_after.parse().ok();
221 }
222 self
223 }
224
225 pub fn is_rate_limit(&self) -> bool {
227 self.status_code == 429
228 }
229
230 pub fn is_server_error(&self) -> bool {
232 self.status_code >= 500
233 }
234}
235
236#[deprecated(note = "Use ModelApiError instead")]
238pub type ModelAPIError = ModelApiError;
239
240#[derive(Debug, Clone, PartialEq, Eq)]
242pub enum HttpErrorKind {
243 Timeout,
245 Connection,
247 Request,
249 Response {
251 status: Option<u16>,
253 },
254}
255
256#[derive(Error, Debug, Clone)]
258pub struct ModelHttpError {
259 pub kind: HttpErrorKind,
261 pub message: String,
263 pub retry_after: Option<Duration>,
265}
266
267impl fmt::Display for ModelHttpError {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 match self.kind {
270 HttpErrorKind::Timeout => write!(f, "Request timeout: {}", self.message),
271 HttpErrorKind::Connection => write!(f, "Connection error: {}", self.message),
272 HttpErrorKind::Request => write!(f, "HTTP request error: {}", self.message),
273 HttpErrorKind::Response { status } => {
274 if let Some(status) = status {
275 write!(
276 f,
277 "HTTP response error (status {}): {}",
278 status, self.message
279 )
280 } else {
281 write!(f, "HTTP response error: {}", self.message)
282 }
283 }
284 }
285 }
286}
287
288impl ModelHttpError {
289 pub fn new(kind: HttpErrorKind, message: impl Into<String>) -> Self {
291 Self {
292 kind,
293 message: message.into(),
294 retry_after: None,
295 }
296 }
297
298 pub fn timeout(message: impl Into<String>) -> Self {
300 Self::new(HttpErrorKind::Timeout, message)
301 }
302
303 pub fn connection(message: impl Into<String>) -> Self {
305 Self::new(HttpErrorKind::Connection, message)
306 }
307
308 pub fn request(message: impl Into<String>) -> Self {
310 Self::new(HttpErrorKind::Request, message)
311 }
312
313 pub fn response(status: Option<u16>, message: impl Into<String>) -> Self {
315 Self::new(HttpErrorKind::Response { status }, message)
316 }
317
318 pub fn with_retry_after(mut self, retry_after: Duration) -> Self {
320 self.retry_after = Some(retry_after);
321 self
322 }
323}
324
325#[deprecated(note = "Use ModelHttpError instead")]
327pub type ModelHTTPError = ModelHttpError;
328
329#[derive(Error, Debug, Clone, Serialize, Deserialize)]
331pub struct UserError {
332 pub message: String,
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub details: Option<serde_json::Value>,
337}
338
339impl fmt::Display for UserError {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 write!(f, "User error: {}", self.message)
342 }
343}
344
345impl UserError {
346 pub fn new(message: impl Into<String>) -> Self {
348 Self {
349 message: message.into(),
350 details: None,
351 }
352 }
353
354 pub fn with_details(mut self, details: serde_json::Value) -> Self {
356 self.details = Some(details);
357 self
358 }
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
363#[serde(rename_all = "snake_case")]
364pub enum UsageLimitType {
365 RequestTokens,
367 ResponseTokens,
369 TotalTokens,
371 Requests,
373}
374
375impl fmt::Display for UsageLimitType {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 match self {
378 Self::RequestTokens => write!(f, "request_tokens"),
379 Self::ResponseTokens => write!(f, "response_tokens"),
380 Self::TotalTokens => write!(f, "total_tokens"),
381 Self::Requests => write!(f, "requests"),
382 }
383 }
384}
385
386#[derive(Error, Debug, Clone, Serialize, Deserialize)]
388pub struct UsageLimitExceeded {
389 pub limit_type: UsageLimitType,
391 pub current: u64,
393 pub max: u64,
395 #[serde(skip_serializing_if = "Option::is_none")]
397 pub message: Option<String>,
398}
399
400impl fmt::Display for UsageLimitExceeded {
401 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 write!(
403 f,
404 "Usage limit exceeded: {} is {} but max is {}",
405 self.limit_type, self.current, self.max
406 )
407 }
408}
409
410impl UsageLimitExceeded {
411 pub fn new(limit_type: UsageLimitType, current: u64, max: u64) -> Self {
413 Self {
414 limit_type,
415 current,
416 max,
417 message: None,
418 }
419 }
420
421 pub fn with_message(mut self, message: impl Into<String>) -> Self {
423 self.message = Some(message.into());
424 self
425 }
426}
427
428#[derive(Error, Debug, Clone)]
430pub struct UnexpectedModelBehavior {
431 pub message: String,
433 pub response: Option<String>,
435}
436
437impl fmt::Display for UnexpectedModelBehavior {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 write!(f, "Unexpected model behavior: {}", self.message)
440 }
441}
442
443impl UnexpectedModelBehavior {
444 pub fn new(message: impl Into<String>) -> Self {
446 Self {
447 message: message.into(),
448 response: None,
449 }
450 }
451
452 pub fn with_response(mut self, response: impl Into<String>) -> Self {
454 self.response = Some(response.into());
455 self
456 }
457}
458
459#[derive(Error, Debug, Clone, Serialize, Deserialize)]
461pub struct ToolRetryError {
462 pub tool_name: String,
464 pub message: String,
466 #[serde(skip_serializing_if = "Option::is_none")]
468 pub tool_call_id: Option<String>,
469}
470
471impl fmt::Display for ToolRetryError {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 write!(f, "Tool '{}' retry: {}", self.tool_name, self.message)
474 }
475}
476
477impl ToolRetryError {
478 pub fn new(tool_name: impl Into<String>, message: impl Into<String>) -> Self {
480 Self {
481 tool_name: tool_name.into(),
482 message: message.into(),
483 tool_call_id: None,
484 }
485 }
486
487 pub fn with_tool_call_id(mut self, id: impl Into<String>) -> Self {
489 self.tool_call_id = Some(id.into());
490 self
491 }
492}
493
494#[derive(Error, Debug, Clone, Serialize, Deserialize)]
496pub struct ApprovalRequired {
497 pub tool_name: String,
499 pub args: serde_json::Value,
501 #[serde(skip_serializing_if = "Option::is_none")]
503 pub reason: Option<String>,
504 #[serde(skip_serializing_if = "Option::is_none")]
506 pub tool_call_id: Option<String>,
507}
508
509impl fmt::Display for ApprovalRequired {
510 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511 write!(f, "Approval required for tool '{}'", self.tool_name)?;
512 if let Some(ref reason) = self.reason {
513 write!(f, ": {}", reason)?;
514 }
515 Ok(())
516 }
517}
518
519impl ApprovalRequired {
520 pub fn new(tool_name: impl Into<String>, args: serde_json::Value) -> Self {
522 Self {
523 tool_name: tool_name.into(),
524 args,
525 reason: None,
526 tool_call_id: None,
527 }
528 }
529
530 pub fn with_reason(mut self, reason: impl Into<String>) -> Self {
532 self.reason = Some(reason.into());
533 self
534 }
535
536 pub fn with_tool_call_id(mut self, id: impl Into<String>) -> Self {
538 self.tool_call_id = Some(id.into());
539 self
540 }
541}
542
543#[derive(Error, Debug, Clone, Serialize, Deserialize)]
545pub struct CallDeferred {
546 pub tool_name: String,
548 pub args: serde_json::Value,
550 #[serde(skip_serializing_if = "Option::is_none")]
552 pub reason: Option<String>,
553 #[serde(skip_serializing_if = "Option::is_none")]
555 pub tool_call_id: Option<String>,
556}
557
558impl fmt::Display for CallDeferred {
559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560 write!(f, "Tool call '{}' deferred", self.tool_name)?;
561 if let Some(ref reason) = self.reason {
562 write!(f, ": {}", reason)?;
563 }
564 Ok(())
565 }
566}
567
568impl CallDeferred {
569 pub fn new(tool_name: impl Into<String>, args: serde_json::Value) -> Self {
571 Self {
572 tool_name: tool_name.into(),
573 args,
574 reason: None,
575 tool_call_id: None,
576 }
577 }
578
579 pub fn with_reason(mut self, reason: impl Into<String>) -> Self {
581 self.reason = Some(reason.into());
582 self
583 }
584
585 pub fn with_tool_call_id(mut self, id: impl Into<String>) -> Self {
587 self.tool_call_id = Some(id.into());
588 self
589 }
590}
591
592#[derive(Error, Debug, Clone, Serialize, Deserialize)]
594pub struct IncompleteToolCall {
595 #[serde(skip_serializing_if = "Option::is_none")]
597 pub tool_name: Option<String>,
598 pub message: String,
600 #[serde(skip_serializing_if = "Option::is_none")]
602 pub partial_data: Option<serde_json::Value>,
603}
604
605impl fmt::Display for IncompleteToolCall {
606 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607 write!(f, "Incomplete tool call")?;
608 if let Some(ref name) = self.tool_name {
609 write!(f, " for '{}'", name)?;
610 }
611 write!(f, ": {}", self.message)
612 }
613}
614
615impl IncompleteToolCall {
616 pub fn new(message: impl Into<String>) -> Self {
618 Self {
619 tool_name: None,
620 message: message.into(),
621 partial_data: None,
622 }
623 }
624
625 pub fn with_tool_name(mut self, name: impl Into<String>) -> Self {
627 self.tool_name = Some(name.into());
628 self
629 }
630
631 pub fn with_partial_data(mut self, data: serde_json::Value) -> Self {
633 self.partial_data = Some(data);
634 self
635 }
636}
637
638#[derive(Error, Debug, Clone)]
640pub struct FallbackExceptionGroup {
641 pub message: String,
643 pub errors: Vec<String>,
645}
646
647impl fmt::Display for FallbackExceptionGroup {
648 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649 write!(f, "{} ({} errors)", self.message, self.errors.len())
650 }
651}
652
653impl FallbackExceptionGroup {
654 pub fn new(message: impl Into<String>, errors: Vec<String>) -> Self {
656 Self {
657 message: message.into(),
658 errors,
659 }
660 }
661
662 pub fn from_errors<E: std::error::Error>(message: impl Into<String>, errors: Vec<E>) -> Self {
664 Self {
665 message: message.into(),
666 errors: errors.iter().map(|e| e.to_string()).collect(),
667 }
668 }
669
670 pub fn is_empty(&self) -> bool {
672 self.errors.is_empty()
673 }
674
675 pub fn len(&self) -> usize {
677 self.errors.len()
678 }
679}
680
681#[cfg(test)]
682mod tests {
683 use super::*;
684
685 #[test]
686 fn test_model_retry() {
687 let err = ModelRetry::new("Invalid JSON output");
688 assert_eq!(err.message, "Invalid JSON output");
689 assert!(err.to_string().contains("Invalid JSON output"));
690 }
691
692 #[test]
693 fn test_usage_limit_exceeded() {
694 let err = UsageLimitExceeded::new(UsageLimitType::TotalTokens, 5000, 4000);
695 assert_eq!(err.current, 5000);
696 assert_eq!(err.max, 4000);
697 assert!(err.to_string().contains("5000"));
698 }
699
700 #[test]
701 fn test_api_error_is_rate_limit() {
702 let err = ModelApiError::new(429, "Rate limited");
703 assert!(err.is_rate_limit());
704 assert!(err.retryable);
705 }
706
707 #[test]
708 fn test_fallback_group() {
709 let group = FallbackExceptionGroup::new(
710 "All providers failed",
711 vec!["Error 1".to_string(), "Error 2".to_string()],
712 );
713 assert_eq!(group.len(), 2);
714 assert!(!group.is_empty());
715 }
716}