import os import httpx from app.ml.enhanced_chat import get_enhanced_mock_response, rag_retriever try: from app.ml.rag import retrieve_doctor_context except ImportError: retrieve_doctor_context = None OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "") BASE_URL = "https://openrouter.ai/api/v1" # Free models available on OpenRouter — fallback chain MODELS = [ "deepseek/deepseek-chat-v3-0324:free", "google/gemma-3-27b-it:free", "meta-llama/llama-4-maverick:free", ] def build_system_prompt(guc: dict) -> str: """ Builds the Dr. Raahat system prompt by injecting the full Global User Context. """ name = guc.get("name", "Patient") age = guc.get("age", "") gender = guc.get("gender", "") language = guc.get("language", "EN") location = guc.get("location", "India") report = guc.get("latestReport", {}) summary_en = report.get("overall_summary_english", "No report uploaded yet.") organs = ", ".join(report.get("affected_organs", [])) or "None identified" severity = report.get("severity_level", "NORMAL") dietary_flags = ", ".join(report.get("dietary_flags", [])) or "None" exercise_flags = ", ".join(report.get("exercise_flags", [])) or "None" findings = report.get("findings", []) abnormal = [ f"{f['parameter']}: {f['value']} {f['unit']} ({f['status']})" for f in findings if f.get("status") in ["HIGH", "LOW", "CRITICAL"] ] abnormal_str = "\n".join(f" - {a}" for a in abnormal) or " - None" medications = guc.get("medicationsActive", []) meds_str = ", ".join(medications) if medications else "None reported" allergy_flags = guc.get("allergyFlags", []) allergies_str = ", ".join(allergy_flags) if allergy_flags else "None reported" stress = guc.get("mentalWellness", {}).get("stressLevel", 5) sleep = guc.get("mentalWellness", {}).get("sleepQuality", 5) lang_instruction = ( "Always respond in Hindi (Devanagari script). " "Use simple everyday Hindi words, not medical jargon." if language == "HI" else "Always respond in simple English." ) empathy_note = ( "\nNOTE: This patient has high stress levels. " "Be extra gentle, reassuring and empathetic in your responses. " "Acknowledge their feelings before giving medical information." if int(stress) <= 3 else "" ) prompt = f"""You are Dr. Raahat, a friendly and empathetic Indian doctor. You speak both Hindi and English fluently. PATIENT PROFILE: - Name: {name} - Age: {age}, Gender: {gender} - Location: {location} LATEST MEDICAL REPORT SUMMARY: - Overall: {summary_en} - Organs affected: {organs} - Severity: {severity} ABNORMAL FINDINGS: {abnormal_str} DIETARY FLAGS: {dietary_flags} EXERCISE FLAGS: {exercise_flags} ACTIVE MEDICATIONS: {meds_str} ALLERGIES/RESTRICTIONS: {allergies_str} STRESS LEVEL: {stress}/10 | SLEEP QUALITY: {sleep}/10 LANGUAGE: {lang_instruction} {empathy_note} IMPORTANT RULES: - Never make up diagnoses or prescribe medications - If asked something outside your knowledge, say "Please see a doctor in person for this" - Always reference the patient's actual report data when answering - Keep answers concise — 3-5 sentences maximum - End every response with one actionable tip - Be like a caring family doctor, not a cold clinical system - Never create panic. Always give hope alongside facts.""" return prompt def _call_openrouter(messages: list[dict]) -> str | None: """ Call OpenRouter API with the given messages. Tries each model in MODELS until one succeeds. Returns the reply string, or None on failure. """ if not OPENROUTER_API_KEY or OPENROUTER_API_KEY.startswith("placeholder"): return None headers = { "Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json", "HTTP-Referer": "https://reportraahat.app", "X-Title": "ReportRaahat", } for model in MODELS: try: payload = { "model": model, "messages": messages, "max_tokens": 500, "temperature": 0.7, } with httpx.Client(timeout=30.0) as client: resp = client.post( f"{BASE_URL}/chat/completions", headers=headers, json=payload, ) if resp.status_code == 200: data = resp.json() reply = data["choices"][0]["message"]["content"] print(f"✅ OpenRouter reply via {model}: {len(reply)} chars") return reply.strip() else: print(f"⚠️ OpenRouter {model} returned {resp.status_code}: {resp.text[:200]}") continue except Exception as e: print(f"⚠️ OpenRouter {model} error: {e}") continue return None def chat( message: str, history: list[dict], guc: dict ) -> str: """ Send a message to Dr. Raahat via OpenRouter. Injects GUC context + RAG-retrieved knowledge. Falls back to enhanced mock responses if API fails. """ # Build system prompt with full GUC context system_prompt = build_system_prompt(guc) # Build conversation messages messages = [{"role": "system", "content": system_prompt}] # Add RAG-retrieved context if available try: if retrieve_doctor_context: docs = retrieve_doctor_context(message, top_k=3) if docs: context = "\n".join(f"- {d['text']}" for d in docs) messages.append({ "role": "system", "content": f"Relevant medical knowledge:\n{context}" }) except Exception as e: print(f"⚠️ RAG retrieval failed: {e}") # Add chat history for msg in history[-10:]: # Last 10 messages for context role = msg.get("role", "user") content = msg.get("content", msg.get("text", "")) if content: messages.append({"role": role, "content": content}) # Add current message messages.append({"role": "user", "content": message}) # Try OpenRouter API first reply = _call_openrouter(messages) if reply: return reply # Fallback to enhanced mock responses print("⚠️ OpenRouter unavailable, using mock responses") retrieved_docs = [] return get_enhanced_mock_response(message, guc, retrieved_docs)