Spaces:
Running
Running
| 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) | |