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