Skip to main content

chat_core/
error.rs

1use crate::transport::TransportError;
2use crate::types::metadata::Metadata;
3use thiserror::Error;
4
5#[derive(Clone, Debug, Error)]
6pub enum ChatError {
7    #[error("network error: {0}")]
8    Network(String),
9
10    #[error("provider error: {0}")]
11    Provider(String),
12
13    #[error("rate limited")]
14    RateLimited,
15
16    #[error("exceeded maximum steps")]
17    MaxStepsExceeded,
18
19    #[error("invalid response: {0}")]
20    InvalidResponse(String),
21
22    #[error("error in callback function: {0}")]
23    Callback(String),
24
25    #[error("unknown error: {0}")]
26    Other(String),
27}
28
29impl ChatError {
30    /// Whether this error is transient and worth retrying.
31    ///
32    /// Retryable: `RateLimited`, `Network` (transient failures).
33    /// Not retryable: `Provider`, `InvalidResponse`, `MaxStepsExceeded`,
34    /// `Callback`, `Other` (request/response or client-side issues).
35    pub fn is_retryable(&self) -> bool {
36        matches!(self, ChatError::RateLimited | ChatError::Network(_))
37    }
38}
39
40#[derive(Clone, Debug, Error)]
41#[error("Chat engine failed: {err}")]
42pub struct ChatFailure {
43    pub metadata: Option<Metadata>,
44    #[source]
45    pub err: ChatError,
46}
47
48impl From<TransportError> for ChatError {
49    fn from(err: TransportError) -> Self {
50        match err {
51            TransportError::Connection(msg) => ChatError::Network(msg),
52            TransportError::Stream(msg) => ChatError::Network(msg),
53            TransportError::Request { status, message } => match status {
54                Some(429 | 529) => ChatError::RateLimited,
55                _ => ChatError::Provider(message),
56            },
57        }
58    }
59}
60
61impl ChatFailure {
62    pub fn from_err<E: Into<ChatError>>(err: E) -> Self {
63        Self {
64            metadata: None,
65            err: err.into(),
66        }
67    }
68}