rust_agent/agents/
agent.rs

1// Agent interface and related structure definitions
2use anyhow::Error;
3use std::collections::HashMap;
4use crate::tools::{ExampleTool, Tool};
5use crate::core::Runnable;
6
7// Action executed by Agent (simplified)
8#[derive(Clone, Debug)]
9pub struct AgentAction {
10    pub tool: String,
11    pub tool_input: String,
12    pub log: String,
13    pub thought: Option<String>,
14}
15
16// Result when Agent completes execution (simplified)
17#[derive(Clone, Debug)]
18pub struct AgentFinish {
19    pub return_values: HashMap<String, String>,
20}
21
22// Unified Agent output type
23#[derive(Clone, Debug)]
24pub enum AgentOutput {
25    Action(AgentAction),
26    Finish(AgentFinish),
27}
28
29// Minimal Agent interface (separated from Runnable functionality)
30pub trait Agent: Send + Sync {
31    // Get list of available tools
32    fn tools(&self) -> Vec<Box<dyn Tool + Send + Sync>>;
33    
34    // Execute Agent action (consistent with README)
35    fn execute(&self, action: &AgentAction) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<String, Error>> + Send + '_>> {
36        let tools = self.tools();
37        let tool_name = action.tool.clone();
38        let tool_input = action.tool_input.clone();
39        
40        Box::pin(async move {
41            // Find the corresponding tool
42            for tool in tools {
43                if tool.name() == tool_name {
44                    return tool.invoke(&tool_input).await;
45                }
46            }
47            Err(Error::msg(format!("The tool {} not found", tool_name)))
48        })
49    }
50    
51    // Clone agent instance
52    fn clone_agent(&self) -> Box<dyn Agent>;
53}
54
55// Agent runner - specifically handles execution logic
56pub trait AgentRunner: Runnable<HashMap<String, String>, AgentOutput> {
57    // Note: Since it inherits from Runnable, there's no need to redefine the invoke method here
58    // This method will be automatically provided when implementing the Runnable trait
59}
60
61// Simple Agent implementation
62pub struct SimpleAgent {
63    tools: Vec<Box<dyn Tool + Send + Sync>>,
64}
65
66impl SimpleAgent {
67    pub fn new() -> Self {
68        Self {
69            tools: Vec::new(),
70        }
71    }
72    
73    pub fn add_tool(&mut self, tool: Box<dyn Tool + Send + Sync>) {
74        self.tools.push(tool);
75    }
76}
77
78impl Agent for SimpleAgent {
79    fn tools(&self) -> Vec<Box<dyn Tool + Send + Sync>> {
80        // Since Box<dyn Tool> cannot be directly cloned, return an empty vector as a minimal implementation
81        Vec::new()
82    }
83    
84    fn clone_agent(&self) -> Box<dyn Agent> {
85        let mut cloned = SimpleAgent::new();
86        // Clone all tools
87        for tool in &self.tools {
88            let name = tool.name();
89            let description = tool.description();
90            let new_tool = Box::new(ExampleTool::new(name.to_string(), description.to_string()));
91            cloned.add_tool(new_tool);
92        }
93        Box::new(cloned)
94    }
95}
96
97// Simple AgentRunner implementation
98pub struct SimpleAgentRunner {
99    agent: Box<dyn Agent>,
100}
101
102impl SimpleAgentRunner {
103    pub fn new(agent: impl Agent + 'static) -> Self {
104        Self {
105            agent: Box::new(agent),
106        }
107    }
108}
109
110impl Runnable<HashMap<String, String>, AgentOutput> for SimpleAgentRunner {
111    fn invoke(&self, inputs: HashMap<String, String>) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<AgentOutput, Error>> + Send>> {
112        let inputs_clone = inputs.clone();
113        
114        Box::pin(async move {
115            // This is just a simple implementation, in practice should use LLM to decide whether to call a tool or directly return results
116            // For demonstration, we assume if the input contains a "tool" field, call the corresponding tool
117            if let Some(tool_name) = inputs_clone.get("tool") {
118                let tool_input = inputs_clone.get("input").unwrap_or(&"input empty".to_string()).clone();
119                
120                Ok(AgentOutput::Action(AgentAction {
121                    tool: tool_name.to_string(),
122                    tool_input,
123                    log: format!("Invoking tool: {}", tool_name),
124                    thought: Some("Invoking tool".to_string()),
125                }))
126            } else {
127                // Otherwise return a simple completion result
128                let output_text = inputs_clone.get("input").unwrap_or(&"".to_string()).clone();
129                let mut return_values = HashMap::new();
130                return_values.insert("output".to_string(), format!("Finish processing input: {}", output_text));
131                
132                Ok(AgentOutput::Finish(AgentFinish {
133                    return_values,
134                }))
135            }
136        })
137    }
138    
139    fn clone_to_owned(&self) -> Box<dyn Runnable<HashMap<String, String>, AgentOutput> + Send + Sync> {
140        // Use the newly added clone_agent method
141        Box::new(SimpleAgentRunner { agent: self.agent.clone_agent() })
142    }
143}
144
145// Implement Clone trait for SimpleAgent
146impl Clone for SimpleAgent {
147    fn clone(&self) -> Self {
148        let mut agent = SimpleAgent::new();
149        // Since Tool trait doesn't require Clone, we need to create new tool instances
150        // For ExampleTool, we can directly create new instances
151        // This is a minimal implementation
152        for tool in &self.tools {
153            // Try to get tool name to create a new instance
154            let name = tool.name();
155            let description = tool.description();
156            // Create a new ExampleTool as a clone
157            let new_tool = Box::new(ExampleTool::new(name.to_string(), description.to_string()));
158            agent.add_tool(new_tool);
159        }
160        agent
161    }
162}