langchain_rust/agent/
executor.rs1use std::{collections::HashMap, sync::Arc};
2
3use async_trait::async_trait;
4use serde_json::json;
5use tokio::sync::Mutex;
6
7use super::{agent::Agent, AgentError};
8use crate::schemas::{LogTools, Message};
9use crate::{
10 chain::{chain_trait::Chain, ChainError},
11 language_models::GenerateResult,
12 memory::SimpleMemory,
13 prompt::PromptArgs,
14 schemas::{
15 agent::{AgentAction, AgentEvent},
16 memory::BaseMemory,
17 },
18 tools::Tool,
19};
20
21pub struct AgentExecutor<A>
22where
23 A: Agent,
24{
25 agent: A,
26 max_iterations: Option<i32>,
27 break_if_error: bool,
28 pub memory: Option<Arc<Mutex<dyn BaseMemory>>>,
29}
30
31impl<A> AgentExecutor<A>
32where
33 A: Agent,
34{
35 pub fn from_agent(agent: A) -> Self {
36 Self {
37 agent,
38 max_iterations: Some(10),
39 break_if_error: false,
40 memory: None,
41 }
42 }
43
44 pub fn with_max_iterations(mut self, max_iterations: i32) -> Self {
45 self.max_iterations = Some(max_iterations);
46 self
47 }
48
49 pub fn with_memory(mut self, memory: Arc<Mutex<dyn BaseMemory>>) -> Self {
50 self.memory = Some(memory);
51 self
52 }
53
54 pub fn with_break_if_error(mut self, break_if_error: bool) -> Self {
55 self.break_if_error = break_if_error;
56 self
57 }
58
59 fn get_name_to_tools(&self) -> HashMap<String, Arc<dyn Tool>> {
60 let mut name_to_tool = HashMap::new();
61 for tool in self.agent.get_tools().iter() {
62 log::debug!("Loading Tool:{}", tool.name());
63 name_to_tool.insert(tool.name().trim().replace(" ", "_"), tool.clone());
64 }
65 name_to_tool
66 }
67}
68
69#[async_trait]
70impl<A> Chain for AgentExecutor<A>
71where
72 A: Agent + Send + Sync,
73{
74 async fn call(&self, input_variables: PromptArgs) -> Result<GenerateResult, ChainError> {
75 let mut input_variables = input_variables.clone();
76 let name_to_tools = self.get_name_to_tools();
77 let mut steps: Vec<(AgentAction, String)> = Vec::new();
78 log::debug!("steps: {:?}", steps);
79 if let Some(memory) = &self.memory {
80 let memory = memory.lock().await;
81 input_variables.insert("chat_history".to_string(), json!(memory.messages()));
82 } else {
83 input_variables.insert(
84 "chat_history".to_string(),
85 json!(SimpleMemory::new().messages()),
86 );
87 }
88
89 loop {
90 let agent_event = self
91 .agent
92 .plan(&steps, input_variables.clone())
93 .await
94 .map_err(|e| ChainError::AgentError(format!("Error in agent planning: {}", e)))?;
95 match agent_event {
96 AgentEvent::Action(actions) => {
97 for action in actions {
98 log::debug!("Action: {:?}", action.tool_input);
99 let tool = name_to_tools
100 .get(&action.tool.trim().replace(" ", "_"))
101 .ok_or_else(|| {
102 AgentError::ToolError(format!("Tool {} not found", action.tool))
103 })
104 .map_err(|e| ChainError::AgentError(e.to_string()))?;
105
106 let observation_result = tool.call(&action.tool_input).await;
107
108 let observation = match observation_result {
109 Ok(result) => result,
110 Err(err) => {
111 log::info!(
112 "The tool return the following error: {}",
113 err.to_string()
114 );
115 if self.break_if_error {
116 return Err(ChainError::AgentError(
117 AgentError::ToolError(err.to_string()).to_string(),
118 ));
119 } else {
120 format!("The tool return the following error: {}", err)
121 }
122 }
123 };
124
125 steps.push((action, observation));
126 }
127 }
128 AgentEvent::Finish(finish) => {
129 if let Some(memory) = &self.memory {
130 let mut memory = memory.lock().await;
131
132 memory.add_user_message(match &input_variables["input"] {
133 serde_json::Value::String(s) => s,
135 x => x, });
137
138 let mut tools_ai_message_seen: HashMap<String, ()> = HashMap::default();
139 for (action, observation) in steps {
140 let Ok(LogTools { tool_id, tools }) =
143 serde_json::from_str::<LogTools>(&action.log)
144 else {
145 continue;
146 };
147 let tools_value: serde_json::Value = serde_json::from_str(&tools)?;
148 if tools_ai_message_seen.insert(tools, ()).is_none() {
149 memory.add_message(
150 Message::new_ai_message("").with_tool_calls(tools_value),
151 );
152 }
153 memory.add_message(Message::new_tool_message(observation, tool_id));
154 }
155
156 memory.add_ai_message(&finish.output);
157 }
158 return Ok(GenerateResult {
159 generation: finish.output,
160 ..Default::default()
161 });
162 }
163 }
164
165 if let Some(max_iterations) = self.max_iterations {
166 if steps.len() >= max_iterations as usize {
167 return Ok(GenerateResult {
168 generation: "Max iterations reached".to_string(),
169 ..Default::default()
170 });
171 }
172 }
173 }
174 }
175
176 async fn invoke(&self, input_variables: PromptArgs) -> Result<String, ChainError> {
177 let result = self.call(input_variables).await?;
178 Ok(result.generation)
179 }
180}