Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions examples/unit_conversion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env npx tsx

import { unitConversionTool, getSupportedUnits, performConversion } from '../src/tools/unitConversionTool';

async function demonstrateUnitConversion() {
console.log('=== Unit Conversion Tool Demo ===\n');

// Display supported units
const supportedUnits = getSupportedUnits();
console.log('📊 Supported Units:');
console.log(' Static conversions:', supportedUnits.static.slice(0, 10).join(', '), '...');
console.log(' Currencies:', supportedUnits.currencies.slice(0, 10).join(', '), '...\n');

// Demo conversions
const demos = [
// Weight conversions
{ value: 10, fromUnit: 'kg', toUnit: 'lbs', description: 'Weight: Kilograms to Pounds' },
{ value: 150, fromUnit: 'lb', toUnit: 'kg', description: 'Weight: Pounds to Kilograms' },
{ value: 5000, fromUnit: 'g', toUnit: 'lb', description: 'Weight: Grams to Pounds' },

// Temperature conversions
{ value: 25, fromUnit: 'C', toUnit: 'F', description: 'Temperature: Celsius to Fahrenheit' },
{ value: 98.6, fromUnit: 'F', toUnit: 'C', description: 'Temperature: Fahrenheit to Celsius' },
{ value: 0, fromUnit: 'C', toUnit: 'K', description: 'Temperature: Celsius to Kelvin' },

// Length conversions
{ value: 5, fromUnit: 'km', toUnit: 'mi', description: 'Length: Kilometers to Miles' },
{ value: 6, fromUnit: 'ft', toUnit: 'm', description: 'Length: Feet to Meters' },
{ value: 100, fromUnit: 'yd', toUnit: 'm', description: 'Length: Yards to Meters' },

// Volume conversions
{ value: 3.5, fromUnit: 'L', toUnit: 'gal', description: 'Volume: Liters to Gallons' },
{ value: 500, fromUnit: 'ml', toUnit: 'cup', description: 'Volume: Milliliters to Cups' },

// Area conversions
{ value: 1000, fromUnit: 'm²', toUnit: 'ft²', description: 'Area: Square Meters to Square Feet' },
{ value: 2, fromUnit: 'acre', toUnit: 'hectare', description: 'Area: Acres to Hectares' },

// Currency conversions (will use mock rates if no API key)
{ value: 1000, fromUnit: 'INR', toUnit: 'USD', description: 'Currency: Indian Rupees to US Dollars' },
{ value: 100, fromUnit: 'USD', toUnit: 'EUR', description: 'Currency: US Dollars to Euros' },
{ value: 500, fromUnit: 'GBP', toUnit: 'JPY', description: 'Currency: British Pounds to Japanese Yen' },
];

console.log('🔄 Running Conversions:\n');

for (const demo of demos) {
try {
console.log(`📏 ${demo.description}`);
console.log(` Input: ${demo.value} ${demo.fromUnit}`);

const result = await unitConversionTool.execute({
value: demo.value,
fromUnit: demo.fromUnit,
toUnit: demo.toUnit
});

console.log(` Output: ${result.convertedValue.toFixed(2)} ${result.toUnit}`);
if (result.formula) {
console.log(` Method: ${result.formula}`);
}
console.log();
} catch (error) {
console.error(` Error: ${error instanceof Error ? error.message : 'Unknown error'}\n`);
}
}

// Demo error handling
console.log('❌ Error Handling Demo:\n');

const errorCases = [
{ value: 100, fromUnit: 'kg', toUnit: 'C', description: 'Invalid: Different categories' },
{ value: 50, fromUnit: 'xyz', toUnit: 'kg', description: 'Invalid: Unknown unit' },
];

for (const errorCase of errorCases) {
try {
console.log(`🚫 ${errorCase.description}`);
console.log(` Attempting: ${errorCase.value} ${errorCase.fromUnit} → ${errorCase.toUnit}`);

await unitConversionTool.execute({
value: errorCase.value,
fromUnit: errorCase.fromUnit,
toUnit: errorCase.toUnit
});

console.log(' Unexpected: No error thrown');
console.log();
} catch (error) {
console.log(` Expected error: ${error instanceof Error ? error.message : 'Unknown error'}`);
console.log();
}
}

// Environment variable info
console.log('💡 Tips:');
console.log(' - Set CURRENCY_API_KEY environment variable for live exchange rates');
console.log(' - Supported providers: exchangerate-api.com, currencyapi.com, etc.');
console.log(' - Without API key, mock rates are used for demonstration\n');

// Advanced usage example
console.log('🚀 Advanced Usage Example:\n');
console.log('```typescript');
console.log('// Import the tool');
console.log("import { unitConversionTool } from '../src/tools/unitConversionTool';");
console.log('');
console.log('// Use the tool directly');
console.log('const result = await unitConversionTool.execute({');
console.log(' value: 100,');
console.log(" fromUnit: 'USD',");
console.log(" toUnit: 'EUR'");
console.log('});');
console.log('');
console.log('// Or use the performConversion function');
console.log("import { performConversion } from '../src/tools/unitConversionTool';");
console.log('const result = await performConversion({');
console.log(' value: 100,');
console.log(" fromUnit: 'kg',");
console.log(" toUnit: 'lbs'");
console.log('});');
console.log('```\n');
}

// Run the demonstration
demonstrateUnitConversion().catch(console.error);
242 changes: 242 additions & 0 deletions src/tools/unitConversionTool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
export interface ConversionRequest {
value: number;
fromUnit: string;
toUnit: string;
}

export interface ConversionResult {
originalValue: number;
convertedValue: number;
fromUnit: string;
toUnit: string;
formula?: string;
}

interface ConversionUnit {
category: 'weight' | 'temperature' | 'length' | 'volume' | 'area' | 'currency';
unit: string;
toBase: (value: number) => number;
fromBase: (value: number) => number;
}

interface CurrencyRates {
[currency: string]: number;
}

const CONVERSION_UNITS: ConversionUnit[] = [
// Weight conversions (base: kg)
{ category: 'weight', unit: 'kg', toBase: (v) => v, fromBase: (v) => v },
{ category: 'weight', unit: 'g', toBase: (v) => v / 1000, fromBase: (v) => v * 1000 },
{ category: 'weight', unit: 'mg', toBase: (v) => v / 1000000, fromBase: (v) => v * 1000000 },
{ category: 'weight', unit: 'lb', toBase: (v) => v * 0.453592, fromBase: (v) => v / 0.453592 },
{ category: 'weight', unit: 'lbs', toBase: (v) => v * 0.453592, fromBase: (v) => v / 0.453592 },
{ category: 'weight', unit: 'oz', toBase: (v) => v * 0.0283495, fromBase: (v) => v / 0.0283495 },
{ category: 'weight', unit: 'ton', toBase: (v) => v * 1000, fromBase: (v) => v / 1000 },
{ category: 'weight', unit: 'tonne', toBase: (v) => v * 1000, fromBase: (v) => v / 1000 },

// Temperature conversions (base: C)
{ category: 'temperature', unit: 'C', toBase: (v) => v, fromBase: (v) => v },
{ category: 'temperature', unit: 'F', toBase: (v) => (v - 32) * 5/9, fromBase: (v) => v * 9/5 + 32 },
{ category: 'temperature', unit: 'K', toBase: (v) => v - 273.15, fromBase: (v) => v + 273.15 },

// Length conversions (base: m)
{ category: 'length', unit: 'm', toBase: (v) => v, fromBase: (v) => v },
{ category: 'length', unit: 'km', toBase: (v) => v * 1000, fromBase: (v) => v / 1000 },
{ category: 'length', unit: 'cm', toBase: (v) => v / 100, fromBase: (v) => v * 100 },
{ category: 'length', unit: 'mm', toBase: (v) => v / 1000, fromBase: (v) => v * 1000 },
{ category: 'length', unit: 'mi', toBase: (v) => v * 1609.34, fromBase: (v) => v / 1609.34 },
{ category: 'length', unit: 'mile', toBase: (v) => v * 1609.34, fromBase: (v) => v / 1609.34 },
{ category: 'length', unit: 'yd', toBase: (v) => v * 0.9144, fromBase: (v) => v / 0.9144 },
{ category: 'length', unit: 'ft', toBase: (v) => v * 0.3048, fromBase: (v) => v / 0.3048 },
{ category: 'length', unit: 'in', toBase: (v) => v * 0.0254, fromBase: (v) => v / 0.0254 },

// Volume conversions (base: L)
{ category: 'volume', unit: 'L', toBase: (v) => v, fromBase: (v) => v },
{ category: 'volume', unit: 'l', toBase: (v) => v, fromBase: (v) => v },
{ category: 'volume', unit: 'mL', toBase: (v) => v / 1000, fromBase: (v) => v * 1000 },
{ category: 'volume', unit: 'ml', toBase: (v) => v / 1000, fromBase: (v) => v * 1000 },
{ category: 'volume', unit: 'gal', toBase: (v) => v * 3.78541, fromBase: (v) => v / 3.78541 },
{ category: 'volume', unit: 'qt', toBase: (v) => v * 0.946353, fromBase: (v) => v / 0.946353 },
{ category: 'volume', unit: 'pt', toBase: (v) => v * 0.473176, fromBase: (v) => v / 0.473176 },
{ category: 'volume', unit: 'cup', toBase: (v) => v * 0.236588, fromBase: (v) => v / 0.236588 },
{ category: 'volume', unit: 'fl oz', toBase: (v) => v * 0.0295735, fromBase: (v) => v / 0.0295735 },

// Area conversions (base: m²)
{ category: 'area', unit: 'm2', toBase: (v) => v, fromBase: (v) => v },
{ category: 'area', unit: 'm²', toBase: (v) => v, fromBase: (v) => v },
{ category: 'area', unit: 'km2', toBase: (v) => v * 1000000, fromBase: (v) => v / 1000000 },
{ category: 'area', unit: 'km²', toBase: (v) => v * 1000000, fromBase: (v) => v / 1000000 },
{ category: 'area', unit: 'cm2', toBase: (v) => v / 10000, fromBase: (v) => v * 10000 },
{ category: 'area', unit: 'cm²', toBase: (v) => v / 10000, fromBase: (v) => v * 10000 },
{ category: 'area', unit: 'ft2', toBase: (v) => v * 0.092903, fromBase: (v) => v / 0.092903 },
{ category: 'area', unit: 'ft²', toBase: (v) => v * 0.092903, fromBase: (v) => v / 0.092903 },
{ category: 'area', unit: 'in2', toBase: (v) => v * 0.00064516, fromBase: (v) => v / 0.00064516 },
{ category: 'area', unit: 'in²', toBase: (v) => v * 0.00064516, fromBase: (v) => v / 0.00064516 },
{ category: 'area', unit: 'acre', toBase: (v) => v * 4046.86, fromBase: (v) => v / 4046.86 },
{ category: 'area', unit: 'hectare', toBase: (v) => v * 10000, fromBase: (v) => v / 10000 },
];

const CURRENCY_CODES = [
'USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'CHF', 'CNY', 'INR', 'MXN',
'BRL', 'ZAR', 'SGD', 'HKD', 'NZD', 'SEK', 'NOK', 'DKK', 'PLN', 'RUB',
'TRY', 'KRW', 'MYR', 'IDR', 'THB', 'PHP', 'VND', 'CZK', 'HUF', 'ILS'
];

async function fetchCurrencyRates(baseCurrency: string = 'USD'): Promise<CurrencyRates> {
const apiKey = process.env.CURRENCY_API_KEY;

if (!apiKey) {
console.warn('CURRENCY_API_KEY not set, using mock rates for demonstration');
return getMockCurrencyRates();
}

try {
const apiUrl = process.env.CURRENCY_API_URL || 'https://api.exchangerate-api.com/v4/latest';
const response = await fetch(`${apiUrl}/${baseCurrency}?apikey=${apiKey}`);

if (!response.ok) {
throw new Error(`Currency API error: ${response.status}`);
}

const data = await response.json();
return data.rates || getMockCurrencyRates();
} catch (error) {
console.error('Failed to fetch currency rates:', error);
return getMockCurrencyRates();
}
}

function getMockCurrencyRates(): CurrencyRates {
return {
USD: 1.0,
EUR: 0.85,
GBP: 0.73,
JPY: 110.5,
AUD: 1.35,
CAD: 1.25,
CHF: 0.92,
CNY: 6.45,
INR: 83.50,
MXN: 18.20,
BRL: 5.25,
ZAR: 15.80,
SGD: 1.35,
HKD: 7.85,
NZD: 1.45,
};
}

function findConversionUnit(unit: string): ConversionUnit | null {
return CONVERSION_UNITS.find(u =>
u.unit.toLowerCase() === unit.toLowerCase()
) || null;
}

function isCurrency(unit: string): boolean {
return CURRENCY_CODES.includes(unit.toUpperCase());
}

async function convertCurrency(value: number, from: string, to: string): Promise<number> {
const fromCurrency = from.toUpperCase();
const toCurrency = to.toUpperCase();

const rates = await fetchCurrencyRates('USD');

if (!rates[fromCurrency] || !rates[toCurrency]) {
throw new Error(`Currency not supported: ${!rates[fromCurrency] ? fromCurrency : toCurrency}`);
}

// Convert through USD as base
const usdValue = value / rates[fromCurrency];
return usdValue * rates[toCurrency];
}

function convertStatic(value: number, fromUnit: ConversionUnit, toUnit: ConversionUnit): number {
if (fromUnit.category !== toUnit.category) {
throw new Error(`Cannot convert between different categories: ${fromUnit.category} to ${toUnit.category}`);
}

const baseValue = fromUnit.toBase(value);
return toUnit.fromBase(baseValue);
}

export async function performConversion(request: ConversionRequest): Promise<ConversionResult> {
const { value, fromUnit, toUnit } = request;

// Check if it's currency conversion
if (isCurrency(fromUnit) && isCurrency(toUnit)) {
const convertedValue = await convertCurrency(value, fromUnit, toUnit);
return {
originalValue: value,
convertedValue,
fromUnit: fromUnit.toUpperCase(),
toUnit: toUnit.toUpperCase(),
formula: `Using live exchange rates`
};
}

// Check if it's static unit conversion
const from = findConversionUnit(fromUnit);
const to = findConversionUnit(toUnit);

if (!from) {
throw new Error(`Unknown unit: ${fromUnit}`);
}

if (!to) {
throw new Error(`Unknown unit: ${toUnit}`);
}

const convertedValue = convertStatic(value, from, to);

return {
originalValue: value,
convertedValue,
fromUnit,
toUnit,
formula: `${fromUnit} → ${from.category} base → ${toUnit}`
};
}

export function getSupportedUnits(): { static: string[], currencies: string[] } {
return {
static: CONVERSION_UNITS.map(u => u.unit),
currencies: CURRENCY_CODES
};
}

export const unitConversionTool = {
name: 'unitConversion',
description: 'Convert between various units of measurement including weight, temperature, length, volume, area, and currencies',

parameters: {
type: 'object',
properties: {
value: {
type: 'number',
description: 'The numeric value to convert'
},
fromUnit: {
type: 'string',
description: 'The unit to convert from (e.g., kg, lb, C, F, USD, EUR)'
},
toUnit: {
type: 'string',
description: 'The unit to convert to'
}
},
required: ['value', 'fromUnit', 'toUnit']
},

execute: async (params: ConversionRequest): Promise<ConversionResult> => {
if (typeof params.value !== 'number' || isNaN(params.value)) {
throw new Error('Value must be a valid number');
}

if (!params.fromUnit || !params.toUnit) {
throw new Error('Both fromUnit and toUnit are required');
}

return await performConversion(params);
}
};
Loading