Files
aionboarding/ai-patient-onboarding/app/services/medical_speech_normalizer.py
T

71 lines
3.1 KiB
Python

import re
# Common Indian phonetic variations & clinical abbreviations
PHONETIC_REPLACEMENTS = {
# Medical terms & symptoms
r"\b(suger|shugar|sugur)\b": "sugar (diabetes)",
r"\b(presure|presur|blood preser)\b": "blood pressure",
r"\b(bp)\b": "BP",
r"\b(chest painu|chestpain|chst pain)\b": "chest pain",
r"\b(hart attack|heart attacku)\b": "heart attack",
r"\b(stomech pain|stomach painu)\b": "stomach pain",
r"\b(head ache|hedache|headacheu)\b": "headache",
r"\b(breath kashtam|breathing problem|moochu thinaral)\b": "breathing difficulty",
r"\b(injekshan|injaction|injecshun)\b": "injection",
r"\b(opration|oparation)\b": "operation",
r"\b(tablat|tablit|mathirai|goli)\b": "tablets",
r"\b(alargy|alergi)\b": "allergy",
r"\b(fevar|favar)\b": "fever",
r"\b(coughu|coff)\b": "cough",
r"\b(vomit|vomitingu|vomitin)\b": "vomiting",
# Common Indian Medicines (Phonetic normalization)
r"\b(parasetamal|paracitamol|paracetamol 650|dolo 650|dolo)\b": "Paracetamol (Dolo 650)",
r"\b(metformin|methformin|glycomet)\b": "Metformin",
r"\b(telmisartan|telma|telmikind)\b": "Telmisartan",
r"\b(amlodipine|amlong|amlo)\b": "Amlodipine",
r"\b(pantoprazole|pantocid|pan d|pan 40)\b": "Pantoprazole",
r"\b(omeprazole|omez)\b": "Omeprazole",
r"\b(atorvastatin|atorva|atorlip)\b": "Atorvastatin",
r"\b(cetirizine|cetzine|citrizin)\b": "Cetirizine",
r"\b(azithromycin|azithral|azithro)\b": "Azithromycin",
r"\b(amoxicillin|mox|augmentin)\b": "Amoxicillin",
r"\b(insulin injection|insuline)\b": "Insulin",
}
def normalize_medical_speech(transcript: str, language_code: str = "en-IN") -> str:
"""
Cleans, sanitizes, and normalizes spoken transcripts from STT models
to ensure high clinical fidelity while retaining the patient's original meaning.
"""
if not transcript or not transcript.strip():
return ""
cleaned = transcript.strip()
# Remove repeated filler words or audio stutter artifacts
cleaned = re.sub(r"\b(um+|uh+|ah+|erm+|er+)\b", "", cleaned, flags=re.IGNORECASE)
cleaned = re.sub(r"\s+", " ", cleaned).strip()
# If it's English or Romanized Indic code-mixed speech, apply phonetic dictionary
lang_lower = language_code.lower()
if lang_lower.startswith("en") or any(c.isascii() for c in cleaned):
for pattern, replacement in PHONETIC_REPLACEMENTS.items():
cleaned = re.sub(pattern, replacement, cleaned, flags=re.IGNORECASE)
# HIPAA & DPDP Act 2023 PII/SPI Redaction: Scrub sensitive non-medical national IDs & card numbers
cleaned = re.sub(r"\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b", "[REDACTED_FINANCIAL_CARD]", cleaned)
cleaned = re.sub(r"\b\d{4}[-\s]?\d{4}[-\s]?\d{4}\b", "[REDACTED_NATIONAL_ID]", cleaned)
cleaned = re.sub(r"\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b", "[REDACTED_SSN]", cleaned)
# Standardize spaces and punctuation
cleaned = re.sub(r"\s+([.,!?])", r"\1", cleaned)
cleaned = re.sub(r"\s+", " ", cleaned).strip()
# Capitalize first letter if needed
if cleaned and len(cleaned) > 1:
cleaned = cleaned[0].upper() + cleaned[1:]
return cleaned