--- title: Lumea Health Platform emoji: πŸ₯ colorFrom: blue colorTo: green sdk: docker app_port: 7860 pinned: false license: mit short_description: AI preventive health companion with OCR and digital twin --- # Lumea Health Platform A full-stack health companion platform for preventive health management. Upload medical reports, extract health metrics via OCR, track trends, and receive AI-powered health recommendations. > **Disclaimer**: This platform is a support tool for personal health tracking. It does not provide medical advice, diagnosis, or treatment. Always consult a licensed healthcare professional for medical decisions. --- ## Table of Contents - [Overview](#overview) - [Tech Stack](#tech-stack) - [Architecture](#architecture) - [Features](#features) - [Memory & Knowledge Graph: Intelligent Health Context](#memory--knowledge-graph-intelligent-health-context) - [Medicines: Find Cheap Alternatives](#medicines-find-cheap-alternatives) - [Voice Agent: AI Health Assistant](#voice-agent-ai-health-assistant) - [Physics Twin: Health Telemetry & Organ Scoring](#physics-twin-health-telemetry--organ-scoring) - [Getting Started](#getting-started) - [Environment Variables](#environment-variables) - [Database](#database) - [API Reference](#api-reference) - [WebSocket Events](#websocket-events) - [Testing](#testing) - [Deployment](#deployment) - [Contributing](#contributing) - [License](#license) --- ## Overview Lumea is a unified medical companion platform that enables users to: - **Upload medical reports** (PDF, images) and automatically extract health metrics via OCR - **Track health profiles** with comprehensive intake forms (conditions, medications, allergies, family history) - **Monitor health trends** with a computed Health Index and interactive charts - **Receive AI recommendations** based on extracted lab values and health patterns powered by OpenRouter (Claude Sonnet 4.5) - **Compare reports over time** with AI-powered summaries and trend analysis - **Chat with an AI assistant** grounded in your personal health data - **Memory & Knowledge Graph**: Persistent health memory with Mem0 and interconnected insights via Neo4j/Graphiti for temporal reasoning and contradiction detection - **Voice Agent**: Speak naturally to an AI health assistant for hands-free access to personalized health insights - **Physics Twin**: Interactive 2D organ simulation with real-time telemetry, condition detection, and evidence-based health recommendations powered by your actual medical data --- ## Tech Stack | Layer | Technology | |-------|------------| | **Frontend** | React 18, TypeScript, Vite, Framer Motion, Recharts, React Router, i18next | | **Backend** | FastAPI (Python 3.13+), SQLAlchemy 2.0 (async), Pydantic v2 | | **Database** | PostgreSQL (Neon or local), Alembic migrations | | **Graph Database** | Neo4j (knowledge graph for health relationships) | | **Vector Stores** | ChromaDB (RAG), Mem0 (persistent health memory) | | **OCR/Extraction** | PaddleOCR, pdfplumber, PyMuPDF | | **AI/LLM** | OpenRouter (Claude Sonnet 4.5 primary), Google Gemini API (fallback), Groq (fast inference), Ollama (local LLM/embeddings) | | **Memory Layer** | Mem0 (user-scoped persistent memory with automatic fact extraction) | | **Knowledge Graph** | Graphiti-core (temporal reasoning, entity relationships, contradiction detection) | | **Embeddings** | Ollama (nomic-embed-text local), OpenAI-compatible APIs | | **Voice Agent** | Web Speech API (STT), ElevenLabs TTS, Google Gemini | | **Realtime** | WebSocket (FastAPI native), Server-Sent Events (telemetry streaming) | | **Auth** | JWT (python-jose), bcrypt, rate limiting middleware | --- ## Architecture ```mermaid flowchart TB subgraph Client["Frontend (React)"] UI[React UI] WS[WebSocket Client] end subgraph API["Backend (FastAPI)"] Auth[Auth Routes] Dashboard[Dashboard Routes] Reports[Reports Routes] Profile[Profile Routes] Recommendations[Recommendations Routes] AISummary[AI Summary Routes] Assistant[Assistant Routes] WSServer[WebSocket Server] end subgraph Services["Backend Services"] OCR[PDF/OCR Extractor] Classifier[Document Classifier] MetricExtractor[Metric Extractor] MetricsService[Metrics Service] RecommendationEngine[Recommendation Engine] AISummaryService[AI Summary Service] RAG[RAG Service] LLM[LLM Service] end subgraph External["External Services"] GrokAPI[Grok API] Ollama[Ollama / MedGemma] Gemini[Gemini API] end subgraph Storage["Data Layer"] DB[(PostgreSQL / Neon)] ChromaDB[(ChromaDB Vector Store)] FileStorage[File Storage] end UI --> Auth UI --> Dashboard UI --> Reports UI --> Profile UI --> Recommendations UI --> AISummary UI --> Assistant WS <--> WSServer Reports --> OCR OCR --> Classifier Classifier --> MetricExtractor MetricExtractor --> MetricsService MetricsService --> RecommendationEngine AISummary --> AISummaryService AISummaryService --> GrokAPI Assistant --> RAG RAG --> ChromaDB RAG --> LLM LLM --> Ollama LLM --> Gemini Auth --> DB Dashboard --> DB Reports --> DB Reports --> FileStorage Profile --> DB Recommendations --> DB AISummary --> DB ``` ### Data Flow: Report Upload to Health Index ```mermaid sequenceDiagram participant User participant Frontend participant Backend participant OCR participant Classifier participant Extractor participant DB participant WebSocket User->>Frontend: Upload PDF/Image Frontend->>Backend: POST /api/reports/upload Backend->>DB: Create report (status: uploaded) Backend-->>Frontend: {id, status: uploaded} Backend->>OCR: Extract text (background) OCR-->>Backend: Raw text Backend->>Classifier: Classify document Classifier-->>Backend: Category + doc_type Backend->>Extractor: Extract metrics Extractor-->>Backend: Observations[] Backend->>DB: Save observations + update report Backend->>DB: Recompute health index Backend->>WebSocket: emit report_parsed WebSocket-->>Frontend: report_parsed event Frontend->>Frontend: Refresh UI ``` --- ## Features ### Physics Twin - Digital Health Telemetry & Organ Scoring - **6-Organ System**: Deterministic health scoring for kidney, heart, liver, lungs, brain, blood - **Real Data Integration**: Automatic computation from your actual extracted medical reports (not simulated) - **Interactive 3D Body**: Click to explore organ scores with animated visualizations - **Real-Time Telemetry**: Live vital signs stream (heart rate, blood pressure, SpOβ‚‚, glucose, stress, sleep) - **Condition Detection**: 10 rule-based health conditions auto-detected with severity mapping - **Body Impact Overlay**: 2D SVG visualization showing organs affected by detected conditions - **Smart Recommendations**: Evidence-based health actions with YouTube educational links per condition - **AI Video Recommendations**: OpenRouter-powered personalized health video suggestions for each organ based on abnormal metrics, with per-organ caching - **Explainability Cards**: Side-by-side "Improve Your Health" and "Why This Score?" panels showing video recommendations and scoring breakdowns - **Time-Series History**: View per-report organ scores and historical trends - **Lifestyle Integration**: Incorporates self-reported profile data (sleep hours, stress level) ### Document Upload & OCR - Supported formats: PDF, PNG, JPG, JPEG, TIFF - Max file size: 50MB - Automatic text extraction (text-first, OCR fallback) - Document classification: Lab, Dental, MRI, X-ray, Prescription, Sleep ### Health Profile & Reminders - Multi-step wizard with 6 steps (basics, measurements, conditions, medications, lifestyle, etc.) - Tracks conditions, symptoms, medications, supplements, allergies - Family medical history and genetic test results - **Once completed, users are never re-asked** – profile status persists in DB - "Profile Complete" indicator with quick-edit access via Settings page - **Real-time SMS reminders** via Twilio (or mock mode for testing) - Background scheduler processes due reminders every 60 seconds - Default reminders auto-generated: medication, appointment, checkup, hydration ### Health Index & Trends - Computed health index (0-100) based on lab values - Factor contributions breakdown (glucose, lipids, vitamins, etc.) - Time-series trends (1D, 1W, 1M views) - Abnormal value flagging with reference ranges ### AI Recommendations - Rule-based engine analyzing lab values vs reference ranges - Severity levels: INFO, WARNING, URGENT - Categories: lifestyle, screening, follow-up, urgent - Evidence-based with citations ### AI Report Summary - Single report AI summary with key findings - Multi-report comparison (2-6 reports, same type) - Highlights: positive, needs attention, next steps - Cached results with hash-based invalidation ### Health Assistant - RAG-powered chat grounded in user's health data - Citations from reports and observations - WebSocket streaming for real-time responses ### Voice Agent (AI Health Assistant) - **Natural voice conversations** with your health data - **Speech-to-Text**: Browser Web Speech API for hands-free input - **AI Processing**: Google Gemini with personalized health context - **Text-to-Speech**: ElevenLabs studio-quality voice (with browser fallback) - **Safety features**: Emergency detection, dosage inquiry protection - **Real-time feedback**: Visual orb animations for listening/thinking/speaking - **Comprehensive context**: Uses profile, conditions, medications, allergies, reports, RAG data - **Accessibility-first**: Ideal for hands-free use, visual impairments, or quick queries --- ## Memory & Knowledge Graph: Intelligent Health Context ### Overview The **Memory & Knowledge Graph** system provides persistent, intelligent health context that evolves with your medical journey. Built on **Mem0** (memory layer), **Graphiti** (knowledge graph engine), and **Neo4j** (graph database), this architecture enables the platform to: - **Remember** health facts across sessions (conditions, metrics, lifestyle patterns, medication changes) - **Connect** disparate health data points into meaningful relationships - **Reason temporally** about health trends and contradictions - **Generate insights** from interconnected health knowledge ### Architecture ```mermaid flowchart TB subgraph Input["Health Data Sources"] Profile[Health Profile] Reports[Medical Reports] Questionnaire[User Questionnaires] Assistant[AI Assistant Chats] end subgraph Memory["Mem0 Memory Layer"] Extraction[Fact Extraction
via Groq LLM] Storage[ChromaDB
Vector Store] Retrieval[Semantic Search] end subgraph Graph["Graphiti Knowledge Graph"] Entities[Entity Nodes
conditions, metrics, meds] Relationships[Temporal Relationships
affects, caused_by, treats] Episodes[Health Episodes
time-stamped events] Insights[AI-Generated Insights
contradictions, patterns] end subgraph DB["Neo4j Graph Database"] Nodes[Entity Nodes] Edges[Relationship Edges] Indexes[Vector Indexes
Ollama embeddings] end Input --> Extraction Extraction --> Storage Storage --> Retrieval Input --> Entities Entities --> Relationships Relationships --> Episodes Episodes --> Insights Entities --> Nodes Relationships --> Edges Edges --> Indexes Retrieval -.->|Enriches| Recommendations[AI Recommendations] Insights -.->|Powers| Dashboard[Features Dashboard] ``` ### Key Features #### 1. **Persistent Memory (Mem0)** - **Automatic Fact Extraction**: Converts natural language health data into structured facts - "Sleep is about 5 hours per night" β†’ Extracted memory fact - "Has condition High Blood Pressure" β†’ Indexed and searchable - "BMI is 25.2 which falls in overweight range" β†’ Computed and stored - **User-Scoped Storage**: Each user's memories are isolated and secure - **Vector-Based Retrieval**: Semantic search powered by local Ollama embeddings (nomic-embed-text) - **Deduplication**: Prevents redundant facts with intelligent merging #### 2. **Knowledge Graph (Graphiti + Neo4j)** - **Entity Relationship Mapping**: - Conditions linked with symptoms, medications, and risk factors - Lab values connected to reference ranges and health implications - Lifestyle factors (sleep, diet, exercise) linked to health outcomes - **Temporal Reasoning**: - Track health changes over time (BP trend: 140/90 in Jan β†’ 130/85 in Feb) - Episode creation for significant health events - Valid/expired relationships based on temporal context - **Contradiction Detection**: - Identifies conflicting health data (e.g., "no allergies" vs. "allergic to dust") - Highlights diagnostic uncertainties - Flags medication interactions - **Graph-Powered Insights**: - **Temporal Patterns**: "BP decreased after starting medication X" - **Key Relationships**: "High BMI correlates with elevated cholesterol" - **Contradictions**: "Sleep reported as both 5hrs and 8hrs - needs clarification" #### 3. **Interactive Visualization** **Features Dashboard** (`/features`) provides: - **Memory Dashboard**: - View all extracted health facts - Search memories semantically - Add/delete facts manually - Real-time sync status - **Knowledge Graph Viewer**: - Interactive node-edge visualization - Filter by conditions, medications, metrics - Zoom/pan for detailed exploration - Click nodes/edges for metadata - **AI Insights Panel**: - Temporal insights (trend analysis) - Relationship insights (correlation patterns) - Contradiction insights (data conflicts) - Each insight includes evidence and confidence score ### Technical Implementation #### Mem0 Configuration ```python # Memory layer uses: # - Groq (llama-3.3-70b-versatile) for fact extraction # - ChromaDB for vector storage # - HuggingFace embeddings (local) # - User-scoped collections ``` #### Graphiti Configuration ```python # Knowledge graph uses: # - OpenRouter (Claude Sonnet 4.5) for entity extraction # - Ollama (nomic-embed-text) for embeddings # - Neo4j for graph persistence # - User-scoped graph isolation via group_ids ``` #### Data Flow 1. **Profile Sync**: Health profile questionnaire auto-syncs facts to Mem0 and Graphiti 2. **Report Upload**: PDF extraction triggers memory and graph updates 3. **Assistant Chat**: Conversational context stored as episodic memories 4. **Background Processing**: Fact extraction and graph building happen asynchronously ### API Endpoints #### Memory API (`/api/memory`) - `GET /api/memory/facts` - List all user memories - `POST /api/memory/search` - Semantic search across memories - `POST /api/memory/add` - Manually add a memory fact - `DELETE /api/memory/{memory_id}` - Delete specific memory #### Graph API (`/api/graph`) - `GET /api/graph/relationships?limit=30` - Get graph nodes and edges - `POST /api/graph/insights` - Request AI-generated insights (temporal/relationships/contradictions) - `GET /api/graph/stats` - Graph statistics (node count, relationship count) ### Performance & Scalability - **Rate Limiting**: Groq free tier handles ~30 requests/minute (automatic retry with exponential backoff) - **Local Embeddings**: Ollama eliminates API costs for vector operations - **Caching**: Frequently accessed graph patterns cached in-memory - **Async Processing**: Profile/report sync uses background tasks to avoid blocking HTTP requests - **Neo4j Indexes**: Full-text and vector indexes optimize graph queries ### Privacy & Security - **User Isolation**: Mem0 and Graphiti enforce user-scoped data via `user_id` and `group_ids` - **No Cross-User Leakage**: Client-side filtering ensures graph results never leak between users - **JWT Authentication**: All memory/graph endpoints require valid JWT tokens - **Local Processing**: Embeddings done locally (Ollama) - no external API calls for vectors ### Use Cases 1. **Medication Adherence**: Graph tracks "Started medication X on date Y" β†’ Generate reminders 2. **Lifestyle Impact Analysis**: Correlate sleep patterns with BP readings over time 3. **Family History Mapping**: Link genetic conditions to risk factors 4. **Report Comparison**: Memory of previous lab values enables delta analysis 5. **Contraindication Detection**: Graph identifies medication-condition conflicts --- ## Medicines: Find Cheap Alternatives Lumea includes a comprehensive medicine management system that helps users find affordable generic alternatives to prescribed medicines and locate nearby pharmacies, including government-sponsored Jan Aushadhi Kendras offering subsidized medications. > **Medical Disclaimer**: This feature is a support tool for informational purposes only. It does NOT provide medical advice. Always consult your doctor or pharmacist before switching medicines or starting new treatments. ### Overview The Medicines feature enables users to: - **Search for medicines** by brand name or free-text queries - **Upload prescriptions** and automatically extract medicines via OCR/AI - **Find affordable alternatives** with ranked matching by clinical equivalence - **Compare prices** including Jan Aushadhi (government-fixed price) options - **Locate nearby pharmacies** including generic and Jan Aushadhi pharmacies - **Track medications** with personal notes and dosage schedules ### User Flow 1. **Input Medicine Information** - User enters brand name (e.g., "Aspirin 500mg") or uploads prescription image - System extracts text via OCR or Grok AI parsing 2. **Normalize Medicine Data** - `MedicineNormalizer` service extracts: salt, strength, form (tablet/capsule/liquid), release type (immediate/sustained) - Queries `GenericCatalog` table to validate and standardize extraction 3. **Find Substitutes** - `SubstituteFinder` queries database for alternatives with 4-tier ranked matching: - **Tier 1** (1.0 score): Exact match on salt + strength + form + release type - **Tier 2** (0.8 score): Same salt + strength + form - **Tier 3** (0.6 score): Same salt + strength - **Tier 4** (0.4 score): Same salt only - Results sorted by price (Jan Aushadhi first for lowest cost) 4. **Locate Pharmacies** (Optional) - User enters location (latitude/longitude) and search radius - `PharmacyLocator` queries Google Places API for pharmacies - Results include Jan Aushadhi Kendras (government pharmacies) and private pharmacies - Results cached for 1 hour to reduce API calls 5. **Save & Track** - User can save medicines to personal list with notes - Each save creates `UserSavedMedicine` entry for quick reference ### Architecture Diagram ```mermaid flowchart TB subgraph Frontend["Frontend (React)"] MedicinesPage["Medicines.tsx Page"] SubstitutePanel["Substitute Finder Panel"] PharmacyPanel["Pharmacy Locator Panel"] end subgraph API["Backend API (FastAPI)"] NormalizeRoute["POST /normalize
POST /normalize/batch"] SubstituteRoute["POST /substitutes
POST /substitutes/from-text"] PharmacyRoute["GET /pharmacies/nearby
GET /pharmacies/{id}
POST /pharmacies/{id}/click"] SavedRoute["POST /saved
GET /saved
DELETE /saved/{id}"] end subgraph Services["Backend Services"] MedicineNormalizer["MedicineNormalizer
- normalize()
- normalize_batch()"] SubstituteFinder["SubstituteFinder
- find_substitutes()
- save_medicine()"] PharmacyLocator["PharmacyLocator
- search_nearby()
- get_place_details()
- 1hr cache"] GrokService["GrokMedicineService
- get_alternatives_for_text()"] end subgraph External["External Services"] GooglePlaces["Google Places API
(Pharmacy Search)"] GrokAPI["Grok xAI API
(Medicine Parsing)"] end subgraph Storage["Data Layer"] DB[(PostgreSQL)] GenericCatalog["GenericCatalog Table
(600K+ medicines)"] UserSavedMedicine["UserSavedMedicine Table"] SubstituteQuery["SubstituteQuery Table
(Analytics)"] PharmacyClick["PharmacyClick Table
(Analytics)"] end MedicinesPage --> SubstitutePanel MedicinesPage --> PharmacyPanel SubstitutePanel --> SubstituteRoute PharmacyPanel --> PharmacyRoute PharmacyPanel --> SavedRoute SubstituteRoute --> GrokService SubstituteRoute --> MedicineNormalizer SubstituteRoute --> SubstituteFinder PharmacyRoute --> PharmacyLocator MedicineNormalizer --> DB MedicineNormalizer --> GenericCatalog SubstituteFinder --> DB SubstituteFinder --> GenericCatalog SubstituteFinder --> SubstituteQuery PharmacyLocator --> GooglePlaces PharmacyLocator --> PharmacyClick GrokService --> GrokAPI GrokAPI -->|Parsed results| SubstituteFinder ``` ### API Endpoints | Method | Endpoint | Description | Auth | Key Parameters | |--------|----------|-------------|------|-----------------| | POST | `/api/medicines/normalize` | Normalize single medicine text | Yes | `text: str` β†’ Returns `NormalizedMedicine` | | POST | `/api/medicines/normalize/batch` | Normalize multiple medicines (prescription lines) | Yes | `texts: List[str]` β†’ Returns `List[NormalizedMedicine]` | | POST | `/api/medicines/substitutes` | Find substitutes from structured data | Yes | `salt, strength, form, release_type` β†’ Returns ranked substitutes | | POST | `/api/medicines/substitutes/from-text` | Find substitutes from free-form text (AI-powered) | Yes | `text: str` β†’ Grok AI parsing β†’ Returns alternatives with prices | | GET | `/api/medicines/pharmacies/nearby` | Search nearby pharmacies by location | Yes | `lat, lng, radius_m=1000, type=all, page_token` β†’ Returns paginated pharmacies | | GET | `/api/medicines/pharmacies/{place_id}` | Get pharmacy details | Yes | `place_id: str` β†’ Returns address, phone, hours, rating | | POST | `/api/medicines/pharmacies/{place_id}/click` | Log pharmacy interaction (analytics) | Yes | `action: directions\|call\|website` | | POST | `/api/medicines/saved` | Save medicine to user list | Yes | `brand_name, salt, strength, form, notes` | | GET | `/api/medicines/saved` | Get user's saved medicines | Yes | Returns list of `UserSavedMedicine` | | DELETE | `/api/medicines/saved/{medicine_id}` | Delete saved medicine | Yes | `medicine_id: UUID` | ### Request/Response Examples **POST /api/medicines/substitutes/from-text** Request: ```json { "text": "Aspirin 500mg tablets" } ``` Response: ```json { "original_text": "Aspirin 500mg tablets", "normalized": { "brand_name": "Aspirin", "salt": "Acetylsalicylic Acid", "strength": "500", "form": "tablet", "release_type": "immediate", "confidence": 0.95 }, "substitutes": [ { "rank": 1, "product_name": "Jan Aushadhi Aspirin 500mg", "salt": "Acetylsalicylic Acid", "strength": "500", "form": "tablet", "mrp": "5.00", "is_jan_aushadhi": true, "match_score": 1.0, "match_reason": "Exact match: Same salt, strength, form, and type" }, { "rank": 2, "product_name": "Ecosprin 500mg", "salt": "Acetylsalicylic Acid", "strength": "500", "form": "tablet", "mrp": "8.50", "is_jan_aushadhi": false, "match_score": 1.0, "match_reason": "Exact match: Same salt, strength, form, and type" } ], "disclaimer": "Always confirm with your doctor or pharmacist before switching medicines" } ``` **GET /api/medicines/pharmacies/nearby** Request: ``` GET /api/medicines/pharmacies/nearby?lat=40.7128&lng=-74.0060&radius_m=1000&type=all ``` Response: ```json { "pharmacies": [ { "place_id": "ChIJN1blbgBQwokRzKgy6E_B_1Q", "name": "Jan Aushadhi Kendra - Downtown", "address": "123 Main St, New York, NY 10001", "latitude": 40.7128, "longitude": -74.0060, "rating": 4.7, "is_open": true, "is_jan_aushadhi": true, "phone": "+1-212-555-0123" }, { "place_id": "ChIJrc_p_1BQwokRzKgy6E_B_2Q", "name": "Metro Pharmacy", "address": "456 Broadway, New York, NY 10002", "latitude": 40.7150, "longitude": -74.0030, "rating": 4.2, "is_open": true, "is_jan_aushadhi": false, "phone": "+1-212-555-0456" } ], "next_page_token": null, "total_results": 2 } ``` --- ## Voice Agent: AI Health Assistant Lumea includes a fully integrated **Voice Agent** that provides an ElevenLabs-style conversational health assistant experience. Users can speak naturally to the AI agent, which responds with personalized health guidance based on their profile, conditions, medications, allergies, and recent lab reports. > **Medical Disclaimer**: The Voice Agent is an informational support tool. It does NOT provide medical diagnosis, dosage recommendations, or emergency care. Always consult licensed healthcare professionals for medical decisions. ### Overview The Voice Agent enables users to interact with their health data through natural voice conversations powered by AI. **Key Capabilities**: - **Voice Interaction**: Speak naturally using browser-based speech recognition (Web Speech API) - **Personalized AI Responses**: Powered by Google Gemini with access to your complete health profile - **Natural Speech Output**: Voice responses via ElevenLabs TTS (with browser fallback) - **Medical Safety Filters**: Detects emergency keywords, dosage inquiries, and provides appropriate safety responses - **Real-time Feedback**: Visual orb animations reflect listening/thinking/speaking states - **Transcript View**: Optional chat-mode panel displays conversation history - **Context-Aware**: Accesses your health conditions, medications, allergies, recent reports, and RAG-indexed data ### How It Works The Voice Agent uses a three-stage pipeline to deliver personalized health conversations: ```mermaid graph LR A[User Speaks] --> B[Browser STT
Web Speech API] B --> C[LLM Processing
Google Gemini] C --> D[Health Context
Profile + Reports + RAG] D --> C C --> E[Text-to-Speech
ElevenLabs API] E --> F[Browser Fallback
Web Speech API] E --> G[User Hears Response] F --> G ``` #### Stage 1: Speech-to-Text (STT) - **Technology**: Web Speech API (browser native) - **Process**: Real-time audio capture and transcription - **Benefits**: No external API costs, works offline for transcription - **Language Support**: Multi-language support via browser capabilities #### Stage 2: AI Processing & Context Retrieval - **LLM Provider**: Google Gemini API (`gemini-flash-latest`) - **Health Context Integration**: - User demographics (age, gender, BMI) - Active medical conditions - Current medications (name, dose, frequency) - Known allergies and their severity - Recent lab reports and observations - Lifestyle factors (sleep, exercise, smoking, alcohol) - **RAG Enhancement**: Queries ChromaDB vector store for relevant historical health data - **Safety Layer**: HIPAA-compliant system prompt with strict medical guidelines: - Never diagnose conditions - Never recommend medication dosages - Always suggest professional consultation - Detect emergency situations and direct to 911 #### Stage 3: Text-to-Speech (TTS) - **Primary**: ElevenLabs TTS API - Model: `eleven_turbo_v2_5` (optimized for speed, free tier compatible) - Voice: Rachel (`21m00Tcm4TlvDq8ikWAM`) - Quality: Natural voice synthesis - **Fallback**: Browser Text-to-Speech API - Activates automatically if ElevenLabs unavailable (503 errors) - Uses system voices - Zero latency, no API costs ### Real-World Benefits **For Everyday Users**: - **Hands-Free Access**: Check health info while cooking, exercising, or commuting - **Natural Conversations**: Ask questions in plain language, no medical jargon required - **Accessibility**: Ideal for users with visual impairments or reading difficulties - **Quick Insights**: Faster than navigating multiple screens and charts **For Health-Conscious Individuals**: - **Report Interpretation**: "What do my cholesterol levels mean?" - **Medication Context**: "Why am I taking this medication?" - **Trend Analysis**: "How has my blood pressure changed over time?" - **Personalized Guidance**: Responses tailored to your specific health profile **Safety & Reliability**: - **Emergency Detection**: Recognizes crisis keywords (chest pain, can't breathe, etc.) - **Dosage Protection**: Refuses to recommend medication changes - **HIPAA Guidelines**: All responses include disclaimers and professional consultation advice - **Fallback Mechanisms**: Graceful degradation if AI/TTS services unavailable ### API Endpoints | Method | Endpoint | Description | Auth | Response | |--------|----------|-------------|------|----------| | GET | `/api/voice/context` | Get user health context summary with profile completeness status | Yes | `{profile_complete: bool, summary: {...}, has_personalization: bool}` | | POST | `/api/voice/answer` | Generate personalized AI answer using Gemini + health context | Yes | `{answer_text: string, flags: [string], used_context: {...}}` | | POST | `/api/voice/tts` | Convert text to speech using ElevenLabs API | Yes | Binary audio stream (MP3) | | GET | `/api/voice/tts/status` | Check ElevenLabs TTS configuration status | Yes | `{configured: bool, voice_id: string, runtime_check: {...}}` | **Context Response Details**: ```json { "profile_complete": true, "has_personalization": true, "summary": { "name": "Darshan Ved", "age": 49, "gender": "Male", "bmi": 21.5, "conditions": ["Hypertension"], "medications": ["Lisinopril 10mg"], "allergies": ["Penicillin"], "reports_count": 5, "sleep_hours": 7, "exercise_frequency": "3-4 times/week" } } ``` **Safety Flags**: - `emergency`: Detected emergency keywords β†’ directs to 911 - `dosage_inquiry`: Detected medication dosage question β†’ refuses specific advice - `error`: Processing error β†’ fallback message ### Configuration & Setup #### Required Environment Variables Add to **root `.env`** (for Docker) and **`backend/.env`** (for local development): ```env # ===== AI/LLM Configuration ===== # Google Gemini API (Primary LLM for Voice Agent) USE_GEMINI_FALLBACK=true GEMINI_API_KEY=AIzaSy... # Get from https://aistudio.google.com/apikey # ===== Text-to-Speech Configuration ===== # ElevenLabs TTS for Voice Agent (Optional - has browser fallback) ELEVENLABS_API_KEY=sk_... # Get from https://elevenlabs.io/ ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAM # Rachel voice (recommended) ``` #### Docker Configuration Ensure `docker-compose.yml` includes these environment variables in the backend service: ```yaml services: backend: environment: # ... other env vars ... - GEMINI_API_KEY=${GEMINI_API_KEY:-} - USE_GEMINI_FALLBACK=${USE_GEMINI_FALLBACK:-true} - ELEVENLABS_API_KEY=${ELEVENLABS_API_KEY:-} - ELEVENLABS_VOICE_ID=${ELEVENLABS_VOICE_ID:-21m00Tcm4TlvDq8ikWAM} ``` #### Getting API Keys **Google Gemini API** (Free Tier Available): 1. Visit [Google AI Studio](https://aistudio.google.com/apikey) 2. Sign in with your Google account 3. Click "Get API Key" β†’ "Create API key" 4. Copy the key (starts with `AIzaSy...`) 5. Free tier includes: 15 requests/minute, 1 million tokens/day 6. Supported model: `gemini-flash-latest` (auto-maps to best available) **ElevenLabs API** (Optional - Free Tier Available): 1. Sign up at [ElevenLabs](https://elevenlabs.io/) 2. Navigate to Settings β†’ API Keys 3. Create new API key (starts with `sk_...`) 4. Free tier includes: 10,000 characters/month 5. Recommended voice IDs: - `21m00Tcm4TlvDq8ikWAM` - Rachel (conversational, female) - `ErXwobaYiN019PkySvjV` - Antoni (clear, male) - `EXAVITQu4vr4xnSDxMaL` - Bella (warm, female) **Free Tier Model Compatibility**: - Works: `gemini-flash-latest`, `gemini-1.5-flash-8b` - Quota exceeded: `gemini-2.0-flash`, `gemini-2.5-flash` (requires paid tier) ### Technology Stack | Component | Technology | Purpose | |-----------|------------|---------| | **Frontend STT** | Web Speech API (`webkitSpeechRecognition`) | Real-time voice transcription | | **LLM Provider** | Google Gemini API (`gemini-flash-latest`) | Natural language understanding & generation | | **Vector Database** | ChromaDB | RAG-based retrieval of historical health data | | **Primary TTS** | ElevenLabs API (`eleven_turbo_v2_5`) | Studio-quality voice synthesis | | **Fallback TTS** | Web Speech API (`SpeechSynthesis`) | Browser-native text-to-speech | | **Backend Framework** | FastAPI | REST API endpoints | | **Authentication** | JWT | Secure user-specific health context | ### Frontend Implementation **Location**: `frontend/src/pages/VoiceAgent.tsx` **Key Features**: - **Orb Animation**: Uses Framer Motion for fluid listening/thinking/speaking states - **State Management**: React hooks for recording, processing, speaking states - **Error Handling**: Automatic fallback to browser TTS on ElevenLabs 503 errors - **Accessibility**: Keyboard shortcuts, ARIA labels, screen reader support - **Responsive Design**: Matches Lumea's light theme with purple accent colors **User Flow**: 1. Click microphone button or press spacebar 2. Speak question (e.g., "What do my cholesterol levels mean?") 3. Watch orb animate while processing 4. Hear personalized response with health context 5. View transcript in chat panel (optional) ### Testing Voice Agent #### 1. Check TTS Configuration Status ```bash curl -X GET http://localhost:8000/api/voice/tts/status \ -H "Authorization: Bearer " ``` **Expected Response**: ```json { "configured": true, "voice_id": "21m00Tcm4TlvDq8ikWAM", "runtime_check": { "api_key_loaded": true, "api_key_prefix": "sk_d78...", "voice_id": "21m00Tcm4TlvDq8ikWAM" } } ``` #### 2. Test Health Context Retrieval ```bash curl -X GET http://localhost:8000/api/voice/context \ -H "Authorization: Bearer " ``` **Expected Response**: ```json { "profile_complete": true, "has_personalization": true, "summary": { "name": "Darshan Ved", "age": 49, "gender": "Male", "bmi": 21.5, "conditions": ["Hypertension"], "medications": ["Lisinopril"], "allergies": ["Penicillin"], "reports_count": 5 } } ``` #### 3. Test AI Answer Generation ```bash curl -X POST http://localhost:8000/api/voice/answer \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "text": "What do my cholesterol levels mean?" }' ``` **Expected Response**: ```json { "answer_text": "Based on your recent lab reports, your total cholesterol is 195 mg/dL, which falls within the desirable range (below 200 mg/dL). Your LDL cholesterol is 110 mg/dL, also in the optimal range. However, I recommend discussing these results with your healthcare provider for personalized guidance based on your hypertension condition.", "flags": [], "used_context": { "has_profile": true, "conditions_count": 1, "medications_count": 1, "has_rag_context": true } } ``` #### 4. Test Text-to-Speech Conversion ```bash curl -X POST http://localhost:8000/api/voice/tts \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"text": "Hello! Your health data shows positive trends."}' \ --output test_voice.mp3 # Play the audio file # Windows: start test_voice.mp3 # Mac: open test_voice.mp3 # Linux: mpg123 test_voice.mp3 ``` #### 5. Test Safety Features **Emergency Detection Test**: ```bash curl -X POST http://localhost:8000/api/voice/answer \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"text": "I am having severe chest pain"}' ``` **Expected Response**: ```json { "answer_text": "I'm detecting words that suggest this might be an emergency situation. Please call emergency services immediately (911) or go to the nearest emergency room...", "flags": ["emergency"], "used_context": {} } ``` **Dosage Inquiry Test**: ```bash curl -X POST http://localhost:8000/api/voice/answer \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"text": "Can I double my medication dosage?"}' ``` **Expected Response**: ```json { "answer_text": "I cannot provide specific dosage recommendations. Please consult your doctor or pharmacist before making any changes to your medications...", "flags": ["dosage_inquiry"], "used_context": {...} } ``` ### Troubleshooting #### Issue: "TTS service not configured" (503 Error) **Symptoms**: Voice Agent uses browser TTS instead of ElevenLabs **Solutions**: 1. **Check API Key Loading**: ```bash docker exec ggw-backend python -c "from app.settings import settings; print(f'API Key: {settings.ELEVENLABS_API_KEY[:20] if settings.ELEVENLABS_API_KEY else None}...')" ``` 2. **Verify Environment Variables**: - Ensure `ELEVENLABS_API_KEY` is in root `.env` file - Ensure `docker-compose.yml` includes the env var mapping - Restart Docker containers: `docker compose restart backend` 3. **Check API Key Validity**: ```bash curl https://api.elevenlabs.io/v1/voices \ -H "xi-api-key: sk_your_key_here" ``` #### Issue: Gemini API 404 or 429 Errors **Symptoms**: "Model not found" or "Quota exceeded" **Solutions**: 1. **Verify Model Compatibility**: ```bash docker exec ggw-backend python -c "import google.generativeai as genai; import os; genai.configure(api_key=os.environ['GEMINI_API_KEY']); models = [m.name for m in genai.list_models() if 'generateContent' in m.supported_generation_methods]; print('\n'.join(models))" ``` 2. **Check Free Tier Limits**: - Free tier model: `gemini-flash-latest` - Paid tier models: `gemini-2.0-flash`, `gemini-2.5-flash` - Rate limits: 15 requests/minute, 1M tokens/day 3. **Update Model Name** (if needed): - Edit `backend/app/services/llm_service.py` - Change model to `gemini-flash-latest` - Restart backend #### Issue: "ProfileMedication object has no attribute 'medication_name'" **Symptoms**: Context loading errors in logs **Solution**: Already fixed in codebase - medication attribute is `name`, not `medication_name` #### Issue: Browser STT Not Working **Symptoms**: Microphone icon doesn't activate **Solutions**: 1. **Check Browser Compatibility**: Use Chrome, Edge, or Safari (Firefox has limited support) 2. **Enable Microphone Permissions**: Allow microphone access in browser settings 3. **Use HTTPS or localhost**: Web Speech API requires secure context 4. **Check Console Errors**: Open DevTools β†’ Console for detailed error messages ### Example Conversations **General Health Query**: - User: "What does my health profile show?" - Assistant: "You're a 49-year-old male with a healthy BMI of 21.5. Your profile shows hypertension, and you're currently taking Lisinopril. You have 5 health reports on file. Is there specific information you'd like me to explain?" **Lab Result Interpretation**: - User: "Explain my recent cholesterol test" - Assistant: "Your recent cholesterol panel shows total cholesterol at 195 mg/dL, which is in the desirable range. Your LDL is 110 mg/dL and HDL is 55 mg/dL. Given your hypertension, maintaining these levels is important. Discuss with your doctor about your diet and medication effectiveness." **Medication Context**: - User: "Why am I taking Lisinopril?" - Assistant: "Lisinopril is an ACE inhibitor prescribed for managing hypertension, which is noted in your health profile. It helps lower blood pressure by relaxing blood vessels. Always take it as prescribed and discuss any side effects with your healthcare provider." --- ## Physics Twin: Health Telemetry & Organ Scoring Lumea's **Physics Twin** is a comprehensive digital health analysis engine that transforms raw health metrics from uploaded medical reports into deterministic organ-level health scores and real-time condition detection. It provides an interactive 3D body visualization, time-series telemetry monitoring, and AI-powered health recommendations grounded in your actual medical data. > **Medical Disclaimer**: The Physics Twin is a visualization and analysis support tool. It does NOT provide medical diagnosis or treatment. Values are computed from extracted lab data and should be verified by healthcare professionals. Use for personal health tracking onlyβ€”always consult a licensed healthcare professional for medical decisions. ### Overview The Physics Twin delivers: - **Deterministic Organ Scoring**: Evidence-based scoring for 6 organs (kidney, heart, liver, lungs, brain, blood) based on reference ranges and clinical normalisation - **Real Data Integration**: Pulls metrics directly from your uploaded medical reports (not simulated) via the `Observation` table - **Interactive 3D Body**: Clickable organ hotspots with animated scoring visualizations and trend indicators - **Real-Time Telemetry**: Simulated vital signs stream via SSE (Server-Sent Events) with SpO2, heart rate, blood pressure, stress, and glucose tracking - **Condition Detection**: 10 rule-based conditions (Hypertension, Tachycardia, Kidney Stress, Liver Stress, Hyperglycemia, Sleep Deprivation, etc.) automatically detected from current metrics - **Body Impact Overlay**: 2D SVG body silhouette highlighting organs affected by detected conditions with severity-based color coding - **Smart Recommendations**: Context-aware health recommendations with YouTube educational links for each detected condition - **AI Video Recommendations**: OpenRouter-powered personalized health video suggestions for each organ based on abnormal metrics, displayed in a side-by-side "Improve Your Health" card with per-organ caching - **Explainability Panel**: Side-by-side "Why This Score?" card showing metric contributions, weights, coverage, and confidence for each organ - **Time-Series History**: View per-report organ scores, historical trends, and scored snapshots over time - **Lifestyle Integration**: Incorporates UserProfile self-reported data (sleep hours, stress level, activity level) into organ scoring ### How It Works The Physics Twin operates in a three-layer pipeline: ``` LAYER 1: Data Aggregation β”œβ”€ Queries Observation table for authentic lab metrics (last 90 days) β”œβ”€ Extracts canonical metric names (systolic_bp, glucose, alt, etc.) β”œβ”€ Enriches with UserProfile lifestyle data (sleep_hours, stress_level) └─ Deduplicates per metric_name, keeping most recent value LAYER 2: Deterministic Scoring β”œβ”€ Applies physics_config scoring rules β”œβ”€ Normalises each metric to 0-1 range based on reference bounds β”œβ”€ Computes per-organ weighted scores (0-100) β”œβ”€ Calculates overall composite score across all organs └─ Generates contribution breakdown for explainability LAYER 3: Condition Detection & Recommendations β”œβ”€ Evaluates current metrics against 10+ condition rules β”œβ”€ Detects severity (mild/moderate/severe) per condition β”œβ”€ Maps conditions to affected organs β”œβ”€ Generates evidence-based recommendations └─ Enriches with YouTube educational links per condition ``` ### Architecture & Data Flow ```mermaid graph TB subgraph Database["PostgreSQL"] Observation["Observation Table
(extracted metrics)"] UserProfile["UserProfile Table
(lifestyle data)"] Report["Report Table
(source documents)"] end subgraph Backend["Backend Services"] PhysicsConfig["physics_config.py
- Organ specs
- Metric weights
- Reference ranges"] PhysicsRoute["physics.py Routes
- GET /latest (auto-compute)
- GET /history (per-report)
- POST /metrics (manual)
- GET /config"] Scorer["Scoring Engine
- compute_organ_score()
- compute_all_organs()"] ConditionEngine["conditions.py
- detect_conditions()
- 10 rule-based conditions
- Severity mapping
- Organ mapping"] TelemetryRoute["telemetry.py Routes
- GET /stream (SSE)
- GET /latest
- GET /history"] end subgraph Frontend["Frontend Components"] PhysicsTwin["PhysicsTwin.tsx
- Main page orchestrator
- Tab navigation (Twin/Metrics/History)"] TwinViewer["TwinViewer.tsx
- 3D body with Three.js
- Organ hotspots
- Auto-rotate"] BodyOverlay["BodyImpactOverlay.tsx
- SVG body silhouette
- Animated organ zones
- Gender toggle"] OrganTelemetry["OrganTelemetryCard.tsx
- Overall score display
- Selected organ detail
- Metric values"] Explainability["ExplainabilityCard.tsx
- Scoring breakdown
- Weight contributions
- Coverage/confidence"] Recommendations["RecommendationsCard.tsx
- Detected conditions
- Evidence-based actions
- YouTube links"] ConditionsEngine["conditionsEngine.ts
- Client-side condition detection
- Rapid re-evaluation
- Trend computation"] TelemetryHook["useTelemetryStream.ts
- SSE connection manager
- Fallback simulation
- History buffer (120 readings)"] end Observation -->|Query last 90 days| PhysicsRoute UserProfile -->|Enrich with lifestyle| PhysicsRoute Report -->|Group snapshots by| PhysicsRoute PhysicsRoute -->|Load config| PhysicsConfig PhysicsRoute -->|Compute scores| Scorer Scorer -->|Detect conditions| ConditionEngine TelemetryRoute -->|Generate readings| TelemetryHook PhysicsRoute -->|Return snapshot| PhysicsTwin PhysicsTwin -->|Render organs| TwinViewer PhysicsTwin -->|Show overlay| BodyOverlay PhysicsTwin -->|Display score| OrganTelemetry PhysicsTwin -->|Show breakdown| Explainability PhysicsTwin -->|Detect conditions| ConditionsEngine ConditionsEngine -->|Map to organs| BodyOverlay ConditionsEngine -->|Show recommendations| Recommendations TelemetryHook -->|Stream vitals| PhysicsTwin ``` ### Core Concepts #### Organ Systems (6 Total) | Organ | Metrics | Purpose | |-------|---------|---------| | **Kidney** | creatinine, urea, egfr, sodium, potassium, systolic_bp | Filtration & electrolyte balance | | **Heart** | heart_rate, systolic_bp, diastolic_bp, spo2, cholesterol, triglycerides | Cardiovascular function | | **Liver** | alt, ast, bilirubin_total, albumin, alp, total_protein | Detoxification & synthesis | | **Lungs** | spo2, respiratory_rate, hemoglobin | Oxygen exchange & gas transfer | | **Brain** | sleep_hours, stress_level, glucose, systolic_bp, tsh | Neurological health | | **Blood** | hemoglobin, wbc_total, platelet_count, rbc_count, glucose, hba1c, ferritin | Cell counts & glucose | #### Scoring Methodology Each metric is **normalised to 0-1** based on clinical reference ranges: ``` If metric_value is in reference_range [ref_min, ref_max]: normalised_score = 1.0 (perfect) Else if metric_value < ref_min: normalised_score = (metric_value - abs_min) / (ref_min - abs_min) (linear decay to 0 at absolute minimum) Else if metric_value > ref_max: normalised_score = 1.0 - (metric_value - ref_max) / (abs_max - ref_max) (linear decay to 0 at absolute maximum) ``` **Organ Score** = weighted average of present metrics: ``` score = Ξ£(weight_i Γ— normalised_i) / Ξ£(weight_present) Γ— 100 ``` **Overall Score** = mean of all organ scores with data **Status Classification**: - **Healthy**: 75-100 - **Watch**: 50-74 - **Risk**: 0-49 #### Metric Name Alignment All metric names match PostgreSQL `Observation.metric_name` canonical keys: ```python # Correct DB names (used everywhere) "systolic_bp", "diastolic_bp", "heart_rate", "spo2", "respiratory_rate", "creatinine", "urea", "egfr", "sodium", "glucose", "ast", "alt", "bilirubin_total", "hemoglobin", "stress_level", "sleep_hours" ``` This ensures consistent data flow from OCR extraction β†’ database storage β†’ physics scoring. #### Conditions Detection 10 rule-based conditions evaluated against current metrics: | Condition | Severity | Trigger Metrics | Affected Organs | |-----------|----------|-----------------|-----------------| | Hypertension | mild/moderate/severe | systolic_bp β‰₯ 130/140/160 mmHg | heart, kidney, brain | | Tachycardia | mild/moderate/severe | heart_rate β‰₯ 100/120/150 bpm | heart | | Bradycardia | mild/moderate/severe | heart_rate ≀ 55/50/40 bpm | heart, brain | | Hypoxemia | mild/moderate/severe | spo2 ≀ 94/90/85 % | lungs, heart, brain | | Kidney Stress | mild/moderate/severe | creatinine β‰₯ 1.3/1.8/3.0 mg/dL | kidney | | Liver Stress | mild/moderate/severe | alt β‰₯ 60/100/200 U/L | liver | | Hyperglycemia | mild/moderate/severe | glucose β‰₯ 110/140/200 mg/dL | kidney, heart, brain | | High Stress | mild/moderate/severe | stress_level β‰₯ 4/6/8 | brain, heart | | Sleep Deprivation | mild/moderate/severe | sleep_hours ≀ 6.5/5/4 hrs | brain, heart | | Tachypnea | mild/moderate/severe | respiratory_rate β‰₯ 22/26/30 bpm | lungs | ### Backend Implementation #### Files Modified/Created 1. **`app/services/physics_config.py`**: Organ & metric specifications - 6 organ definitions with metric weights - Reference ranges & absolute bounds per metric - `compute_organ_score()`: Deterministic scoring function - `compute_all_organs()`: Multi-organ aggregation 2. **`app/routes/physics.py`**: Smart API endpoints - `GET /api/physics/latest`: Auto-compute from real DB data (90-day window) - `GET /api/physics/history`: Per-report snapshots - `POST /api/physics/metrics`: Manual submission (fallback) - `GET /api/physics/config`: Frontend config download 3. **`app/services/conditions.py`**: Rule engine - 10 `ConditionRule` definitions - `detect_conditions()`: Main evaluation function - Threshold checking with severity mapping - Organ affection mapping 4. **`app/routes/telemetry.py`**: Simulated SSE stream - Baseline vital signs (heart_rate, bp, spo2, glucose, etc.) - Per-user simulation state tracking - Realistic jitter + periodic spikes - `GET /stream` SSE endpoint #### Data QueryQuery Pattern (physics.py) ```python # Gather last 90 days of metrics for user result = await db.execute( select(Observation) .where( and_( Observation.user_id == user_id, Observation.observed_at >= cutoff # 90 days ago ) ) .order_by(desc(Observation.observed_at)) ) # Deduplicate: keep most recent value per metric_name metrics = {} for obs in sorted_observations: if obs.metric_name not in seen: metrics[obs.metric_name] = float(obs.value) ``` ### Frontend Implementation #### Main Components 1. **`PhysicsTwin.tsx`** (616 lines): Page orchestrator - Three tabs: Twin View (3D/overlay), Metrics (vitals grid), History (trends + snapshots) - Auto-loads latest snapshot on mount - Streams real-time telemetry via SSE hook - Auto-detects conditions from current metrics - Handles organ selection state 2. **`TwinViewer.tsx`** (297 lines): 3D body renderer - Uses `@react-three/fiber` + `@react-three/drei` + Three.js - Stylized torso + head mesh - 6 animated organ orbs with onClick handlers - Auto-rotating when no organ selected - Smooth camera tween to selected organ - Responsive scaling & glow effects 3. **`BodyImpactOverlay.tsx`** (263 lines): 2D condition visualization - SVG body silhouette (male/female toggle) - 6 organ zones with animated pulsing - Severity-based color coding (mild/moderate/severe) - Condition pill badges at top - Gender-specific body paths 4. **`OrganTelemetryCard.tsx`** (159 lines): Score summary - Large animated overall score display - Selected organ detail (coverage %, status badge) - Per-metric contribution breakdown with visual bars - Last updated timestamp 5. **`ExplainabilityCard.tsx`**: Scoring transparency - Breakdown table: metric name, raw value, normalised score, weight - Contribution % calculation - Coverage metric (% of available metrics present) - Confidence score (weighted average) 6. **`YouTubeRecommendationsCard.tsx`**: AI-powered video recommendations - Generates organ-specific health video suggestions via OpenRouter (GPT-4o-mini) - Per-organ caching with `useRef` so recommendations load instantly on re-selection - AbortController + request ID pattern prevents stale API responses - Retry mechanism with cache-clearing for error recovery - Styled with `dash-card` CSS to match the project design system - Side-by-side layout with ExplainabilityCard in a 50/50 CSS grid row 7. **`RecommendationsCard.tsx`**: Condition actions - Dynamic pill badges grouped by severity - Evidence-based recommendations for each condition - YouTube search links for educational content - Expandable/collapsible sections #### Supporting Files - **`physicsApi.ts`**: API client with types for snapshots, organs, conditions - **`conditionsEngine.ts`**: Client-side condition detection mirroring backend rules (instant feedback on metric changes) - **`useTelemetryStream.ts`**: SSE hook with automatic fallback to simulated vitals if SSE unavailable - **`openRouterService.ts`**: OpenRouter API client for generating video recommendations (GPT-4o-mini, with local fallback) ### API Endpoints | Method | Endpoint | Description | Auth | Response | |--------|----------|-------------|------|----------| | GET | `/api/physics/latest` | Get auto-computed snapshot from user's real reports | Yes | `PhysicsSnapshot` (includes all organs + conditions) | | GET | `/api/physics/history?days=90` | Get per-report snapshots grouped by date/report_id | Yes | `List[PhysicsSnapshot]` | | POST | `/api/physics/metrics` | Submit manual metrics for what-if analysis | Yes | `PhysicsSnapshot` (not persisted) | | GET | `/api/physics/config` | Download organ/metric specifications | Yes | `{ organs: {...} }` (used by frontend for explainability) | | GET | `/api/telemetry/stream` | SSE stream of simulated vitals (2s intervals) | Yes | EventStream of `TelemetryReading` | | GET | `/api/telemetry/latest` | Get latest telemetry reading | Yes | `TelemetryReading` | | GET | `/api/telemetry/history?minutes=60` | Get recent telemetry history | Yes | `List[TelemetryReading]` | ### Request/Response Examples **GET /api/physics/latest** Response: ```json { "id": "550e8400-e29b-41d4-a716-446655440000_2025-02-06T10:30:00", "user_id": "550e8400-e29b-41d4-a716-446655440000", "timestamp": "2025-02-06T10:30:00", "overall_score": 72.3, "overall_status": "Watch", "data_source": "reports", "raw_metrics": { "systolic_bp": 145, "diastolic_bp": 88, "heart_rate": 78, "glucose": 115, "alt": 35, "creatinine": 1.1, "spo2": 97, "hemoglobin": 14.2, "stress_level": 5.2, "sleep_hours": 6.5 }, "organs": { "heart": { "score": 68.5, "status": "Watch", "coverage": 0.83, "contributions": [ { "name": "systolic_bp", "value": 145, "normalised": 0.75, "weight": 0.25, "weighted": 0.1875, "unit": "mmHg" } ] }, "kidney": { "score": 82.1, "status": "Healthy", "coverage": 1.0, "contributions": [] } }, "conditions": [ { "id": "hypertension", "name": "Hypertension", "severity": "moderate", "affected_organs": ["heart", "kidney", "brain"], "trigger_metrics": { "systolic_bp": 145 }, "recommendations": [ "Reduce sodium intake to under 2,300 mg/day", "Engage in 30 minutes of moderate exercise daily" ], "youtube_queries": ["how to lower blood pressure naturally"] } ], "organ_conditions": { "heart": ["hypertension"], "kidney": ["hypertension"] }, "organ_severities": { "heart": "moderate", "kidney": "mild" } } ``` **GET /api/telemetry/stream** Response (Server-Sent Events stream): ``` data: {"timestamp":"2025-02-06T10:30:00Z","metrics":{"heart_rate":75,"systolic_bp":120,"diastolic_bp":76,"spo2":97.5,"respiratory_rate":16,"stress_level":2.4,"glucose":95},"units":{"heart_rate":"bpm","systolic_bp":"mmHg"}} data: {"timestamp":"2025-02-06T10:30:02Z","metrics":{"heart_rate":76,"systolic_bp":119,"diastolic_bp":75,"spo2":97.2,"respiratory_rate":16,"stress_level":2.5,"glucose":94},"units":{...}} ``` ### User Experience Flow 1. **Page Load**: - Auto-fetches `/api/physics/latest` β†’ loads real data from user's reports - If user has reports: snapshot shows immediately with organ scores visible - If no reports: Shows "No health data found" overlay with option to load demo data 2. **3D Viewer Interaction**: - Click any organ β†’ auto-selects, triggers camera tween to that organ - OrganTelemetryCard shows score breakdown (e.g., "Heart: 68.5% β€” Watch") - ExplainabilityCard shows metric contributions to selected organ score - Each metric bar shows current value vs reference range 3. **Body Overlay Mode**: - Click "Impact View" toggle β†’ SVG body appears - Detected conditions pulse with color coding (mild=yellow, moderate=orange, severe=red) - Each organ zone highlights if affected by a condition - Hover shows condition name 4. **Real-Time Metrics Tab**: - Vital signs grid shows latest values (from telemetry SSE stream) - Trend arrows (↑ up, ↓ down, β†’ stable) based on 10-reading window - "Score Metrics" button submits current readings for manual analysis 5. **History Tab**: - Line chart of selected metric over last 60 telemetry readings - Scored snapshots section lists per-report scores with timestamps - Click any snapshot to replay its organ breakdown 6. **Recommendations**: - Dynamically updates as conditions detected/cleared - Each condition shows severity, affected organs, evidence-based actions - YouTube links for patient education (e.g., "DASH diet for hypertension") ### Key Features Breakdown #### Real Data, Not Simulation - `/api/physics/latest` queries actual `Observation` table - Uses real extracted metrics from uploaded medical reports - Falls back gracefully if user has zero observations (shows empty state) - Enriches with self-reported profile data (sleep, stress) #### Deterministic Scoring - Reference ranges stored in `physics_config.py` - All calculations deterministic (no AI/ML, pure math) - Explainable: every score component shown in breakdown - Aligned with clinical normalisation standards #### 3D Interactive Body - 6 clickable organ hotspots - Smooth camera animation on selection - Real-time score badges on each organ - Auto-rotating when idle - Works on mobile and desktop #### Condition Detection - 10 rule-based conditions automatically detected - Severity mapping (mild ≀ moderate ≀ severe) - Organ affection mapping (which organs are affected?) - Real-time evaluation as metrics change - Client-side + server-side dual detection for instant feedback #### Educational Context - Recommendations tailored per condition - YouTube search links for each condition - Evidence citations (reference ranges explained) - Safety disclaimers on all pages #### AI Video Recommendations (OpenRouter) - Personalized health video suggestions generated via OpenRouter (GPT-4o-mini) for each organ - Video recommendations based on organ-specific abnormal metrics, not generic searches - Per-organ caching with `useRef` for instant retrieval on organ re-selection - AbortController + request ID guards prevent stale responses from overwriting current data - Fallback recommendations generated locally if API is unavailable - Displayed in a side-by-side "Improve Your Health" card alongside the "Why This Score?" explainability panel ### Performance & Optimization - **Frontend Bundle**: 3D viewer lazy-loaded (905 KB gzipped) - **Data Loading**: Latest snapshot ~50-200ms (DB index on user_id + observed_at) - **Real-Time**: SSE stream 2s interval (configurable) - **History Rendering**: 60 telemetry readings + recharts <100ms re-render - **Condition Detection**: Client-side evaluation <5ms per metrics update --- ## Getting Started ### Prerequisites - Node.js 18+ and npm/yarn - Python 3.10+ - PostgreSQL 14+ (or Neon cloud database) - (Optional) Ollama for local LLM ### 1. Clone the Repository ```bash git clone https://github.com/darved2305/Co-Code-2.0-ggw.git cd Co-Code-2.0-ggw ``` ### 2. Backend Setup ```bash cd backend # Create virtual environment python -m venv .venv # Activate (Windows) .venv\Scripts\activate # Activate (macOS/Linux) source .venv/bin/activate # Install dependencies pip install -r requirements.txt # Copy and configure environment cp .env.example .env # Edit .env with your DATABASE_URL, JWT_SECRET, GROK_API_KEY # Run database migrations alembic upgrade head # Start the server uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 ``` ### 3. Frontend Setup ```bash cd frontend # Install dependencies npm install # Start development server npm run dev ``` ### 4. Access the Application - Frontend: http://localhost:5173 - Backend API: http://localhost:8000 - API Docs: http://localhost:8000/docs ### Docker Setup (Alternative) ```bash # From project root docker-compose up --build ``` Services: - Frontend: http://localhost:5173 - Backend: http://localhost:8000 - PostgreSQL: localhost:5432 --- ## Environment Variables Create a `.env` file in the `backend/` directory: | Variable | Description | Example | Required | |----------|-------------|---------|----------| | `DATABASE_URL` | PostgreSQL connection string (asyncpg) | `postgresql+asyncpg://user:pass@host:5432/db` | Yes | | `JWT_SECRET` | Secret key for JWT signing | `your-secret-key-min-32-chars` | Yes | | `JWT_ALGORITHM` | JWT algorithm | `HS256` | No (default: HS256) | | `ACCESS_TOKEN_EXPIRE_MINUTES` | Token expiration | `10080` (7 days) | No (default: 10080) | | `FRONTEND_ORIGIN` | CORS allowed origin | `http://localhost:5173` | Yes | | **AI/LLM Configuration** ||| | `OPENROUTER_API_KEY` | OpenRouter API key (primary LLM for recommendations) | `sk-or-v1-...` | **Yes** | | `OPENROUTER_MODEL` | OpenRouter model identifier | `anthropic/claude-sonnet-4` | No (default: claude-sonnet-4) | | `GROK_API_KEY` | xAI Grok API key (for AI summaries & medicine parsing) | `gsk_...` | Yes | | `GROQ_API_KEY` | Groq API key (fast inference for Mem0/Graphiti) | `gsk_...` | **Yes** | | `XAI_API_BASE` | xAI API endpoint base URL | `https://api.x.ai/v1` | No (default provided) | | `GROK_MODEL` | Grok model identifier | `grok-beta` | No (default: grok-beta) | | `GEMINI_API_KEY` | Google Gemini API key (fallback for summaries & voice) | `AIza...` | Yes (for voice agent) | | `OLLAMA_BASE_URL` | Ollama server URL (local LLM/embeddings) | `http://localhost:11434` | No (default: localhost:11434) | | `OLLAMA_MODEL` | Ollama model name | `llama3.2:latest` | No (default: llama3.2:latest) | | `USE_GEMINI_FALLBACK` | Enable Gemini fallback when primary LLM unavailable | `true` | No (default: false) | | **Memory & Graph Configuration** ||| | `NEO4J_URI` | Neo4j connection URI | `bolt://localhost:7687` | **Yes** (for graph features) | | `NEO4J_USER` | Neo4j database username | `neo4j` | **Yes** | | `NEO4J_PASSWORD` | Neo4j database password | `your-password` | **Yes** | | `GRAPHITI_GROQ_MODEL` | Groq model for Graphiti entity extraction | `llama-3.3-70b-versatile` | No (default provided) | | `MEM0_GROQ_MODEL` | Groq model for Mem0 fact extraction | `llama-3.3-70b-versatile` | No (default provided) | | **Other Services** ||| | `GOOGLE_PLACES_API_KEY` | Google Maps API key (for pharmacy search) | `AIzaSyD...` | No (fallback to mock data) | | `ELEVENLABS_API_KEY` | ElevenLabs TTS API key (for voice agent) | `sk_...` | No (browser TTS fallback) | | `ELEVENLABS_VOICE_ID` | ElevenLabs voice ID | `21m00Tcm4TlvDq8ikWAM` | No (default: Rachel) | | **SMS Reminders** ||| | `SMS_MODE` | SMS sending mode: `twilio` (real) or `mock` (test/log only) | `mock` | No (default: mock) | | `SMS_TEST_TO_NUMBER` | Default phone for test SMS (mock mode) | `+15551234567` | No | | `TWILIO_ACCOUNT_SID` | Twilio Account SID (required for `twilio` mode) | `ACxxxxxxxx` | No* | | `TWILIO_AUTH_TOKEN` | Twilio Auth Token (required for `twilio` mode) | `xxxxxxxxx` | No* | | `TWILIO_FROM_NUMBER` | Twilio phone number (required for `twilio` mode) | `+15559876543` | No* | | `REMINDER_SCHEDULER_ENABLED` | Enable/disable background reminder scheduler | `true` | No (default: true) | | `REMINDER_CHECK_INTERVAL_SECONDS` | How often scheduler checks for due reminders | `60` | No (default: 60) | **Memory & Graph Feature Notes:** - **NEO4J_***: Required for knowledge graph features. Use Docker: `docker run -p 7687:7687 -p 7474:7474 -e NEO4J_AUTH=neo4j/your-password neo4j:latest` - **GROQ_API_KEY**: Powers Mem0 fact extraction and Graphiti entity extraction. Free tier: ~30 requests/min (automatic retry with backoff) - **OPENROUTER_API_KEY**: Primary LLM for recommendations. Supports Claude, GPT-4, and other models. Get key from [OpenRouter](https://openrouter.ai/) - **Ollama embeddings**: Local nomic-embed-text model eliminates API costs. Install: `ollama pull nomic-embed-text` **AI/LLM Feature Notes:** - **OPENROUTER_API_KEY**: Primary recommendation engine. Supports 100+ models with unified API - **GROK_API_KEY**: Required for medicine parsing and AI summaries - **GEMINI_API_KEY**: Required for voice agent, optional fallback for recommendations - **OLLAMA_BASE_URL**: Local LLM for offline operation. Supports medical models like medgemma **SMS Reminders Feature Notes:** - **SMS_MODE**: Set to `mock` for development/testing (logs to console), `twilio` for production - **TWILIO_***: Required only when `SMS_MODE=twilio`. Get credentials from [Twilio Console](https://console.twilio.com/) - **REMINDER_SCHEDULER_ENABLED**: Scheduler runs in-process with APScheduler; disable during tests if needed See [backend/.env.example](backend/.env.example) for a complete template. --- ## Database ### PostgreSQL (Primary Database) Migrations are managed with Alembic: ```bash cd backend # Apply all migrations alembic upgrade head # Create a new migration alembic revision --autogenerate -m "description" # Check current revision alembic current ``` ### Core Tables | Table | Description | |-------|-------------| | `users` | User accounts (email, password_hash, onboarding status) | | `login_events` | Login audit trail | | `reports` | Uploaded documents (file path, OCR text, classification) | | `observations` | Extracted health metrics (value, unit, reference range, flag) | | `health_metrics` | Computed scores (health_index, contributions) | | `user_profiles` | Health profile data (basics, measurements, lifestyle, is_completed, phone_number) | | `profile_conditions` | User medical conditions | | `profile_medications` | Current medications | | `profile_allergies` | Allergies and reactions | | `profile_family_history` | Family medical history | | `profile_recommendations` | Generated recommendations | | `reminders` | User health reminders (medication, appointment, checkup, hydration) | | `reminder_events` | SMS send audit log (status, error messages, timestamps) | | `chat_sessions` | Assistant chat sessions | | `chat_messages` | Chat message history | | `report_ai_summaries` | Cached AI report summaries | | `report_ai_comparisons` | Cached AI report comparisons | | `generic_catalog` | Medicine database (600K+ products, indexed by salt/product_name) | | `user_saved_medicines` | User's saved medicines (brand name, dosage, notes) | | `substitute_queries` | Analytics: medicine searches and results | | `pharmacy_clicks` | Analytics: pharmacy interaction tracking (directions, calls, website) | | `user_location_consent` | Location permissions for pharmacy search | ### Entity Relationships ```mermaid erDiagram users ||--o{ reports : uploads users ||--o{ observations : has users ||--o{ health_metrics : has users ||--o| user_profiles : has users ||--o{ profile_conditions : has users ||--o{ profile_medications : takes users ||--o{ profile_allergies : has users ||--o{ chat_sessions : owns users ||--o{ report_ai_summaries : generates users ||--o{ report_ai_comparisons : generates users ||--o{ user_saved_medicines : saves users ||--o{ substitute_queries : searches users ||--o{ pharmacy_clicks : clicks users ||--o| user_location_consent : grants user_profiles ||--o{ reminders : has reminders ||--o{ reminder_events : logs reports ||--o{ observations : extracts reports ||--o{ report_ai_summaries : summarized_by chat_sessions ||--o{ chat_messages : contains user_saved_medicines ||--o{ generic_catalog : references substitute_queries ||--o{ generic_catalog : queries ``` ### Medicines Database Schema **generic_catalog** - Seeded from PMBI (Pharmaceutical Market Bureau of India) data | Column | Type | Notes | |--------|------|-------| | `id` | UUID | Primary key | | `product_name` | VARCHAR | Brand/product name (indexed) | | `salt` | VARCHAR | Active pharmaceutical ingredient (indexed) | | `strength` | VARCHAR | e.g., "500mg", "100mcg" | | `form` | VARCHAR | e.g., "tablet", "capsule", "liquid", "injection" | | `release_type` | VARCHAR | e.g., "immediate", "sustained" | | `mrp` | DECIMAL | Maximum retail price | | `manufacturer` | VARCHAR | Manufacturing company | | `source` | VARCHAR | Data source (e.g., "jan_aushadhi", "pmbi") | | `is_jan_aushadhi` | BOOLEAN | Government-fixed price indicator | | `created_at` | TIMESTAMP | Record creation time | **user_saved_medicines** - User's tracked medicines | Column | Type | Notes | |--------|------|-------| | `id` | UUID | Primary key | | `user_id` | UUID | Foreign key β†’ users | | `original_name` | VARCHAR | User-entered medicine name | | `salt` | VARCHAR | Normalized salt | | `strength` | VARCHAR | Normalized strength | | `form` | VARCHAR | Normalized form | | `release_type` | VARCHAR | Normalized release type | | `schedule_json` | JSONB | Optional: dosage schedule (morning, noon, evening) | | `notes` | TEXT | User's personal notes | | `created_at` | TIMESTAMP | When medicine was saved | **substitute_queries** - Analytics table | Column | Type | Notes | |--------|------|-------| | `id` | UUID | Primary key | | `user_id` | UUID | Foreign key β†’ users | | `query_raw` | VARCHAR | Original user input | | `normalized_json` | JSONB | Extracted fields (salt, strength, form, release_type) | | `results_json` | JSONB | Array of matching substitutes | | `results_count` | INTEGER | Number of alternatives found | | `created_at` | TIMESTAMP | Query timestamp | **pharmacy_clicks** - Analytics table | Column | Type | Notes | |--------|------|-------| | `id` | UUID | Primary key | | `user_id` | UUID | Foreign key β†’ users | | `place_id` | VARCHAR | Google Places API place ID | | `place_name` | VARCHAR | Pharmacy name | | `mode` | VARCHAR | Action: "directions", "call", "website" | | `created_at` | TIMESTAMP | Click timestamp | **user_location_consent** - Privacy tracking | Column | Type | Notes | |--------|------|-------| | `id` | UUID | Primary key | | `user_id` | UUID | Foreign key β†’ users (unique) | | `consent` | BOOLEAN | Location permission granted/revoked | | `updated_at` | TIMESTAMP | Last update | --- ## API Reference ### Authentication | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | POST | `/api/auth/register` | Create new account | No | | POST | `/api/auth/login` | Login, get JWT | No | | POST | `/api/auth/logout` | Logout, clear cookie | Yes | ### Dashboard | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | GET | `/api/me/bootstrap` | Get user state after login | Yes | | GET | `/api/dashboard/summary` | Health index with factor breakdown | Yes | | GET | `/api/dashboard/trends` | Time-series data for metrics | Yes | ### Reports | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | GET | `/api/reports` | List user's reports | Yes | | POST | `/api/reports/upload` | Upload new report | Yes | | GET | `/api/reports/{id}` | Get report details | Yes | | POST | `/api/reports/{id}/confirm` | Confirm extracted values | Yes | | DELETE | `/api/reports/{id}` | Delete report | Yes | | GET | `/api/reports/{id}/debug` | Debug extraction info | Yes | ### Profile | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | GET | `/api/profile` | Get full profile | Yes | | PUT | `/api/profile` | Update profile | Yes | | POST | `/api/profile/conditions` | Add conditions | Yes | | POST | `/api/profile/medications` | Add medications | Yes | | POST | `/api/profile/allergies` | Add allergies | Yes | | POST | `/api/profile/recompute` | Trigger recomputation | Yes | ### Profile/Me (Simplified) | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | GET | `/api/profile/me` | Get current user's profile with completion status | Yes | | PUT | `/api/profile/me` | Create or update profile (upsert) | Yes | | PATCH | `/api/profile/me` | Partial profile update | Yes | ### Reminders | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | GET | `/api/reminders` | List all reminders for user | Yes | | POST | `/api/reminders` | Create new reminder | Yes | | PATCH | `/api/reminders/{id}` | Update reminder | Yes | | DELETE | `/api/reminders/{id}` | Delete reminder | Yes | | POST | `/api/reminders/generate-default` | Generate default reminders | Yes | ### SMS (Testing) | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | POST | `/api/sms/test` | Send test SMS (mock or real based on SMS_MODE) | Yes | ### Recommendations | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | GET | `/api/recommendations` | Get recommendations | Yes | | GET | `/api/recommendations/summary` | Get summary counts | Yes | | POST | `/api/recommendations/regenerate` | Force regeneration | Yes | ### AI Summary | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | GET | `/api/ai/reports-for-summary` | List reports for selection | Yes | | GET | `/api/ai/reports/{id}/file` | Get report file | Yes | | POST | `/api/ai/report-summary` | Generate single report summary | Yes | | POST | `/api/ai/report-compare` | Compare multiple reports | Yes | | POST | `/api/ai/validate-comparison` | Validate selection | Yes | | GET | `/api/ai/categories` | Get distinct categories | Yes | ### Assistant | Method | Endpoint | Description | Auth | |--------|----------|-------------|------| | POST | `/api/assistant/chat` | Chat with AI assistant | Yes | ### Example Request/Response **POST /api/auth/login** Request: ```json { "email": "user@example.com", "password": "securepassword" } ``` Response: ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "full_name": "John Doe", "created_at": "2026-01-15T10:30:00Z" } } ``` **POST /api/ai/report-summary** Request: ```json { "report_id": "550e8400-e29b-41d4-a716-446655440001", "force_regenerate": false } ``` Response: ```json { "summary_json": { "title": "Blood Panel Analysis - January 2026", "highlights": { "positive": ["Hemoglobin within normal range", "Glucose levels stable"], "needs_attention": ["LDL cholesterol slightly elevated"], "next_steps": ["Consider dietary changes", "Retest in 3 months"] }, "plain_language_summary": "Your blood panel shows mostly healthy values...", "key_findings": [ {"item": "LDL Cholesterol", "evidence": "142 mg/dL (ref: <100)"} ], "confidence": 0.85 }, "cached": false, "generated_at": "2026-02-02T10:30:00Z", "model_name": "grok-beta" } ``` --- ## WebSocket Events Connect: `ws://localhost:8000/ws?token=` ### Events Sent to Client | Event | Payload | Description | |-------|---------|-------------| | `connected` | `{message, user_id}` | Connection established | | `pong` | `{timestamp}` | Response to ping | | `report_processing_started` | `{report_id, progress}` | OCR processing began | | `report_parsed` | `{report_id, extracted_metrics_count, status}` | OCR completed | | `health_index_updated` | `{score, breakdown, confidence, updated_at}` | Health index recalculated | | `trends_updated` | `{metrics: [...]}` | Trend data changed | | `reports_list_updated` | `{}` | Reports list changed | | `recommendations_updated` | `{count, urgent_count}` | Recommendations regenerated | | `profile_updated` | `{updated_at}` | Profile changed | | `chat_token` | `{token}` | Streaming chat token | | `chat_complete` | `{full_response, citations, session_id}` | Chat response complete | ### Events Received from Client | Event | Payload | Description | |-------|---------|-------------| | `ping` | `{}` | Keepalive ping | | `subscribe` | `{topics: [...]}` | Subscribe to topics | | `chat_request` | `{message, session_id}` | Start streaming chat | --- ## Testing ### Run Tests ```bash cd backend # Run all tests pytest # Run with verbose output pytest -v # Run specific test file pytest tests/test_routes_auth.py ``` ### Test Files - `test_routes_auth.py` - Authentication endpoints - `test_routes_dashboard.py` - Dashboard endpoints - `test_routes_reports.py` - Report upload/management - `test_routes_recommendations.py` - Recommendation endpoints - `test_lab_parser.py` - Lab value extraction - `test_pdf_extractor.py` - PDF/OCR extraction - `test_metrics_service.py` - Health index computation - `test_websocket_manager.py` - WebSocket connection management ### Manual QA Checklist 1. Register a new account 2. Login and verify dashboard loads 3. Upload a PDF lab report 4. Verify OCR extraction completes (WebSocket notification) 5. Check extracted metrics appear in reports list 6. Verify health index updates on dashboard 7. Complete health profile wizard 8. Check recommendations generate 9. Open AI Summary page, select a report 10. Generate AI summary, verify it displays 11. Select 2 reports of same type, generate comparison 12. Try selecting mixed types, verify warning appears 13. Test Medicines feature: search for a medicine (e.g., "Aspirin 500mg") 14. Verify substitutes appear with prices and Jan Aushadhi options highlighted 15. Test pharmacy search by entering your location 16. Verify nearby pharmacies appear with ratings and distance 17. Try filtering pharmacies by type (all/jan_aushadhi/generic) 18. Save a medicine and verify it appears in saved list 19. Open Physics Twin page, verify organ scores load from reports 20. Click an organ, verify "Why This Score?" breakdown and "Improve Your Health" video recommendations appear side-by-side 21. Switch organs, verify video recommendations update and cache correctly ### Medicines Feature Testing ```bash cd backend # Test medicines normalization pytest tests/test_medicines_normalizer.py # Test substitute finder pytest tests/test_medicines_substitutes.py # Test pharmacy locator pytest tests/test_medicines_pharmacy_locator.py # Test medicines routes pytest tests/test_routes_medicines.py ``` ### Medicines Manual Testing 1. **Test Medicine Normalization** ```bash curl -X POST http://localhost:8000/api/medicines/normalize \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"text": "Aspirin 500mg tablet"}' ``` Expected: `NormalizedMedicine` with extracted salt, strength, form 2. **Test Substitute Search (AI-Powered)** ```bash curl -X POST http://localhost:8000/api/medicines/substitutes/from-text \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"text": "Crocin 650mg for fever"}' ``` Expected: Ranked list of alternatives with prices 3. **Test Pharmacy Search** ```bash curl -X GET "http://localhost:8000/api/medicines/pharmacies/nearby?lat=40.7128&lng=-74.0060&radius_m=1000&type=all" \ -H "Authorization: Bearer " ``` Expected: List of nearby pharmacies with ratings 4. **Test Save Medicine** ```bash curl -X POST http://localhost:8000/api/medicines/saved \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "brand_name": "Aspirin", "salt": "Acetylsalicylic Acid", "strength": "500", "form": "tablet", "release_type": "immediate", "notes": "Take with food" }' ``` Expected: `UserSavedMedicine` object saved to database ### Medicines Troubleshooting **Problem: "No alternatives found" when searching for a medicine** - **Cause**: Medicine not in `generic_catalog` table - **Solution**: - Verify PMBI/Jan Aushadhi seed data was loaded during migration - Check database: `SELECT COUNT(*) FROM generic_catalog;` should show 600K+ records - Try searching by generic name instead of brand name (e.g., "Acetylsalicylic Acid" instead of "Aspirin") - Check logs for Grok API errors if using free-text search **Problem: "Google Places API 401" or pharmacy search returns empty** - **Cause**: `GOOGLE_PLACES_API_KEY` not set or API quota exceeded - **Solution**: - Verify `GOOGLE_PLACES_API_KEY` is set in `.env`: `echo $GOOGLE_PLACES_API_KEY` - Check Google Cloud Console for API key validity and quota limits - If quota exceeded, wait 24 hours or upgrade billing - During development, feature falls back to mock pharmacy data when API key is missing - Mock data is useful for frontend testing without API costs **Problem: Slow medicine normalization or substitute search** - **Cause**: Missing database indexes on high-cardinality columns - **Solution**: - Verify indexes exist on `generic_catalog`: `SELECT indexname FROM pg_indexes WHERE tablename='generic_catalog';` - Should see indexes on: `product_name`, `salt`, `user_id` - If missing, run: `alembic upgrade head` to apply latest migrations - Check database query performance: Enable `EXPLAIN ANALYZE` in PostgreSQL - Profile with: `SELECT pg_size_pretty(pg_total_relation_size('generic_catalog'));` **Problem: Grok API errors when parsing prescription images** - **Cause**: Invalid image format, API key expired, or API rate limit - **Solution**: - Verify image is clear and readable (JPG, PNG, TIFF) - Test API key directly: `curl -H "Authorization: Bearer $GROK_API_KEY" https://api.x.ai/v1/models` - Check Grok API status at https://status.x.ai - Review backend logs: `docker logs ` or `tail uvicorn.log` - If rate-limited, implement request throttling in `medicine_normalizer.py` **Problem: Pharmacy search cache not updating** - **Cause**: In-memory cache TTL (1 hour) not expired - **Solution**: - Manually clear cache by restarting backend service - Cache is per-search key (lat/lng/radius): different locations create new cache entries - Check cache size in logs: Search for "pharmacy_cache" debug messages - To disable caching for testing, set `PharmacyLocator.CACHE_TTL_SECONDS = 0` **Problem: User saved medicines not persisting** - **Cause**: Database migration not applied or user_id foreign key constraint - **Solution**: - Verify `user_saved_medicines` table exists: `\dt user_saved_medicines` in psql - Run migrations: `cd backend && alembic upgrade head` - Verify user exists and authenticated: Check JWT token contains valid `sub` (user_id) - Check database logs for constraint errors: `docker logs ` --- ## Deployment ### Docker Compose (Development/Staging) ```bash docker-compose up -d ``` Includes: - PostgreSQL 16 (Alpine) - FastAPI backend with hot reload - Vite React frontend with hot reload ### Production Considerations 1. **Database**: Use Neon, Supabase, or managed PostgreSQL 2. **Backend**: Deploy to Railway, Render, or AWS ECS 3. **Frontend**: Deploy to Vercel, Netlify, or Cloudflare Pages 4. **Secrets**: Use environment variables, never commit `.env` 5. **HTTPS**: Enable secure cookies in production 6. **CORS**: Update `FRONTEND_ORIGIN` for production domain --- ## Contributing 1. Fork the repository 2. Create a feature branch: `git checkout -b feature/my-feature` 3. Make changes and test 4. Commit with clear messages: `git commit -m "Add: feature description"` 5. Push and create a Pull Request ### Code Style - Backend: Follow PEP 8, use type hints - Frontend: Follow ESLint/Prettier config - Commits: Use conventional commit format --- ## License This project is licensed under the MIT License. See [LICENSE](LICENSE) for details. --- ## Support For issues or questions, open a GitHub issue or contact the maintainers.