1
2use crate::errors::AgentError;
14use crate::models::model_traits::Model;
15use crate::models::openai::ToolCall;
16use crate::models::types::Message;
17use crate::models::types::MessageRole;
18use crate::prompts::{
19 user_prompt_plan, TOOL_CALLING_SYSTEM_PROMPT, SYSTEM_PROMPT_FACTS, SYSTEM_PROMPT_PLAN,
20};
21use crate::tools::{AnyTool, FinalAnswerTool, ToolGroup, ToolInfo};
22use std::collections::HashMap;
23
24use crate::logger::LOGGER;
25use anyhow::Result;
26use colored::Colorize;
27use log::info;
28
29use serde_json::json;
30#[cfg(feature = "code-agent")]
31use {
32 crate::errors::InterpreterError, crate::local_python_interpreter::LocalPythonInterpreter,
33 crate::models::openai::FunctionCall, crate::prompts::CODE_SYSTEM_PROMPT, regex::Regex,
34};
35
36
37
38const DEFAULT_TOOL_DESCRIPTION_TEMPLATE: &str = r#"
39{{ tool.name }}: {{ tool.description }}
40 Takes inputs: {{tool.inputs}}
41"#;
42
43use std::fmt::Debug;
44
45pub fn get_tool_description_with_args(tool: &ToolInfo) -> String {
46 let mut description = DEFAULT_TOOL_DESCRIPTION_TEMPLATE.to_string();
47 description = description.replace("{{ tool.name }}", tool.function.name);
48 description = description.replace("{{ tool.description }}", tool.function.description);
49 description = description.replace(
50 "{{tool.inputs}}",
51 json!(&tool.function.parameters.schema)["properties"]
52 .to_string()
53 .as_str(),
54 );
55
56 description
57}
58
59pub fn get_tool_descriptions(tools: &[ToolInfo]) -> Vec<String> {
60 tools.iter().map(get_tool_description_with_args).collect()
61}
62pub fn format_prompt_with_tools(tools: Vec<ToolInfo>, prompt_template: &str) -> String {
63 let tool_descriptions = get_tool_descriptions(&tools);
64 let mut prompt = prompt_template.to_string();
65 prompt = prompt.replace("{{tool_descriptions}}", &tool_descriptions.join("\n"));
66 if prompt.contains("{{tool_names}}") {
67 let tool_names: Vec<String> = tools
68 .iter()
69 .map(|tool| tool.function.name.to_string())
70 .collect();
71 prompt = prompt.replace("{{tool_names}}", &tool_names.join(", "));
72 }
73 prompt
74}
75
76pub fn show_agents_description(managed_agents: &HashMap<String, Box<dyn Agent>>) -> String {
77 let mut managed_agent_description = r#"You can also give requests to team members.
78Calling a team member works the same as for calling a tool: simply, the only argument you can give in the call is 'request', a long string explaining your request.
79Given that this team member is a real human, you should be very verbose in your request.
80Here is a list of the team members that you can call:"#.to_string();
81
82 for (name, agent) in managed_agents.iter() {
83 managed_agent_description.push_str(&format!("{}: {:?}\n", name, agent.description()));
84 }
85
86 managed_agent_description
87}
88
89pub fn format_prompt_with_managed_agent_description(
90 prompt_template: String,
91 managed_agents: &HashMap<String, Box<dyn Agent>>,
92 agent_descriptions_placeholder: Option<&str>,
93) -> Result<String> {
94 let agent_descriptions_placeholder =
95 agent_descriptions_placeholder.unwrap_or("{{managed_agents_descriptions}}");
96
97 if managed_agents.keys().len() > 0 {
98 Ok(prompt_template.replace(
99 agent_descriptions_placeholder,
100 &show_agents_description(managed_agents),
101 ))
102 } else {
103 Ok(prompt_template.replace(agent_descriptions_placeholder, ""))
104 }
105}
106
107pub trait Agent {
108 fn name(&self) -> &'static str;
109 fn get_max_steps(&self) -> usize;
110 fn get_step_number(&self) -> usize;
111 fn increment_step_number(&mut self);
112 fn get_logs_mut(&mut self) -> &mut Vec<Step>;
113 fn set_task(&mut self, task: &str);
114 fn get_system_prompt(&self) -> &str;
115 fn description(&self) -> String {
116 "".to_string()
117 }
118 fn model(&self) -> &dyn Model;
119 fn step(&mut self, log_entry: &mut Step) -> Result<Option<String>>;
120 fn direct_run(&mut self, _task: &str) -> Result<String> {
121 let mut final_answer: Option<String> = None;
122 while final_answer.is_none() && self.get_step_number() < self.get_max_steps() {
123 let mut step_log = Step::ActionStep(AgentStep {
124 agent_memory: None,
125 llm_output: None,
126 tool_call: None,
127 error: None,
128 observations: None,
129 _step: self.get_step_number(),
130 });
131
132 final_answer = self.step(&mut step_log)?;
133 self.get_logs_mut().push(step_log);
134 self.increment_step_number();
135 }
136
137 if final_answer.is_none() && self.get_step_number() >= self.get_max_steps() {
138 final_answer = self.provide_final_answer(_task)?;
139 }
140 info!(
141 "Final answer: {}",
142 final_answer
143 .clone()
144 .unwrap_or("Could not find answer".to_string())
145 );
146 Ok(final_answer.unwrap_or_else(|| "Max steps reached without final answer".to_string()))
147 }
148 fn stream_run(&mut self, _task: &str) -> Result<String> {
149 todo!()
150 }
151 fn run(&mut self, task: &str, stream: bool, reset: bool) -> Result<String> {
152 self.set_task(task);
154
155 let system_prompt_step = Step::SystemPromptStep(self.get_system_prompt().to_string());
156 if reset {
157 self.get_logs_mut().clear();
158 self.get_logs_mut().push(system_prompt_step);
159 } else if self.get_logs_mut().is_empty() {
160 self.get_logs_mut().push(system_prompt_step);
161 } else {
162 self.get_logs_mut()[0] = system_prompt_step;
163 }
164 self.get_logs_mut().push(Step::TaskStep(task.to_string()));
165 match stream {
166 true => self.stream_run(task),
167 false => self.direct_run(task),
168 }
169 }
170 fn provide_final_answer(&mut self, task: &str) -> Result<Option<String>> {
171 let mut input_messages = vec![Message {
172 role: MessageRole::System,
173 content: "An agent tried to answer a user query but it got stuck and failed to do so. You are tasked with providing an answer instead. Here is the agent's memory:".to_string(),
174 }];
175
176 input_messages.extend(self.write_inner_memory_from_logs(Some(true))?[1..].to_vec());
177 input_messages.push(Message {
178 role: MessageRole::User,
179 content: format!("Based on the above, please provide an answer to the following user request: \n```\n{}", task),
180 });
181 let response = self
182 .model()
183 .run(input_messages, vec![], None, None)?
184 .get_response()?;
185 Ok(Some(response))
186 }
187
188 fn write_inner_memory_from_logs(&mut self, summary_mode: Option<bool>) -> Result<Vec<Message>> {
189 let mut memory = Vec::new();
190 let summary_mode = summary_mode.unwrap_or(false);
191 for log in self.get_logs_mut() {
192 match log {
193 Step::ToolCall(_) => {}
194 Step::PlanningStep(plan, facts) => {
195 memory.push(Message {
196 role: MessageRole::Assistant,
197 content: "[PLAN]:\n".to_owned() + plan.as_str(),
198 });
199
200 if !summary_mode {
201 memory.push(Message {
202 role: MessageRole::Assistant,
203 content: "[FACTS]:\n".to_owned() + facts.as_str(),
204 });
205 }
206 }
207 Step::TaskStep(task) => {
208 memory.push(Message {
209 role: MessageRole::User,
210 content: "New Task: ".to_owned() + task.as_str(),
211 });
212 }
213 Step::SystemPromptStep(prompt) => {
214 memory.push(Message {
215 role: MessageRole::System,
216 content: prompt.to_string(),
217 });
218 }
219 Step::ActionStep(step_log) => {
220 if step_log.llm_output.is_some() && !summary_mode {
221 memory.push(Message {
222 role: MessageRole::Assistant,
223 content: step_log.llm_output.clone().unwrap_or_default(),
224 });
225 }
226 if step_log.tool_call.is_some() {
227 let tool_call_message = Message {
228 role: MessageRole::Assistant,
229 content: serde_json::to_string(&step_log.tool_call.clone().unwrap())?,
230 };
231 memory.push(tool_call_message);
232 }
233 if step_log.tool_call.is_none() && step_log.error.is_some() {
234 let message_content = "Error: ".to_owned() + step_log.error.clone().unwrap().message()+"\nNow let's retry: take care not to repeat previous errors! If you have retried several times, try a completely different approach.\n";
235 memory.push(Message {
236 role: MessageRole::Assistant,
237 content: message_content,
238 });
239 }
240 if step_log.tool_call.is_some()
241 && (step_log.error.is_some() || step_log.observations.is_some())
242 {
243 let mut message_content = "".to_string();
244 if step_log.error.is_some() {
245 message_content = "Error: ".to_owned() + step_log.error.as_ref().unwrap().message()+"\nNow let's retry: take care not to repeat previous errors! If you have retried several times, try a completely different approach.\n";
246 } else if step_log.observations.is_some() {
247 message_content = "Observations: ".to_owned()
248 + step_log.observations.as_ref().unwrap().as_str();
249 }
250 let tool_response_message = {
251 Message {
252 role: MessageRole::User,
253 content: format!(
254 "Call id: {}\n{}",
255 step_log
256 .tool_call
257 .as_ref()
258 .unwrap()
259 .id
260 .clone()
261 .unwrap_or_default(),
262 message_content
263 ),
264 }
265 };
266 memory.push(tool_response_message);
267 }
268
269 if step_log.observations.is_some() || step_log.error.is_some() {
270 let mut message_content = "".to_string();
271 if step_log.error.is_some() {
272 message_content = "Error: ".to_owned() + step_log.error.as_ref().unwrap().message()+"\nNow let's retry: take care not to repeat previous errors! If you have retried several times, try a completely different approach.\n";
273 } else if step_log.observations.is_some() {
274 message_content = "Observations: ".to_owned()
275 + step_log.observations.as_ref().unwrap().as_str();
276 }
277 memory.push(Message {
278 role: MessageRole::Assistant,
279 content: message_content,
280 });
281 }
282 }
283 }
284 }
285 Ok(memory)
286 }
287}
288
289#[derive(Debug)]
290pub enum Step {
291 PlanningStep(String, String),
292 TaskStep(String),
293 SystemPromptStep(String),
294 ActionStep(AgentStep),
295 ToolCall(ToolCall),
296}
297
298#[derive(Debug, Clone)]
299pub struct AgentStep {
300 agent_memory: Option<Vec<Message>>,
301 llm_output: Option<String>,
302 tool_call: Option<ToolCall>,
303 error: Option<AgentError>,
304 observations: Option<String>,
305 _step: usize,
306}
307
308pub struct MultiStepAgent<M: Model> {
311 pub model: M,
312 pub tools: Vec<Box<dyn AnyTool>>,
313 pub system_prompt_template: String,
314 pub name: &'static str,
315 pub managed_agents: Option<HashMap<String, Box<dyn Agent>>>,
316 pub description: String,
317 pub max_steps: usize,
318 pub step_number: usize,
319 pub task: String,
320 pub input_messages: Option<Vec<Message>>,
321 pub logs: Vec<Step>,
322}
323
324impl<M: Model + Debug> Agent for MultiStepAgent<M> {
325 fn name(&self) -> &'static str {
326 self.name
327 }
328 fn get_max_steps(&self) -> usize {
329 self.max_steps
330 }
331 fn get_step_number(&self) -> usize {
332 self.step_number
333 }
334 fn set_task(&mut self, task: &str) {
335 self.task = task.to_string();
336 }
337 fn get_system_prompt(&self) -> &str {
338 &self.system_prompt_template
339 }
340 fn increment_step_number(&mut self) {
341 self.step_number += 1;
342 }
343 fn get_logs_mut(&mut self) -> &mut Vec<Step> {
344 &mut self.logs
345 }
346 fn description(&self) -> String {
347 self.description.clone()
348 }
349 fn model(&self) -> &dyn Model {
350 &self.model
351 }
352
353 fn step(&mut self, _: &mut Step) -> Result<Option<String>> {
357 todo!()
358 }
359}
360
361impl<M: Model> MultiStepAgent<M> {
362 pub fn new(
363 model: M,
364 mut tools: Vec<Box<dyn AnyTool>>,
365 system_prompt: Option<&str>,
366 managed_agents: Option<HashMap<String, Box<dyn Agent>>>,
367 description: Option<&str>,
368 max_steps: Option<usize>,
369 ) -> Result<Self> {
370 log::set_logger(&LOGGER).unwrap();
372 log::set_max_level(log::LevelFilter::Info);
373
374 let name = "MultiStepAgent";
375
376 let system_prompt_template = match system_prompt {
377 Some(prompt) => prompt.to_string(),
378 None => TOOL_CALLING_SYSTEM_PROMPT.to_string(),
379 };
380 let description = match description {
381 Some(desc) => desc.to_string(),
382 None => "A multi-step agent that can solve tasks using a series of tools".to_string(),
383 };
384
385 let final_answer_tool = FinalAnswerTool::new();
386 tools.push(Box::new(final_answer_tool));
387
388 let mut agent = MultiStepAgent {
389 model,
390 tools,
391 system_prompt_template,
392 name,
393 managed_agents,
394 description,
395 max_steps: max_steps.unwrap_or(10),
396 step_number: 0,
397 task: "".to_string(),
398 logs: Vec::new(),
399 input_messages: None,
400 };
401
402 agent.initialize_system_prompt()?;
403 Ok(agent)
404 }
405
406 fn initialize_system_prompt(&mut self) -> Result<String> {
407 let tools = self.tools.tool_info();
408 self.system_prompt_template = format_prompt_with_tools(tools, &self.system_prompt_template);
409 match &self.managed_agents {
410 Some(managed_agents) => {
411 self.system_prompt_template = format_prompt_with_managed_agent_description(
412 self.system_prompt_template.clone(),
413 managed_agents,
414 None,
415 )?;
416 }
417 None => {
418 self.system_prompt_template = format_prompt_with_managed_agent_description(
419 self.system_prompt_template.clone(),
420 &HashMap::new(),
421 None,
422 )?;
423 }
424 }
425 self.system_prompt_template = self
426 .system_prompt_template
427 .replace("{{current_time}}", &chrono::Local::now().to_string());
428 Ok(self.system_prompt_template.clone())
429 }
430
431 pub fn planning_step(&mut self, task: &str, is_first_step: bool, _step: usize) {
432 if is_first_step {
433 let message_prompt_facts = Message {
434 role: MessageRole::System,
435 content: SYSTEM_PROMPT_FACTS.to_string(),
436 };
437 let message_prompt_task = Message {
438 role: MessageRole::User,
439 content: format!(
440 "Here is the task: ```
441 {}
442 ```
443 Now Begin!
444 ",
445 task
446 ),
447 };
448
449 let answer_facts = self
450 .model
451 .run(
452 vec![message_prompt_facts, message_prompt_task],
453 vec![],
454 None,
455 None,
456 )
457 .unwrap()
458 .get_response()
459 .unwrap_or("".to_string());
460 let message_system_prompt_plan = Message {
461 role: MessageRole::System,
462 content: SYSTEM_PROMPT_PLAN.to_string(),
463 };
464 let tool_descriptions = serde_json::to_string(
465 &self
466 .tools
467 .iter()
468 .map(|tool| tool.tool_info())
469 .collect::<Vec<_>>(),
470 )
471 .unwrap();
472 let message_user_prompt_plan = Message {
473 role: MessageRole::User,
474 content: user_prompt_plan(
475 task,
476 &tool_descriptions,
477 &show_agents_description(
478 self.managed_agents.as_ref().unwrap_or(&HashMap::new()),
479 ),
480 &answer_facts,
481 ),
482 };
483 let answer_plan = self
484 .model
485 .run(
486 vec![message_system_prompt_plan, message_user_prompt_plan],
487 vec![],
488 None,
489 Some(HashMap::from([(
490 "stop".to_string(),
491 vec!["Observation:".to_string()],
492 )])),
493 )
494 .unwrap()
495 .get_response()
496 .unwrap();
497 let final_plan_redaction = format!(
498 "Here is the plan of action that I will follow for the task: \n{}",
499 answer_plan
500 );
501 let final_facts_redaction =
502 format!("Here are the facts that I know so far: \n{}", answer_facts);
503 self.logs.push(Step::PlanningStep(
504 final_plan_redaction.clone(),
505 final_facts_redaction,
506 ));
507 info!("Plan: {}", final_plan_redaction.blue().bold());
508 }
509 }
510}
511
512pub struct FunctionCallingAgent<M: Model> {
513 base_agent: MultiStepAgent<M>,
514}
515
516impl<M: Model + Debug> FunctionCallingAgent<M> {
517 pub fn new(
518 model: M,
519 tools: Vec<Box<dyn AnyTool>>,
520 system_prompt: Option<&str>,
521 managed_agents: Option<HashMap<String, Box<dyn Agent>>>,
522 description: Option<&str>,
523 max_steps: Option<usize>,
524 ) -> Result<Self> {
525 let system_prompt = system_prompt.unwrap_or(TOOL_CALLING_SYSTEM_PROMPT);
526 let base_agent = MultiStepAgent::new(
527 model,
528 tools,
529 Some(system_prompt),
530 managed_agents,
531 description,
532 max_steps,
533 )?;
534 Ok(Self { base_agent })
535 }
536}
537
538impl<M: Model + Debug> Agent for FunctionCallingAgent<M> {
539 fn name(&self) -> &'static str {
540 self.base_agent.name()
541 }
542 fn set_task(&mut self, task: &str) {
543 self.base_agent.set_task(task);
544 }
545 fn get_system_prompt(&self) -> &str {
546 self.base_agent.get_system_prompt()
547 }
548 fn get_max_steps(&self) -> usize {
549 self.base_agent.get_max_steps()
550 }
551 fn get_step_number(&self) -> usize {
552 self.base_agent.get_step_number()
553 }
554 fn increment_step_number(&mut self) {
555 self.base_agent.increment_step_number();
556 }
557 fn get_logs_mut(&mut self) -> &mut Vec<Step> {
558 self.base_agent.get_logs_mut()
559 }
560 fn model(&self) -> &dyn Model {
561 self.base_agent.model()
562 }
563
564 fn step(&mut self, log_entry: &mut Step) -> Result<Option<String>> {
568 match log_entry {
569 Step::ActionStep(step_log) => {
570 let agent_memory = self.base_agent.write_inner_memory_from_logs(None)?;
571 self.base_agent.input_messages = Some(agent_memory.clone());
572 step_log.agent_memory = Some(agent_memory.clone());
573 let tools = self
574 .base_agent
575 .tools
576 .iter()
577 .map(|tool| tool.tool_info())
578 .collect::<Vec<_>>();
579 let model_message = self
580 .base_agent
581 .model
582 .run(
583 self.base_agent.input_messages.as_ref().unwrap().clone(),
584 tools,
585 None,
586 Some(HashMap::from([(
587 "stop".to_string(),
588 vec!["Observation:".to_string()],
589 )])),
590 )
591 .unwrap();
592
593 let mut observations = Vec::new();
594
595 if let Ok(response) = model_message.get_response() {
596 if !response.trim().is_empty() {
597 observations.push(response);
598 }
599 }
600
601 let tools = model_message.get_tools_used()?;
602
603 for tool in tools {
604 let function_name = tool.clone().function.name;
605
606 match function_name.as_str() {
607 "final_answer" => {
608 info!("Executing tool call: {}", function_name);
609 let answer = self.base_agent.tools.call(&tool.function)?;
610 return Ok(Some(answer));
611 }
612 _ => {
613 step_log.tool_call = Some(tool.clone());
614
615 info!(
616 "Executing tool call: {} with arguments: {:?}",
617 function_name, tool.function.arguments
618 );
619 let observation = self.base_agent.tools.call(&tool.function);
620 match observation {
621 Ok(observation) => {
622 observations.push(format!(
623 "Observation from {}: {}",
624 function_name,
625 observation.chars().take(3000).collect::<String>()
626 ));
627 }
628 Err(e) => {
629 step_log.error = Some(AgentError::Execution(e.to_string()));
630 info!("Error: {}", e);
631 }
632 }
633 step_log.observations = Some(observations.join("\n"));
634 info!(
635 "Observation: {}",
636 step_log.observations.clone().unwrap_or_default().trim()
637 );
638 }
639 }
640 }
641 Ok(None)
642 }
643 _ => {
644 todo!()
645 }
646 }
647 }
648}
649
650#[cfg(feature = "code-agent")]
651pub struct CodeAgent<M: Model> {
652 base_agent: MultiStepAgent<M>,
653 local_python_interpreter: LocalPythonInterpreter,
654}
655
656#[cfg(feature = "code-agent")]
657impl<M: Model> CodeAgent<M> {
658 pub fn new(
659 model: M,
660 tools: Vec<Box<dyn AnyTool>>,
661 system_prompt: Option<&str>,
662 managed_agents: Option<HashMap<String, Box<dyn Agent>>>,
663 description: Option<&str>,
664 max_steps: Option<usize>,
665 ) -> Result<Self> {
666 let system_prompt = system_prompt.unwrap_or(CODE_SYSTEM_PROMPT);
667
668 let base_agent = MultiStepAgent::new(
669 model,
670 tools,
671 Some(system_prompt),
672 managed_agents,
673 description,
674 max_steps,
675 )?;
676 let local_python_interpreter = LocalPythonInterpreter::new(
677 base_agent
678 .tools
679 .iter()
680 .map(|tool| tool.clone_box())
681 .collect(),
682 );
683
684 Ok(Self {
685 base_agent,
686 local_python_interpreter,
687 })
688 }
689}
690
691#[cfg(feature = "code-agent")]
692impl<M: Model + Debug> Agent for CodeAgent<M> {
693 fn name(&self) -> &'static str {
694 self.base_agent.name()
695 }
696 fn get_max_steps(&self) -> usize {
697 self.base_agent.get_max_steps()
698 }
699 fn get_step_number(&self) -> usize {
700 self.base_agent.get_step_number()
701 }
702 fn increment_step_number(&mut self) {
703 self.base_agent.increment_step_number()
704 }
705 fn get_logs_mut(&mut self) -> &mut Vec<Step> {
706 self.base_agent.get_logs_mut()
707 }
708 fn set_task(&mut self, task: &str) {
709 self.base_agent.set_task(task);
710 }
711 fn get_system_prompt(&self) -> &str {
712 self.base_agent.get_system_prompt()
713 }
714 fn model(&self) -> &dyn Model {
715 self.base_agent.model()
716 }
717 fn step(&mut self, log_entry: &mut Step) -> Result<Option<String>> {
718 match log_entry {
719 Step::ActionStep(step_log) => {
720 let agent_memory = self.base_agent.write_inner_memory_from_logs(None)?;
721 self.base_agent.input_messages = Some(agent_memory.clone());
722 step_log.agent_memory = Some(agent_memory);
723
724 let llm_output = self.base_agent.model.run(
725 self.base_agent.input_messages.as_ref().unwrap().clone(),
726 vec![],
727 None,
728 Some(HashMap::from([(
729 "stop".to_string(),
730 vec!["Observation:".to_string(), "<end_code>".to_string()],
731 )])),
732 )?;
733
734 let response = llm_output.get_response()?;
735
736 let code = match parse_code_blobs(&response) {
737 Ok(code) => code,
738 Err(e) => {
739 step_log.error = Some(e);
740 return Ok(None);
741 }
742 };
743
744 info!("Code: {}", code);
745 step_log.tool_call = Some(ToolCall {
746 id: None,
747 call_type: Some("function".to_string()),
748 function: FunctionCall {
749 name: "python_interpreter".to_string(),
750 arguments: serde_json::json!({ "code": code }),
751 },
752 });
753 let result = self.local_python_interpreter.forward(&code, &mut None);
754 match result {
755 Ok(result) => {
756 let (result, execution_logs) = result;
757 let mut observation = if !execution_logs.is_empty() {
758 format!(
759 "Observation: {}\nExecution logs: {}",
760 result, execution_logs
761 )
762 } else {
763 format!("Observation: {}", result)
764 };
765 if observation.len() > 20000 {
766 observation = observation.chars().take(20000).collect::<String>();
767 observation = format!("{} \n....This content has been truncated due to the 20000 character limit.....", observation);
768 }
769 info!("{}", observation);
770
771 step_log.observations =
772 Some(observation.chars().take(20000).collect::<String>());
773 }
774 Err(e) => match e {
775 InterpreterError::FinalAnswer(answer) => {
776 return Ok(Some(answer));
777 }
778 _ => {
779 step_log.error = Some(AgentError::Execution(e.to_string()));
780 info!("Error: {}", e);
781 }
782 },
783 }
784 }
785 _ => {
786 todo!()
787 }
788 }
789
790 Ok(None)
791 }
792}
793
794#[cfg(feature = "code-agent")]
795pub fn parse_code_blobs(code_blob: &str) -> Result<String, AgentError> {
796 let pattern = r"```(?:py|python)?\n([\s\S]*?)\n```";
797 let re = Regex::new(pattern).map_err(|e| AgentError::Execution(e.to_string()))?;
798
799 let matches: Vec<String> = re
800 .captures_iter(code_blob)
801 .map(|cap| cap[1].trim().to_string())
802 .collect();
803
804 if matches.is_empty() {
805 if code_blob.contains("final") && code_blob.contains("answer") {
807 return Err(AgentError::Parsing(
808 "The code blob is invalid. It seems like you're trying to return the final answer. Use:\n\
809 Code:\n\
810 ```py\n\
811 final_answer(\"YOUR FINAL ANSWER HERE\")\n\
812 ```".to_string(),
813 ));
814 }
815
816 return Err(AgentError::Parsing(
817 "The code blob is invalid. Make sure to include code with the correct pattern, for instance:\n\
818 Thoughts: Your thoughts\n\
819 Code:\n\
820 ```py\n\
821 # Your python code here\n\
822 ```".to_string(),
823 ));
824 }
825
826 Ok(matches.join("\n\n"))
827}