1use gemini_genai_rs::session::SessionError;
4
5pub type AgentResult<T> = std::result::Result<T, AgentError>;
10
11#[derive(Debug, thiserror::Error)]
13pub enum AgentError {
14 #[error("Session error: {0}")]
16 Session(#[from] SessionError),
17
18 #[error("Tool error: {0}")]
20 Tool(#[from] ToolError),
21
22 #[error("Unknown agent: {0}")]
24 UnknownAgent(String),
25
26 #[error("Transfer requested to agent: {0}")]
28 TransferRequested(String),
29
30 #[error("Agent transfer failed: {0}")]
32 TransferFailed(String),
33
34 #[error("Agent session closed")]
36 SessionClosed,
37
38 #[error("Timeout")]
40 Timeout,
41
42 #[error("Configuration error: {0}")]
44 Config(String),
45
46 #[error("{0}")]
48 Other(String),
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
59#[error("{}", issues.join("; "))]
60pub struct ConfigError {
61 pub issues: Vec<String>,
63}
64
65impl ConfigError {
66 pub fn new(issue: impl Into<String>) -> Self {
68 Self {
69 issues: vec![issue.into()],
70 }
71 }
72
73 pub fn from_issues(issues: Vec<String>) -> Result<(), Self> {
75 if issues.is_empty() {
76 Ok(())
77 } else {
78 Err(Self { issues })
79 }
80 }
81}
82
83impl From<ConfigError> for AgentError {
84 fn from(err: ConfigError) -> Self {
85 AgentError::Config(err.to_string())
86 }
87}
88
89#[derive(Debug, Clone, thiserror::Error)]
91pub enum ToolError {
92 #[error("Tool execution failed: {0}")]
94 ExecutionFailed(String),
95
96 #[error("Tool not found: {0}")]
98 NotFound(String),
99
100 #[error("Invalid arguments: {0}")]
102 InvalidArgs(String),
103
104 #[error("Tool cancelled")]
106 Cancelled,
107
108 #[error("Tool execution timed out after {0:?}")]
110 Timeout(std::time::Duration),
111
112 #[error("{0}")]
114 Other(String),
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use std::time::Duration;
121
122 #[test]
123 fn agent_error_display_messages() {
124 let err = AgentError::UnknownAgent("foo".into());
125 assert_eq!(err.to_string(), "Unknown agent: foo");
126
127 let err = AgentError::TransferRequested("bar".into());
128 assert_eq!(err.to_string(), "Transfer requested to agent: bar");
129
130 let err = AgentError::TransferFailed("baz".into());
131 assert_eq!(err.to_string(), "Agent transfer failed: baz");
132
133 let err = AgentError::SessionClosed;
134 assert_eq!(err.to_string(), "Agent session closed");
135
136 let err = AgentError::Timeout;
137 assert_eq!(err.to_string(), "Timeout");
138
139 let err = AgentError::Config("bad value".into());
140 assert_eq!(err.to_string(), "Configuration error: bad value");
141
142 let err = AgentError::Other("something".into());
143 assert_eq!(err.to_string(), "something");
144 }
145
146 #[test]
147 fn agent_error_from_session_error() {
148 use gemini_genai_rs::session::SessionError;
149 use gemini_genai_rs::session::WebSocketError;
150
151 let ws_err = SessionError::WebSocket(WebSocketError::ConnectionRefused("refused".into()));
152 let agent_err: AgentError = ws_err.into();
153 let msg = agent_err.to_string();
154 assert!(msg.contains("Session error"), "got: {msg}");
155 }
156
157 #[test]
158 fn agent_error_from_tool_error() {
159 let tool_err = ToolError::NotFound("my_tool".into());
160 let agent_err: AgentError = tool_err.into();
161 let msg = agent_err.to_string();
162 assert!(msg.contains("Tool error"), "got: {msg}");
163 assert!(msg.contains("my_tool"), "got: {msg}");
164 }
165
166 #[test]
167 fn config_error_joins_issues_and_converts() {
168 let err = ConfigError {
169 issues: vec!["a".into(), "b".into()],
170 };
171 assert_eq!(err.to_string(), "a; b");
172 assert!(ConfigError::from_issues(vec![]).is_ok());
173 let agent_err: AgentError = ConfigError::new("bad").into();
174 assert_eq!(agent_err.to_string(), "Configuration error: bad");
175 }
176
177 #[test]
178 fn tool_error_display_messages() {
179 assert_eq!(
180 ToolError::ExecutionFailed("boom".into()).to_string(),
181 "Tool execution failed: boom"
182 );
183 assert_eq!(
184 ToolError::NotFound("x".into()).to_string(),
185 "Tool not found: x"
186 );
187 assert_eq!(
188 ToolError::InvalidArgs("bad".into()).to_string(),
189 "Invalid arguments: bad"
190 );
191 assert_eq!(ToolError::Cancelled.to_string(), "Tool cancelled");
192 assert_eq!(ToolError::Other("misc".into()).to_string(), "misc");
193 }
194
195 #[test]
196 fn tool_error_timeout_shows_duration() {
197 let err = ToolError::Timeout(Duration::from_secs(5));
198 let msg = err.to_string();
199 assert!(msg.contains("5s"), "got: {msg}");
200 assert!(msg.contains("timed out"), "got: {msg}");
201 }
202
203 #[test]
204 fn tool_error_is_clone() {
205 let err = ToolError::ExecutionFailed("test".into());
206 let cloned = err.clone();
207 assert_eq!(err.to_string(), cloned.to_string());
208
209 let err2 = ToolError::Timeout(Duration::from_millis(100));
210 let cloned2 = err2.clone();
211 assert_eq!(err2.to_string(), cloned2.to_string());
212 }
213}