1#[derive(Debug, Clone, thiserror::Error)]
13pub enum LlmError {
14 #[error("Config error: {0}")]
16 Config(String),
17
18 #[error("API error {status}: {message}")]
20 LlmApi { status: u16, message: String },
21
22 #[error("LLM error: {0}")]
24 Llm(String),
25
26 #[error("Stream error: {0}")]
28 Stream(String),
29}
30
31impl LlmError {
32 pub fn config(msg: impl Into<String>) -> Self {
33 Self::Config(msg.into())
34 }
35
36 pub fn llm(msg: impl Into<String>) -> Self {
37 Self::Llm(msg.into())
38 }
39
40 pub fn stream(msg: impl Into<String>) -> Self {
41 Self::Stream(msg.into())
42 }
43
44 pub fn api(status: u16, message: impl Into<String>) -> Self {
45 Self::LlmApi {
46 status,
47 message: message.into(),
48 }
49 }
50
51 pub fn status(&self) -> Option<u16> {
55 match self {
56 Self::LlmApi { status, .. } => Some(*status),
57 _ => None,
58 }
59 }
60}
61
62impl From<serde_json::Error> for LlmError {
65 fn from(e: serde_json::Error) -> Self {
66 LlmError::Llm(format!("JSON error: {e}"))
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn display_config() {
76 let err = LlmError::config("missing key");
77 assert_eq!(err.to_string(), "Config error: missing key");
78 }
79
80 #[test]
81 fn display_api() {
82 let err = LlmError::api(401, "unauthorized");
83 assert_eq!(err.to_string(), "API error 401: unauthorized");
84 assert_eq!(err.status(), Some(401));
85 }
86
87 #[test]
88 fn status_is_none_for_non_api_errors() {
89 assert_eq!(LlmError::config("missing key").status(), None);
90 assert_eq!(LlmError::llm("timeout").status(), None);
91 assert_eq!(LlmError::stream("bad SSE").status(), None);
92 }
93
94 #[test]
95 fn display_llm() {
96 let err = LlmError::llm("timeout");
97 assert_eq!(err.to_string(), "LLM error: timeout");
98 }
99
100 #[test]
101 fn display_stream() {
102 let err = LlmError::stream("bad SSE");
103 assert_eq!(err.to_string(), "Stream error: bad SSE");
104 }
105
106 #[test]
107 fn from_serde_json_error() {
108 let json_err = serde_json::from_str::<serde_json::Value>("bad").unwrap_err();
109 let llm_err: LlmError = json_err.into();
110 assert!(llm_err.to_string().contains("JSON error"));
111 }
112
113 #[test]
114 fn is_clone() {
115 let err = LlmError::llm("test");
116 let cloned = err.clone();
117 assert_eq!(err.to_string(), cloned.to_string());
118 }
119}