Skip to main content

agentlib_reasoning/
react.rs

1use 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            // Model has thoughts but also tools
37            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            // No tool calls -> done
45            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 all tool calls
55            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}