mojentic/examples/react/
formatters.rs1use crate::llm::tools::LlmTool;
7
8use super::models::CurrentContext;
9
10pub fn format_current_context(context: &CurrentContext) -> String {
20 let user_query = format!(
21 "The user has asked us to answer the following query:\n> {}\n",
22 context.user_query
23 );
24
25 let plan = if context.plan.steps.is_empty() {
26 "You have not yet made a plan.\n".to_string()
27 } else {
28 let mut output = "Current plan:\n".to_string();
29 for step in &context.plan.steps {
30 output.push_str(&format!("- {}\n", step));
31 }
32 output
33 };
34
35 let history = if context.history.is_empty() {
36 "No steps have yet been taken.\n".to_string()
37 } else {
38 let mut output = "What's been done so far:\n".to_string();
39 for (i, step) in context.history.iter().enumerate() {
40 output.push_str(&format!(
41 "{}.\n Thought: {}\n Action: {}\n Observation: {}\n",
42 i + 1,
43 step.thought,
44 step.action,
45 step.observation
46 ));
47 }
48 output
49 };
50
51 format!("Current Context:\n{}{}{}\n", user_query, plan, history)
52}
53
54pub fn format_available_tools(tools: &[&dyn LlmTool]) -> String {
64 if tools.is_empty() {
65 return String::new();
66 }
67
68 let mut output = "Tools available:\n".to_string();
69
70 for tool in tools {
71 let descriptor = tool.descriptor();
72 output.push_str(&format!(
73 "- {}: {}\n",
74 descriptor.function.name, descriptor.function.description
75 ));
76
77 if let Some(params) = descriptor.function.parameters.as_object() {
79 if let Some(properties) = params.get("properties").and_then(|p| p.as_object()) {
80 output.push_str(" Parameters:\n");
81
82 let required: Vec<String> = params
83 .get("required")
84 .and_then(|r| r.as_array())
85 .map(|arr| {
86 arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect()
87 })
88 .unwrap_or_default();
89
90 for (param_name, param_info) in properties {
91 let param_desc =
92 param_info.get("description").and_then(|d| d.as_str()).unwrap_or("");
93 let is_required = required.contains(param_name);
94 let req_str = if is_required {
95 " (required)"
96 } else {
97 " (optional)"
98 };
99 output.push_str(&format!(" - {}{}: {}\n", param_name, req_str, param_desc));
100 }
101 }
102 }
103 }
104
105 output
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::llm::tools::simple_date_tool::SimpleDateTool;
112 use crate::llm::tools::LlmTool;
113
114 use super::super::models::{Plan, ThoughtActionObservation};
115
116 #[test]
117 fn test_format_current_context_empty() {
118 let context = CurrentContext::new("What is the weather?");
119 let formatted = format_current_context(&context);
120
121 assert!(formatted.contains("What is the weather?"));
122 assert!(formatted.contains("You have not yet made a plan"));
123 assert!(formatted.contains("No steps have yet been taken"));
124 }
125
126 #[test]
127 fn test_format_current_context_with_plan() {
128 let mut context = CurrentContext::new("Calculate total");
129 context.plan = Plan {
130 steps: vec![
131 "Step 1: Get data".to_string(),
132 "Step 2: Sum values".to_string(),
133 ],
134 };
135
136 let formatted = format_current_context(&context);
137
138 assert!(formatted.contains("Current plan:"));
139 assert!(formatted.contains("Step 1: Get data"));
140 assert!(formatted.contains("Step 2: Sum values"));
141 }
142
143 #[test]
144 fn test_format_current_context_with_history() {
145 let mut context = CurrentContext::new("Get date");
146 context.history.push(ThoughtActionObservation {
147 thought: "Need to resolve date".to_string(),
148 action: "Called resolve_date".to_string(),
149 observation: "2025-11-29".to_string(),
150 });
151
152 let formatted = format_current_context(&context);
153
154 assert!(formatted.contains("What's been done so far:"));
155 assert!(formatted.contains("Need to resolve date"));
156 assert!(formatted.contains("Called resolve_date"));
157 assert!(formatted.contains("2025-11-29"));
158 }
159
160 #[test]
161 fn test_format_available_tools_empty() {
162 let tools: Vec<&dyn LlmTool> = vec![];
163 let formatted = format_available_tools(&tools);
164
165 assert_eq!(formatted, "");
166 }
167
168 #[test]
169 fn test_format_available_tools_with_tools() {
170 let date_tool = SimpleDateTool;
171 let tools: Vec<&dyn LlmTool> = vec![&date_tool];
172
173 let formatted = format_available_tools(&tools);
174
175 assert!(formatted.contains("Tools available:"));
176 assert!(formatted.contains("resolve_date"));
177 assert!(formatted.contains("relative date"));
178 assert!(formatted.contains("Parameters:"));
179 assert!(formatted.contains("relative_date"));
180 assert!(formatted.contains("(required)"));
181 }
182
183 #[test]
184 fn test_format_available_tools_multiple() {
185 let date_tool = SimpleDateTool;
186 let tools: Vec<&dyn LlmTool> = vec![&date_tool];
187
188 let formatted = format_available_tools(&tools);
189
190 let tool_count = formatted.matches("resolve_date").count();
192 assert_eq!(tool_count, 1);
193 }
194}