Skip to main content

wauldo/client/
reasoning.rs

1//! Tree-of-Thought reasoning methods for AgentClient
2
3use serde_json::json;
4
5use crate::error::{Error, Result};
6use crate::types::*;
7
8use super::AgentClient;
9
10impl AgentClient {
11    /// Perform Tree-of-Thought reasoning
12    ///
13    /// # Example
14    ///
15    /// ```rust,no_run
16    /// # use wauldo::{AgentClient, ReasoningOptions};
17    /// # async fn example() -> wauldo::Result<()> {
18    /// let mut client = AgentClient::new().connect().await?;
19    /// let result = client
20    ///     .reason_with_options(
21    ///         "What's the best sorting algorithm?",
22    ///         ReasoningOptions::new().depth(4).branches(3)
23    ///     )
24    ///     .await?;
25    /// # Ok(())
26    /// # }
27    /// ```
28    pub async fn reason(&mut self, problem: &str) -> Result<ReasoningResult> {
29        self.reason_with_options(problem, ReasoningOptions::default())
30            .await
31    }
32
33    /// Perform reasoning with custom options
34    pub async fn reason_with_options(
35        &mut self,
36        problem: &str,
37        options: ReasoningOptions,
38    ) -> Result<ReasoningResult> {
39        if problem.trim().is_empty() {
40            return Err(Error::validation_field(
41                "Problem cannot be empty",
42                "problem",
43            ));
44        }
45
46        let depth = options.depth.unwrap_or(3);
47        let branches = options.branches.unwrap_or(3);
48
49        if !(1..=10).contains(&depth) {
50            return Err(Error::validation_field(
51                "Depth must be between 1 and 10",
52                "depth",
53            ));
54        }
55        if !(1..=10).contains(&branches) {
56            return Err(Error::validation_field(
57                "Branches must be between 1 and 10",
58                "branches",
59            ));
60        }
61
62        let content = self
63            .call_tool(
64                "reason_tree_of_thought",
65                json!({
66                    "problem": problem,
67                    "depth": depth,
68                    "branches": branches
69                }),
70            )
71            .await?;
72
73        Ok(self.parse_reasoning_result(&content, problem, depth, branches))
74    }
75
76    pub(crate) fn parse_reasoning_result(
77        &self,
78        content: &str,
79        problem: &str,
80        depth: usize,
81        branches: usize,
82    ) -> ReasoningResult {
83        // Try JSON first (structured output from server v0.2+)
84        if let Ok(data) = serde_json::from_str::<serde_json::Value>(content) {
85            if let Some(solution) = data.get("solution").and_then(|s| s.as_str()) {
86                return ReasoningResult {
87                    problem: data["problem"].as_str().unwrap_or(problem).to_string(),
88                    solution: solution.to_string(),
89                    thought_tree: data["thought_tree"].as_str().unwrap_or(content).to_string(),
90                    depth: data["depth"].as_u64().unwrap_or(depth as u64) as usize,
91                    branches: data["branches"].as_u64().unwrap_or(branches as u64) as usize,
92                    raw_content: content.to_string(),
93                };
94            }
95        }
96
97        // Fallback: markdown heuristic parser (server v0.1.x)
98        let mut solution = String::new();
99        let mut in_solution = false;
100
101        for line in content.lines() {
102            if line.contains("Solution:") || line.contains("Best path:") {
103                in_solution = true;
104                continue;
105            }
106            if in_solution && !line.trim().is_empty() {
107                solution = line.trim().to_string();
108                break;
109            }
110        }
111
112        ReasoningResult {
113            problem: problem.to_string(),
114            solution: if solution.is_empty() {
115                "See thought tree for analysis".to_string()
116            } else {
117                solution
118            },
119            thought_tree: content.to_string(),
120            depth,
121            branches,
122            raw_content: content.to_string(),
123        }
124    }
125}