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.
- Overview
- Tech Stack
- Architecture
- Features
- Memory & Knowledge Graph: Intelligent Health Context
- Medicines: Find Cheap Alternatives
- Voice Agent: AI Health Assistant
- Physics Twin: Health Telemetry & Organ Scoring
- Getting Started
- Environment Variables
- Database
- API Reference
- WebSocket Events
- Testing
- Deployment
- Contributing
- License
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
| 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 |
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
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
- 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)
- 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
- 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
- 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
- Rule-based engine analyzing lab values vs reference ranges
- Severity levels: INFO, WARNING, URGENT
- Categories: lifestyle, screening, follow-up, urgent
- Evidence-based with citations
- 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
- RAG-powered chat grounded in user's health data
- Citations from reports and observations
- WebSocket streaming for real-time responses
- 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
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
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<br/>via Groq LLM]
Storage[ChromaDB<br/>Vector Store]
Retrieval[Semantic Search]
end
subgraph Graph["Graphiti Knowledge Graph"]
Entities[Entity Nodes<br/>conditions, metrics, meds]
Relationships[Temporal Relationships<br/>affects, caused_by, treats]
Episodes[Health Episodes<br/>time-stamped events]
Insights[AI-Generated Insights<br/>contradictions, patterns]
end
subgraph DB["Neo4j Graph Database"]
Nodes[Entity Nodes]
Edges[Relationship Edges]
Indexes[Vector Indexes<br/>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]
-
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
-
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"
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
# Memory layer uses:
# - Groq (llama-3.3-70b-versatile) for fact extraction
# - ChromaDB for vector storage
# - HuggingFace embeddings (local)
# - User-scoped collections# 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- Profile Sync: Health profile questionnaire auto-syncs facts to Mem0 and Graphiti
- Report Upload: PDF extraction triggers memory and graph updates
- Assistant Chat: Conversational context stored as episodic memories
- Background Processing: Fact extraction and graph building happen asynchronously
GET /api/memory/facts- List all user memoriesPOST /api/memory/search- Semantic search across memoriesPOST /api/memory/add- Manually add a memory factDELETE /api/memory/{memory_id}- Delete specific memory
GET /api/graph/relationships?limit=30- Get graph nodes and edgesPOST /api/graph/insights- Request AI-generated insights (temporal/relationships/contradictions)GET /api/graph/stats- Graph statistics (node count, relationship count)
- 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
- User Isolation: Mem0 and Graphiti enforce user-scoped data via
user_idandgroup_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
- Medication Adherence: Graph tracks "Started medication X on date Y" → Generate reminders
- Lifestyle Impact Analysis: Correlate sleep patterns with BP readings over time
- Family History Mapping: Link genetic conditions to risk factors
- Report Comparison: Memory of previous lab values enables delta analysis
- Contraindication Detection: Graph identifies medication-condition conflicts
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.
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
- Input Medicine Information
- User enters brand name (e.g., "Aspirin 500mg") or uploads prescription image
- System extracts text via OCR or Grok AI parsing
- Normalize Medicine Data
MedicineNormalizerservice extracts: salt, strength, form (tablet/capsule/liquid), release type (immediate/sustained)- Queries
GenericCatalogtable to validate and standardize extraction
- Find Substitutes
SubstituteFinderqueries 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)
- Locate Pharmacies (Optional)
- User enters location (latitude/longitude) and search radius
PharmacyLocatorqueries Google Places API for pharmacies- Results include Jan Aushadhi Kendras (government pharmacies) and private pharmacies
- Results cached for 1 hour to reduce API calls
- Save & Track
- User can save medicines to personal list with notes
- Each save creates
UserSavedMedicineentry for quick reference
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<br/>POST /normalize/batch"]
SubstituteRoute["POST /substitutes<br/>POST /substitutes/from-text"]
PharmacyRoute["GET /pharmacies/nearby<br/>GET /pharmacies/{id}<br/>POST /pharmacies/{id}/click"]
SavedRoute["POST /saved<br/>GET /saved<br/>DELETE /saved/{id}"]
end
subgraph Services["Backend Services"]
MedicineNormalizer["MedicineNormalizer<br/>- normalize()<br/>- normalize_batch()"]
SubstituteFinder["SubstituteFinder<br/>- find_substitutes()<br/>- save_medicine()"]
PharmacyLocator["PharmacyLocator<br/>- search_nearby()<br/>- get_place_details()<br/>- 1hr cache"]
GrokService["GrokMedicineService<br/>- get_alternatives_for_text()"]
end
subgraph External["External Services"]
GooglePlaces["Google Places API<br/>(Pharmacy Search)"]
GrokAPI["Grok xAI API<br/>(Medicine Parsing)"]
end
subgraph Storage["Data Layer"]
DB[(PostgreSQL)]
GenericCatalog["GenericCatalog Table<br/>(600K+ medicines)"]
UserSavedMedicine["UserSavedMedicine Table"]
SubstituteQuery["SubstituteQuery Table<br/>(Analytics)"]
PharmacyClick["PharmacyClick Table<br/>(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
| 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 |
POST /api/medicines/substitutes/from-text
Request:
{
"text": "Aspirin 500mg tablets"
}Response:
{
"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:
{
"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
}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.
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
The Voice Agent uses a three-stage pipeline to deliver personalized health conversations:
graph LR
A[User Speaks] --> B[Browser STT<br/>Web Speech API]
B --> C[LLM Processing<br/>Google Gemini]
C --> D[Health Context<br/>Profile + Reports + RAG]
D --> C
C --> E[Text-to-Speech<br/>ElevenLabs API]
E --> F[Browser Fallback<br/>Web Speech API]
E --> G[User Hears Response]
F --> G
- 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
- 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
- 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
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
| 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:
{
"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 911dosage_inquiry: Detected medication dosage question → refuses specific adviceerror: Processing error → fallback message
Add to root .env (for Docker) and backend/.env (for local development):
# ===== 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)Ensure docker-compose.yml includes these environment variables in the backend service:
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}Google Gemini API (Free Tier Available):
- Visit Google AI Studio
- Sign in with your Google account
- Click "Get API Key" → "Create API key"
- Copy the key (starts with
AIzaSy...) - Free tier includes: 15 requests/minute, 1 million tokens/day
- Supported model:
gemini-flash-latest(auto-maps to best available)
ElevenLabs API (Optional - Free Tier Available):
- Sign up at ElevenLabs
- Navigate to Settings → API Keys
- Create new API key (starts with
sk_...) - Free tier includes: 10,000 characters/month
- 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)
| 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 |
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:
- Click microphone button or press spacebar
- Speak question (e.g., "What do my cholesterol levels mean?")
- Watch orb animate while processing
- Hear personalized response with health context
- View transcript in chat panel (optional)
curl -X GET http://localhost:8000/api/voice/tts/status \
-H "Authorization: Bearer <your_jwt_token>"Expected Response:
{
"configured": true,
"voice_id": "21m00Tcm4TlvDq8ikWAM",
"runtime_check": {
"api_key_loaded": true,
"api_key_prefix": "sk_d78...",
"voice_id": "21m00Tcm4TlvDq8ikWAM"
}
}curl -X GET http://localhost:8000/api/voice/context \
-H "Authorization: Bearer <your_jwt_token>"Expected Response:
{
"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
}
}curl -X POST http://localhost:8000/api/voice/answer \
-H "Authorization: Bearer <your_jwt_token>" \
-H "Content-Type: application/json" \
-d '{
"text": "What do my cholesterol levels mean?"
}'Expected Response:
{
"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
}
}curl -X POST http://localhost:8000/api/voice/tts \
-H "Authorization: Bearer <your_jwt_token>" \
-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.mp3Emergency Detection Test:
curl -X POST http://localhost:8000/api/voice/answer \
-H "Authorization: Bearer <your_jwt_token>" \
-H "Content-Type: application/json" \
-d '{"text": "I am having severe chest pain"}'Expected Response:
{
"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:
curl -X POST http://localhost:8000/api/voice/answer \
-H "Authorization: Bearer <your_jwt_token>" \
-H "Content-Type: application/json" \
-d '{"text": "Can I double my medication dosage?"}'Expected Response:
{
"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": {...}
}Symptoms: Voice Agent uses browser TTS instead of ElevenLabs
Solutions:
- Check API Key Loading:
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}...')"- Verify Environment Variables:
- Ensure
ELEVENLABS_API_KEYis in root.envfile - Ensure
docker-compose.ymlincludes the env var mapping - Restart Docker containers:
docker compose restart backend
- Check API Key Validity:
curl https://api.elevenlabs.io/v1/voices \
-H "xi-api-key: sk_your_key_here"Symptoms: "Model not found" or "Quota exceeded"
Solutions:
- Verify Model Compatibility:
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))"- 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
- Update Model Name (if needed):
- Edit
backend/app/services/llm_service.py - Change model to
gemini-flash-latest - Restart backend
Symptoms: Context loading errors in logs
Solution: Already fixed in codebase - medication attribute is name, not medication_name
Symptoms: Microphone icon doesn't activate
Solutions:
- Check Browser Compatibility: Use Chrome, Edge, or Safari (Firefox has limited support)
- Enable Microphone Permissions: Allow microphone access in browser settings
- Use HTTPS or localhost: Web Speech API requires secure context
- Check Console Errors: Open DevTools → Console for detailed error messages
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."
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.
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
Observationtable - 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
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
graph TB
subgraph Database["PostgreSQL"]
Observation["Observation Table<br/>(extracted metrics)"]
UserProfile["UserProfile Table<br/>(lifestyle data)"]
Report["Report Table<br/>(source documents)"]
end
subgraph Backend["Backend Services"]
PhysicsConfig["physics_config.py<br/>- Organ specs<br/>- Metric weights<br/>- Reference ranges"]
PhysicsRoute["physics.py Routes<br/>- GET /latest (auto-compute)<br/>- GET /history (per-report)<br/>- POST /metrics (manual)<br/>- GET /config"]
Scorer["Scoring Engine<br/>- compute_organ_score()<br/>- compute_all_organs()"]
ConditionEngine["conditions.py<br/>- detect_conditions()<br/>- 10 rule-based conditions<br/>- Severity mapping<br/>- Organ mapping"]
TelemetryRoute["telemetry.py Routes<br/>- GET /stream (SSE)<br/>- GET /latest<br/>- GET /history"]
end
subgraph Frontend["Frontend Components"]
PhysicsTwin["PhysicsTwin.tsx<br/>- Main page orchestrator<br/>- Tab navigation (Twin/Metrics/History)"]
TwinViewer["TwinViewer.tsx<br/>- 3D body with Three.js<br/>- Organ hotspots<br/>- Auto-rotate"]
BodyOverlay["BodyImpactOverlay.tsx<br/>- SVG body silhouette<br/>- Animated organ zones<br/>- Gender toggle"]
OrganTelemetry["OrganTelemetryCard.tsx<br/>- Overall score display<br/>- Selected organ detail<br/>- Metric values"]
Explainability["ExplainabilityCard.tsx<br/>- Scoring breakdown<br/>- Weight contributions<br/>- Coverage/confidence"]
Recommendations["RecommendationsCard.tsx<br/>- Detected conditions<br/>- Evidence-based actions<br/>- YouTube links"]
ConditionsEngine["conditionsEngine.ts<br/>- Client-side condition detection<br/>- Rapid re-evaluation<br/>- Trend computation"]
TelemetryHook["useTelemetryStream.ts<br/>- SSE connection manager<br/>- Fallback simulation<br/>- 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
| 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 |
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
All metric names match PostgreSQL Observation.metric_name canonical keys:
# 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.
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 |
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 functioncompute_all_organs(): Multi-organ aggregation
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 snapshotsPOST /api/physics/metrics: Manual submission (fallback)GET /api/physics/config: Frontend config download
app/services/conditions.py: Rule engine
- 10
ConditionRuledefinitions detect_conditions(): Main evaluation function- Threshold checking with severity mapping
- Organ affection mapping
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 /streamSSE endpoint
# 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)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
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
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
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
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)
YouTubeRecommendationsCard.tsx: AI-powered video recommendations
- Generates organ-specific health video suggestions via OpenRouter (GPT-4o-mini)
- Per-organ caching with
useRef<Map>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-cardCSS to match the project design system - Side-by-side layout with ExplainabilityCard in a 50/50 CSS grid row
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
physicsApi.ts: API client with types for snapshots, organs, conditionsconditionsEngine.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 unavailableopenRouterService.ts: OpenRouter API client for generating video recommendations (GPT-4o-mini, with local fallback)
| 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] |
GET /api/physics/latest
Response:
{
"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":{...}}
- 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
- 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
- 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
- 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
- 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
- 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")
/api/physics/latestqueries actualObservationtable- 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)
- 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
- 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
- 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
- Recommendations tailored per condition
- YouTube search links for each condition
- Evidence citations (reference ranges explained)
- Safety disclaimers on all pages
- 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<Map>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
- 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
- Node.js 18+ and npm/yarn
- Python 3.10+
- PostgreSQL 14+ (or Neon cloud database)
- (Optional) Ollama for local LLM
git clone https://github.com/darved2305/Co-Code-2.0-ggw.git
cd Co-Code-2.0-ggwcd 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 8000cd frontend
# Install dependencies
npm install
# Start development server
npm run dev- Frontend: http://localhost:5173
- Backend API: http://localhost:8000
- API Docs: http://localhost:8000/docs
# From project root
docker-compose up --buildServices:
- Frontend: http://localhost:5173
- Backend: http://localhost:8000
- PostgreSQL: localhost:5432
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
- 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
mockfor development/testing (logs to console),twiliofor production - TWILIO_*: Required only when
SMS_MODE=twilio. Get credentials from Twilio Console - REMINDER_SCHEDULER_ENABLED: Scheduler runs in-process with APScheduler; disable during tests if needed
See backend/.env.example for a complete template.
Migrations are managed with Alembic:
cd backend
# Apply all migrations
alembic upgrade head
# Create a new migration
alembic revision --autogenerate -m "description"
# Check current revision
alembic current| 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 |
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
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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/sms/test |
Send test SMS (mock or real based on SMS_MODE) | Yes |
| 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 |
| 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 |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/assistant/chat |
Chat with AI assistant | Yes |
POST /api/auth/login
Request:
{
"email": "user@example.com",
"password": "securepassword"
}Response:
{
"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:
{
"report_id": "550e8400-e29b-41d4-a716-446655440001",
"force_regenerate": false
}Response:
{
"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"
}Connect: ws://localhost:8000/ws?token=<jwt_token>
| 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 |
| Event | Payload | Description |
|---|---|---|
ping |
{} |
Keepalive ping |
subscribe |
{topics: [...]} |
Subscribe to topics |
chat_request |
{message, session_id} |
Start streaming chat |
cd backend
# Run all tests
pytest
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/test_routes_auth.pytest_routes_auth.py- Authentication endpointstest_routes_dashboard.py- Dashboard endpointstest_routes_reports.py- Report upload/managementtest_routes_recommendations.py- Recommendation endpointstest_lab_parser.py- Lab value extractiontest_pdf_extractor.py- PDF/OCR extractiontest_metrics_service.py- Health index computationtest_websocket_manager.py- WebSocket connection management
- Register a new account
- Login and verify dashboard loads
- Upload a PDF lab report
- Verify OCR extraction completes (WebSocket notification)
- Check extracted metrics appear in reports list
- Verify health index updates on dashboard
- Complete health profile wizard
- Check recommendations generate
- Open AI Summary page, select a report
- Generate AI summary, verify it displays
- Select 2 reports of same type, generate comparison
- Try selecting mixed types, verify warning appears
- Test Medicines feature: search for a medicine (e.g., "Aspirin 500mg")
- Verify substitutes appear with prices and Jan Aushadhi options highlighted
- Test pharmacy search by entering your location
- Verify nearby pharmacies appear with ratings and distance
- Try filtering pharmacies by type (all/jan_aushadhi/generic)
- Save a medicine and verify it appears in saved list
- Open Physics Twin page, verify organ scores load from reports
- Click an organ, verify "Why This Score?" breakdown and "Improve Your Health" video recommendations appear side-by-side
- Switch organs, verify video recommendations update and cache correctly
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- Test Medicine Normalization
curl -X POST http://localhost:8000/api/medicines/normalize \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"text": "Aspirin 500mg tablet"}'Expected: NormalizedMedicine with extracted salt, strength, form
- Test Substitute Search (AI-Powered)
curl -X POST http://localhost:8000/api/medicines/substitutes/from-text \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"text": "Crocin 650mg for fever"}'Expected: Ranked list of alternatives with prices
- Test Pharmacy Search
curl -X GET "http://localhost:8000/api/medicines/pharmacies/nearby?lat=40.7128&lng=-74.0060&radius_m=1000&type=all" \
-H "Authorization: Bearer <token>"Expected: List of nearby pharmacies with ratings
- Test Save Medicine
curl -X POST http://localhost:8000/api/medicines/saved \
-H "Authorization: Bearer <token>" \
-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
Problem: "No alternatives found" when searching for a medicine
- Cause: Medicine not in
generic_catalogtable - 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_KEYnot set or API quota exceeded - Solution:
- Verify
GOOGLE_PLACES_API_KEYis 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 headto apply latest migrations - Check database query performance: Enable
EXPLAIN ANALYZEin 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 <backend-container>ortail 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_medicinestable exists:\dt user_saved_medicinesin 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 <postgres-container>
docker-compose up -dIncludes:
- PostgreSQL 16 (Alpine)
- FastAPI backend with hot reload
- Vite React frontend with hot reload
- Database: Use Neon, Supabase, or managed PostgreSQL
- Backend: Deploy to Railway, Render, or AWS ECS
- Frontend: Deploy to Vercel, Netlify, or Cloudflare Pages
- Secrets: Use environment variables, never commit
.env - HTTPS: Enable secure cookies in production
- CORS: Update
FRONTEND_ORIGINfor production domain
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make changes and test
- Commit with clear messages:
git commit -m "Add: feature description" - Push and create a Pull Request
- Backend: Follow PEP 8, use type hints
- Frontend: Follow ESLint/Prettier config
- Commits: Use conventional commit format
This project is licensed under the MIT License. See LICENSE for details.
For issues or questions, open a GitHub issue or contact the maintainers.