SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
4.2 KB · 99 lines typescript
Raw Blame History
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