calculatorTools.js•9.35 kB
class CalculatorTools {
getToolDefinitions() {
return [
{
name: 'calculate',
description: '執行數學計算(支持基本四則運算和函數)',
inputSchema: {
type: 'object',
properties: {
expression: {
type: 'string',
description: '要計算的數學表達式,例如:2 + 3 * 4, sqrt(16), sin(30)',
},
},
required: ['expression'],
},
},
{
name: 'convert_units',
description: '單位轉換(長度、重量、溫度等)',
inputSchema: {
type: 'object',
properties: {
value: {
type: 'number',
description: '要轉換的數值',
},
from_unit: {
type: 'string',
description: '原始單位(如:meter, kilogram, celsius)',
},
to_unit: {
type: 'string',
description: '目標單位(如:foot, pound, fahrenheit)',
},
},
required: ['value', 'from_unit', 'to_unit'],
},
},
{
name: 'statistics',
description: '計算數組的統計信息(平均值、中位數、標準差等)',
inputSchema: {
type: 'object',
properties: {
numbers: {
type: 'array',
items: { type: 'number' },
description: '數字數組',
},
},
required: ['numbers'],
},
},
];
}
hasToolName(name) {
return this.getToolDefinitions().some(tool => tool.name === name);
}
async handleToolCall(name, args) {
switch (name) {
case 'calculate':
return await this.calculate(args.expression);
case 'convert_units':
return await this.convertUnits(args.value, args.from_unit, args.to_unit);
case 'statistics':
return await this.calculateStatistics(args.numbers);
default:
throw new Error(`Unknown calculator tool: ${name}`);
}
}
async calculate(expression) {
try {
// 安全的數學表達式求值
const result = this.safeEval(expression);
return {
content: [
{
type: 'text',
text: `計算結果: ${expression} = ${result}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `計算錯誤: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
}
safeEval(expression) {
// 移除空格
expression = expression.replace(/\s+/g, '');
// 只允許數字、基本運算符和數學函數
const allowedPattern = /^[0-9+\-*/().,\s]+$|sqrt|sin|cos|tan|log|exp|pow|abs|floor|ceil|round/;
// 替換數學函數
expression = expression
.replace(/sqrt\(([^)]+)\)/g, 'Math.sqrt($1)')
.replace(/sin\(([^)]+)\)/g, 'Math.sin($1 * Math.PI / 180)')
.replace(/cos\(([^)]+)\)/g, 'Math.cos($1 * Math.PI / 180)')
.replace(/tan\(([^)]+)\)/g, 'Math.tan($1 * Math.PI / 180)')
.replace(/log\(([^)]+)\)/g, 'Math.log($1)')
.replace(/exp\(([^)]+)\)/g, 'Math.exp($1)')
.replace(/pow\(([^,]+),([^)]+)\)/g, 'Math.pow($1,$2)')
.replace(/abs\(([^)]+)\)/g, 'Math.abs($1)')
.replace(/floor\(([^)]+)\)/g, 'Math.floor($1)')
.replace(/ceil\(([^)]+)\)/g, 'Math.ceil($1)')
.replace(/round\(([^)]+)\)/g, 'Math.round($1)');
// 檢查是否包含不安全的內容
if (expression.includes('eval') || expression.includes('function') || expression.includes('while') || expression.includes('for')) {
throw new Error('不安全的表達式');
}
try {
return Function('"use strict"; return (' + expression + ')')();
}
catch (error) {
throw new Error('無效的數學表達式');
}
}
async convertUnits(value, fromUnit, toUnit) {
try {
const result = this.performUnitConversion(value, fromUnit.toLowerCase(), toUnit.toLowerCase());
return {
content: [
{
type: 'text',
text: `單位轉換: ${value} ${fromUnit} = ${result} ${toUnit}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `單位轉換錯誤: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
}
performUnitConversion(value, fromUnit, toUnit) {
// 長度轉換
const lengthConversions = {
meter: 1,
m: 1,
kilometer: 1000,
km: 1000,
centimeter: 0.01,
cm: 0.01,
millimeter: 0.001,
mm: 0.001,
inch: 0.0254,
foot: 0.3048,
ft: 0.3048,
yard: 0.9144,
mile: 1609.34,
};
// 重量轉換
const weightConversions = {
kilogram: 1,
kg: 1,
gram: 0.001,
g: 0.001,
pound: 0.453592,
lb: 0.453592,
ounce: 0.0283495,
oz: 0.0283495,
ton: 1000,
};
// 溫度轉換(需要特殊處理)
if (fromUnit === 'celsius' && toUnit === 'fahrenheit') {
return (value * 9 / 5) + 32;
}
if (fromUnit === 'fahrenheit' && toUnit === 'celsius') {
return (value - 32) * 5 / 9;
}
if (fromUnit === 'celsius' && toUnit === 'kelvin') {
return value + 273.15;
}
if (fromUnit === 'kelvin' && toUnit === 'celsius') {
return value - 273.15;
}
// 檢查長度轉換
if (lengthConversions[fromUnit] && lengthConversions[toUnit]) {
return (value * lengthConversions[fromUnit]) / lengthConversions[toUnit];
}
// 檢查重量轉換
if (weightConversions[fromUnit] && weightConversions[toUnit]) {
return (value * weightConversions[fromUnit]) / weightConversions[toUnit];
}
throw new Error(`不支持的單位轉換: ${fromUnit} 到 ${toUnit}`);
}
async calculateStatistics(numbers) {
try {
if (!Array.isArray(numbers) || numbers.length === 0) {
throw new Error('請提供有效的數字數組');
}
const sorted = [...numbers].sort((a, b) => a - b);
const sum = numbers.reduce((acc, num) => acc + num, 0);
const mean = sum / numbers.length;
const median = sorted.length % 2 === 0
? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2
: sorted[Math.floor(sorted.length / 2)];
const variance = numbers.reduce((acc, num) => acc + Math.pow(num - mean, 2), 0) / numbers.length;
const standardDeviation = Math.sqrt(variance);
const min = Math.min(...numbers);
const max = Math.max(...numbers);
const result = {
數量: numbers.length,
總和: sum,
平均值: parseFloat(mean.toFixed(4)),
中位數: parseFloat(median.toFixed(4)),
最小值: min,
最大值: max,
方差: parseFloat(variance.toFixed(4)),
標準差: parseFloat(standardDeviation.toFixed(4)),
};
return {
content: [
{
type: 'text',
text: `統計結果:\n${JSON.stringify(result, null, 2)}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `統計計算錯誤: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
}
}
export const calculatorTools = new CalculatorTools();