""" tools.py — the entire sandbox ------------------------------ Plain, pure, deterministic Python functions. No I/O, no network, no filesystem, no eval/exec. This is the *complete* attack surface the LLM can touch — nothing outside TOOL_REGISTRY is reachable, no matter what language (English or Malayalam) the conversation is happening in. Every function takes typed args and returns a JSON-serializable dict. Docstrings double as the tool schema shown to the model. """ from __future__ import annotations WEIGHT_STATUS_RANGES = [ (18.5, "Underweight"), (25.0, "Normal weight"), (30.0, "Overweight"), (float("inf"), "Obesity"), ] def calculate_bmi(weight_kg: float, height_cm: float) -> dict: """Calculates Body Mass Index (BMI) and its general weight-status category. Args: weight_kg: Body weight in kilograms. height_cm: Height in centimeters. """ if weight_kg <= 0 or height_cm <= 0: return {"error": "weight_kg and height_cm must both be positive numbers."} height_m = height_cm / 100.0 bmi = weight_kg / (height_m ** 2) category = next(label for threshold, label in WEIGHT_STATUS_RANGES if bmi < threshold) return { "bmi": round(bmi, 1), "category": category, "note": "BMI is a screening tool only, not a diagnosis. It does not " "account for muscle mass, bone density, or body composition.", } _UNIT_CONVERSIONS = { ("kg", "lb"): lambda v: v * 2.20462, ("lb", "kg"): lambda v: v / 2.20462, ("cm", "in"): lambda v: v / 2.54, ("in", "cm"): lambda v: v * 2.54, ("c", "f"): lambda v: v * 9 / 5 + 32, ("f", "c"): lambda v: (v - 32) * 5 / 9, ("mg", "mcg"): lambda v: v * 1000, ("mcg", "mg"): lambda v: v / 1000, ("g", "mg"): lambda v: v * 1000, ("mg", "g"): lambda v: v / 1000, } def convert_unit(value: float, from_unit: str, to_unit: str) -> dict: """Converts a numeric health measurement between common units. Supported unit codes: kg, lb (weight); cm, in (length); c, f (temperature); mg, mcg, g (medication mass — conversion only, does NOT validate or recommend any dose). Args: value: The numeric value to convert. from_unit: Source unit code, e.g. "kg". to_unit: Target unit code, e.g. "lb". """ key = (from_unit.strip().lower(), to_unit.strip().lower()) if key[0] == key[1]: return {"result": value, "unit": to_unit} if key not in _UNIT_CONVERSIONS: return {"error": f"Unsupported conversion: {from_unit} -> {to_unit}"} result = _UNIT_CONVERSIONS[key](value) return {"result": round(result, 3), "unit": to_unit} _VITAL_RANGES = { "heart_rate": (60, 100, "bpm"), "systolic_bp": (90, 120, "mmHg"), "diastolic_bp": (60, 80, "mmHg"), "body_temperature_c": (36.1, 37.2, "°C"), "respiratory_rate": (12, 20, "breaths/min"), "spo2": (95, 100, "%"), } def check_vital_sign_range(vital: str, value: float) -> dict: """Compares a vital-sign reading against typical resting reference ranges for a healthy adult. General reference only — not diagnostic. Args: vital: One of "heart_rate", "systolic_bp", "diastolic_bp", "body_temperature_c", "respiratory_rate", "spo2". value: The measured value. """ key = vital.strip().lower() if key not in _VITAL_RANGES: return {"error": f"Unknown vital '{vital}'. Supported: {list(_VITAL_RANGES)}"} low, high, unit = _VITAL_RANGES[key] if value < low: status = "below typical adult resting range" elif value > high: status = "above typical adult resting range" else: status = "within typical adult resting range" return { "vital": key, "value": value, "unit": unit, "typical_range": f"{low}-{high} {unit}", "status": status, "note": "General reference range for a resting healthy adult, not a " "diagnosis. Context (exercise, anxiety, medication) matters.", } _EMERGENCY_KEYWORDS = [ "chest pain", "crushing chest", "can't breathe", "cannot breathe", "difficulty breathing", "shortness of breath at rest", "blue lips", "severe bleeding", "won't stop bleeding", "stroke", "face drooping", "slurred speech", "sudden numbness", "sudden confusion", "anaphylaxis", "throat closing", "severe allergic reaction", "suicidal", "want to end my life", "overdose", "unresponsive", "not breathing", "seizure", "severe burn", "coughing up blood", ] _URGENT_KEYWORDS = [ "high fever", "persistent vomiting", "dehydration", "severe pain", "broken bone", "deep cut", "spreading redness", "worsening rash", "confusion", "fainted", "dizziness that won't go away", ] def symptom_urgency_triage(symptoms: list) -> dict: """Rule-based keyword triage flagging recognized red-flag emergency or urgent-care indicators. A static keyword screen, NOT a diagnostic tool. Args: symptoms: A list of symptom phrases as described by the user, in English (translate the user's words into English first if they described symptoms in Malayalam), e.g. ["chest pain"]. """ joined = " ".join(str(s).lower() for s in symptoms) hits_emergency = [kw for kw in _EMERGENCY_KEYWORDS if kw in joined] if hits_emergency: return { "urgency": "emergency", "matched_flags": hits_emergency, "recommendation": "These symptoms include recognized emergency " "warning signs. Call emergency services (108 " "in India, 911 in the US, or your local " "emergency number) or go to the nearest " "emergency room right now.", } hits_urgent = [kw for kw in _URGENT_KEYWORDS if kw in joined] if hits_urgent: return { "urgency": "urgent", "matched_flags": hits_urgent, "recommendation": "These symptoms are worth same-day medical " "attention — contact an urgent care clinic, " "telehealth line, or your doctor today.", } return { "urgency": "routine_or_self_care", "matched_flags": [], "recommendation": "No recognized emergency or urgent red-flags were " "matched. Routine symptoms can still be worth a " "regular doctor's visit if they persist, worsen, " "or concern you.", } _INTERACTION_TABLE = { frozenset({"ibuprofen", "warfarin"}): "Increased bleeding risk — NSAIDs " "like ibuprofen can amplify warfarin's blood-thinning effect.", frozenset({"aspirin", "warfarin"}): "Increased bleeding risk when combined.", frozenset({"acetaminophen", "alcohol"}): "Heavy alcohol use with " "acetaminophen (paracetamol) increases risk of liver damage.", frozenset({"ibuprofen", "aspirin"}): "Taking both regularly increases " "GI irritation/bleeding risk and ibuprofen can blunt aspirin's " "heart-protective effect if timed incorrectly.", frozenset({"ssri", "maoi"}): "Potentially dangerous serotonin syndrome " "risk — should generally not be combined without close medical " "supervision.", frozenset({"ibuprofen", "lisinopril"}): "NSAIDs can reduce the " "effectiveness of ACE inhibitors like lisinopril and stress the kidneys.", } def drug_interaction_lookup(drug_a: str, drug_b: str) -> dict: """Looks up general, educational interaction-awareness notes between two medication names from a small static reference table. NOT exhaustive — never confirms an absence of interaction. Args: drug_a: Name of the first medication (e.g. "ibuprofen"). drug_b: Name of the second medication (e.g. "warfarin"). """ key = frozenset({drug_a.strip().lower(), drug_b.strip().lower()}) note = _INTERACTION_TABLE.get(key) if note: return {"pair": [drug_a, drug_b], "known_interaction": True, "note": note} return { "pair": [drug_a, drug_b], "known_interaction": False, "note": "No entry in this small reference table for this pair. This " "does NOT mean the combination is safe — always check with a " "pharmacist or doctor, or a full drug interaction database.", } TOOLS = [ calculate_bmi, convert_unit, check_vital_sign_range, symptom_urgency_triage, drug_interaction_lookup, ] TOOL_REGISTRY = {fn.__name__: fn for fn in TOOLS}