1use async_trait::async_trait;
7use lc_core::tools::{BaseTool, Tool, ToolError};
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Deserialize, JsonSchema)]
13pub struct CalculatorInput {
14 pub expression: String,
16}
17
18#[derive(Debug, Serialize)]
20pub struct CalculatorOutput {
21 pub result: f64,
23
24 pub expression: String,
26}
27
28pub struct Calculator;
33
34impl Calculator {
35 pub fn new() -> Self {
36 Self
37 }
38}
39
40impl Default for Calculator {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46#[async_trait]
48impl Tool for Calculator {
49 type Input = CalculatorInput;
50 type Output = CalculatorOutput;
51
52 async fn invoke(&self, input: Self::Input) -> Result<Self::Output, ToolError> {
53 let result = Self::evaluate_expression(&input.expression)?;
54
55 Ok(CalculatorOutput {
56 result,
57 expression: input.expression,
58 })
59 }
60}
61
62#[async_trait]
64impl BaseTool for Calculator {
65 fn name(&self) -> &str {
66 "calculator"
67 }
68
69 fn description(&self) -> &str {
70 "Calculate math expressions. Supports basic arithmetic, power, trig functions, sqrt, log, exp, abs, and constants (pi, e).
71
72Examples:
73- '2 + 3' -> 5
74- '2 + 3 * 4' -> 14
75- 'sqrt(16)' -> 4
76- '3.14 * 10' -> 31.4
77- 'sin(pi/2)' -> 1
78- '2^10' -> 1024
79- 'log(e)' -> 1
80
81Input format: JSON object with 'expression' field
82Example: {\"expression\": \"2 + 3\"}"
83 }
84
85 async fn run(&self, input: String) -> Result<String, ToolError> {
86 let parsed: CalculatorInput = serde_json::from_str(&input)
88 .map_err(|e| ToolError::InvalidInput(format!("JSON parse error: {}", e)))?;
89
90 let output = self.invoke(parsed).await?;
92
93 Ok(format!("{} = {}", output.expression, output.result))
95 }
96
97 fn args_schema(&self) -> Option<serde_json::Value> {
98 use schemars::schema_for;
99 serde_json::to_value(schema_for!(CalculatorInput)).ok()
100 }
101}
102
103impl Calculator {
104 fn evaluate_expression(expr: &str) -> Result<f64, ToolError> {
106 let expr = expr.trim();
107
108 if let Ok(num) = expr.parse::<f64>() {
110 return Ok(num);
111 }
112
113 meval::eval_str(expr).map_err(|e| {
115 ToolError::ExecutionFailed(format!("Failed to evaluate expression '{}': {}", expr, e))
116 })
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_basic_addition() {
126 assert_eq!(Calculator::evaluate_expression("2 + 3").unwrap(), 5.0);
127 }
128
129 #[test]
130 fn test_operator_precedence() {
131 assert_eq!(Calculator::evaluate_expression("2 + 3 * 4").unwrap(), 14.0);
133 }
134
135 #[test]
136 fn test_chained_addition() {
137 assert_eq!(Calculator::evaluate_expression("1 + 2 + 3").unwrap(), 6.0);
138 }
139
140 #[test]
141 fn test_subtraction() {
142 assert_eq!(Calculator::evaluate_expression("10 - 3").unwrap(), 7.0);
143 }
144
145 #[test]
146 fn test_multiplication() {
147 let result = Calculator::evaluate_expression("3.14 * 10").unwrap();
148 assert!((result - 31.4).abs() < 1e-10);
149 }
150
151 #[test]
152 fn test_division() {
153 let result = Calculator::evaluate_expression("10 / 3").unwrap();
154 assert!((result - 3.3333333333333335).abs() < 1e-10);
155 }
156
157 #[test]
158 fn test_power() {
159 assert_eq!(Calculator::evaluate_expression("2^10").unwrap(), 1024.0);
160 }
161
162 #[test]
163 fn test_sqrt() {
164 assert_eq!(Calculator::evaluate_expression("sqrt(16)").unwrap(), 4.0);
165 }
166
167 #[test]
168 fn test_sin_pi() {
169 let result = Calculator::evaluate_expression("sin(pi/2)").unwrap();
170 assert!((result - 1.0).abs() < 1e-10);
171 }
172
173 #[test]
174 fn test_plain_number() {
175 assert_eq!(Calculator::evaluate_expression("42").unwrap(), 42.0);
176 }
177
178 #[test]
179 fn test_invalid_expression() {
180 assert!(Calculator::evaluate_expression("hello").is_err());
181 }
182
183 #[tokio::test]
184 async fn test_tool_run() {
185 let tool = Calculator::new();
186 let result = tool
187 .run(r#"{"expression": "2 + 3"}"#.to_string())
188 .await
189 .unwrap();
190 assert!(result.contains("5"));
191 }
192}