1pub use crate::ollama::OllamaError;
8pub use crate::openai::responses::types::ResponsesError;
9pub use crate::openai::AssistantError;
10pub use crate::openai::OpenAIError;
11pub use crate::providers::anthropic::error::AnthropicError;
12pub use crate::providers::azure::AzureOpenAIError;
13pub use crate::providers::cohere::CohereError;
14pub use crate::providers::gemini::GeminiError;
15
16#[derive(Debug)]
22#[non_exhaustive]
23pub enum ProviderError {
24 OpenAI(OpenAIError),
26 Anthropic(AnthropicError),
28 Gemini(GeminiError),
30 Azure(AzureOpenAIError),
32 Cohere(CohereError),
34 Ollama(OllamaError),
36 Assistant(AssistantError),
38 Responses(ResponsesError),
40 DeepSeek(OpenAIError),
42 Qwen(OpenAIError),
44 Moonshot(OpenAIError),
46 Zhipu(OpenAIError),
48 Mistral(OpenAIError),
50 OpenAICompatible {
54 provider: String,
56 source: OpenAIError,
58 },
59 Config(String),
61 Testkit(String),
63}
64
65impl std::fmt::Display for ProviderError {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 ProviderError::OpenAI(e) => write!(f, "OpenAI error: {e}"),
69 ProviderError::Anthropic(e) => write!(f, "Anthropic error: {e}"),
70 ProviderError::Gemini(e) => write!(f, "Gemini error: {e}"),
71 ProviderError::Azure(e) => write!(f, "Azure OpenAI error: {e}"),
72 ProviderError::Cohere(e) => write!(f, "Cohere error: {e}"),
73 ProviderError::Ollama(e) => write!(f, "Ollama error: {e}"),
74 ProviderError::Assistant(e) => write!(f, "Assistant error: {e}"),
75 ProviderError::Responses(e) => write!(f, "Responses error: {e}"),
76 ProviderError::DeepSeek(e) => write!(f, "DeepSeek error: {e}"),
77 ProviderError::Qwen(e) => write!(f, "Qwen error: {e}"),
78 ProviderError::Moonshot(e) => write!(f, "Moonshot error: {e}"),
79 ProviderError::Zhipu(e) => write!(f, "Zhipu error: {e}"),
80 ProviderError::Mistral(e) => write!(f, "Mistral error: {e}"),
81 ProviderError::OpenAICompatible { provider, source } => {
82 write!(f, "{provider} (OpenAI-compatible) error: {source}")
83 }
84 ProviderError::Config(msg) => write!(f, "Configuration error: {msg}"),
85 ProviderError::Testkit(msg) => write!(f, "Testkit error: {msg}"),
86 }
87 }
88}
89
90impl std::error::Error for ProviderError {
91 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
92 match self {
93 ProviderError::OpenAI(e) => Some(e),
94 ProviderError::Anthropic(e) => Some(e),
95 ProviderError::Gemini(e) => Some(e),
96 ProviderError::Azure(e) => Some(e),
97 ProviderError::Cohere(e) => Some(e),
98 ProviderError::Ollama(e) => Some(e),
99 ProviderError::Assistant(e) => Some(e),
100 ProviderError::Responses(e) => Some(e),
101 ProviderError::DeepSeek(e) => Some(e),
102 ProviderError::Qwen(e) => Some(e),
103 ProviderError::Moonshot(e) => Some(e),
104 ProviderError::Zhipu(e) => Some(e),
105 ProviderError::Mistral(e) => Some(e),
106 ProviderError::OpenAICompatible { source, .. } => Some(source),
107 ProviderError::Config(_) => None,
108 ProviderError::Testkit(_) => None,
109 }
110 }
111}
112
113impl From<OpenAIError> for ProviderError {
116 fn from(e: OpenAIError) -> Self {
117 ProviderError::OpenAI(e)
118 }
119}
120impl From<AnthropicError> for ProviderError {
121 fn from(e: AnthropicError) -> Self {
122 ProviderError::Anthropic(e)
123 }
124}
125impl From<GeminiError> for ProviderError {
126 fn from(e: GeminiError) -> Self {
127 ProviderError::Gemini(e)
128 }
129}
130impl From<OllamaError> for ProviderError {
131 fn from(e: OllamaError) -> Self {
132 ProviderError::Ollama(e)
133 }
134}
135impl From<AssistantError> for ProviderError {
136 fn from(e: AssistantError) -> Self {
137 ProviderError::Assistant(e)
138 }
139}
140impl From<ResponsesError> for ProviderError {
141 fn from(e: ResponsesError) -> Self {
142 ProviderError::Responses(e)
143 }
144}
145impl From<AzureOpenAIError> for ProviderError {
146 fn from(e: AzureOpenAIError) -> Self {
147 ProviderError::Azure(e)
148 }
149}
150impl From<CohereError> for ProviderError {
151 fn from(e: CohereError) -> Self {
152 ProviderError::Cohere(e)
153 }
154}
155
156impl From<String> for ProviderError {
160 fn from(msg: String) -> Self {
161 ProviderError::Testkit(msg)
162 }
163}
164
165impl From<ProviderError> for lc_core::LcelError {
170 fn from(err: ProviderError) -> Self {
171 lc_core::LcelError::Provider(err.to_string())
172 }
173}
174
175impl From<OpenAIError> for lc_core::LcelError {
182 fn from(err: OpenAIError) -> Self {
183 lc_core::LcelError::Provider(err.to_string())
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
193 fn openai_error_into_lcel_error() {
194 let e = OpenAIError::Api("rate limited".to_string());
195 let lcel: lc_core::LcelError = e.into();
196 assert!(matches!(
197 lcel,
198 lc_core::LcelError::Provider(ref msg) if msg.contains("API error: rate limited")
199 ));
200 }
201
202 #[test]
204 fn qwen_provider_error_into_lcel_error() {
205 let e = ProviderError::Qwen(OpenAIError::Http("timeout".to_string()));
206 let lcel: lc_core::LcelError = e.into();
207 assert!(matches!(
208 lcel,
209 lc_core::LcelError::Provider(ref msg) if msg.contains("Qwen error") && msg.contains("timeout")
210 ));
211 }
212
213 #[test]
214 fn deepseek_provider_error_into_lcel_error() {
215 let e = ProviderError::DeepSeek(OpenAIError::Parse("bad json".to_string()));
216 let lcel: lc_core::LcelError = e.into();
217 assert!(matches!(
218 lcel,
219 lc_core::LcelError::Provider(ref msg) if msg.contains("DeepSeek error")
220 ));
221 }
222
223 #[test]
225 fn openai_compatible_error_label_and_source() {
226 let e = ProviderError::OpenAICompatible {
227 provider: "groq".to_string(),
228 source: OpenAIError::Api("HTTP 429: slow down".to_string()),
229 };
230 let text = e.to_string();
231 assert!(text.contains("groq"), "{text}");
232 assert!(text.contains("OpenAI-compatible"), "{text}");
233 assert!(text.contains("429"), "{text}");
234 assert!(std::error::Error::source(&e).is_some());
235
236 let lcel: lc_core::LcelError = e.into();
237 assert!(matches!(
238 lcel,
239 lc_core::LcelError::Provider(ref msg) if msg.contains("groq")
240 ));
241 }
242}