Skip to main content

reson_agentic/
error.rs

1//! Error types for Reson
2//!
3//! This module defines the error hierarchy for the Reson framework,
4//! distinguishing between retryable and non-retryable errors.
5
6use thiserror::Error;
7
8/// Result type alias for Reson operations
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// Main error type for Reson operations
12#[derive(Error, Debug)]
13pub enum Error {
14    /// Retryable inference error (network issues, rate limits, 5xx errors)
15    #[error("Inference error: {0}")]
16    Inference(String),
17
18    /// Context length exceeded (prompt + completion too long)
19    #[error("Context length exceeded: {0}")]
20    ContextLengthExceeded(String),
21
22    /// Maximum retries exhausted (includes last error for context)
23    #[error("Retries exceeded: {0}")]
24    RetriesExceeded(String),
25
26    /// Non-retryable error (validation, 4xx errors)
27    #[error("Non-retryable error: {0}")]
28    NonRetryable(String),
29
30    /// Parse error when parsing LLM output
31    #[error("Parse error: {0}")]
32    Parse(String),
33
34    /// Validation error for structured outputs
35    #[error("Validation error: {0}")]
36    Validation(String),
37
38    /// Tool not found in registry
39    #[error("Tool not found: {0}")]
40    ToolNotFound(String),
41
42    /// Network error from reqwest
43    #[error("Network error: {0}")]
44    Network(#[from] reqwest::Error),
45
46    /// JSON serialization/deserialization error
47    #[error("JSON error: {0}")]
48    Json(#[from] serde_json::Error),
49
50    /// UTF-8 encoding error
51    #[error("UTF-8 error: {0}")]
52    Utf8(#[from] std::str::Utf8Error),
53
54    /// UTF-8 conversion error from String
55    #[error("UTF-8 conversion error: {0}")]
56    FromUtf8(#[from] std::string::FromUtf8Error),
57
58    /// IO error
59    #[error("IO error: {0}")]
60    Io(#[from] std::io::Error),
61
62    /// Invalid provider specified
63    #[error("Invalid provider: {0}")]
64    InvalidProvider(String),
65
66    /// Missing API key
67    #[error("Missing API key: {0}")]
68    MissingApiKey(String),
69
70    /// Runtime not used (validation error)
71    #[error("Runtime was not used in agentic function")]
72    RuntimeNotUsed,
73}
74
75impl Error {
76    /// Check if error is retryable (suitable for backoff retry)
77    pub fn is_retryable(&self) -> bool {
78        matches!(self, Error::Inference(_) | Error::Network(_) | Error::Io(_))
79    }
80
81    /// Check if error indicates retries were exceeded
82    pub fn is_retries_exceeded(&self) -> bool {
83        matches!(self, Error::RetriesExceeded(_))
84    }
85
86    /// Check if error indicates incomplete/partial data (for streaming)
87    pub fn is_incomplete(&self) -> bool {
88        matches!(self, Error::Parse(msg) if msg.contains("incomplete"))
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_error_display() {
98        let err = Error::Inference("rate limit".to_string());
99        assert_eq!(err.to_string(), "Inference error: rate limit");
100    }
101
102    #[test]
103    fn test_is_retryable() {
104        assert!(Error::Inference("test".to_string()).is_retryable());
105        assert!(Error::Io(std::io::Error::other("test")).is_retryable());
106        assert!(!Error::NonRetryable("test".to_string()).is_retryable());
107        assert!(!Error::Validation("test".to_string()).is_retryable());
108    }
109
110    #[test]
111    fn test_is_retries_exceeded() {
112        assert!(Error::RetriesExceeded("test".to_string()).is_retries_exceeded());
113        assert!(!Error::Inference("test".to_string()).is_retries_exceeded());
114    }
115
116    #[test]
117    fn test_is_incomplete() {
118        assert!(Error::Parse("incomplete data".to_string()).is_incomplete());
119        assert!(!Error::Parse("malformed json".to_string()).is_incomplete());
120    }
121
122    #[test]
123    fn test_from_conversions() {
124        let json_err: Error = serde_json::from_str::<i32>("invalid").unwrap_err().into();
125        assert!(matches!(json_err, Error::Json(_)));
126
127        let io_err: Error = std::io::Error::other("test").into();
128        assert!(matches!(io_err, Error::Io(_)));
129    }
130}