# NeuroSense AI — Deep Clinical Evaluation & Multimodal Stress Assessment via Google Gemini import os import json import re from pathlib import Path from src.assistant.guardrails import is_unrelated_to_mental_health, UNRELATED_PROJECT_REPLY try: from openai import OpenAI OPENAI_AVAILABLE = True except ImportError: OPENAI_AVAILABLE = False class GeminiClinicalEvaluator: """ Multimodal Clinical Stress Evaluator powered by Google Gemini (Free Tier) / Groq / OpenAI. Provides deep psychological assessment, biomarker interpretation, and CBT risk evaluation for both Voice/Speech transcripts and Narrative text inputs. """ def __init__(self): self.provider = "Offline Rule-based Evaluator" self.model_name = "local-clinical-engine" self.client = None # Check environment variables self.gemini_key = os.getenv("GEMINI_API_KEY", "").strip() self.groq_key = os.getenv("GROQ_API_KEY", "").strip() self.openai_key = os.getenv("OPENAI_API_KEY", "").strip() if OPENAI_AVAILABLE and self.groq_key and len(self.groq_key) > 5: try: self.client = OpenAI(api_key=self.groq_key, base_url="https://api.groq.com/openai/v1") self.provider = "Groq (Llama 3.3 70B)" self.model_name = "llama-3.3-70b-versatile" print(f"[Gemini Evaluator] Connected to {self.provider}.") except Exception as e: print(f"[Gemini Evaluator] Could not initialize Groq client: {e}") elif OPENAI_AVAILABLE and self.gemini_key and len(self.gemini_key) > 5: try: self.client = OpenAI(api_key=self.gemini_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/") self.provider = "Google Gemini (Free Tier)" self.model_name = "gemini-flash-latest" print(f"[Gemini Evaluator] Connected to 100% FREE AI: {self.provider}.") except Exception as e: print(f"[Gemini Evaluator] Could not initialize Gemini client: {e}") elif OPENAI_AVAILABLE and self.openai_key and len(self.openai_key) > 5: try: self.client = OpenAI(api_key=self.openai_key) self.provider = "OpenAI ChatGPT" self.model_name = "gpt-4o-mini" print(f"[Gemini Evaluator] Connected to {self.provider}.") except Exception as e: print(f"[Gemini Evaluator] Could not initialize OpenAI client: {e}") def evaluate_clinical_stress(self, text: str, modality: str = "Text Narrative", acoustic_features: dict = None) -> dict: """ Analyzes a person's stress, depression, anxiety, and coping risk using Google Gemini AI. Works for both transcribed voice recordings and written text narratives. Enforces strict guardrails to only evaluate inputs related to mental health and emotional well-being. """ if not text or len(text.strip()) < 3: return self._get_fallback_evaluation("Patient provided minimal or silent input.", modality, 20, "Low / Baseline Calm") # 1. Check if the query is clearly unrelated to mental health / stress / daily life if is_unrelated_to_mental_health(text): return { "ai_provider": f"{self.provider} (Guardrail Filter)", "stress_level_index": 0, "clinical_risk_tier": "Not Related to Project", "detected_symptoms": ["Unrelated Query / Topic"], "empathetic_clinical_summary": UNRELATED_PROJECT_REPLY, "recommended_intervention": "Please submit a text check-in or question directly related to your mental health, emotional state, or daily stress." } if self.client and self.model_name: models_to_try = [self.model_name] if "gemini" in self.model_name.lower(): models_to_try = ["gemini-flash-latest", "gemini-1.5-flash-latest", "gemma-4-31b-it"] prompt = ( f"You are an expert dual-modality clinical psychologist and Cognitive Behavioral Therapy (CBT) diagnostician. " f"Analyze the following patient input from a {modality} check-in:\n\n" f"PATIENT INPUT: \"{text}\"\n\n" f"CRITICAL GUARDRAIL: If the patient input is completely unrelated to mental health, stress, anxiety, depression, relationships, daily pressure, or emotional well-being (e.g. coding questions, general trivia, math homework, recipes, etc.), return exactly:\n" f"{{\n" f" \"stress_level_index\": 0,\n" f" \"clinical_risk_tier\": \"Not Related to Project\",\n" f" \"detected_symptoms\": [\"Unrelated Query / Topic\"],\n" f" \"empathetic_clinical_summary\": \"{UNRELATED_PROJECT_REPLY}\",\n" f" \"recommended_intervention\": \"Please submit a text check-in or question directly related to your mental health, emotional state, or daily stress.\"\n" f"}}\n\n" f"Otherwise, evaluate the emotional state, stress severity (0-100), presence of anxiety/depression or burnout indicators, and provide actionable psychological recommendations.\n" f"Respond ONLY with a valid JSON object containing exactly these keys:\n" f"- \"stress_level_index\": (integer between 0 and 100 representing psychological strain)\n" f"- \"clinical_risk_tier\": (string, one of: \"Low / Baseline Calm\", \"Mild Stress & Fatigue\", \"Moderate Anxiety & Burnout Risk\", \"Severe Emotional Distress / High Risk\")\n" f"- \"detected_symptoms\": (array of 3 to 5 specific psychological/symptomatic strings detected or inferred, e.g. [\"Sleep Deprivation\", \"Catastrophizing\", \"Workload Overwhelm\"])\n" f"- \"empathetic_clinical_summary\": (string under 80 words explaining the clinical diagnosis and underlying stressors with professional empathy)\n" f"- \"recommended_intervention\": (string under 60 words recommending exact CBT or grounding exercises tailored to this subject)" ) for model_id in models_to_try: try: response = self.client.chat.completions.create( model=model_id, messages=[ {"role": "system", "content": "You are a clinical AI stress evaluation assistant. Output only JSON."}, {"role": "user", "content": prompt} ], temperature=0.4, max_tokens=400, timeout=5.5 ) raw_content = response.choices[0].message.content.strip() # Clean markdown code block if present if raw_content.startswith("```json"): raw_content = raw_content[7:] if raw_content.startswith("```"): raw_content = raw_content[3:] if raw_content.endswith("```"): raw_content = raw_content[:-3] parsed = json.loads(raw_content.strip()) parsed["ai_provider"] = f"{self.provider} ({model_id})" return parsed except Exception as e: continue # Try next model if busy or JSON parse error # Offline / Fallback Clinical Evaluation return self._get_fallback_evaluation(text, modality) def _get_fallback_evaluation(self, text: str, modality: str, custom_score: int = None, custom_tier: str = None) -> dict: if is_unrelated_to_mental_health(text): return { "ai_provider": f"{self.provider} (Guardrail Filter)", "stress_level_index": 0, "clinical_risk_tier": "Not Related to Project", "detected_symptoms": ["Unrelated Query / Topic"], "empathetic_clinical_summary": UNRELATED_PROJECT_REPLY, "recommended_intervention": "Please submit a text check-in or question directly related to your mental health, emotional state, or daily stress." } score = custom_score if custom_score is not None else 45 tier = custom_tier if custom_tier is not None else "Mild Stress & Fatigue" t_lower = text.lower() if any(w in t_lower for w in ["overwhelm", "burnout", "can't cope", "exhaust", "crisis"]): score = max(score, 76) tier = "Severe Emotional Distress / High Risk" elif any(w in t_lower for w in ["deadline", "exam", "worry", "anxious", "stress", "pressure"]): score = max(score, 58) tier = "Moderate Anxiety & Burnout Risk" elif any(w in t_lower for w in ["calm", "good", "relax", "happy", "fine"]): score = min(score, 24) tier = "Low / Baseline Calm" symptoms = [] if any(w in t_lower for w in ["sleep", "insomnia", "tired", "exhausted"]): symptoms.append("Sleep Disturbance & Fatigue") if any(w in t_lower for w in ["deadline", "exam", "job", "work", "class", "study", "project"]): symptoms.append("Occupational / Academic Pressure") if any(w in t_lower for w in ["anxious", "worry", "panic", "fear", "scared"]): symptoms.append("Acute Anxiety Symptoms") if any(w in t_lower for w in ["depress", "lonely", "hopeless", "sad", "cry"]): symptoms.append("Depressive Affect & Low Mood") if not symptoms: symptoms = ["Cognitive Load", "Daily Routine Adaptation"] summary = ( f"Analysis of this {modality} check-in indicates {tier.lower()} (Index: {score}/100). " f"The subject exhibits signs of {' and '.join([s.lower() for s in symptoms[:2]]) if len(symptoms)>=2 else symptoms[0].lower()}. " "Empathetic grounding and cognitive reappraisal are advised to mitigate stress accumulation." ) intervention = "Practice structured cognitive reframing, challenge negative assumptions using Socratic questioning, and break pending commitments into manageable 15-minute intervals." return { "ai_provider": "Offline Clinical Evaluator (Rule-Based Fallback)", "stress_level_index": score, "clinical_risk_tier": tier, "detected_symptoms": symptoms, "empathetic_clinical_summary": summary, "recommended_intervention": intervention } if __name__ == "__main__": evaluator = GeminiClinicalEvaluator() print("Testing Voice Transcript Evaluation:") print(json.dumps(evaluator.evaluate_clinical_stress("I feel anxious about my job deadlines and relationship worries.", "Voice Recording"), indent=2))