1use async_trait::async_trait;
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use lc_core::tools::{BaseTool, Tool, ToolError};
11
12#[derive(Debug, Deserialize, JsonSchema)]
14pub struct MathInput {
15 pub operation: String,
17
18 pub value: Option<f64>,
20
21 pub value2: Option<f64>,
23
24 pub base: Option<f64>,
26}
27
28#[derive(Debug, Serialize)]
30pub struct MathOutput {
31 pub result: f64,
33
34 pub operation: String,
36
37 pub details: Option<String>,
39}
40
41pub struct SimpleMathTool;
43
44impl SimpleMathTool {
45 pub fn new() -> Self {
47 Self
48 }
49
50 fn power(&self, base: f64, exponent: f64) -> Result<MathOutput, ToolError> {
51 let result = base.powf(exponent);
52 Ok(MathOutput {
53 result,
54 operation: "power".to_string(),
55 details: Some(format!("{}^{} = {}", base, exponent, result)),
56 })
57 }
58
59 fn sqrt(&self, value: f64) -> Result<MathOutput, ToolError> {
60 if value < 0.0 {
61 return Err(ToolError::InvalidInput(
62 "平方根操作要求非负数值".to_string(),
63 ));
64 }
65 let result = value.sqrt();
66 Ok(MathOutput {
67 result,
68 operation: "sqrt".to_string(),
69 details: Some(format!("√{} = {}", value, result)),
70 })
71 }
72
73 fn log(&self, value: f64, base: f64) -> Result<MathOutput, ToolError> {
74 if value <= 0.0 || base <= 0.0 || base == 1.0 {
75 return Err(ToolError::InvalidInput(
76 "对数操作要求正数值且底数不为1".to_string(),
77 ));
78 }
79 let result = value.log(base);
80 Ok(MathOutput {
81 result,
82 operation: "log".to_string(),
83 details: Some(format!("log_{}({}) = {}", base, value, result)),
84 })
85 }
86
87 fn ln(&self, value: f64) -> Result<MathOutput, ToolError> {
88 if value <= 0.0 {
89 return Err(ToolError::InvalidInput(
90 "自然对数操作要求正数值".to_string(),
91 ));
92 }
93 let result = value.ln();
94 Ok(MathOutput {
95 result,
96 operation: "ln".to_string(),
97 details: Some(format!("ln({}) = {}", value, result)),
98 })
99 }
100
101 fn sin(&self, value: f64) -> Result<MathOutput, ToolError> {
102 let result = value.sin();
103 Ok(MathOutput {
104 result,
105 operation: "sin".to_string(),
106 details: Some(format!("sin({}弧度) = {}", value, result)),
107 })
108 }
109
110 fn cos(&self, value: f64) -> Result<MathOutput, ToolError> {
111 let result = value.cos();
112 Ok(MathOutput {
113 result,
114 operation: "cos".to_string(),
115 details: Some(format!("cos({}弧度) = {}", value, result)),
116 })
117 }
118
119 fn tan(&self, value: f64) -> Result<MathOutput, ToolError> {
120 let result = value.tan();
121 Ok(MathOutput {
122 result,
123 operation: "tan".to_string(),
124 details: Some(format!("tan({}弧度) = {}", value, result)),
125 })
126 }
127
128 fn abs(&self, value: f64) -> Result<MathOutput, ToolError> {
129 let result = value.abs();
130 Ok(MathOutput {
131 result,
132 operation: "abs".to_string(),
133 details: Some(format!("|{}| = {}", value, result)),
134 })
135 }
136
137 fn factorial(&self, value: f64) -> Result<MathOutput, ToolError> {
138 if value < 0.0 {
139 return Err(ToolError::InvalidInput("阶乘操作要求非负整数".to_string()));
140 }
141 if value.is_nan() {
142 return Err(ToolError::InvalidInput(
143 "阶乘操作要求有效数值,不接受 NaN".to_string(),
144 ));
145 }
146 if value != value.floor() {
147 return Err(ToolError::InvalidInput(
148 "阶乘操作要求整数,不接受小数".to_string(),
149 ));
150 }
151 let n = value as u64;
152 if n > 20 {
153 return Err(ToolError::InvalidInput(
154 "阶乘值过大,最大支持20".to_string(),
155 ));
156 }
157 let result = self.compute_factorial(n);
158 Ok(MathOutput {
159 result: result as f64,
160 operation: "factorial".to_string(),
161 details: Some(format!("{}! = {}", n, result)),
162 })
163 }
164
165 fn compute_factorial(&self, n: u64) -> u64 {
166 if n == 0 || n == 1 {
167 1
168 } else {
169 n * self.compute_factorial(n - 1)
170 }
171 }
172
173 fn mod_op(&self, a: f64, b: f64) -> Result<MathOutput, ToolError> {
174 if b == 0.0 {
175 return Err(ToolError::InvalidInput(
176 "取模运算的除数不能为零".to_string(),
177 ));
178 }
179 let result = a % b;
180 Ok(MathOutput {
181 result,
182 operation: "mod".to_string(),
183 details: Some(format!("{} mod {} = {}", a, b, result)),
184 })
185 }
186
187 fn gcd(&self, a: f64, b: f64) -> Result<MathOutput, ToolError> {
188 let a_int = a as i64;
189 let b_int = b as i64;
190
191 if a_int < 0 || b_int < 0 {
192 return Err(ToolError::InvalidInput("GCD 操作要求正整数".to_string()));
193 }
194
195 let result = self.compute_gcd(a_int.abs(), b_int.abs());
196 Ok(MathOutput {
197 result: result as f64,
198 operation: "gcd".to_string(),
199 details: Some(format!("gcd({}, {}) = {}", a_int, b_int, result)),
200 })
201 }
202
203 fn compute_gcd(&self, a: i64, b: i64) -> i64 {
204 if b == 0 {
205 a
206 } else {
207 self.compute_gcd(b, a % b)
208 }
209 }
210
211 fn lcm(&self, a: f64, b: f64) -> Result<MathOutput, ToolError> {
212 let a_int = a as i64;
213 let b_int = b as i64;
214
215 if a_int <= 0 || b_int <= 0 {
216 return Err(ToolError::InvalidInput("LCM 操作要求正整数".to_string()));
217 }
218
219 let gcd = self.compute_gcd(a_int, b_int);
220 let result = (a_int / gcd)
221 .checked_mul(b_int)
222 .ok_or_else(|| ToolError::InvalidInput("LCM 计算结果溢出 i64 范围".to_string()))?;
223 Ok(MathOutput {
224 result: result as f64,
225 operation: "lcm".to_string(),
226 details: Some(format!("lcm({}, {}) = {}", a_int, b_int, result)),
227 })
228 }
229
230 fn pi(&self) -> MathOutput {
231 MathOutput {
232 result: std::f64::consts::PI,
233 operation: "pi".to_string(),
234 details: Some("π ≈ 3.141592653589793".to_string()),
235 }
236 }
237
238 fn e(&self) -> MathOutput {
239 MathOutput {
240 result: std::f64::consts::E,
241 operation: "e".to_string(),
242 details: Some("e ≈ 2.718281828459045".to_string()),
243 }
244 }
245}
246
247impl Default for SimpleMathTool {
248 fn default() -> Self {
249 Self::new()
250 }
251}
252
253#[async_trait]
254impl Tool for SimpleMathTool {
255 type Input = MathInput;
256 type Output = MathOutput;
257
258 async fn invoke(&self, input: Self::Input) -> Result<Self::Output, ToolError> {
259 match input.operation.as_str() {
260 "power" => {
261 let base = input.value.ok_or_else(||
262 ToolError::InvalidInput("power 操作需要 value 参数作为底数".to_string()))?;
263 let exp = input.value2.ok_or_else(||
264 ToolError::InvalidInput("power 操作需要 value2 参数作为指数".to_string()))?;
265 self.power(base, exp)
266 }
267 "sqrt" => {
268 let value = input.value.ok_or_else(||
269 ToolError::InvalidInput("sqrt 操作需要 value 参数".to_string()))?;
270 self.sqrt(value)
271 }
272 "log" => {
273 let value = input.value.ok_or_else(||
274 ToolError::InvalidInput("log 操作需要 value 参数".to_string()))?;
275 let base = input.base.unwrap_or(10.0);
276 self.log(value, base)
277 }
278 "ln" => {
279 let value = input.value.ok_or_else(||
280 ToolError::InvalidInput("ln 操作需要 value 参数".to_string()))?;
281 self.ln(value)
282 }
283 "sin" => {
284 let value = input.value.ok_or_else(||
285 ToolError::InvalidInput("sin 操作需要 value 参数(弧度)".to_string()))?;
286 self.sin(value)
287 }
288 "cos" => {
289 let value = input.value.ok_or_else(||
290 ToolError::InvalidInput("cos 操作需要 value 参数(弧度)".to_string()))?;
291 self.cos(value)
292 }
293 "tan" => {
294 let value = input.value.ok_or_else(||
295 ToolError::InvalidInput("tan 操作需要 value 参数(弧度)".to_string()))?;
296 self.tan(value)
297 }
298 "abs" => {
299 let value = input.value.ok_or_else(||
300 ToolError::InvalidInput("abs 操作需要 value 参数".to_string()))?;
301 self.abs(value)
302 }
303 "factorial" => {
304 let value = input.value.ok_or_else(||
305 ToolError::InvalidInput("factorial 操作需要 value 参数".to_string()))?;
306 self.factorial(value)
307 }
308 "mod" => {
309 let a = input.value.ok_or_else(||
310 ToolError::InvalidInput("mod 操作需要 value 参数".to_string()))?;
311 let b = input.value2.ok_or_else(||
312 ToolError::InvalidInput("mod 操作需要 value2 参数".to_string()))?;
313 self.mod_op(a, b)
314 }
315 "gcd" => {
316 let a = input.value.ok_or_else(||
317 ToolError::InvalidInput("gcd 操作需要 value 参数".to_string()))?;
318 let b = input.value2.ok_or_else(||
319 ToolError::InvalidInput("gcd 操作需要 value2 参数".to_string()))?;
320 self.gcd(a, b)
321 }
322 "lcm" => {
323 let a = input.value.ok_or_else(||
324 ToolError::InvalidInput("lcm 操作需要 value 参数".to_string()))?;
325 let b = input.value2.ok_or_else(||
326 ToolError::InvalidInput("lcm 操作需要 value2 参数".to_string()))?;
327 self.lcm(a, b)
328 }
329 "pi" => Ok(self.pi()),
330 "e" => Ok(self.e()),
331 _ => Err(ToolError::InvalidInput(
332 format!("不支持的操作: {},请使用: power, sqrt, log, ln, sin, cos, tan, abs, factorial, mod, gcd, lcm, pi, e", input.operation)
333 )),
334 }
335 }
336}
337
338#[async_trait]
339impl BaseTool for SimpleMathTool {
340 fn name(&self) -> &str {
341 "math"
342 }
343
344 fn description(&self) -> &str {
345 "高级数学工具。支持多种数学运算:
346
347操作类型:
348- power: 幂运算 (value^value2)
349- sqrt: 平方根
350- log: 对数(可指定底数,默认为10)
351- ln: 自然对数
352- sin, cos, tan: 三角函数(参数为弧度)
353- abs: 绝对值
354- factorial: 阶乘(最大支持20)
355- mod: 取模运算
356- gcd: 最大公约数
357- lcm: 最小公倍数
358- pi: 圆周率
359- e: 自然常数
360
361示例:
362- 幂运算: {\"operation\": \"power\", \"value\": 2, \"value2\": 10}
363- 平方根: {\"operation\": \"sqrt\", \"value\": 16}
364- 对数: {\"operation\": \"log\", \"value\": 100, \"base\": 10}
365- 三角函数: {\"operation\": \"sin\", \"value\": 1.5708}
366- GCD: {\"operation\": \"gcd\", \"value\": 12, \"value2\": 18}"
367 }
368
369 async fn run(&self, input: String) -> Result<String, ToolError> {
370 let parsed: MathInput = serde_json::from_str(&input)
371 .map_err(|e| ToolError::InvalidInput(format!("JSON 解析失败: {}", e)))?;
372
373 let output = self.invoke(parsed).await?;
374
375 Ok(format!(
376 "结果: {}\n详细信息: {}",
377 output.result,
378 output.details.unwrap_or_default()
379 ))
380 }
381
382 fn args_schema(&self) -> Option<serde_json::Value> {
383 use schemars::schema_for;
384 serde_json::to_value(schema_for!(MathInput)).ok()
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[tokio::test]
393 async fn test_math_power() {
394 let tool = SimpleMathTool::new();
395
396 let input = MathInput {
397 operation: "power".to_string(),
398 value: Some(2.0),
399 value2: Some(10.0),
400 base: None,
401 };
402
403 let result = tool.invoke(input).await.unwrap();
404 assert_eq!(result.result, 1024.0);
405 }
406
407 #[tokio::test]
408 async fn test_math_sqrt() {
409 let tool = SimpleMathTool::new();
410
411 let input = MathInput {
412 operation: "sqrt".to_string(),
413 value: Some(16.0),
414 value2: None,
415 base: None,
416 };
417
418 let result = tool.invoke(input).await.unwrap();
419 assert_eq!(result.result, 4.0);
420 }
421
422 #[tokio::test]
423 async fn test_math_log() {
424 let tool = SimpleMathTool::new();
425
426 let input = MathInput {
427 operation: "log".to_string(),
428 value: Some(100.0),
429 value2: None,
430 base: Some(10.0),
431 };
432
433 let result = tool.invoke(input).await.unwrap();
434 assert_eq!(result.result, 2.0);
435 }
436
437 #[tokio::test]
438 async fn test_math_ln() {
439 let tool = SimpleMathTool::new();
440
441 let input = MathInput {
442 operation: "ln".to_string(),
443 value: Some(std::f64::consts::E),
444 value2: None,
445 base: None,
446 };
447
448 let result = tool.invoke(input).await.unwrap();
449 assert!((result.result - 1.0).abs() < 0.0001);
450 }
451
452 #[tokio::test]
453 async fn test_math_sin() {
454 let tool = SimpleMathTool::new();
455
456 let input = MathInput {
457 operation: "sin".to_string(),
458 value: Some(std::f64::consts::PI / 2.0),
459 value2: None,
460 base: None,
461 };
462
463 let result = tool.invoke(input).await.unwrap();
464 assert!((result.result - 1.0).abs() < 0.0001);
465 }
466
467 #[tokio::test]
468 async fn test_math_factorial() {
469 let tool = SimpleMathTool::new();
470
471 let input = MathInput {
472 operation: "factorial".to_string(),
473 value: Some(5.0),
474 value2: None,
475 base: None,
476 };
477
478 let result = tool.invoke(input).await.unwrap();
479 assert_eq!(result.result, 120.0);
480 }
481
482 #[tokio::test]
483 async fn test_math_gcd() {
484 let tool = SimpleMathTool::new();
485
486 let input = MathInput {
487 operation: "gcd".to_string(),
488 value: Some(12.0),
489 value2: Some(18.0),
490 base: None,
491 };
492
493 let result = tool.invoke(input).await.unwrap();
494 assert_eq!(result.result, 6.0);
495 }
496
497 #[tokio::test]
498 async fn test_math_lcm() {
499 let tool = SimpleMathTool::new();
500
501 let input = MathInput {
502 operation: "lcm".to_string(),
503 value: Some(4.0),
504 value2: Some(6.0),
505 base: None,
506 };
507
508 let result = tool.invoke(input).await.unwrap();
509 assert_eq!(result.result, 12.0);
510 }
511
512 #[tokio::test]
513 async fn test_math_pi() {
514 let tool = SimpleMathTool::new();
515
516 let input = MathInput {
517 operation: "pi".to_string(),
518 value: None,
519 value2: None,
520 base: None,
521 };
522
523 let result = tool.invoke(input).await.unwrap();
524 assert!((result.result - std::f64::consts::PI).abs() < 0.0001);
525 }
526
527 #[tokio::test]
528 async fn test_math_abs() {
529 let tool = SimpleMathTool::new();
530
531 let input = MathInput {
532 operation: "abs".to_string(),
533 value: Some(-5.0),
534 value2: None,
535 base: None,
536 };
537
538 let result = tool.invoke(input).await.unwrap();
539 assert_eq!(result.result, 5.0);
540 }
541
542 #[tokio::test]
543 async fn test_math_sqrt_negative_error() {
544 let tool = SimpleMathTool::new();
545
546 let input = MathInput {
547 operation: "sqrt".to_string(),
548 value: Some(-4.0),
549 value2: None,
550 base: None,
551 };
552
553 let result = tool.invoke(input).await;
554 assert!(result.is_err());
555 }
556
557 #[tokio::test]
558 async fn test_math_factorial_overflow_error() {
559 let tool = SimpleMathTool::new();
560
561 let input = MathInput {
562 operation: "factorial".to_string(),
563 value: Some(25.0),
564 value2: None,
565 base: None,
566 };
567
568 let result = tool.invoke(input).await;
569 assert!(result.is_err());
570 }
571
572 #[tokio::test]
573 async fn test_math_base_tool_run() {
574 let tool = SimpleMathTool::new();
575
576 let input = "{\"operation\": \"power\", \"value\": 3, \"value2\": 4}".to_string();
577 let result = tool.run(input).await.unwrap();
578
579 assert!(result.contains("81"));
580 assert!(result.contains("3^4"));
581 }
582}