agentlib_reasoning/
react.rs1use crate::utils::{call_model, execute_tool_calls};
2use agentlib_core::{ReasoningContext, ReasoningEngine, ReasoningStep};
3use anyhow::{Result, anyhow};
4use async_trait::async_trait;
5
6pub struct ReactEngine {
7 max_steps: usize,
8}
9
10impl ReactEngine {
11 pub fn new(max_steps: usize) -> Self {
12 Self { max_steps }
13 }
14}
15
16impl Default for ReactEngine {
17 fn default() -> Self {
18 Self::new(10)
19 }
20}
21
22#[async_trait]
23impl ReasoningEngine for ReactEngine {
24 fn name(&self) -> &str {
25 "react"
26 }
27
28 async fn execute(&self, r_ctx: &mut ReasoningContext<'_>) -> Result<String> {
29 let mut steps = 0;
30
31 while steps < self.max_steps {
32 let messages = r_ctx.ctx.messages.clone();
33 let response = call_model(r_ctx, messages).await?;
34 r_ctx.ctx.messages.push(response.message.clone());
35
36 if !response.message.content.is_empty() && response.message.tool_calls.is_some() {
38 r_ctx.push_step(ReasoningStep::Thought {
39 content: response.message.content.clone(),
40 engine: self.name().to_string(),
41 });
42 }
43
44 let tool_calls = response.message.tool_calls.as_ref();
46 if tool_calls.is_none() || tool_calls.unwrap().is_empty() {
47 r_ctx.push_step(ReasoningStep::Response {
48 content: response.message.content.clone(),
49 engine: self.name().to_string(),
50 });
51 return Ok(response.message.content);
52 }
53
54 execute_tool_calls(r_ctx, &response).await?;
56 steps += 1;
57 }
58
59 Err(anyhow!(
60 "[ReactEngine] Max steps ({}) reached without a final answer.",
61 self.max_steps
62 ))
63 }
64}