Rebasely Beta: data may be purged

examples/MCP-tools Public

Accept unit names and plurals in convert_units

Models often pass what the user typed ("pounds", "°F", "kilometres") instead of the symbol, and each miss cost a retry. Every unit now has a list of names, matched case-insensitively with spaces collapsed. The result still reports the canonical symbol.
9f4ffca0546e5e5872e7fda1d8a0aacaf017629d
Parent 7ee1343 Browse filesHistory

Changed files

2 files changed48 additions36 deletionsagainst 7ee1343

  1. Changedsrc/tools/convert-units.ts+39−36
  2. Changedtest/convert-units.test.ts+9−0
Changedsrc/tools/convert-units.ts+39−36
@@ -8,52 +8,55 @@
// Each unit converts to and from its quantity's base unit: metres, kilograms, litres, kelvin.
type Unit = {
symbol: string;
+ // Other ways people and models write the unit, matched case-insensitively.
+ names: 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 };
+function linear(quantity: Quantity, symbol: string, factor: number, names: string[]): Unit {
+ return { symbol, names, 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 },
+ linear("length", "mm", 0.001, ["millimeter", "millimeters", "millimetre", "millimetres"]),
+ linear("length", "cm", 0.01, ["centimeter", "centimeters", "centimetre", "centimetres"]),
+ linear("length", "m", 1, ["meter", "meters", "metre", "metres"]),
+ linear("length", "km", 1000, ["kilometer", "kilometers", "kilometre", "kilometres"]),
+ linear("length", "in", 0.0254, ["inch", "inches", "\""]),
+ linear("length", "ft", 0.3048, ["foot", "feet", "'"]),
+ linear("length", "yd", 0.9144, ["yard", "yards"]),
+ linear("length", "mi", 1609.344, ["mile", "miles"]),
+ linear("length", "nmi", 1852, ["nautical mile", "nautical miles"]),
+ linear("mass", "mg", 1e-6, ["milligram", "milligrams"]),
+ linear("mass", "g", 0.001, ["gram", "grams"]),
+ linear("mass", "kg", 1, ["kilogram", "kilograms", "kilo", "kilos"]),
+ linear("mass", "t", 1000, ["tonne", "tonnes", "metric ton", "metric tons"]),
+ linear("mass", "oz", 0.028349523125, ["ounce", "ounces"]),
+ linear("mass", "lb", 0.45359237, ["lbs", "pound", "pounds"]),
+ linear("mass", "st", 6.35029318, ["stone", "stones"]),
+ linear("volume", "ml", 0.001, ["milliliter", "milliliters", "millilitre", "millilitres"]),
+ linear("volume", "l", 1, ["liter", "liters", "litre", "litres"]),
+ linear("volume", "m3", 1000, ["m³", "cubic meter", "cubic meters", "cubic metre", "cubic metres"]),
+ linear("volume", "tsp", 0.00492892159375, ["teaspoon", "teaspoons"]),
+ linear("volume", "tbsp", 0.01478676478125, ["tablespoon", "tablespoons"]),
+ linear("volume", "fl-oz", 0.0295735295625, ["fl oz", "floz", "fluid ounce", "fluid ounces"]),
+ linear("volume", "cup", 0.2365882365, ["cups"]),
+ linear("volume", "pt", 0.473176473, ["pint", "pints"]),
+ linear("volume", "qt", 0.946352946, ["quart", "quarts"]),
+ linear("volume", "gal", 3.785411784, ["gallon", "gallons"]),
+ { symbol: "C", names: ["°C", "celsius", "centigrade"], quantity: "temperature", toBase: (v) => v + 273.15, fromBase: (v) => v - 273.15 },
+ { symbol: "F", names: ["°F", "fahrenheit"], quantity: "temperature", toBase: (v) => ((v - 32) * 5) / 9 + 273.15, fromBase: (v) => ((v - 273.15) * 9) / 5 + 32 },
+ { symbol: "K", names: ["kelvin"], quantity: "temperature", toBase: (v) => v, fromBase: (v) => v },
];
-const BY_KEY = new Map(UNITS.map((u) => [u.symbol.toLowerCase(), u]));
+const key = (text: string) => text.trim().toLowerCase().replace(/\s+/g, " ");
+const BY_KEY = new Map(UNITS.flatMap((u) => [u.symbol, ...u.names].map((n) => [key(n), u] as const)));
export function findUnit(text: string): Unit | undefined {
- return BY_KEY.get(text.trim().toLowerCase());
+ return BY_KEY.get(key(text));
}
function unitOrThrow(text: string): Unit {
@@ -78,13 +81,13 @@
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; " +
+ "Give units as symbols or names (km or kilometers, F or fahrenheit). 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."),
+ from: z.string().min(1).describe("The unit the value is in, for example km, pounds or °F."),
+ to: z.string().min(1).describe("The unit to convert to, for example mi, kg or celsius."),
}),
output: z.object({
quantity: z.enum(QUANTITIES),
View file
Changedtest/convert-units.test.ts+9−0
@@ -25,6 +25,15 @@
assert.deepEqual(convert(2.5, "KM", " m "), { quantity: "length", value: 2.5, from: "km", result: 2500, to: "m" });
});
+test("accepts unit names, plurals and degree signs", () => {
+ assert.equal(convert(3, "Feet", "inches").result, 36);
+ assert.equal(convert(2, "kilometres", "meters").result, 2000);
+ assert.equal(convert(1, "pounds", "Kilos").result, 0.45359237);
+ assert.equal(convert(1, "fluid ounces", "tablespoons").result, 2);
+ assert.equal(convert(212, "°F", "celsius").result, 100);
+ assert.deepEqual(convert(1, "gallon", "liters"), { quantity: "volume", value: 1, from: "gal", result: 3.785411784, to: "l" });
+});
+
test("hides floating point noise", () => {
assert.equal(convert(0.1, "m", "cm").result, 10);
assert.equal(convert(0.3, "l", "ml").result, 300);
View file

Checks

No checks reported for this commit.

Runs

No runs for this commit.