feat: complete production hardening implementation, specialty canonicalization, google-genai migration, voice reliability, sqlite concurrency, and test suite consolidation

This commit is contained in:
2026-09-07 12:57:54 +05:30
parent c23d979338
commit 92fa71ae4a
99 changed files with 5167 additions and 3550 deletions
@@ -0,0 +1,95 @@
import asyncio
import os
import sys
from dotenv import load_dotenv
if sys.stdout.encoding != 'utf-8':
try:
sys.stdout.reconfigure(encoding='utf-8')
except Exception:
pass
load_dotenv()
async def diagnose_anthropic():
api_key = os.getenv("ANTHROPIC_API_KEY")
print("\n--- Testing Anthropic Models ---")
candidate_models = [
"claude-3-5-sonnet-latest",
"claude-3-5-haiku-latest",
"claude-3-haiku-20240307",
"claude-3-sonnet-20240229",
"claude-3-opus-20240229",
]
from anthropic import AsyncAnthropic
client = AsyncAnthropic(api_key=api_key)
for model in candidate_models:
try:
res = await client.messages.create(
model=model,
max_tokens=20,
messages=[{"role": "user", "content": "Hi"}],
)
print(f"[OK] Anthropic Model '{model}' WORKS! Response: {res.content[0].text.strip()}")
return model
except Exception as e:
print(f"[X] '{model}' failed: {e}")
return None
async def diagnose_gemini():
api_key = os.getenv("GEMINI_API_KEY")
print("\n--- Testing Gemini Models ---")
from google import genai
genai.configure(api_key=api_key)
try:
models = [m.name for m in genai.list_models() if 'generateContent' in m.supported_generation_methods]
print(f"Available Gemini models: {models[:10]}")
for m_name in models[:5]:
try:
m = genai.GenerativeModel(m_name)
res = await m.generate_content_async("Hi")
print(f"[OK] Gemini Model '{m_name}' WORKS! Response: {res.text.strip()}")
return m_name
except Exception as e:
print(f"[X] Gemini Model '{m_name}' failed: {e}")
except Exception as e:
print(f"[FAIL] Listing Gemini models failed: {e}")
return None
async def diagnose_sarvam():
api_key = os.getenv("SARVAM_API_KEY")
print("\n--- Testing Sarvam Speakers ---")
import httpx2
client = httpx2.AsyncClient(timeout=15.0)
headers = {
"api-subscription-key": api_key,
"Content-Type": "application/json",
}
payload = {
"inputs": ["வணக்கம்"],
"target_language_code": "ta-IN",
"speaker": "kavya",
"model": "bulbul:v1",
}
try:
res = await client.post(
"https://api.sarvam.ai/text-to-speech",
json=payload,
headers=headers,
)
if res.status_code == 200:
print("[OK] Sarvam TTS with speaker 'kavya' WORKS!")
return "kavya"
else:
print(f"[FAIL] Sarvam HTTP {res.status_code}: {res.text}")
except Exception as e:
print(f"[FAIL] Sarvam error: {e}")
return None
async def main():
await diagnose_anthropic()
await diagnose_gemini()
await diagnose_sarvam()
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,37 @@
import httpx
def main():
client = httpx.Client(timeout=60.0)
start_res = client.post('http://127.0.0.1:8000/v1/voice-onboarding/start', json={
'patient': {'id': 'PAT-INTAKE-5', 'first_name': 'Arun', 'last_name': 'Kumar'},
'category': {'type': 'general', 'code': 'general_medicine', 'name': 'General Medicine'},
'language': 'en-IN'
}).json()
sid = start_res['session_id']
print("=== DEBUGGING 5-STAGE DIALOGUE ===")
print("Session ID:", sid)
dialogue = [
"Chest pain and shortness of breath",
"2 days, pain severity is 8 out of 10",
"Yes, sweating and dizziness",
"High blood pressure and Diabetes",
"Taking Amlodipine, no drug allergies"
]
for i, turn_input in enumerate(dialogue, 1):
res = client.post(f"http://127.0.0.1:8000/v1/voice-onboarding/{sid}/turn", data={"text_fallback": turn_input})
print(f"Turn {i} Status Code: {res.status_code}")
if res.status_code != 200:
print("FULL ERROR BODY:", res.text)
break
data = res.json()
print(f"Turn {i} Input: '{turn_input}'")
print(f" --> Next Stage: {data['stage']} | Completed: {data['completed']}")
print("-" * 70)
if __name__ == '__main__':
main()
@@ -0,0 +1,135 @@
import asyncio
import os
import sys
from dotenv import load_dotenv
if sys.stdout.encoding != 'utf-8':
try:
sys.stdout.reconfigure(encoding='utf-8')
except Exception:
pass
load_dotenv()
async def test_openai():
api_key = os.getenv("OPENAI_API_KEY")
model = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
print(f"\n--- [1/4] Testing OpenAI ({model}) ---")
if not api_key:
print("[FAIL] OPENAI_API_KEY is not set.")
return False, "Key missing"
try:
from openai import AsyncOpenAI
client = AsyncOpenAI(api_key=api_key)
response = await client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Respond with JSON: {\"status\": \"ok\"}"}],
response_format={"type": "json_object"},
max_completion_tokens=50,
)
content = response.choices[0].message.content
print(f"[OK] OpenAI Success: {content.strip()}")
return True, "Connected & Working"
except Exception as e:
err_msg = str(e)
print(f"[FAIL] OpenAI Error: {err_msg}")
return False, err_msg
async def test_anthropic():
api_key = os.getenv("ANTHROPIC_API_KEY")
model = os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-5-20250929")
print(f"\n--- [2/4] Testing Anthropic Claude ({model}) ---")
if not api_key:
print("[FAIL] ANTHROPIC_API_KEY is not set.")
return False, "Key missing"
try:
from anthropic import AsyncAnthropic
client = AsyncAnthropic(api_key=api_key)
response = await client.messages.create(
model=model,
max_tokens=50,
messages=[{"role": "user", "content": "Reply with 'ok'"}],
)
content = response.content[0].text
print(f"[OK] Claude Success: {content.strip()}")
return True, "Connected & Working"
except Exception as e:
err_msg = str(e)
print(f"[FAIL] Claude Error: {err_msg}")
return False, err_msg
async def test_gemini():
api_key = os.getenv("GEMINI_API_KEY")
model_name = os.getenv("GEMINI_MODEL", "gemini-3.6-flash")
print(f"\n--- [3/4] Testing Google Gemini ({model_name}) ---")
if not api_key:
print("[FAIL] GEMINI_API_KEY is not set.")
return False, "Key missing"
try:
from google import genai
genai.configure(api_key=api_key)
model = genai.GenerativeModel(model_name)
response = await model.generate_content_async("Say 'ok'")
print(f"[OK] Gemini Success: {response.text.strip()}")
return True, "Connected & Working"
except Exception as e:
err_msg = str(e)
print(f"[FAIL] Gemini Error: {err_msg}")
return False, err_msg
async def test_sarvam():
api_key = os.getenv("SARVAM_API_KEY")
print(f"\n--- [4/4] Testing Sarvam AI (TTS bulbul:v2) ---")
if not api_key:
print("[FAIL] SARVAM_API_KEY is not set.")
return False, "Key missing"
try:
import httpx2
client = httpx2.AsyncClient(timeout=15.0)
headers = {
"api-subscription-key": api_key,
"Content-Type": "application/json",
}
payload = {
"inputs": ["வணக்கம்"],
"target_language_code": "ta-IN",
"speaker": "anushka",
"model": "bulbul:v2",
}
res = await client.post(
"https://api.sarvam.ai/text-to-speech",
json=payload,
headers=headers,
)
if res.status_code == 200:
audios = res.json().get("audios", [])
print(f"[OK] Sarvam AI Success: Generated {len(audios)} audio stream(s)")
return True, "Connected & Working"
else:
err_msg = f"HTTP {res.status_code}: {res.text}"
print(f"[FAIL] Sarvam AI Error: {err_msg}")
return False, err_msg
except Exception as e:
err_msg = str(e)
print(f"[FAIL] Sarvam AI Error: {err_msg}")
return False, err_msg
async def main():
print("========================================")
print("AI Patient Onboarding - Key Verification")
print("========================================")
results = {}
results["OpenAI"] = await test_openai()
results["Anthropic Claude"] = await test_anthropic()
results["Google Gemini"] = await test_gemini()
results["Sarvam AI"] = await test_sarvam()
print("\n========================================")
print("SUMMARY")
print("========================================")
for provider, (passed, msg) in results.items():
tag = "[PASS]" if passed else "[FAIL]"
print(f"{tag} {provider}: {msg}")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,35 @@
import httpx
def main():
client = httpx.Client(timeout=30.0)
test_cases = [
("Cardiology Test", "எனக்கு நெஞ்சு வலி மற்றும் மூச்சு திணறல் இருக்கு"),
("Dermatology Test", "தோல் அரிப்பு மற்றும் படை உள்ளது"),
("Orthopedics Test", "மூட்டு வலி நடக்க முடியல"),
("Neurology Test", "headache and dizziness for 3 days"),
("Gastroenterology Test", "வயிற்று வலி மற்றும் வாந்தி"),
("Pulmonology Test", "இருமல் மற்றும் சளி அதிகமாக உள்ளது"),
("ENT Test", "காது வலி மற்றும் தொண்டை வலி")
]
print("=== AUTOMATIC SPECIALTY TRIAGE ENGINE TEST ===")
for label, symptom in test_cases:
start_res = client.post("http://127.0.0.1:8000/v1/voice-onboarding/start", json={
"patient": {"id": "PAT-AUTO", "first_name": "Arun", "last_name": "Kumar"},
"category": {"type": "general", "code": "general_medicine", "name": "General Medicine"},
"language": "ta-IN"
})
sid = start_res.json()["session_id"]
turn_res = client.post(f"http://127.0.0.1:8000/v1/voice-onboarding/{sid}/turn", data={"text_fallback": symptom})
spec = turn_res.json().get("suggested_specialty", {})
print(f"Label: {label}")
print(f" Patient Symptom: '{symptom}'")
print(f" --> Auto-Assigned Specialty: {spec.get('name')} (Code: {spec.get('code')})")
print(f" --> Reasoning: {spec.get('reason')}\n")
if __name__ == '__main__':
main()
@@ -0,0 +1,16 @@
from app.ai.orchestrator import classify_specialty_from_symptoms
test_cases = [
("Cardiology", "நெஞ்சு வலி மற்றும் மூச்சு திணறல்"),
("Dermatology", "தோல் அரிப்பு மற்றும் படை"),
("Orthopedics", "மூட்டு வலி"),
("Neurology", "headache and dizziness"),
("Gastroenterology", "வயிற்று வலி மற்றும் வாந்தி"),
("Pulmonology", "இருமல் மற்றும் சளி"),
("ENT", "காது வலி மற்றும் தொண்டை வலி")
]
print("=== DIRECT SYMPTOM SPECIALTY CLASSIFIER TEST ===")
for label, text in test_cases:
res = classify_specialty_from_symptoms(text)
print("Test:", label, "| Code:", res["code"], "| Specialty:", res["name"])
@@ -0,0 +1,60 @@
import asyncio
import os
import sys
from dotenv import load_dotenv
if sys.stdout.encoding != 'utf-8':
try:
sys.stdout.reconfigure(encoding='utf-8')
except Exception:
pass
load_dotenv()
async def test_sarvam_anushka():
api_key = os.getenv("SARVAM_API_KEY")
import httpx2
client = httpx2.AsyncClient(timeout=15.0)
headers = {
"api-subscription-key": api_key,
"Content-Type": "application/json",
}
payload = {
"inputs": ["வணக்கம், உங்கள் உடல்நலம் எப்படி உள்ளது?"],
"target_language_code": "ta-IN",
"speaker": "anushka",
"model": "bulbul:v2",
}
res = await client.post(
"https://api.sarvam.ai/text-to-speech",
json=payload,
headers=headers,
)
if res.status_code == 200:
audios = res.json().get("audios", [])
print(f"[OK] Sarvam TTS bulbul:v2 (anushka) SUCCESS! Audio segments: {len(audios)}")
return True
else:
print(f"[FAIL] Sarvam error {res.status_code}: {res.text}")
return False
async def list_anthropic_models():
api_key = os.getenv("ANTHROPIC_API_KEY")
from anthropic import AsyncAnthropic
client = AsyncAnthropic(api_key=api_key)
try:
models = await client.models.list()
print("Available Anthropic models on this account:")
for m in models.data:
print(f" - {m.id} ({m.display_name})")
except Exception as e:
print(f"Anthropic list models error: {e}")
async def main():
print("--- 1. Testing Sarvam ---")
await test_sarvam_anushka()
print("\n--- 2. Listing Anthropic Models ---")
await list_anthropic_models()
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,87 @@
import asyncio
import os
import sys
from dotenv import load_dotenv
if sys.stdout.encoding != 'utf-8':
try:
sys.stdout.reconfigure(encoding='utf-8')
except Exception:
pass
load_dotenv()
async def test_sarvam_v2():
api_key = os.getenv("SARVAM_API_KEY")
import httpx2
client = httpx2.AsyncClient(timeout=15.0)
headers = {
"api-subscription-key": api_key,
"Content-Type": "application/json",
}
payload = {
"inputs": ["வணக்கம், உங்கள் உடல்நலம் எப்படி உள்ளது?"],
"target_language_code": "ta-IN",
"speaker": "kavya",
"model": "bulbul:v2",
}
res = await client.post(
"https://api.sarvam.ai/text-to-speech",
json=payload,
headers=headers,
)
if res.status_code == 200:
audios = res.json().get("audios", [])
print(f"[OK] Sarvam TTS bulbul:v2 WORKS! Generated {len(audios)} audio clips.")
return True
else:
print(f"[FAIL] Sarvam error {res.status_code}: {res.text}")
return False
async def test_gemini_models():
api_key = os.getenv("GEMINI_API_KEY")
from google import genai
genai.configure(api_key=api_key)
for model_name in ["gemini-3.6-flash", "gemini-flash-latest", "gemini-2.5-flash-lite", "gemma-4-26b-a4b-it"]:
try:
m = genai.GenerativeModel(model_name)
res = await m.generate_content_async("Hello")
print(f"[OK] Gemini '{model_name}' WORKS! Response: {res.text.strip()}")
return model_name
except Exception as e:
print(f"[X] Gemini '{model_name}' failed: {e}")
return None
async def test_anthropic_models():
api_key = os.getenv("ANTHROPIC_API_KEY")
from anthropic import AsyncAnthropic
client = AsyncAnthropic(api_key=api_key)
for model_name in [
"claude-3-7-sonnet-20250219",
"claude-3-5-sonnet-20240620",
"claude-3-sonnet-20240229",
"claude-2.1",
"claude-instant-1.2"
]:
try:
res = await client.messages.create(
model=model_name,
max_tokens=20,
messages=[{"role": "user", "content": "Hi"}],
)
print(f"[OK] Anthropic '{model_name}' WORKS! Response: {res.content[0].text.strip()}")
return model_name
except Exception as e:
print(f"[X] Anthropic '{model_name}' failed: {e}")
return None
async def main():
print("--- Testing Sarvam v2 ---")
await test_sarvam_v2()
print("\n--- Testing Gemini Models ---")
await test_gemini_models()
print("\n--- Testing Anthropic Models ---")
await test_anthropic_models()
if __name__ == "__main__":
asyncio.run(main())