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
35 changes: 35 additions & 0 deletions skills/utilities/unit_converter/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Unit Converter Skill

You have access to a unit conversion tool. Use it whenever the user asks to convert a value from one unit to another.

## When to use this skill

- The user mentions a numeric value alongside a unit (e.g. "100km", "70kg", "32°F")
- The user asks "how much is X in Y"
- The user needs a unit conversion as part of a larger calculation

## Supported categories

- **Length**: km, m, cm, mm, miles, yards, feet, inches
- **Weight**: kg, g, mg, lbs, oz
- **Temperature**: celsius, fahrenheit, kelvin
- **Speed**: kph, mph, mps, knots

## How to call the tool

Always pass three parameters:
- `value` — the numeric amount (number, not a string)
- `from_unit` — the unit to convert from, lowercase (e.g. "celsius", "km", "lbs")
- `to_unit` — the unit to convert to, lowercase

## Handling errors

If the tool returns an `error` key, explain the issue clearly to the user. Common causes:
- Unrecognised unit name — suggest the closest supported unit
- Mismatched categories — explain that e.g. weight cannot be converted to length
- Missing value — ask the user to provide a numeric amount

## Response style

Always confirm the original value and unit alongside the result. For example:
"100 km is equal to 62.14 miles."
34 changes: 34 additions & 0 deletions skills/utilities/unit_converter/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: "utilities/unit_converter"
version: "0.1.0"
description: "Converts values between common units of measurement including length, weight, temperature, and speed."
short_description: "Converts values between common units of measurement."
issuer:
name: Sameer
email: sam90509@gmail.comcom
github: sameer08055
org: ""
category: "utilities"
parameters:
type: object
properties:
value:
type: "number"
description: "The numeric value to convert."
from_unit:
type: "string"
description: "The unit to convert from (e.g. 'km', 'kg', 'celsius', 'mph')."
to_unit:
type: "string"
description: "The unit to convert to (e.g. 'miles', 'lbs', 'fahrenheit', 'kph')."
required:
- value
- from_unit
- to_unit
requirements: []
constitution: |
1. ACCURACY: Use standard conversion factors only. Never approximate loosely.
2. TRANSPARENCY: Always return the original value and units alongside the result.
3. SAFETY: Return a clear error if a unit is unrecognised or a conversion is physically impossible.
presentation:
icon: "ruler"
color: "#2ecc71"
116 changes: 116 additions & 0 deletions skills/utilities/unit_converter/skill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from typing import Any, Dict
from skillware.core.base_skill import BaseSkill

class UnitConverterSkill(BaseSkill):
"""
Converts values between common units of measurement:
length, weight, temperature, and speed.
"""

@property
def manifest(self) -> Dict[str, Any]:
return {
"name": "utilities/unit_converter",
"version": "0.1.0",
}

CONVERSIONS = {
# Length — base unit: meters
"km": ("length", 1000),
"m": ("length", 1),
"cm": ("length", 0.01),
"mm": ("length", 0.001),
"miles": ("length", 1609.34),
"mile": ("length", 1609.34),
"yards": ("length", 0.9144),
"feet": ("length", 0.3048),
"inches": ("length", 0.0254),

# Weight — base unit: kilograms
"kg": ("weight", 1),
"g": ("weight", 0.001),
"mg": ("weight", 0.000001),
"lbs": ("weight", 0.453592),
"lb": ("weight", 0.453592),
"ounces": ("weight", 0.0283495),
"oz": ("weight", 0.0283495),

# Speed — base unit: meters per second
"mps": ("speed", 1),
"kph": ("speed", 0.277778),
"mph": ("speed", 0.44704),
"knots": ("speed", 0.514444),
}

def _convert_temperature(self, value: float, from_unit: str, to_unit: str):
"""Temperature needs its own logic since it's not a simple multiply."""
f = from_unit.lower()
t = to_unit.lower()

# Convert to Celsius first
if f == "celsius":
celsius = value
elif f == "fahrenheit":
celsius = (value - 32) * 5 / 9
elif f == "kelvin":
celsius = value - 273.15
else:
return None

# Convert from Celsius to target
if t == "celsius":
return celsius
elif t == "fahrenheit":
return (celsius * 9 / 5) + 32
elif t == "kelvin":
return celsius + 273.15
else:
return None

def execute(self, params: Dict[str, Any]) -> Any:
value = params.get("value")
from_unit = str(params.get("from_unit", "")).lower().strip()
to_unit = str(params.get("to_unit", "")).lower().strip()

if value is None:
return {"error": "value is required."}
if not from_unit or not to_unit:
return {"error": "from_unit and to_unit are required."}

# Handle temperature separately
temp_units = {"celsius", "fahrenheit", "kelvin"}
if from_unit in temp_units or to_unit in temp_units:
if from_unit not in temp_units or to_unit not in temp_units:
return {"error": f"Cannot convert between temperature and non-temperature units."}
result = self._convert_temperature(value, from_unit, to_unit)
if result is None:
return {"error": f"Unrecognised temperature unit."}
return {
"original_value": value,
"from_unit": from_unit,
"to_unit": to_unit,
"converted_value": round(result, 6),
}

# Handle all other unit types
if from_unit not in self.CONVERSIONS:
return {"error": f"Unrecognised unit: '{from_unit}'."}
if to_unit not in self.CONVERSIONS:
return {"error": f"Unrecognised unit: '{to_unit}'."}

from_category, from_factor = self.CONVERSIONS[from_unit]
to_category, to_factor = self.CONVERSIONS[to_unit]

if from_category != to_category:
return {"error": f"Cannot convert '{from_unit}' ({from_category}) to '{to_unit}' ({to_category})."}

# Convert: source → base unit → target unit
base_value = value * from_factor
result = base_value / to_factor

return {
"original_value": value,
"from_unit": from_unit,
"to_unit": to_unit,
"converted_value": round(result, 6),
}
69 changes: 69 additions & 0 deletions skills/utilities/unit_converter/test_skill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import pytest
from skills.utilities.unit_converter.skill import UnitConverterSkill

@pytest.fixture
def skill():
return UnitConverterSkill()


# --- Length ---

def test_km_to_miles(skill):
result = skill.execute({"value": 100, "from_unit": "km", "to_unit": "miles"})
assert abs(result["converted_value"] - 62.1371) < 0.01

def test_meters_to_feet(skill):
result = skill.execute({"value": 1, "from_unit": "m", "to_unit": "feet"})
assert abs(result["converted_value"] - 3.28084) < 0.01


# --- Weight ---

def test_kg_to_lbs(skill):
result = skill.execute({"value": 70, "from_unit": "kg", "to_unit": "lbs"})
assert abs(result["converted_value"] - 154.32) < 0.01

def test_oz_to_grams(skill):
result = skill.execute({"value": 16, "from_unit": "oz", "to_unit": "g"})
assert abs(result["converted_value"] - 453.59) < 0.01


# --- Temperature ---

def test_celsius_to_fahrenheit(skill):
result = skill.execute({"value": 100, "from_unit": "celsius", "to_unit": "fahrenheit"})
assert result["converted_value"] == 212.0

def test_freezing_point(skill):
result = skill.execute({"value": 0, "from_unit": "celsius", "to_unit": "fahrenheit"})
assert result["converted_value"] == 32.0

def test_celsius_to_kelvin(skill):
result = skill.execute({"value": 0, "from_unit": "celsius", "to_unit": "kelvin"})
assert result["converted_value"] == 273.15


# --- Speed ---

def test_mph_to_kph(skill):
result = skill.execute({"value": 60, "from_unit": "mph", "to_unit": "kph"})
assert abs(result["converted_value"] - 96.56) < 0.1


# --- Error handling ---

def test_unknown_unit(skill):
result = skill.execute({"value": 10, "from_unit": "furlongs", "to_unit": "km"})
assert "error" in result

def test_mismatched_categories(skill):
result = skill.execute({"value": 10, "from_unit": "kg", "to_unit": "km"})
assert "error" in result

def test_temperature_mixed_with_length(skill):
result = skill.execute({"value": 100, "from_unit": "celsius", "to_unit": "km"})
assert "error" in result

def test_missing_value(skill):
result = skill.execute({"from_unit": "km", "to_unit": "miles"})
assert "error" in result