4 changed files with 82 additions and 17 deletions
+45 -12
View File
@@ -60,26 +60,59 @@ class AIOrchestrator:
stage = session.stage stage = session.stage
code = specialty.get("code", "general") code = specialty.get("code", "general")
lang = (session.language or "ta-IN").lower() lang = (session.language or "ta-IN").lower()
msg_lower = user_message.lower().strip() msg_lower = (user_message or "").lower().strip()
is_negative = msg_lower in ["no", "no more", "nothing", "nothing else", "know nothing", "none", "illness none", "இல்லை", "ஒன்றுமில்லை", "नहीं"] # 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) # Localized clinical phrases (Spoken & Caring Tone)
if lang.startswith("ta"): if lang.startswith("ta"):
conclusion_text = "மிக்க நன்றி. உங்கள் உடல்நல விவரங்கள் சிறப்பாக பதிவு செய்யப்பட்டுள்ளன. மருத்துவர் விரைவில் உங்களைப் பரிசீலிப்பார்." conclusion_text = "மிக்க நன்றி. உங்கள் உடல்நல விவரங்கள் சிறப்பாக பதிவு செய்யப்பட்டுள்ளன. மருத்துவர் விரைவில் உங்களைப் பரிசீலிப்பார்."
symptom_q = "இந்த அறிகுறிகள் எத்தனை நாளா இருக்கு? இதுக்காக ஏதாச்சும் மாத்திரை சாப்பிட்டீங்களா?" symptom_q = "சரிங்க, உங்களோட அறிகுறிகள் பற்றி சொன்னீங்க. இந்த பிரச்சனை எத்தனை நாளா இருக்கு? இதுக்காக ஏதாச்சும் மாத்திரை சாப்பிட்டீங்களா?"
med_q = "சரிங்க. உங்களுக்கு வேற ஏதேனும் உடம்பு பிரச்சனைகள் அல்லது மாத்திரை ஒவ்வாமை (Allergy) இருக்கா?" med_q = "சரிங்க. உங்களுக்கு வேற ஏதேனும் உடம்பு பிரச்சனைகள் அல்லது மாத்திரை ஒவ்வாமை (Allergy) இருக்கா?"
elif lang.startswith("hi"): elif lang.startswith("hi"):
conclusion_text = "धन्यवाद। आपकी स्वास्थ्य जानकारी दर्ज कर ली गई है। डॉक्टर जल्द ही समीक्षा करेंगे।" conclusion_text = "धन्यवाद। आपकी स्वास्थ्य जानकारी सफलतापूर्वक दर्ज कर ली गई है। डॉक्टर जल्द ही आपकी समीक्षा करेंगे।"
symptom_q = "आपको यह समस्या कितने दिनों से है और क्या आपने कोई दवाई ली है?" symptom_q = "जी समझ गया। आपको यह समस्या कितने दिनों से है और क्या इसके लिए आपने कोई दवाई ली है?"
med_q = "धन्यवाद। क्या आपको किसी दवाई से एलर्जी या कोई अन्य बीमारी है?" med_q = "ठीक है। क्या आपको किसी दवाई से एलर्जी या कोई अन्य बीमारी है?"
elif lang.startswith("te"): elif lang.startswith("te"):
conclusion_text = "ధన్యవాదాలు. మీ ఆరోగ్య వివరాలు నమోదు చేయబడ్డాయి." conclusion_text = "చాలా ధన్యవాదాలు. మీ ఆరోగ్య వివరాలు నమోదు చేయబడ్డాయి. వైద్యులు త్వరలోనే పరిశీలిస్తారు."
symptom_q = "ఈ సమస్య మీకు ఎన్ని రోజుల నుండి ఉంది మరియు ఏవైనా మందులు వాడారా?" symptom_q = "సరేనండి, ఈ సమస్య మీకు ఎన్ని రోజుల నుండి ఉంది? దీని కోసం ఏవైనా మందులు వాడారా?"
med_q = "ధన్యవాదాలు. మీకు ఏవైనా మందుల పడనితనం (Allergy) ఉందా?" med_q = "ధన్యవాదాలు. మీకు ఏవైనా మందుల పడనితనం (Allergy) లేదా ఇతర ఆరోగ్య సమస్యలు ఉన్నాయా?"
elif lang.startswith("kn"): elif lang.startswith("kn"):
conclusion_text = "ಧನ್ಯವಾದಗಳು. ನಿಮ್ಮ ಆರೋಗ್ಯದ ಮಾಹಿತಿ ದಾಖಲಾಗಿದೆ." conclusion_text = "ತುಂಬಾ ಧನ್ಯವಾದಗಳು. ನಿಮ್ಮ ಆರೋಗ್ಯದ ಮಾಹಿತಿ ದಾಖಲಾಗಿದೆ. ವೈದ್ಯರು ಶೀಘ್ರದಲ್ಲೇ ಪರಿಶೀಲಿಸಲಿದ್ದಾರೆ."
symptom_q = "ಈ ಸಮಸ್ಯೆ ನಿಮಗೆ ಎಷ್ಟು ದಿನಗಳಿಂದ ಇದೆ ಮತ್ತು ಯಾವುದೇ ಮಾತ್ರೆಗಳನ್ನು ತೆಗೆದುಕೊಂಡಿದ್ದೀರಾ?" symptom_q = "ಸರಿ, ಈ ತೊಂದರೆ ನಿಮಗೆ ಎಷ್ಟು ದಿನಗಳಿಂದ ಇದೆ? ಯಾವುದಾದರೂ ಮಾತ್ರೆ ತೆಗೆದುಕೊಂಡಿದ್ದೀರಾ?"
med_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: else:
conclusion_text = "Thank you. I have recorded your intake details for the doctor to review." 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?" symptom_q = "How long have you had these symptoms, and have you taken any medicines?"
@@ -1,3 +1,4 @@
import base64
import json import json
from uuid import uuid4 from uuid import uuid4
from typing import Optional from typing import Optional
@@ -75,10 +76,11 @@ async def start_voice_onboarding(
) )
# Synthesize audio greeting # Synthesize audio greeting
audio_id, _ = await voice_service.text_to_speech( audio_id, greeting_bytes = await voice_service.text_to_speech(
greeting_text, greeting_text,
request.language, 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 # Save initial assistant message
onboarding_service.save_message( onboarding_service.save_message(
@@ -99,6 +101,7 @@ async def start_voice_onboarding(
stage=session.stage, stage=session.stage,
greeting_text=greeting_text, greeting_text=greeting_text,
audio_url=f"/v1/voice-onboarding/audio/{audio_id}", 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) # 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, text=ai_reply_text,
language_code=session.language, 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( return VoiceTurnResponse(
success=True, success=True,
session_id=session_id, session_id=session_id,
@@ -265,6 +270,7 @@ async def voice_turn(
stage=session.stage, stage=session.stage,
completed=is_completed, completed=is_completed,
audio_url=f"/v1/voice-onboarding/audio/{audio_id}", audio_url=f"/v1/voice-onboarding/audio/{audio_id}",
audio_base64=audio_b64,
) )
@@ -19,6 +19,7 @@ class StartVoiceOnboardingResponse(BaseModel):
stage: str stage: str
greeting_text: str greeting_text: str
audio_url: str audio_url: str
audio_base64: Optional[str] = None
class VoiceTurnResponse(BaseModel): class VoiceTurnResponse(BaseModel):
@@ -29,3 +30,4 @@ class VoiceTurnResponse(BaseModel):
stage: str stage: str
completed: bool = False completed: bool = False
audio_url: str audio_url: str
audio_base64: Optional[str] = None
@@ -3,6 +3,7 @@ import io
import os import os
import wave import wave
import struct import struct
import hashlib
from pathlib import Path from pathlib import Path
from uuid import uuid4 from uuid import uuid4
import httpx2 import httpx2
@@ -13,6 +14,8 @@ from app.services.medical_speech_normalizer import normalize_medical_speech
BASE_DIR = Path(__file__).resolve().parent.parent.parent BASE_DIR = Path(__file__).resolve().parent.parent.parent
AUDIO_DIR = BASE_DIR / "data" / "audio" 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) AUDIO_DIR.mkdir(parents=True, exist_ok=True)
@@ -33,9 +36,19 @@ class VoiceService:
elif lang_lower.startswith("hi"): elif lang_lower.startswith("hi"):
return f"नमस्ते {name} जी, CureSelect अस्पताल में आपका स्वागत है। आज आपको क्या तकलीफ या लक्षण महसूस हो रहे हैं, कृपया बताएंगे?" return f"नमस्ते {name} जी, CureSelect अस्पताल में आपका स्वागत है। आज आपको क्या तकलीफ या लक्षण महसूस हो रहे हैं, कृपया बताएंगे?"
elif lang_lower.startswith("te"): elif lang_lower.startswith("te"):
return f"నమస్కారం {name} గారు, CureSelect హాస్పిటల్‌కు స్వాగతం. ఈరోజు మీకున్న ఆరోగ్య సమస్య గురించి చెప్పగలరా?" return f"నమస్కారం {name} గారు, CureSelect హాస్పిటల్‌కు స్వాగతం. ఈరోజు మీకున్న ఆరోగ్య సమస్యలు లేదా లక్షణాల గురించి చెప్పగలరా?"
elif lang_lower.startswith("kn"): 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: else:
return f"Hello {name}, welcome to CureSelect Healthcare. Could you please share what health symptoms you are experiencing today?" 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() .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 ( if (
self.sarvam_key self.sarvam_key
and self.sarvam_key.strip() and self.sarvam_key.strip()
@@ -132,6 +154,7 @@ class VoiceService:
"target_language_code": language_code, "target_language_code": language_code,
"speaker": speaker, "speaker": speaker,
"model": "bulbul:v3", "model": "bulbul:v3",
"pace": 1.15,
} }
res = await client.post( res = await client.post(
"https://api.sarvam.ai/text-to-speech", "https://api.sarvam.ai/text-to-speech",
@@ -145,6 +168,7 @@ class VoiceService:
audio_data = base64.b64decode(audios[0]) audio_data = base64.b64decode(audios[0])
file_path = AUDIO_DIR / f"{audio_id}.wav" file_path = AUDIO_DIR / f"{audio_id}.wav"
file_path.write_bytes(audio_data) file_path.write_bytes(audio_data)
cache_path.write_bytes(audio_data)
return audio_id, audio_data return audio_id, audio_data
else: else:
print(f"[Sarvam TTS HTTP Error]: {res.status_code} - {res.text}") print(f"[Sarvam TTS HTTP Error]: {res.status_code} - {res.text}")