autumn_plugin_agent/
error.rs1use http::StatusCode;
12use thiserror::Error;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum ErrorKind {
21 Config,
23 Authentication,
26 RateLimited,
28 Provider,
30 Transport,
32 Tool,
34 Budget,
36 Decode,
38}
39
40impl ErrorKind {
41 #[must_use]
46 pub const fn status_code(self) -> StatusCode {
47 match self {
48 Self::Config | Self::Tool | Self::Decode => StatusCode::INTERNAL_SERVER_ERROR,
49 Self::Authentication | Self::Provider => StatusCode::BAD_GATEWAY,
50 Self::RateLimited => StatusCode::TOO_MANY_REQUESTS,
51 Self::Transport => StatusCode::SERVICE_UNAVAILABLE,
52 Self::Budget => StatusCode::PAYLOAD_TOO_LARGE,
53 }
54 }
55}
56
57#[derive(Debug, Error)]
63#[error("[{kind:?}] {message}")]
64pub struct AgentError {
65 kind: ErrorKind,
66 message: String,
67 #[source]
68 source: Option<Box<dyn std::error::Error + Send + Sync>>,
69}
70
71impl AgentError {
72 #[must_use]
74 pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
75 Self {
76 kind,
77 message: message.into(),
78 source: None,
79 }
80 }
81
82 pub fn with_source<E>(kind: ErrorKind, message: impl Into<String>, source: E) -> Self
84 where
85 E: std::error::Error + Send + Sync + 'static,
86 {
87 Self {
88 kind,
89 message: message.into(),
90 source: Some(Box::new(source)),
91 }
92 }
93
94 #[must_use]
96 pub const fn kind(&self) -> ErrorKind {
97 self.kind
98 }
99
100 #[must_use]
102 pub const fn status_code(&self) -> StatusCode {
103 self.kind.status_code()
104 }
105
106 #[must_use]
108 pub fn message(&self) -> &str {
109 &self.message
110 }
111
112 #[must_use]
120 pub fn into_autumn_error(self) -> autumn_web::AutumnError {
121 let message = self.to_string();
122 match self.kind() {
123 ErrorKind::Config | ErrorKind::Tool | ErrorKind::Decode => {
124 autumn_web::AutumnError::internal_server_error_msg(message)
125 }
126 ErrorKind::Authentication | ErrorKind::Provider => {
127 autumn_web::AutumnError::internal_server_error_msg(message)
128 .with_status(StatusCode::BAD_GATEWAY)
129 }
130 ErrorKind::RateLimited => autumn_web::AutumnError::service_unavailable_msg(message)
131 .with_status(StatusCode::TOO_MANY_REQUESTS),
132 ErrorKind::Transport => autumn_web::AutumnError::service_unavailable_msg(message),
133 ErrorKind::Budget => autumn_web::AutumnError::internal_server_error_msg(message)
134 .with_status(StatusCode::PAYLOAD_TOO_LARGE),
135 }
136 }
137}
138
139impl From<serde_json::Error> for AgentError {
140 fn from(err: serde_json::Error) -> Self {
141 Self::with_source(ErrorKind::Decode, "failed to decode JSON", err)
142 }
143}
144
145impl From<reqwest::Error> for AgentError {
146 fn from(err: reqwest::Error) -> Self {
147 let kind = if err.is_timeout() || err.is_connect() {
148 ErrorKind::Transport
149 } else if err.is_decode() {
150 ErrorKind::Decode
151 } else {
152 ErrorKind::Transport
153 };
154 Self::with_source(kind, "LLM provider request failed", err)
155 }
156}
157
158#[cfg(test)]
159mod tests;