diff --git a/ai-patient-onboarding/app/ai/orchestrator.py b/ai-patient-onboarding/app/ai/orchestrator.py index 488a4cc..c28c00d 100644 --- a/ai-patient-onboarding/app/ai/orchestrator.py +++ b/ai-patient-onboarding/app/ai/orchestrator.py @@ -60,26 +60,59 @@ class AIOrchestrator: stage = session.stage code = specialty.get("code", "general") lang = (session.language or "ta-IN").lower() - msg_lower = user_message.lower().strip() - is_negative = msg_lower in ["no", "no more", "nothing", "nothing else", "know nothing", "none", "illness none", "இல்லை", "ஒன்றுமில்லை", "नहीं"] + msg_lower = (user_message or "").lower().strip() + # Broad negative word matching across Indian languages & dialects + negative_keywords = [ + "no", "no more", "nothing", "nothing else", "none", "no illness", "no problem", + "இல்லை", "இல்லைங்க", "இல்ல", "ஒன்றுமில்லை", "ஒன்னுமில்ல", "ஏதுமில்லை", + "नहीं", "कुछ नहीं", "कोई नहीं", "नाही", + "లేదు", "ఏమీ లేదు", "లేదండి", + "ಇಲ್ಲ", "ಇಲ್ಲವೇ ಇಲ್ಲ", "ಏನೂ ಇಲ್ಲ", + "ഇല്ല", "ഒന്നുമില്ല", "ഇല്ല കട്ടായം", + "না", "কিছু না", "নাই", + "नाही", "काही नाही", + "નથી", "કાંઈ નથી", + "ਨਹੀਂ", "ਕੁਝ ਨਹੀਂ" + ] + is_negative = any(k in msg_lower for k in negative_keywords) # Localized clinical phrases (Spoken & Caring Tone) if lang.startswith("ta"): conclusion_text = "மிக்க நன்றி. உங்கள் உடல்நல விவரங்கள் சிறப்பாக பதிவு செய்யப்பட்டுள்ளன. மருத்துவர் விரைவில் உங்களைப் பரிசீலிப்பார்." - symptom_q = "இந்த அறிகுறிகள் எத்தனை நாளா இருக்கு? இதுக்காக ஏதாச்சும் மாத்திரை சாப்பிட்டீங்களா?" + symptom_q = "சரிங்க, உங்களோட அறிகுறிகள் பற்றி சொன்னீங்க. இந்த பிரச்சனை எத்தனை நாளா இருக்கு? இதுக்காக ஏதாச்சும் மாத்திரை சாப்பிட்டீங்களா?" med_q = "சரிங்க. உங்களுக்கு வேற ஏதேனும் உடம்பு பிரச்சனைகள் அல்லது மாத்திரை ஒவ்வாமை (Allergy) இருக்கா?" elif lang.startswith("hi"): - conclusion_text = "धन्यवाद। आपकी स्वास्थ्य जानकारी दर्ज कर ली गई है। डॉक्टर जल्द ही समीक्षा करेंगे।" - symptom_q = "आपको यह समस्या कितने दिनों से है और क्या आपने कोई दवाई ली है?" - med_q = "धन्यवाद। क्या आपको किसी दवाई से एलर्जी या कोई अन्य बीमारी है?" + conclusion_text = "धन्यवाद। आपकी स्वास्थ्य जानकारी सफलतापूर्वक दर्ज कर ली गई है। डॉक्टर जल्द ही आपकी समीक्षा करेंगे।" + symptom_q = "जी समझ गया। आपको यह समस्या कितने दिनों से है और क्या इसके लिए आपने कोई दवाई ली है?" + med_q = "ठीक है। क्या आपको किसी दवाई से एलर्जी या कोई अन्य बीमारी है?" elif lang.startswith("te"): - conclusion_text = "ధన్యవాదాలు. మీ ఆరోగ్య వివరాలు నమోదు చేయబడ్డాయి." - symptom_q = "ఈ సమస్య మీకు ఎన్ని రోజుల నుండి ఉంది మరియు ఏవైనా మందులు వాడారా?" - med_q = "ధన్యవాదాలు. మీకు ఏవైనా మందుల పడనితనం (Allergy) ఉందా?" + conclusion_text = "చాలా ధన్యవాదాలు. మీ ఆరోగ్య వివరాలు నమోదు చేయబడ్డాయి. వైద్యులు త్వరలోనే పరిశీలిస్తారు." + symptom_q = "సరేనండి, ఈ సమస్య మీకు ఎన్ని రోజుల నుండి ఉంది? దీని కోసం ఏవైనా మందులు వాడారా?" + med_q = "ధన్యవాదాలు. మీకు ఏవైనా మందుల పడనితనం (Allergy) లేదా ఇతర ఆరోగ్య సమస్యలు ఉన్నాయా?" elif lang.startswith("kn"): - conclusion_text = "ಧನ್ಯವಾದಗಳು. ನಿಮ್ಮ ಆರೋಗ್ಯದ ಮಾಹಿತಿ ದಾಖಲಾಗಿದೆ." - symptom_q = "ಈ ಸಮಸ್ಯೆ ನಿಮಗೆ ಎಷ್ಟು ದಿನಗಳಿಂದ ಇದೆ ಮತ್ತು ಯಾವುದೇ ಮಾತ್ರೆಗಳನ್ನು ತೆಗೆದುಕೊಂಡಿದ್ದೀರಾ?" - med_q = "ಧನ್ಯವಾದಗಳು. ನಿಮಗೆ ಯಾವುದೇ ಔಷಧಿ ಅಲರ್ಜಿ ಇದೆಯೇ?" + conclusion_text = "ತುಂಬಾ ಧನ್ಯವಾದಗಳು. ನಿಮ್ಮ ಆರೋಗ್ಯದ ಮಾಹಿತಿ ದಾಖಲಾಗಿದೆ. ವೈದ್ಯರು ಶೀಘ್ರದಲ್ಲೇ ಪರಿಶೀಲಿಸಲಿದ್ದಾರೆ." + symptom_q = "ಸರಿ, ಈ ತೊಂದರೆ ನಿಮಗೆ ಎಷ್ಟು ದಿನಗಳಿಂದ ಇದೆ? ಯಾವುದಾದರೂ ಮಾತ್ರೆ ತೆಗೆದುಕೊಂಡಿದ್ದೀರಾ?" + med_q = "ಧನ್ಯವಾದಗಳು. ನಿಮಗೆ ಯಾವುದಾದರೂ ಔಷಧಿ ಅಲರ್ಜಿ (Allergy) ಅಥವಾ ಇತರ ಆರೋಗ್ಯ ಸಮಸ್ಯೆ ಇದೆಯೇ?" + elif lang.startswith("ml"): + conclusion_text = "വളരെ നന്ദി. താങ്കളുടെ വിവരങ്ങൾ രേഖപ്പെടുത്തിയിട്ടുണ്ട്. ഡോക്ടർ ഉടൻ പരിശോധിക്കുന്നതാണ്." + symptom_q = "ശരി, ഈ ലക്ഷണങ്ങൾ എത്ര ദിവസമായിട്ടുണ്ട്? ഇതിനായി എന്തെങ്കിലും മരുന്ന് കഴിച്ചിട്ടുണ്ടോ?" + med_q = "നന്ദി. താങ്കൾക്ക് എന്തെങ്കിലും മരുന്ന് അലർജിയോ മറ്റ് ആരോഗ്യ പ്രശ്നങ്ങളോ ഉണ്ടോ?" + elif lang.startswith("bn"): + conclusion_text = "অনেক ধন্যবাদ। আপনার স্বাস্থ্য সম্পর্কিত তথ্য জমা নেওয়া হয়েছে। চিকিৎসক শীঘ্রই পর্যালোচনা করবেন।" + symptom_q = "ঠিক আছে, এই সমস্যা আপনার কত দিন ধরে হচ্ছে? এর জন্য কোনো ওষুধ খেয়েছেন কি?" + med_q = "ধন্যবাদ। আপনার কি কোনো ওষুধে অ্যালার্জি বা অন্য কোনো রোগ আছে?" + elif lang.startswith("mr"): + conclusion_text = "खूप खूप धन्यवाद. आपली आरोग्य माहिती नोंदवली गेली आहे. डॉक्टर लवकरच तपासणी करतील." + symptom_q = "होय, हा त्रास तुम्हाला किती दिवसांपासून होत आहे आणि काही औषध घेतले आहे का?" + med_q = "धन्यवाद. आपल्याला काही औषधांची ॲलर्जी किंवा इतर कोणताही आजार आहे का?" + elif lang.startswith("gu"): + conclusion_text = "ખૂબ ખૂબ આભાર. આપની આરોગ્ય વિગતો નોંધાઈ ગઈ છે. ડોક્ટર જલ્દી તપાસ કરશે." + symptom_q = "બરાબર, આ તકલીફ તમને કેટલા દિવસથી છે અને કોઈ દવા લીધી છે?" + med_q = "આભાર. તમને કોઈ દવાની એલર્જી કે અન્ય કોઈ બીમારી છે?" + elif lang.startswith("pa"): + conclusion_text = "ਬਹੁਤ ਧੰਨਵਾਦ। ਤੁਹਾਡੀ ਜਾਣਕਾਰੀ ਦਰਜ ਕਰ ਲਈ ਗਈ ਹੈ। ਡਾਕਟਰ ਜਲਦੀ ਹੀ ਜਾਂਚ ਕਰਨਗੇ।" + symptom_q = "ਜੀ ਠੀਕ ਹੈ, ਇਹ ਸਮੱਸਿਆ ਤੁਹਾਨੂੰ ਕਿੰਨੇ ਦਿਨਾਂ ਤੋਂ ਹੈ ਅਤੇ ਕੀ ਕੋਈ ਦਵਾਈ ਲਈ ਹੈ?" + med_q = "ਧੰਨਵਾਦ। ਕੀ ਤੁਹਾਨੂੰ ਕਿਸੇ ਦਵਾਈ ਤੋਂ ਐਲਰਜੀ ਜਾਂ ਕੋਈ ਹੋਰ ਬੀਮਾਰੀ ਹੈ?" else: conclusion_text = "Thank you. I have recorded your intake details for the doctor to review." symptom_q = "How long have you had these symptoms, and have you taken any medicines?" diff --git a/ai-patient-onboarding/app/api/voice_onboarding.py b/ai-patient-onboarding/app/api/voice_onboarding.py index 59858c5..0a22bd8 100644 --- a/ai-patient-onboarding/app/api/voice_onboarding.py +++ b/ai-patient-onboarding/app/api/voice_onboarding.py @@ -1,3 +1,4 @@ +import base64 import json from uuid import uuid4 from typing import Optional @@ -75,10 +76,11 @@ async def start_voice_onboarding( ) # Synthesize audio greeting - audio_id, _ = await voice_service.text_to_speech( + audio_id, greeting_bytes = await voice_service.text_to_speech( greeting_text, request.language, ) + greeting_b64 = f"data:audio/wav;base64,{base64.b64encode(greeting_bytes).decode('utf-8')}" if greeting_bytes else None # Save initial assistant message onboarding_service.save_message( @@ -99,6 +101,7 @@ async def start_voice_onboarding( stage=session.stage, greeting_text=greeting_text, audio_url=f"/v1/voice-onboarding/audio/{audio_id}", + audio_base64=greeting_b64, ) @@ -252,11 +255,13 @@ async def voice_turn( ) # 9. Synthesize AI Speech Audio (TTS) - audio_id, _ = await voice_service.text_to_speech( + audio_id, audio_bytes = await voice_service.text_to_speech( text=ai_reply_text, language_code=session.language, ) + audio_b64 = f"data:audio/wav;base64,{base64.b64encode(audio_bytes).decode('utf-8')}" if audio_bytes else None + return VoiceTurnResponse( success=True, session_id=session_id, @@ -265,6 +270,7 @@ async def voice_turn( stage=session.stage, completed=is_completed, audio_url=f"/v1/voice-onboarding/audio/{audio_id}", + audio_base64=audio_b64, ) diff --git a/ai-patient-onboarding/app/schemas/voice.py b/ai-patient-onboarding/app/schemas/voice.py index c45a632..4ad56df 100644 --- a/ai-patient-onboarding/app/schemas/voice.py +++ b/ai-patient-onboarding/app/schemas/voice.py @@ -19,6 +19,7 @@ class StartVoiceOnboardingResponse(BaseModel): stage: str greeting_text: str audio_url: str + audio_base64: Optional[str] = None class VoiceTurnResponse(BaseModel): @@ -29,3 +30,4 @@ class VoiceTurnResponse(BaseModel): stage: str completed: bool = False audio_url: str + audio_base64: Optional[str] = None diff --git a/ai-patient-onboarding/app/services/voice_service.py b/ai-patient-onboarding/app/services/voice_service.py index a5d8340..d841d1c 100644 --- a/ai-patient-onboarding/app/services/voice_service.py +++ b/ai-patient-onboarding/app/services/voice_service.py @@ -3,6 +3,7 @@ import io import os import wave import struct +import hashlib from pathlib import Path from uuid import uuid4 import httpx2 @@ -13,6 +14,8 @@ from app.services.medical_speech_normalizer import normalize_medical_speech BASE_DIR = Path(__file__).resolve().parent.parent.parent AUDIO_DIR = BASE_DIR / "data" / "audio" +CACHE_DIR = AUDIO_DIR / "cache" +CACHE_DIR.mkdir(parents=True, exist_ok=True) AUDIO_DIR.mkdir(parents=True, exist_ok=True) @@ -33,9 +36,19 @@ class VoiceService: elif lang_lower.startswith("hi"): return f"नमस्ते {name} जी, CureSelect अस्पताल में आपका स्वागत है। आज आपको क्या तकलीफ या लक्षण महसूस हो रहे हैं, कृपया बताएंगे?" elif lang_lower.startswith("te"): - return f"నమస్కారం {name} గారు, CureSelect హాస్పిటల్‌కు స్వాగతం. ఈరోజు మీకున్న ఆరోగ్య సమస్య గురించి చెప్పగలరా?" + return f"నమస్కారం {name} గారు, CureSelect హాస్పిటల్‌కు స్వాగతం. ఈరోజు మీకున్న ఆరోగ్య సమస్యలు లేదా లక్షణాల గురించి చెప్పగలరా?" elif lang_lower.startswith("kn"): - return f"ನಮಸ್ಕಾರ {name} ಅವರೇ, CureSelect ಆಸ್ಪತ್ರೆಗೆ ಸುಸ್ವಾಗತ. ಇಂದು ನಿಮ್ಮ ಆರೋಗ್ಯದ ತೊಂದರೆ ಬಗ್ಗೆ ತಿಳಿಸಬಹುದೇ?" + return f"ನಮಸ್ಕಾರ {name} ಅವರೇ, CureSelect ಆಸ್ಪತ್ರೆಗೆ ಸುಸ್ವಾಗತ. ಇಂದು ನಿಮ್ಮ ಆರೋಗ್ಯದಲ್ಲಿ ಏನು ತೊಂದರೆ ಇದೆ ಎಂದು ತಿಳಿಸಬಹುದೇ?" + elif lang_lower.startswith("ml"): + return f"നമസ്കാരം {name}, CureSelect ആശുപത്രിയിലേക്ക് സ്വാഗതം. ഇന്ന് താങ്കൾക്ക് എന്തെല്ലാം ആരോഗ്യ ബുദ്ധിമുട്ടുകളാണ് ഉള്ളത് എന്ന് പറയാമോ?" + elif lang_lower.startswith("bn"): + return f"নমস্কার {name}, CureSelect হাসপাতালে আপনাকে স্বাগতম। আজ আপনার কি ধরনের শারীরিক অসুবিধা হচ্ছে বলবেন কি?" + elif lang_lower.startswith("mr"): + return f"नमस्कार {name} जी, CureSelect रुग्णालयात आपले स्वागत आहे। आज आपल्याला काय त्रास होत आहे, ते सांगू शकाल का?" + elif lang_lower.startswith("gu"): + return f"નમસ્તે {name} જી, CureSelect હોસ્પિટલમાં આપનું સ્વાગત છે। આજે આપને શું તકલીફ કે લક્ષણો છે, જરા જણાવશો?" + elif lang_lower.startswith("pa"): + return f"ਸਤਿ ਸ਼੍ਰੀ ਅਕਾਲ {name} ਜੀ, CureSelect ਹਸਪਤਾਲ ਵਿੱਚ ਤੁਹਾਡਾ ਸਵਾਗਤ ਹੈ। ਅੱਜ ਤੁਹਾਨੂੰ ਕੀ ਤਕਲੀਫ਼ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਦੱਸੋਗੇ?" else: return f"Hello {name}, welcome to CureSelect Healthcare. Could you please share what health symptoms you are experiencing today?" @@ -113,7 +126,16 @@ class VoiceService: .strip() ) - # 1. Use Sarvam AI TTS for Indian Languages (Native Indian Accent) + # Instant Disk Cache Lookup (< 0.001s response) + cache_key = hashlib.md5(f"{language_code}:{cleaned_text}".encode("utf-8")).hexdigest() + cache_path = CACHE_DIR / f"{cache_key}.wav" + if cache_path.exists(): + cached_data = cache_path.read_bytes() + # Also save to current session audio_id for endpoint streaming + (AUDIO_DIR / f"{audio_id}.wav").write_bytes(cached_data) + return audio_id, cached_data + + # 1. Use Sarvam AI TTS for Indian Languages (Native Indian Accent with 1.15x Human Pace) if ( self.sarvam_key and self.sarvam_key.strip() @@ -132,6 +154,7 @@ class VoiceService: "target_language_code": language_code, "speaker": speaker, "model": "bulbul:v3", + "pace": 1.15, } res = await client.post( "https://api.sarvam.ai/text-to-speech", @@ -145,6 +168,7 @@ class VoiceService: audio_data = base64.b64decode(audios[0]) file_path = AUDIO_DIR / f"{audio_id}.wav" file_path.write_bytes(audio_data) + cache_path.write_bytes(audio_data) return audio_id, audio_data else: print(f"[Sarvam TTS HTTP Error]: {res.status_code} - {res.text}")