feat: complete production hardening implementation, specialty canonicalization, google-genai migration, voice reliability, sqlite concurrency, and test suite consolidation

This commit is contained in:
2026-09-07 12:57:54 +05:30
parent c23d979338
commit 92fa71ae4a
99 changed files with 5167 additions and 3550 deletions
@@ -0,0 +1,8 @@
"""
Safety package.
"""
from app.ai.safety.emergency import check_emergency
from app.ai.safety.crisis import check_crisis
__all__ = ["check_emergency", "check_crisis"]
@@ -0,0 +1,69 @@
"""
Mental health crisis and self-harm detection engine.
"""
import re
from typing import Optional, Tuple
from app.ai.prompt_rules.safety_rules import CRISIS_KEYWORDS_EN, CRISIS_KEYWORDS_INDIC
TRANSLITERATED_CRISIS_PHRASES = [
"saaganam pola irukku", "uyiroda irukka virumbala", "saaga poraen", "valave pidikala",
"mar jane ka man", "apne aap ko nuksan", "khudkushee",
"chani povali", "sathokobeku",
]
def check_crisis(user_message: str, lang: str = "en") -> Tuple[bool, Optional[str], Optional[str]]:
"""
Evaluates patient message for mental health crisis or self-harm intent.
Returns: (is_crisis, reason, crisis_reply)
"""
if not user_message or not user_message.strip():
return False, None, None
# Check Indic script crisis keywords FIRST against raw message
for kw in CRISIS_KEYWORDS_INDIC:
if kw in user_message:
reply = _build_crisis_reply(lang)
return True, f"Matched Indic crisis phrase: '{kw}'", reply
indic_extra = ["காயப்படுத்திக்கணும்", "கொல்லணும்", "வாழவே பிடிக்கல", "உயிரோட இருக்க", "தற்கொலை", "உயிரை"]
for kw in indic_extra:
if kw in user_message:
reply = _build_crisis_reply(lang)
return True, f"Matched Indic crisis phrase: '{kw}'", reply
msg = user_message.strip().lower()
msg_clean = re.sub(r"[^\w\s]", " ", msg)
msg_clean = re.sub(r"\s+", " ", msg_clean)
# Check English crisis keywords
for kw in CRISIS_KEYWORDS_EN:
kw_clean = re.sub(r"[^\w\s]", " ", kw.lower())
if re.search(rf"\b{re.escape(kw_clean)}\b", msg_clean) or kw.lower() in msg:
reply = _build_crisis_reply(lang)
return True, f"Matched crisis phrase: '{kw}'", reply
# Check Transliterated crisis phrases
for kw in TRANSLITERATED_CRISIS_PHRASES:
if kw in msg_clean or kw in msg:
reply = _build_crisis_reply(lang)
return True, f"Matched transliterated crisis phrase: '{kw}'", reply
return False, None, None
def _build_crisis_reply(lang: str) -> str:
lang_clean = lang.lower() if lang else "en"
if lang_clean.startswith("ta"):
return "உங்களுக்கு மன உளைச்சல் இருந்தால், தயவுசெய்து டெலி-மானாஸ் (Tele-MANAS) உதவி எண் 14416-ஐ தொடர்பு கொள்ளவும் அல்லது மருத்துவமனை ஊழியர்களிடம் தெரிவிக்கவும்."
elif lang_clean.startswith("hi"):
return "यदि आप परेशानी में हैं, तो कृपया तुरंत टेली-मानस (Tele-MANAS) 14416 पर संपर्क करें या अस्पताल के कर्मचारियों को सूचित करें।"
elif lang_clean.startswith("te"):
return "మీరు తీవ్రమైన ఒత్తిడిలో ఉంటే, దయచేసి టెలి-మానస్ (Tele-MANAS) 14416 కు ఫోన్ చేయండి లేదా ఆసుపత్రి సిబ్బందికి చెప్పండి."
elif lang_clean.startswith("kn"):
return "ನೀವು ಮನೋಒತ್ತಡದಲ್ಲಿದ್ದರೆ, ದಯವಿಟ್ಟು ಟೆಲಿ-ಮಾನಸ್ (Tele-MANAS) 14416 ಗೆ ಕರೆ ಮಾಡಿ ಅಥವಾ ಆಸ್ಪತ್ರೆಯ ಸಿಬ್ಬಂದಿಗೆ ತಿಳಿಸಿ."
else:
return "If you're in distress, please reach out to Tele-MANAS at 14416 or alert the hospital staff right now."
@@ -0,0 +1,91 @@
"""
Emergency detection engine with strict safety precedence and false-positive protection.
"""
import re
from typing import Optional, Tuple
from app.ai.prompt_rules.safety_rules import EMERGENCY_KEYWORDS_EN, EMERGENCY_KEYWORDS_INDIC
TRANSLITERATED_EMERGENCY_PHRASES = [
"nenju romba valikuthu", "nenju valla valikuthu", "nenju kadumaya valikuthu",
"moochu vida mudiyala", "moochu adaikuthu", "moochu thinaral",
"mayangi vilunthuten", "mayakkam varuthu", "paralysis vanthuruchu",
"cheest mein bahut dard", "seene mein bahut tej dard", "saans lene mein bahut dikkat",
"saans nahi aa rahi", "behosh ho gaya", "behosh ho gayi",
"chathi lo thivramaina noppi", "oophiri aadam ledhu", "spruha thappi",
"edeyalli theevra novu", "usiraadalu thumba kashta", "pragne thappi",
]
def check_emergency(user_message: str, lang: str = "en") -> Tuple[bool, Optional[str], Optional[str]]:
"""
Evaluates patient message for medical emergency indicators.
Returns: (is_emergency, reason, emergency_reply)
"""
if not user_message or not user_message.strip():
return False, None, None
# Check Indic script emergency keywords against raw user_message FIRST
for kw in EMERGENCY_KEYWORDS_INDIC:
if kw in user_message:
reply = _build_emergency_reply(lang)
return True, f"Matched Indic emergency phrase: '{kw}'", reply
# Additional Tamil/Hindi spoken emergency phrases
indic_extra = ["மூச்சு விட முடியல", "மூச்சு திணறல்", "நெஞ்சு வலி", "நெஞ்சு பாரம்", "கடும் நெஞ்சு வலி", "நெஞ்சு அதிகமா வலி"]
for kw in indic_extra:
if kw in user_message and any(x in user_message for x in ["மூச்சு", "கடும்", "வலி", "அதிகமா"]):
reply = _build_emergency_reply(lang)
return True, f"Matched Indic emergency phrase: '{kw}'", reply
msg = user_message.strip().lower()
msg_clean = re.sub(r"[^\w\s]", " ", msg)
msg_clean = re.sub(r"\s+", " ", msg_clean)
# False Positive Protection: Negated emergency statements ("no chest pain")
negation_patterns = [
r"\bno\s+(?:chest\s+pain|breathing\s+difficulty|heart\s+attack|paralysis|bleeding)\b",
r"\bdon't\s+have\s+(?:chest\s+pain|breathing\s+difficulty|heart\s+attack)\b",
r"\bnot\s+having\s+(?:chest\s+pain|breathing\s+difficulty)\b",
r"\bnever\s+had\s+(?:chest\s+pain|breathing\s+difficulty)\b",
r"\billness\s+none\b",
]
for neg_pat in negation_patterns:
if re.search(neg_pat, msg_clean):
if not any(pos in msg_clean for pos in ["severe chest pain", "cannot breathe", "collapsed"]):
return False, None, None
# Check English emergency keywords
for kw in EMERGENCY_KEYWORDS_EN:
if re.search(rf"\b{re.escape(kw)}\b", msg_clean):
reply = _build_emergency_reply(lang)
return True, f"Matched emergency phrase: '{kw}'", reply
# Check combinations (e.g. "chest pain" AND "difficulty breathing")
if "chest pain" in msg_clean and ("breath" in msg_clean or "shortness" in msg_clean or "difficulty" in msg_clean):
reply = _build_emergency_reply(lang)
return True, "Matched combined chest pain and breathing difficulty", reply
# Check Transliterated emergency phrases
for kw in TRANSLITERATED_EMERGENCY_PHRASES:
if kw in msg_clean:
reply = _build_emergency_reply(lang)
return True, f"Matched transliterated emergency phrase: '{kw}'", reply
return False, None, None
def _build_emergency_reply(lang: str) -> str:
lang_clean = lang.lower() if lang else "en"
if lang_clean.startswith("ta"):
return "இது அவசர மருத்துவ உதவி தேவைப்படும் நிலை. தயவுசெய்து உடனடியாக அருகில் உள்ள மருத்துவமனை அவசர சிகிச்சைப் பிரிவுக்குச் செல்லவும்."
elif lang_clean.startswith("hi"):
return "यह एक आपातकालीन स्थिति हो सकती है। कृपया तुरंत निकटतम अस्पताल के आपातकालीन कक्ष (ER) में जाएँ।"
elif lang_clean.startswith("te"):
return "ఇది అత్యవసర వైద్య పరిస్థితి కావచ్చు. దయచేసి వెంటనే దగ్గరలోని ఆసుపత్రి ఎమర్జెన్సీ విభాగానికి వెళ్ళండి."
elif lang_clean.startswith("kn"):
return "ಇದು ತುರ್ತು ವೈದ್ಯಕೀಯ ಪರಿಸ್ಥಿತಿಯಾಗಿರಬಹುದು. ದಯವಿಟ್ಟು ತಕ್ಷಣ ಹತ್ತಿರದ ಆಸ್ಪತ್ರೆಯ ತುರ್ತು ವಿಭಾಗಕ್ಕೆ ಹೋಗಿ."
else:
return "This requires urgent medical attention. Please inform the nearest hospital staff or proceed directly to the Emergency Room right away."