feat: implement AI-orchestrated result extraction and service connectivity tests

This commit is contained in:
2026-08-27 17:45:07 +05:30
parent 59c1372fe3
commit 3d2596ced1
11 changed files with 546 additions and 123 deletions
+67 -17
View File
@@ -3,7 +3,7 @@ import json
from app.ai.openai_client import OpenAIClient
from app.ai.prompts import build_system_prompt
from app.ai.validator import validate_ai_response
from app.config import OPENAI_API_KEY
from app.config import OPENAI_API_KEY, GEMINI_API_KEY, GEMINI_MODEL, ANTHROPIC_API_KEY, ANTHROPIC_MODEL
from app.specialties.registry import get_specialty_config
@@ -49,7 +49,22 @@ class AIOrchestrator:
prompt = build_system_prompt(context)
# Real OpenAI Call if valid API key is set
# 1. Primary: Google Gemini (High-Speed Multilingual Indic Reasoning ~0.8s)
if GEMINI_API_KEY and GEMINI_API_KEY.strip() not in ["your_gemini_key", ""]:
try:
import google.generativeai as genai
genai.configure(api_key=GEMINI_API_KEY)
model = genai.GenerativeModel(GEMINI_MODEL or "gemini-3.6-flash")
full_prompt = f"{prompt}\n\nPATIENT MESSAGE: {user_message}\n\nRespond strictly with JSON matching the schema above."
res = await model.generate_content_async(full_prompt)
raw_text = res.text.replace("```json", "").replace("```", "").strip()
parsed = json.loads(raw_text)
validated = validate_ai_response(parsed)
return validated.model_dump()
except Exception as e:
print(f"[Gemini Orchestrator Error]: {e}")
# 2. Secondary: OpenAI Call if valid API key is set
if OPENAI_API_KEY and OPENAI_API_KEY.strip() not in ["your_openai_api_key", "your_openai_key", ""]:
try:
raw_response = await self.openai.generate_response(
@@ -62,7 +77,25 @@ class AIOrchestrator:
except Exception as e:
print(f"[OpenAI Orchestrator Error]: {e}")
# Fallback simulator for offline development with concise clinical flow
# 3. Tertiary: Anthropic Claude
if ANTHROPIC_API_KEY and ANTHROPIC_API_KEY.strip() not in ["your_anthropic_key", ""]:
try:
from anthropic import AsyncAnthropic
client = AsyncAnthropic(api_key=ANTHROPIC_API_KEY)
res = await client.messages.create(
model=ANTHROPIC_MODEL or "claude-sonnet-4-5-20250929",
max_tokens=600,
system=prompt,
messages=[{"role": "user", "content": user_message}],
)
raw_text = res.content[0].text.replace("```json", "").replace("```", "").strip()
parsed = json.loads(raw_text)
validated = validate_ai_response(parsed)
return validated.model_dump()
except Exception as e:
print(f"[Claude Orchestrator Error]: {e}")
# 4. Fallback simulator for offline development with localized language support
parsed = self._simulate_specialty_response(session, specialty, user_message, len(conversation_history))
validated = validate_ai_response(parsed)
return validated.model_dump()
@@ -70,13 +103,36 @@ class AIOrchestrator:
def _simulate_specialty_response(self, session, specialty, user_message, turn_count=0):
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"]
is_negative = msg_lower in ["no", "no more", "nothing", "nothing else", "know nothing", "none", "illness none", "இல்லை", "ஒன்றுமில்லை", "नहीं"]
# Fast conclusion if patient says "No" or already had 4 turns
# Localized clinical phrases
if lang.startswith("ta"):
conclusion_text = "நன்றி. உங்கள் உடல்நல விவரங்கள் பதிவு செய்யப்பட்டுள்ளன. மருத்துவர் விரைவில் பரிசீலிப்பார்."
symptom_q = "இந்த அறிகுறிகள் எவ்வளவு நாட்களாக இருக்கிறது, மற்றும் ஏதேனும் மாத்திரைகள் சாப்பிட்டீர்களா?"
med_q = "நன்றி. உங்களுக்கு ஏதேனும் மருந்து ஒவ்வாமை (Allergy) அல்லது வேறு உடல்நல பிரச்சனைகள் உள்ளதா?"
elif lang.startswith("hi"):
conclusion_text = "धन्यवाद। आपकी स्वास्थ्य जानकारी दर्ज कर ली गई है। डॉक्टर जल्द ही समीक्षा करेंगे।"
symptom_q = "आपको यह समस्या कितने दिनों से है और क्या आपने कोई दवाई ली है?"
med_q = "धन्यवाद। क्या आपको किसी दवाई से एलर्जी या कोई अन्य बीमारी है?"
elif lang.startswith("te"):
conclusion_text = "ధన్యవాదాలు. మీ ఆరోగ్య వివరాలు నమోదు చేయబడ్డాయి."
symptom_q = "ఈ సమస్య మీకు ఎన్ని రోజుల నుండి ఉంది మరియు ఏవైనా మందులు వాడారా?"
med_q = "ధన్యవాదాలు. మీకు ఏవైనా మందుల పడనితనం (Allergy) ఉందా?"
elif lang.startswith("kn"):
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?"
med_q = "Thank you. Do you have any known drug allergies or existing health conditions?"
# Fast conclusion if patient says "No" or already had 6 turns
if is_negative or turn_count >= 6:
return {
"reply": "Thank you. I have recorded your intake details for the doctor to review.",
"reply": conclusion_text,
"next_stage": "confirmation",
"state_updates": {
"reason_for_visit": None,
@@ -89,12 +145,8 @@ class AIOrchestrator:
}
if stage == "greeting":
if code == "cardiology":
reply = "Could you please share what cardiac or chest symptoms you are experiencing?"
else:
reply = "Could you please describe what primary symptoms you are experiencing today?"
return {
"reply": reply,
"reply": symptom_q,
"next_stage": "reason_for_visit",
"state_updates": {
"reason_for_visit": user_message,
@@ -106,9 +158,8 @@ class AIOrchestrator:
"completed": False
}
elif stage == "reason_for_visit":
reply = "How long have you had these symptoms, and have you taken any medicines?"
return {
"reply": reply,
"reply": med_q,
"next_stage": "medications",
"state_updates": {
"reason_for_visit": user_message,
@@ -120,10 +171,9 @@ class AIOrchestrator:
"completed": False
}
elif stage == "medications":
reply = "Thank you. Do you have any known drug allergies or existing health conditions?"
return {
"reply": reply,
"next_stage": "allergies",
"reply": conclusion_text,
"next_stage": "confirmation",
"state_updates": {
"reason_for_visit": None,
"symptoms": [],
@@ -135,7 +185,7 @@ class AIOrchestrator:
}
else:
return {
"reply": "Thank you. I have recorded your intake details for the doctor to review.",
"reply": conclusion_text,
"next_stage": "confirmation",
"state_updates": {
"reason_for_visit": None,
@@ -55,7 +55,7 @@ class SarvamClient:
return None
lang_prefix = language_code.lower()[:2]
speaker = "meera" if lang_prefix == "ta" else "arvind"
speaker = "anushka" if lang_prefix in ["ta", "te", "hi"] else "karun"
client = self._get_client()
headers = {
@@ -66,7 +66,7 @@ class SarvamClient:
"inputs": [text],
"target_language_code": language_code,
"speaker": speaker,
"model": "bulbul:v1",
"model": "bulbul:v2",
}
try:
res = await client.post(