TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1import type { UnifiedToolDefinition } from "@/lib/ai/core/types";23/**4 * Built-in, provider-neutral tools executed server-side. Each is a JSON-schema function5 * the model may call; PolyLLM runs it and feeds the result back (max N rounds).6 * They are deliberately side-effect free (no network, no filesystem).7 */8export interface BuiltinTool {9 id: string;10 label: string;11 description: string;12 definition: UnifiedToolDefinition;13 run: (args: Record<string, unknown>) => Promise<unknown>;14}1516function safeCalc(expr: string): number {17 if (!/^[\d\s+\-*/().,%^eE]+$/.test(expr) || expr.length > 200) throw new Error("Only numbers and + - * / ( ) ^ % are allowed");18 // shunting-yard would be overkill; use Function on a validated arithmetic-only string.19 const normalized = expr.replace(/\^/g, "**").replace(/,/g, "");20 const result = Function(`"use strict"; return (${normalized});`)() as unknown;21 if (typeof result !== "number" || !Number.isFinite(result)) throw new Error("Expression did not evaluate to a finite number");22 return result;23}2425export const BUILTIN_TOOLS: BuiltinTool[] = [26 {27 id: "calculator",28 label: "Calculator",29 description: "Evaluate arithmetic expressions precisely.",30 definition: {31 name: "calculator",32 description: "Evaluate an arithmetic expression (numbers, + - * / ^ % and parentheses) and return the exact result. Use it for any non-trivial math.",33 parameters: { type: "object", properties: { expression: { type: "string", description: "The arithmetic expression, e.g. (1234 * 5678) / 3" } }, required: ["expression"], additionalProperties: false },34 strict: true,35 },36 run: async (args) => {37 const expression = String(args.expression ?? "");38 return { expression, result: safeCalc(expression) };39 },40 },41 {42 id: "clock",43 label: "Clock",44 description: "Current date and time in any IANA time zone.",45 definition: {46 name: "current_datetime",47 description: "Get the current date and time. Optionally in a specific IANA time zone (e.g. America/Toronto).",48 parameters: { type: "object", properties: { timeZone: { type: "string", description: "IANA time zone, defaults to UTC" } }, required: ["timeZone"], additionalProperties: false },49 strict: true,50 },51 run: async (args) => {52 const tz = typeof args.timeZone === "string" && args.timeZone ? args.timeZone : "UTC";53 const now = new Date();54 let local: string;55 try {56 local = new Intl.DateTimeFormat("en-US", { timeZone: tz, dateStyle: "full", timeStyle: "long" }).format(now);57 } catch {58 throw new Error(`Unknown time zone: ${tz}`);59 }60 return { iso: now.toISOString(), timeZone: tz, local, unixMs: now.getTime() };61 },62 },63 {64 id: "random",65 label: "Random",66 description: "Cryptographically secure random integers.",67 definition: {68 name: "random_integer",69 description: "Return a cryptographically secure random integer between min and max (inclusive).",70 parameters: { type: "object", properties: { min: { type: "integer" }, max: { type: "integer" } }, required: ["min", "max"], additionalProperties: false },71 strict: true,72 },73 run: async (args) => {74 const { randomInt } = await import("node:crypto");75 const min = Math.trunc(Number(args.min));76 const max = Math.trunc(Number(args.max));77 if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) throw new Error("max must be greater than min");78 return { value: randomInt(min, max + 1), min, max };79 },80 },81];8283export function resolveTools(ids: string[] | undefined): BuiltinTool[] {84 if (!ids?.length) return [];85 return BUILTIN_TOOLS.filter((t) => ids.includes(t.id));86}8788export async function runBuiltinTool(name: string, args: Record<string, unknown>): Promise<{ result: unknown; isError: boolean; durationMs: number }> {89 const t0 = Date.now();90 const tool = BUILTIN_TOOLS.find((t) => t.definition.name === name);91 if (!tool) return { result: { error: `Unknown tool ${name}` }, isError: true, durationMs: 0 };92 try {93 const result = await tool.run(args);94 return { result, isError: false, durationMs: Date.now() - t0 };95 } catch (e) {96 return { result: { error: (e as Error).message }, isError: true, durationMs: Date.now() - t0 };97 }98}99