/** * 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 => { 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', }; } };