llm_observatory_sdk/
error.rs

1// Copyright 2025 LLM Observatory Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Error types for the LLM Observatory SDK.
5
6/// Result type alias using the SDK's Error type.
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Error types for SDK operations.
10#[derive(Debug, thiserror::Error)]
11pub enum Error {
12    /// Configuration error
13    #[error("Configuration error: {0}")]
14    Config(String),
15
16    /// OpenTelemetry initialization error
17    #[error("OpenTelemetry error: {0}")]
18    OpenTelemetry(String),
19
20    /// HTTP request error
21    #[error("HTTP error: {0}")]
22    Http(#[from] reqwest::Error),
23
24    /// API error from provider
25    #[error("API error: {status} - {message}")]
26    Api {
27        /// HTTP status code
28        status: u16,
29        /// Error message
30        message: String,
31    },
32
33    /// Rate limit exceeded
34    #[error("Rate limit exceeded: {0}")]
35    RateLimit(String),
36
37    /// Authentication error
38    #[error("Authentication error: {0}")]
39    Auth(String),
40
41    /// Invalid API key
42    #[error("Invalid API key")]
43    InvalidApiKey,
44
45    /// Serialization/deserialization error
46    #[error("Serialization error: {0}")]
47    Serialization(#[from] serde_json::Error),
48
49    /// Invalid input
50    #[error("Invalid input: {0}")]
51    InvalidInput(String),
52
53    /// Streaming error
54    #[error("Streaming error: {0}")]
55    Stream(String),
56
57    /// Cost calculation error
58    #[error("Cost calculation error: {0}")]
59    CostCalculation(String),
60
61    /// Model not found
62    #[error("Model not found: {0}")]
63    ModelNotFound(String),
64
65    /// Timeout error
66    #[error("Request timeout")]
67    Timeout,
68
69    /// Internal SDK error
70    #[error("Internal error: {0}")]
71    Internal(String),
72
73    /// Core error propagation
74    #[error("Core error: {0}")]
75    Core(#[from] llm_observatory_core::Error),
76}
77
78impl Error {
79    /// Create a configuration error.
80    pub fn config(msg: impl Into<String>) -> Self {
81        Self::Config(msg.into())
82    }
83
84    /// Create an API error.
85    pub fn api(status: u16, message: impl Into<String>) -> Self {
86        Self::Api {
87            status,
88            message: message.into(),
89        }
90    }
91
92    /// Create a rate limit error.
93    pub fn rate_limit(msg: impl Into<String>) -> Self {
94        Self::RateLimit(msg.into())
95    }
96
97    /// Create an authentication error.
98    pub fn auth(msg: impl Into<String>) -> Self {
99        Self::Auth(msg.into())
100    }
101
102    /// Create an invalid input error.
103    pub fn invalid_input(msg: impl Into<String>) -> Self {
104        Self::InvalidInput(msg.into())
105    }
106
107    /// Create a streaming error.
108    pub fn stream(msg: impl Into<String>) -> Self {
109        Self::Stream(msg.into())
110    }
111
112    /// Create a cost calculation error.
113    pub fn cost_calculation(msg: impl Into<String>) -> Self {
114        Self::CostCalculation(msg.into())
115    }
116
117    /// Create an internal error.
118    pub fn internal(msg: impl Into<String>) -> Self {
119        Self::Internal(msg.into())
120    }
121
122    /// Check if the error is retryable.
123    pub fn is_retryable(&self) -> bool {
124        matches!(
125            self,
126            Error::RateLimit(_) | Error::Timeout | Error::Api { status: 429, .. }
127        )
128    }
129
130    /// Check if the error is an authentication error.
131    pub fn is_auth_error(&self) -> bool {
132        matches!(
133            self,
134            Error::Auth(_) | Error::InvalidApiKey | Error::Api { status: 401, .. }
135        )
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_error_creation() {
145        let err = Error::config("test config error");
146        assert!(matches!(err, Error::Config(_)));
147    }
148
149    #[test]
150    fn test_is_retryable() {
151        let rate_limit = Error::rate_limit("too many requests");
152        assert!(rate_limit.is_retryable());
153
154        let timeout = Error::Timeout;
155        assert!(timeout.is_retryable());
156
157        let config = Error::config("bad config");
158        assert!(!config.is_retryable());
159    }
160
161    #[test]
162    fn test_is_auth_error() {
163        let invalid_key = Error::InvalidApiKey;
164        assert!(invalid_key.is_auth_error());
165
166        let auth_err = Error::auth("invalid token");
167        assert!(auth_err.is_auth_error());
168
169        let api_401 = Error::api(401, "unauthorized");
170        assert!(api_401.is_auth_error());
171
172        let api_500 = Error::api(500, "server error");
173        assert!(!api_500.is_auth_error());
174    }
175}