1use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct AgentAction {
12 pub tool: String,
14
15 pub tool_input: ToolInput,
17
18 pub log: String,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(tag = "type", rename_all = "snake_case")]
32pub enum ToolInput {
33 String { value: String },
35
36 Object { value: serde_json::Value },
38}
39
40impl Default for ToolInput {
41 fn default() -> Self {
42 ToolInput::String {
43 value: String::new(),
44 }
45 }
46}
47
48impl std::fmt::Display for ToolInput {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 ToolInput::String { value } => write!(f, "{}", value),
52 ToolInput::Object { value } => write!(
53 f,
54 "{}",
55 serde_json::to_string(value).unwrap_or_else(|_| "unknown".to_string())
56 ),
57 }
58 }
59}
60
61impl TryFrom<serde_json::Value> for ToolInput {
67 type Error = String;
68
69 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
70 match value {
71 serde_json::Value::String(s) => Ok(ToolInput::String { value: s }),
72 serde_json::Value::Object(_) => Ok(ToolInput::Object { value }),
73 other => Err(format!(
74 "ToolInput must be a string or object, got: {}",
75 other
76 )),
77 }
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct AgentFinish {
86 pub return_values: HashMap<String, serde_json::Value>,
88
89 pub log: String,
91}
92
93impl AgentFinish {
94 pub fn new(output: String, log: String) -> Self {
96 let mut return_values = HashMap::new();
97 return_values.insert("output".to_string(), serde_json::Value::String(output));
98 Self { return_values, log }
99 }
100
101 pub fn output(&self) -> Option<&str> {
103 self.return_values.get("output").and_then(|v| v.as_str())
104 }
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct AgentStep {
112 pub action: AgentAction,
114
115 pub observation: String,
117}
118
119impl AgentStep {
120 pub fn new(action: AgentAction, observation: String) -> Self {
122 Self {
123 action,
124 observation,
125 }
126 }
127}
128
129#[derive(Debug, Clone)]
133pub enum AgentOutput {
134 Action(AgentAction),
136
137 Actions(Vec<AgentAction>),
139
140 Finish(AgentFinish),
142}
143
144impl AgentOutput {
145 pub fn is_finish(&self) -> bool {
147 matches!(self, AgentOutput::Finish(_))
148 }
149
150 pub fn is_action(&self) -> bool {
152 matches!(self, AgentOutput::Action(_) | AgentOutput::Actions(_))
153 }
154
155 pub fn action(&self) -> Option<&AgentAction> {
157 match self {
158 AgentOutput::Action(action) => Some(action),
159 _ => None,
160 }
161 }
162
163 pub fn actions(&self) -> Vec<&AgentAction> {
165 match self {
166 AgentOutput::Action(action) => vec![action],
167 AgentOutput::Actions(actions) => actions.iter().collect(),
168 _ => vec![],
169 }
170 }
171
172 pub fn finish(&self) -> Option<&AgentFinish> {
174 match self {
175 AgentOutput::Finish(finish) => Some(finish),
176 _ => None,
177 }
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 fn create_action(tool: &str, input: &str) -> AgentAction {
186 AgentAction {
187 tool: tool.to_string(),
188 tool_input: ToolInput::String {
189 value: input.to_string(),
190 },
191 log: "test".to_string(),
192 }
193 }
194
195 #[test]
196 fn test_agent_output_single_action() {
197 let action = create_action("calculator", "1+2");
198 let output = AgentOutput::Action(action);
199
200 assert!(output.is_action());
201 assert!(!output.is_finish());
202 assert_eq!(output.actions().len(), 1);
203 }
204
205 #[test]
206 fn test_agent_output_multiple_actions() {
207 let actions = vec![
208 create_action("calculator", "1+2"),
209 create_action("datetime", "now"),
210 ];
211 let output = AgentOutput::Actions(actions);
212
213 assert!(output.is_action());
214 assert!(!output.is_finish());
215 assert_eq!(output.actions().len(), 2);
216 assert!(output.action().is_none());
217 }
218
219 #[test]
220 fn test_agent_output_finish() {
221 let finish = AgentFinish::new("answer".to_string(), "log".to_string());
222 let output = AgentOutput::Finish(finish);
223
224 assert!(!output.is_action());
225 assert!(output.is_finish());
226 assert_eq!(output.actions().len(), 0);
227 assert!(output.finish().is_some());
228 }
229
230 #[test]
231 fn test_agent_finish_output() {
232 let finish = AgentFinish::new("the answer is 42".to_string(), String::new());
233 assert_eq!(finish.output(), Some("the answer is 42"));
234 }
235}