feat: implement voice-based patient onboarding pipeline with AI orchestration, multi-language STT/TTS support, and clinical state extraction.

This commit is contained in:
2026-08-29 15:09:31 +05:30
parent 5e500dcfbc
commit dc2799a95b
14 changed files with 733 additions and 191 deletions
@@ -54,6 +54,11 @@ def normalize_medical_speech(transcript: str, language_code: str = "en-IN") -> s
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()
@@ -99,9 +99,11 @@ class VoiceService:
self,
text: str,
language_code: str = "en-IN",
audio_id: str | None = None,
) -> tuple[str, bytes]:
lang_prefix = language_code.lower()[:2]
audio_id = f"aud_{uuid4().hex[:12]}"
if not audio_id:
audio_id = f"aud_{uuid4().hex[:12]}"
# Clean punctuation and brackets before synthesis
cleaned_text = (
@@ -121,8 +123,8 @@ class VoiceService:
and lang_prefix in ["ta", "hi", "te", "kn", "bn", "mr", "gu", "ml", "pa", "od", "en"]
):
try:
speaker = "kavya" if lang_prefix in ["ta", "hi", "te", "kn"] else "gokul"
async with httpx2.AsyncClient(timeout=5.0) as client:
speaker = "kavya"
async with httpx2.AsyncClient(timeout=6.0) as client:
headers = {
"api-subscription-key": self.sarvam_key,
"Content-Type": "application/json",
@@ -153,11 +155,11 @@ class VoiceService:
# 2. Use OpenAI TTS
if self.openai_key and self.openai_key.strip() not in ["your_openai_api_key", "your_openai_key", ""]:
client = AsyncOpenAI(api_key=self.openai_key, timeout=5.0)
client = AsyncOpenAI(api_key=self.openai_key, timeout=6.0)
try:
response = await client.audio.speech.create(
model="tts-1",
voice="shimmer",
voice="nova",
input=cleaned_text,
speed=1.0,
)
@@ -176,6 +178,15 @@ class VoiceService:
file_path.write_bytes(audio_data)
return audio_id, audio_data
async def synthesize_to_file(
self,
audio_id: str,
text: str,
language_code: str = "en-IN",
) -> bytes:
_, audio_data = await self.text_to_speech(text, language_code=language_code, audio_id=audio_id)
return audio_data
def _generate_fallback_wav(self) -> bytes:
buf = io.BytesIO()
with wave.open(buf, "wb") as wf: