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