Spaces:
Build error
Build error
File size: 8,697 Bytes
be76ce1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | """
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} |