Spaces:
Build error
Build error
| """ | |
| 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} |