83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
/**
|
|
* Calculator tool - evaluates mathematical expressions
|
|
*/
|
|
|
|
import { Tool } from 'ollama';
|
|
import { ToolHandler, ToolResult } from './types';
|
|
|
|
export const calculatorTool: Tool = {
|
|
type: 'function',
|
|
function: {
|
|
name: 'calculator',
|
|
description:
|
|
'Evaluate a mathematical expression. Supports basic arithmetic (+, -, *, /), exponents (^), parentheses, and common math functions (sqrt, sin, cos, tan, log, abs, round, floor, ceil).',
|
|
parameters: {
|
|
type: 'object',
|
|
properties: {
|
|
expression: {
|
|
type: 'string',
|
|
description:
|
|
'The mathematical expression to evaluate, e.g., "2 + 2", "sqrt(16)", "sin(3.14159/2)"',
|
|
},
|
|
},
|
|
required: ['expression'],
|
|
},
|
|
},
|
|
};
|
|
|
|
// Safe math evaluation using Function constructor with limited scope
|
|
function safeEvaluate(expression: string): number {
|
|
// Replace common math notation
|
|
const expr = expression
|
|
.replace(/\^/g, '**') // Exponents
|
|
.replace(/sqrt/g, 'Math.sqrt')
|
|
.replace(/sin/g, 'Math.sin')
|
|
.replace(/cos/g, 'Math.cos')
|
|
.replace(/tan/g, 'Math.tan')
|
|
.replace(/log/g, 'Math.log')
|
|
.replace(/abs/g, 'Math.abs')
|
|
.replace(/round/g, 'Math.round')
|
|
.replace(/floor/g, 'Math.floor')
|
|
.replace(/ceil/g, 'Math.ceil')
|
|
.replace(/pi/gi, 'Math.PI')
|
|
.replace(/e(?![a-z])/gi, 'Math.E');
|
|
|
|
// Validate: only allow safe characters
|
|
if (!/^[0-9+\-*/().%\s,Math.sqrtincoablgEPI]+$/.test(expr)) {
|
|
throw new Error('Invalid characters in expression');
|
|
}
|
|
|
|
// Evaluate using Function constructor (safer than eval)
|
|
const result = new Function(`"use strict"; return (${expr})`)();
|
|
|
|
if (typeof result !== 'number' || !isFinite(result)) {
|
|
throw new Error('Expression did not evaluate to a valid number');
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export const calculatorHandler: ToolHandler = async (args): Promise<ToolResult> => {
|
|
const expression = args.expression as string;
|
|
|
|
if (!expression) {
|
|
return {
|
|
success: false,
|
|
error: 'No expression provided',
|
|
};
|
|
}
|
|
|
|
try {
|
|
const result = safeEvaluate(expression);
|
|
return {
|
|
success: true,
|
|
result: `${expression} = ${result}`,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Failed to evaluate expression',
|
|
};
|
|
}
|
|
};
|