1use std::fmt;
2
3#[derive(Debug, Clone)]
5pub enum RuntimeError {
6 Workflow(WorkflowError),
8
9 Agent(AgentError),
11
12 Llm(LlmError),
14
15 Tool(ToolError),
17
18 Config(ConfigError),
20
21 RetryExhausted {
23 operation: String,
24 attempts: u32,
25 last_error: Box<RuntimeError>,
26 },
27
28 Timeout { operation: String, duration_ms: u64 },
30}
31
32#[derive(Debug, Clone)]
34pub struct WorkflowError {
35 pub code: WorkflowErrorCode,
36 pub message: String,
37 pub step_id: Option<String>,
38 pub context: Option<String>,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum WorkflowErrorCode {
43 StepExecutionFailed,
44 InvalidStepOutput,
45 CycleDetected,
46 MaxIterationsExceeded,
47 ConditionalEvaluationFailed,
48}
49
50#[derive(Debug, Clone)]
52pub struct AgentError {
53 pub code: AgentErrorCode,
54 pub message: String,
55 pub agent_name: Option<String>,
56 pub context: Option<String>,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum AgentErrorCode {
61 ExecutionFailed,
62 InvalidInput,
63 InvalidOutput,
64 ToolExecutionFailed,
65 MaxToolIterationsExceeded,
66 MissingLlmClient,
67 MissingSystemPrompt,
68}
69
70#[derive(Debug, Clone)]
72pub struct LlmError {
73 pub code: LlmErrorCode,
74 pub message: String,
75 pub provider: Option<String>,
76 pub model: Option<String>,
77 pub retryable: bool,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum LlmErrorCode {
82 NetworkError,
83 AuthenticationFailed,
84 RateLimitExceeded,
85 InvalidRequest,
86 InvalidResponse,
87 ModelNotFound,
88 ContextLengthExceeded,
89 ServerError,
90 ParseError,
91}
92
93#[derive(Debug, Clone)]
95pub struct ToolError {
96 pub code: ToolErrorCode,
97 pub message: String,
98 pub tool_name: Option<String>,
99 pub context: Option<String>,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum ToolErrorCode {
104 InvalidParameters,
105 ExecutionFailed,
106 Timeout,
107 NotFound,
108 McpConnectionFailed,
109 McpToolCallFailed,
110}
111
112#[derive(Debug, Clone)]
114pub struct ConfigError {
115 pub code: ConfigErrorCode,
116 pub message: String,
117 pub field: Option<String>,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum ConfigErrorCode {
122 MissingRequiredField,
123 InvalidValue,
124 ValidationFailed,
125 FileNotFound,
126 ParseError,
127}
128
129impl fmt::Display for RuntimeError {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 match self {
133 RuntimeError::Workflow(e) => write!(f, "Workflow error: {}", e),
134 RuntimeError::Agent(e) => write!(f, "Agent error: {}", e),
135 RuntimeError::Llm(e) => write!(f, "LLM error: {}", e),
136 RuntimeError::Tool(e) => write!(f, "Tool error: {}", e),
137 RuntimeError::Config(e) => write!(f, "Configuration error: {}", e),
138 RuntimeError::RetryExhausted {
139 operation,
140 attempts,
141 last_error,
142 } => {
143 write!(
144 f,
145 "Retry exhausted for '{}' after {} attempts: {}",
146 operation, attempts, last_error
147 )
148 }
149 RuntimeError::Timeout {
150 operation,
151 duration_ms,
152 } => {
153 write!(
154 f,
155 "Operation '{}' timed out after {}ms",
156 operation, duration_ms
157 )
158 }
159 }
160 }
161}
162
163impl fmt::Display for WorkflowError {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 write!(f, "[{:?}] {}", self.code, self.message)?;
166 if let Some(step_id) = &self.step_id {
167 write!(f, " (step: {})", step_id)?;
168 }
169 if let Some(context) = &self.context {
170 write!(f, " - {}", context)?;
171 }
172 Ok(())
173 }
174}
175
176impl fmt::Display for AgentError {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(f, "[{:?}] {}", self.code, self.message)?;
179 if let Some(agent_name) = &self.agent_name {
180 write!(f, " (agent: {})", agent_name)?;
181 }
182 if let Some(context) = &self.context {
183 write!(f, " - {}", context)?;
184 }
185 Ok(())
186 }
187}
188
189impl fmt::Display for LlmError {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 write!(f, "[{:?}] {}", self.code, self.message)?;
192 if let Some(provider) = &self.provider {
193 write!(f, " (provider: {})", provider)?;
194 }
195 if let Some(model) = &self.model {
196 write!(f, " [model: {}]", model)?;
197 }
198 if self.retryable {
199 write!(f, " (retryable)")?;
200 }
201 Ok(())
202 }
203}
204
205impl fmt::Display for ToolError {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 write!(f, "[{:?}] {}", self.code, self.message)?;
208 if let Some(tool_name) = &self.tool_name {
209 write!(f, " (tool: {})", tool_name)?;
210 }
211 if let Some(context) = &self.context {
212 write!(f, " - {}", context)?;
213 }
214 Ok(())
215 }
216}
217
218impl fmt::Display for ConfigError {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "[{:?}] {}", self.code, self.message)?;
221 if let Some(field) = &self.field {
222 write!(f, " (field: {})", field)?;
223 }
224 Ok(())
225 }
226}
227
228impl std::error::Error for RuntimeError {}
230impl std::error::Error for WorkflowError {}
231impl std::error::Error for AgentError {}
232impl std::error::Error for LlmError {}
233impl std::error::Error for ToolError {}
234impl std::error::Error for ConfigError {}
235
236impl LlmError {
238 pub fn is_retryable(&self) -> bool {
240 matches!(
241 self.code,
242 LlmErrorCode::NetworkError
243 | LlmErrorCode::RateLimitExceeded
244 | LlmErrorCode::ServerError
245 )
246 }
247
248 pub fn network(message: impl Into<String>) -> Self {
250 Self {
251 code: LlmErrorCode::NetworkError,
252 message: message.into(),
253 provider: None,
254 model: None,
255 retryable: true,
256 }
257 }
258
259 pub fn rate_limit(message: impl Into<String>) -> Self {
261 Self {
262 code: LlmErrorCode::RateLimitExceeded,
263 message: message.into(),
264 provider: None,
265 model: None,
266 retryable: true,
267 }
268 }
269
270 pub fn server_error(message: impl Into<String>) -> Self {
272 Self {
273 code: LlmErrorCode::ServerError,
274 message: message.into(),
275 provider: None,
276 model: None,
277 retryable: true,
278 }
279 }
280}
281
282impl From<WorkflowError> for RuntimeError {
284 fn from(e: WorkflowError) -> Self {
285 RuntimeError::Workflow(e)
286 }
287}
288
289impl From<AgentError> for RuntimeError {
290 fn from(e: AgentError) -> Self {
291 RuntimeError::Agent(e)
292 }
293}
294
295impl From<LlmError> for RuntimeError {
296 fn from(e: LlmError) -> Self {
297 RuntimeError::Llm(e)
298 }
299}
300
301impl From<ToolError> for RuntimeError {
302 fn from(e: ToolError) -> Self {
303 RuntimeError::Tool(e)
304 }
305}
306
307impl From<ConfigError> for RuntimeError {
308 fn from(e: ConfigError) -> Self {
309 RuntimeError::Config(e)
310 }
311}