tower_llm/error.rs
1//! # Error Handling for the Agents SDK
2//!
3//! This module defines the centralized error handling system for the SDK. It
4//! provides a unified `Result` type and a comprehensive `AgentsError` enum that
5//! covers all potential issues that can arise during an agent's execution.
6//!
7//! ## The `AgentsError` Enum
8//!
9//! The [`AgentsError`] enum is the primary error type used throughout the SDK. It
10//! encapsulates a wide range of possible failures, from API-level errors to
11//! internal logic issues like guardrail violations or tool execution failures.
12//! The use of `thiserror` allows for clean and descriptive error messages.
13//!
14//! ## The `Result` Type Alias
15//!
16//! For convenience, this module provides a `Result<T>` type alias, which is a
17//! shorthand for `std::result::Result<T, AgentsError>`. This simplifies function
18//! signatures throughout the codebase and ensures consistent error handling.
19//!
20//! ### Example: Using the Custom `Result` Type
21//!
22//! ```rust
23//! use tower_llm::error::{Result, AgentsError};
24//!
25//! fn check_input(input: &str) -> Result<()> {
26//! if input.is_empty() {
27//! Err(AgentsError::UserError {
28//! message: "Input cannot be empty.".to_string(),
29//! })
30//! } else {
31//! Ok(())
32//! }
33//! }
34//!
35//! assert!(check_input("Hello").is_ok());
36//! let error = check_input("").unwrap_err();
37//! assert_eq!(error.to_string(), "User error: Input cannot be empty.");
38//! ```
39//! Error types for the Agents SDK
40
41use thiserror::Error;
42
43/// A specialized `Result` type for the Agents SDK.
44///
45/// This type alias simplifies function signatures by providing a default
46/// error type of [`AgentsError`].
47pub type Result<T> = std::result::Result<T, AgentsError>;
48
49/// The main error enum for the Agents SDK.
50///
51/// `AgentsError` consolidates all possible errors that can occur within the
52/// SDK into a single, comprehensive type. This allows for robust and centralized
53/// error handling.
54#[derive(Debug, Error)]
55pub enum AgentsError {
56 /// An error originating from the underlying `async-openai` crate.
57 #[error("OpenAI API error: {0}")]
58 OpenAIError(#[from] async_openai::error::OpenAIError),
59
60 /// Indicates that the maximum number of turns for an agent run has been
61 /// exceeded, preventing infinite loops.
62 #[error("Maximum turns exceeded: {max_turns}")]
63 MaxTurnsExceeded { max_turns: usize },
64
65 /// An input guardrail was triggered, indicating that the user's input
66 /// violated a predefined constraint.
67 #[error("Input guardrail triggered: {message}")]
68 InputGuardrailTriggered { message: String },
69
70 /// An output guardrail was triggered, indicating that the agent's response
71 /// violated a predefined constraint.
72 #[error("Output guardrail triggered: {message}")]
73 OutputGuardrailTriggered { message: String },
74
75 /// An error occurred during the execution of a tool.
76 #[error("Tool execution error: {message}")]
77 ToolExecutionError { message: String },
78
79 /// An error occurred during a handoff between agents.
80 #[error("Handoff error: {message}")]
81 HandoffError { message: String },
82
83 /// An error indicating unexpected or invalid behavior from the LLM.
84 #[error("Model behavior error: {message}")]
85 ModelBehaviorError { message: String },
86
87 /// An error caused by invalid user input or configuration.
88 #[error("User error: {message}")]
89 UserError { message: String },
90
91 /// An error related to session management, such as a failure to read or
92 /// write to the session store.
93 #[error("Session error: {0}")]
94 SessionError(String),
95
96 /// An error that occurred during JSON serialization or deserialization.
97 #[error("Serialization error: {0}")]
98 SerializationError(#[from] serde_json::Error),
99
100 /// An I/O error, typically related to file system operations.
101 #[error("IO error: {0}")]
102 IoError(#[from] std::io::Error),
103
104 /// An error from the database, used by persistent session stores.
105 #[error("Database error: {0}")]
106 DatabaseError(#[from] sqlx::Error),
107
108 /// A catch-all for any other type of error.
109 #[error("{0}")]
110 Other(String),
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_error_display() {
119 let err = AgentsError::MaxTurnsExceeded { max_turns: 10 };
120 assert_eq!(err.to_string(), "Maximum turns exceeded: 10");
121
122 let err = AgentsError::InputGuardrailTriggered {
123 message: "Inappropriate content".to_string(),
124 };
125 assert_eq!(
126 err.to_string(),
127 "Input guardrail triggered: Inappropriate content"
128 );
129 }
130
131 #[test]
132 fn test_error_from_openai() {
133 // This tests that the From trait is properly implemented
134 let openai_err = async_openai::error::OpenAIError::InvalidArgument("test".to_string());
135 let agents_err: AgentsError = openai_err.into();
136 assert!(matches!(agents_err, AgentsError::OpenAIError(_)));
137 }
138
139 #[test]
140 fn test_result_type() {
141 fn example_function() -> Result<String> {
142 Ok("success".to_string())
143 }
144
145 let result = example_function();
146 assert!(result.is_ok());
147 assert_eq!(result.unwrap(), "success");
148 }
149
150 #[test]
151 fn test_error_chaining() {
152 fn might_fail() -> Result<()> {
153 Err(AgentsError::UserError {
154 message: "Something went wrong".to_string(),
155 })
156 }
157
158 let result = might_fail();
159 assert!(result.is_err());
160
161 if let Err(e) = result {
162 assert!(matches!(e, AgentsError::UserError { .. }));
163 }
164 }
165}