Rebasely Beta: data may be purged

examples/MCP-tools Public

TypeScript · 108 lines · 4.1 KiB · at ee5ceb5

import { z } from "zod";import { ToolError } from "./errors.ts";import { defineTool } from "./types.ts";const QUANTITIES = ["length", "mass", "volume", "temperature"] as const;type Quantity = (typeof QUANTITIES)[number];// Each unit converts to and from its quantity's base unit: metres, kilograms, litres, kelvin.type Unit = {  symbol: string;  quantity: Quantity;  toBase: (v: number) => number;  fromBase: (v: number) => number;};function linear(quantity: Quantity, symbol: string, factor: number): Unit {  return { symbol, quantity, toBase: (v) => v * factor, fromBase: (v) => v / factor };}// Imperial volumes are US customary; the description says so.const UNITS: Unit[] = [  linear("length", "mm", 0.001),  linear("length", "cm", 0.01),  linear("length", "m", 1),  linear("length", "km", 1000),  linear("length", "in", 0.0254),  linear("length", "ft", 0.3048),  linear("length", "yd", 0.9144),  linear("length", "mi", 1609.344),  linear("length", "nmi", 1852),  linear("mass", "mg", 1e-6),  linear("mass", "g", 0.001),  linear("mass", "kg", 1),  linear("mass", "t", 1000),  linear("mass", "oz", 0.028349523125),  linear("mass", "lb", 0.45359237),  linear("mass", "st", 6.35029318),  linear("volume", "ml", 0.001),  linear("volume", "l", 1),  linear("volume", "m3", 1000),  linear("volume", "tsp", 0.00492892159375),  linear("volume", "tbsp", 0.01478676478125),  linear("volume", "fl-oz", 0.0295735295625),  linear("volume", "cup", 0.2365882365),  linear("volume", "pt", 0.473176473),  linear("volume", "qt", 0.946352946),  linear("volume", "gal", 3.785411784),  { symbol: "C", quantity: "temperature", toBase: (v) => v + 273.15, fromBase: (v) => v - 273.15 },  { symbol: "F", quantity: "temperature", toBase: (v) => ((v - 32) * 5) / 9 + 273.15, fromBase: (v) => ((v - 273.15) * 9) / 5 + 32 },  { symbol: "K", quantity: "temperature", toBase: (v) => v, fromBase: (v) => v },];const BY_KEY = new Map(UNITS.map((u) => [u.symbol.toLowerCase(), u]));export function findUnit(text: string): Unit | undefined {  return BY_KEY.get(text.trim().toLowerCase());}function unitOrThrow(text: string): Unit {  const unit = findUnit(text);  if (!unit) throw new ToolError(`Unknown unit "${text}". Known units, by quantity: ${knownUnits()}.`);  return unit;}function knownUnits(): string {  return QUANTITIES.map((q) => `${q}: ${UNITS.filter((u) => u.quantity === q).map((u) => u.symbol).join(", ")}`).join("; ");}// Twelve significant digits hides float noise (0.30000000000000004) without losing anything a// person would read.function tidy(v: number): number {  return Number(v.toPrecision(12));}export const convertUnits = defineTool({  name: "convert_units",  title: "Convert units",  description:    "Convert a value between units of length, mass, volume or temperature. " +    "Use it instead of doing the arithmetic yourself whenever the user needs an exact figure. " +    "Units are short symbols: length mm, cm, m, km, in, ft, yd, mi, nmi; mass mg, g, kg, t, oz, lb, st; " +    "volume ml, l, m3, tsp, tbsp, fl-oz, cup, pt, qt, gal (US customary); temperature C, F, K. " +    "Both units must measure the same quantity.",  input: z.object({    value: z.number().describe("The number to convert, in the `from` unit."),    from: z.string().min(1).describe("The unit the value is in, for example km or F."),    to: z.string().min(1).describe("The unit to convert to, for example mi or C."),  }),  output: z.object({    quantity: z.enum(QUANTITIES),    value: z.number(),    from: z.string(),    result: z.number(),    to: z.string(),  }),  run({ value, from, to }) {    const a = unitOrThrow(from);    const b = unitOrThrow(to);    if (a.quantity !== b.quantity) {      throw new ToolError(`Cannot convert ${a.symbol} (${a.quantity}) to ${b.symbol} (${b.quantity}). Pick two units of the same quantity.`);    }    const base = a.toBase(value);    if (a.quantity === "temperature" && base < 0) {      throw new ToolError(`${value} ${a.symbol} is below absolute zero (${tidy(a.fromBase(0))} ${a.symbol}).`);    }    return { quantity: a.quantity, value, from: a.symbol, result: tidy(b.fromBase(base)), to: b.symbol };  },});