YGC (Your Guided Care) is a full-stack medical-report platform. Patients upload lab PDFs, prescriptions, and scans. The system extracts structured facts, runs deterministic clinical safety checks, and answers questions in plain language — citing the exact page the answer came from.
It is a review and preparation companion, not a clinician. It does not diagnose, prescribe, or replace professional medical advice.
Upload a report → extract labs / meds / allergies → run safety rules
↓
Ask in plain language ← cite the source page ← store facts in MongoDB
| Layer | What it is | Default local URL |
|---|---|---|
| Frontend | React + Vite dashboard | http://localhost:5173 |
| Backend | Node / Express API | http://localhost:4000 |
| RAG service | FastAPI clinical pipeline | http://127.0.0.1:8000 |
| Database | MongoDB (single source of truth) | local in-memory, Compose, or Atlas |
- Features
- How complex is this?
- Architecture
- Modules and what they do
- Tech stack — what is used
- Prerequisites
- Quick start (local)
- Environment files
- Docker Compose
- Demo mode (no account)
- Tests
- Production deploy
- Safety and privacy
- Further reading
- Upload PDF, PNG, JPG, and WebP reports (digital or scanned).
- Multi-engine extraction: pdfplumber / PyMuPDF for text-layer PDFs, EasyOCR for scans, optional Tesseract cross-check, optional TrOCR handwriting, optional vision-LLM fallback.
- Each lab, medication, and allergy keeps page, line, and snippet provenance.
- Unreadable pages are flagged instead of guessed. Engine disagreement is marked
uncorroboratedand can require human verification before save. - Async extract jobs (
queued → ocr → parsing → merging → done) so the UI can poll progress instead of blocking for 60–90 seconds.
Safety findings are produced by curated tables and rules, not by the language model:
| Check | What fires |
|---|---|
| Drug–drug interactions | Curated pairs + RxNav + openFDA |
| Allergy contradictions | Cross-reactivity matrix (e.g. penicillin → amoxicillin) |
| Duplicate / same-class therapy | Exact match and therapeutic-class match |
| Dose limits | Adult / elderly / pediatric max, renal eGFR bands |
| Drug–lab contradictions | e.g. metformin + high creatinine, ARB + hyperkalemia |
| Lab risk flags | Reference-range and critical-value classifier |
| Longitudinal trends | Unit-normalised series, slope, range-crossing |
| Prompt-injection defence | Instruction-like text is stripped at ingest |
- The LLM (Groq, Llama 3.3 70B by default) is an explanation layer only. It never invents a lab value or a diagnosis.
- Common “what does my X mean?” questions are answered from 110 curated lab templates.
- Follow-up pronouns (“is it high?”) resolve through a structured state machine, not chat-memory guessing.
- Self-consistency: the model is called twice; divergence lowers confidence.
- LLM answers are hard-capped at 60% confidence. Deterministic / crisis answers keep their own bounds.
- If Groq is missing or down, answers degrade to templates — they never return empty
{}.
- Dashboard, report library, upload review, chat assistant.
- Longitudinal timeline with drift markers and lab sparklines.
- Auto-built health profile (trends + risk indicators, no inferred diagnoses).
- Click-to-verify citations open the stored page with the snippet highlighted (PDF.js when original bytes are stored).
- Human correction workflow for misread labs and doses, with an audit trail.
- Sync-audit page to resync or clean orphaned / stale clinical facts.
- Clinician-review dashboard for the curated safety tables.
- Account: email verification, password reset, optional TOTP 2FA, session list, data export / account delete.
- PDF medical-history export for appointments.
Open http://localhost:5173/demo — no account, no MongoDB required for the scenario loader. Seven synthetic patients prove the safety features (warfarin bleed, penicillin allergy, renal metformin, hyperkalemia + ARB, mixed-unit glucose, no-hallucination troponin, prompt injection). All demo data is fictional.
High. This is a three-service clinical system with a rules engine, multi-engine OCR, and a hallucination-resistant chat pipeline — not a CRUD wrapper around an LLM.
| Dimension | Scale |
|---|---|
| Application Python (RAG) | ~26,000 lines |
| Node backend | ~4,100 lines |
| React frontend | ~16,000 lines |
| Core clinical modules | 26 files under rag_model/core/ |
| RAG test files | 58 (test_*.py) |
| Curated lab explanations | 110 analytes |
| Specialist domains | 7 (oncology, autoimmune, pregnancy, pediatric, infection, mental health, rare/genetic) |
| Dose-limit table | 23 drugs with renal bands |
| Trend thresholds | 27 analytes |
| Runtime processes | Frontend + Backend + RAG + MongoDB |
Complexity is concentrated where it should be: extraction quality, fail-closed safety rules, and making sure the LLM cannot invent a fact. Auth, storage, and the dashboard are conventional.
What you need to run it locally
- Comfortable with Node 18+, Python 3.10+, and a
.envfile. - Optional: Docker, a Groq key, Tesseract, MongoDB Atlas.
- First Python install pulls EasyOCR / PyTorch and can take several minutes and a few GB of disk.
What you can skip
- Groq key → deterministic / template answers still work.
- OCR extras → digital (text-layer) PDFs still extract.
- Atlas → local in-memory MongoDB in the backend, or
docker composeMongo.
flowchart TB
subgraph Client
Browser["Browser<br/>React + Vite SPA"]
end
subgraph PublicAPI["Backend — Node / Express :4000"]
Auth["JWT auth · rate limits · CORS"]
Users["users / sessions / 2FA"]
Reports["report metadata"]
ChatProxy["chat + extract proxy"]
Demo["/api/demo — synthetic only"]
end
subgraph RAG["RAG service — FastAPI :8000"]
Extract["OCR + parser"]
Rules["Clinical rules engine"]
Chat["Intent · templates · Groq"]
Validate["Response validator"]
Memory["Memory + health profile"]
end
subgraph Data["MongoDB — single store"]
AppDB["users · reports · chats"]
ClinicalDB["labs · meds · allergies<br/>pages · issue flags · memory"]
end
subgraph External["Optional external APIs"]
Groq["Groq LLM"]
RxNav["RxNav / RxNorm"]
FDA["openFDA"]
Medline["MedlinePlus"]
Gmail["Gmail API — mail"]
end
Browser -->|VITE_API_URL| Auth
Auth --> Users
Auth --> Reports
Auth --> ChatProxy
Auth --> Demo
Users --> AppDB
Reports --> AppDB
ChatProxy -->|x-internal-api-key| Extract
ChatProxy --> Chat
Demo --> Extract
Extract --> Rules
Extract --> ClinicalDB
Rules --> ClinicalDB
Chat --> Validate
Chat --> Memory
Memory --> ClinicalDB
Chat -.-> Groq
Rules -.-> RxNav
Rules -.-> FDA
Chat -.-> Medline
Users -.-> Gmail
The browser talks only to the Node backend. The backend proxies extraction and chat to FastAPI with a shared RAG_INTERNAL_API_KEY. Both services write to the same MongoDB. There is no SQLite.
User drops a PDF
│
▼
Frontend POST /api/reports/extract (or async job + poll)
│
▼
Backend auth, size/type checks, multer, rate limit
│ x-internal-api-key
▼
RAG sanitize → OCR / text extract → parse labs/meds/allergies
→ multi-engine cross-check → clinical JSON + citations
│
▼
Frontend review grid (human verify if engines disagree)
│
▼
Backend save report metadata in MongoDB
RAG persist clinical facts, run rules, write issue flags
(optional) generate health profile
Question
│
├─ crisis / self-harm language? → crisis resources, no report analysis
├─ specialist / out-of-scope? → specialist module + “cannot conclude”
├─ safety intent (interaction…)? → deterministic issue store
├─ “what does my X mean?” → lab explanation template
└─ otherwise → bounded Groq prompt
│
├─ self-consistency check
├─ fact validator
├─ confidence cap (LLM ≤ 0.60)
└─ citations from stored facts
If Groq is down → same template / deterministic path. Never an empty answer.
┌──────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
│ Uploaded docs │ │ Curated KB (signed) │ │ RxNav / openFDA / │
│ extracted facts │ │ dose / DDI / labs │ │ MedlinePlus │
└────────┬─────────┘ └──────────┬───────────┘ └──────────┬──────────┘
│ │ │
└──────────────┬───────────┴────────────────────────────┘
▼
Facts database (MongoDB)
│
▼
Rules engine (0% LLM)
│
▼
Template assembler
│
▼
LLM paraphrase only ──► validator ──► UI
(optional, confidence-capped)
The model is a language interface, never a knowledge source. Every factual claim must trace to an uploaded document, a curated table, or an authoritative API.
YGC/
├── medreport_dashboard/ React + Vite frontend
├── Backend/ Node / Express API
├── rag_model/ FastAPI RAG + clinical engine
├── demo_assets/ Synthetic competition dataset
├── deploy/ Oracle VM + production notes
├── docs/ Architecture, deploy, eval
├── scripts/ Dataset, OCR gate, clinician report
├── setup_local.sh Install all three services
├── run_local.sh Start all three services
├── docker-compose.yml mongo + rag + backend + frontend
└── render.yaml Free-tier Render blueprint
| Path | Role |
|---|---|
pages/Landing |
Marketing site, pipeline explainer, FAQ |
pages/auth/* |
Login, signup, verify email, forgot password |
pages/dashboard |
Metrics, recent reports, quick actions |
pages/upload-report |
Async extract, verification grid, save |
pages/my-reports |
Report library |
pages/chat-assistant |
Grounded Q&A with citations |
pages/timeline |
Longitudinal labs + drift markers |
pages/health-profile |
Generated profile |
pages/clinical-review |
Clinician sign-off of curated tables |
pages/sync-audit |
Resync / cleanup stale facts |
pages/demo |
One-click synthetic evaluation |
pages/profile-settings |
Account, 2FA, sessions, export |
components/evidence/* |
PDF.js viewer + snippet highlight |
contexts/AuthContext |
JWT session |
| Path | Role |
|---|---|
index.js |
Express app, health, rate limits, graceful shutdown |
config/env.js |
Bootstraps .env, validates secrets |
routes/userRoutes.js |
Signup, login, verify, reset, profile, 2FA |
routes/reportRoutes.js |
Extract, save, list, delete, audit, corrections |
routes/chatRoutes.js |
Proxies chat to the RAG service |
routes/demoRoutes.js |
Unauthenticated synthetic scenarios |
controllers/userController.js |
Auth + account |
controllers/chatController.js |
RAG proxy, 120s timeout |
models/{user,report,chat}Model.js |
Mongoose schemas |
middleware/auth.js |
JWT |
middleware/security.js |
CORS, headers, rate limit |
utils/emailService.js |
Gmail API OAuth2 |
utils/totp.js |
Two-factor auth |
db/index.js |
Mongo connect; in-memory fallback in local dev |
| Path | Role |
|---|---|
rag_api.py |
FastAPI surface (extract, chat, timeline, audit, demo) |
extraction/parser.py |
Medical report parser |
extraction/ocr.py |
EasyOCR + Tesseract, rotation / deskew |
extraction/cross_validation.py |
Multi-engine arbitration |
extraction/job_queue.py |
Async extract jobs |
extraction/vision_fallback.py |
Optional GPT-4o page fallback |
extraction/handwriting_ocr.py |
Optional TrOCR |
clinical_extraction.py |
Labs / meds / allergies / visits JSON |
clinical_rules.py |
All deterministic safety rules |
clinical_store_mongo.py |
Clinical persistence |
clinical_derivations.py |
Pure views: trends, timeline, continuity |
drug_interaction_service.py |
Curated + RxNav + openFDA |
knowledge_service.py / kb_adapter.py |
Lab knowledge base |
loinc_service.py |
LOINC normalisation |
medlineplus_client.py |
Patient-education quotes |
memory_engine.py |
Chat + report memory (optional Chroma) |
pdf_generator.py |
Downloadable history PDF |
response_validator.py |
Ground generated text in facts |
structured_output.py |
Strict chat JSON schema |
demo_service.py |
Synthetic scenario loader |
chat/intent_detector.py |
Route to rules vs LLM |
chat/response_builders.py |
Templates, Groq, fallbacks |
core/pipeline.py |
End-to-end RAG orchestration |
core/input_safety.py |
Prompt-injection sanitizer |
core/safety_policy.py |
Query gates, “I don’t know” |
core/dose_parser.py / dose_limits.py |
SIG parse + max-dose table |
core/allergy_cross_reactivity.py |
Family / class matrix |
core/risk_classifier.py |
Lab risk bands |
core/unit_converter.py |
mmol/L ↔ mg/dL etc. |
core/canonical.py |
Brand → generic, lab aliases |
core/citations.py / provenance.py |
Click-to-verify locators |
core/explanation_templates.py |
110 lab explanations |
core/followup_state.py |
Deterministic “it / that” resolution |
core/self_consistency.py |
Dual LLM call + compare |
core/groq_client.py |
Lazy Groq client, hard timeouts |
core/patient_graph.py |
Structured patient graph |
core/sync_worker.py |
Background fact reconciliation |
core/clinical_review.py |
Clinician sign-off workflow |
data/*.json |
Curated clinical tables |
| Piece | Version / notes |
|---|---|
| Node.js | 18+ (Express 5, ESM) |
| Python | 3.10+ (FastAPI 0.115) |
| React | 18 + Vite 6 |
| MongoDB | 7 (Compose) or Atlas M0 |
React Router 7, Redux Toolkit, Tailwind CSS 3, Radix Slot, Framer Motion, Recharts, D3, pdf.js, react-markdown, react-hook-form, react-hot-toast, axios, lucide-react, date-fns.
Express, Mongoose, JWT, bcrypt, multer, cors, morgan, winston, dotenv, axios, nodemailer, googleapis (Gmail API), mongodb-memory-server (local fallback).
FastAPI, Uvicorn, Pydantic, Groq SDK, pdfplumber, PyMuPDF, Pillow, EasyOCR, NumPy, httpx, requests, pymongo, pytest. Optional: Tesseract, OpenAI vision, torch + transformers (TrOCR), ChromaDB vector recall (off by default).
| Service | Cost | Used for |
|---|---|---|
| MongoDB | Free Atlas M0 or local | All durable state |
| Groq | Free tier, optional | Natural-language explanations |
| RxNav / RxNorm (NLM) | Free, no key | Drug identity + interactions |
| openFDA | Free; optional key raises rate limit | Drug labels |
| MedlinePlus | Free, no key | Patient-education quotes |
| Gmail API | Free OAuth | Verification / reset email |
| ChromaDB | Local, optional | Derived vector index only |
There is no paid PillChecker / DrugBank key. Paid APIs were replaced by RxNav + openFDA + curated JSON.
| Target | Role |
|---|---|
| Vercel | Static frontend |
| Render | Backend + optional lite RAG (render.yaml) |
| Oracle Always Free VM | Full EasyOCR RAG for demo day |
| Docker Compose | One-command local / VM stack |
| UptimeRobot | Keep the free-tier backend awake |
- Node.js 18+ and npm
- Python 3.10+ and pip
- Git
- Optional: Docker Desktop (Compose path)
- Optional: Tesseract OCR (
tesseract-ocr+eng.traineddata) for the second extraction engine - Optional: a free Groq API key for fluent chat
- Optional: MongoDB Atlas (or any MongoDB URI). Local backend starts an in-memory MongoDB if
MONGO_DB_URIis empty.
git clone https://github.com/Inkithai/YGC.git
cd YGCchmod +x setup_local.sh run_local.sh
./setup_local.shThis will:
- Copy every
*.env.exampleto.envif the target is missing. - Generate a local
ACCESS_TOKEN_SECRETinBackend/.env. npm installinBackend/andmedreport_dashboard/.- Create
rag_model/.venvandpip install -r requirements.txt.
Edit rag_model/.env:
GROQ_API_KEY=gsk_your_key_hereWithout it, extraction and safety rules still run. Chat uses deterministic templates.
./run_local.sh| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend health | http://localhost:4000/health |
| RAG health | http://127.0.0.1:8000/health |
| Live demo | http://localhost:5173/demo |
| RAG OpenAPI | http://127.0.0.1:8000/docs |
Press Ctrl+C to stop all three.
# Terminal 1 — RAG
cd rag_model
source .venv/bin/activate
uvicorn rag_api:app --host 127.0.0.1 --port 8000 --reload
# Terminal 2 — Backend
cd Backend
npm run dev
# Terminal 3 — Frontend
cd medreport_dashboard
npm start -- --host 0.0.0.0 --port 5173There are four env files. Copy the matching example; never commit a real .env.
cp .env.example .env # Docker Compose only
cp Backend/.env.example Backend/.env
cp medreport_dashboard/.env.example medreport_dashboard/.env
cp rag_model/.env.example rag_model/.env./setup_local.sh does this for you.
medreport_dashboard/.env → Vite (browser). Only VITE_* are exposed.
Backend/.env → Express API.
rag_model/.env → FastAPI / OCR / LLM / clinical flags.
.env (repo root) → docker compose env_file. Not used by run_local.sh.
MONGO_DB_URI and RAG_INTERNAL_API_KEY must be identical on the backend and the RAG service whenever you are not on the local in-memory database.
VITE_API_URL=http://localhost:4000
VITE_MAX_UPLOAD_MB=15
VITE_RAW_TEXT_MAX_CHARS=50000
# Off: chat can contain sensitive medical text.
VITE_ENABLE_LOCAL_CHAT_CACHE=false
# Async extract pipeline (queued → ocr → parsing → merging → done)
VITE_USE_ASYNC_EXTRACT=true
VITE_JOB_POLL_INTERVAL_MS=800
VITE_EXTRACT_TIMEOUT_MS=300000| Variable | Required | Meaning |
|---|---|---|
VITE_API_URL |
Yes | Backend origin. In production this is https://<ygc-backend>.onrender.com. |
VITE_MAX_UPLOAD_MB |
No | Client-side size hint (default 15). |
VITE_USE_ASYNC_EXTRACT |
No | Poll jobs instead of one long request. |
VITE_ENABLE_LOCAL_CHAT_CACHE |
No | Keep false for medical data. |
Vite only exposes variables that start with VITE_. Do not put secrets here.
NODE_ENV=development
PORT=4000
TRUST_PROXY=false
# Empty = in-memory MongoDB for local dev.
# Production: Atlas SRV string. URL-encode @ : / ? # % in the password.
MONGO_DB_URI=
MONGO_DB_NAME=medreport_local
MONGO_SERVER_SELECTION_TIMEOUT_MS=10000
MONGO_AUTO_RETRY=true
MONGO_RETRY_MAX_ATTEMPTS=10
# openssl rand -base64 48
ACCESS_TOKEN_SECRET=local-dev-secret-change-me-please-32-characters-minimum
REQUIRE_EMAIL_VERIFICATION=false
FRONTEND_URL=http://localhost:5173
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
RAG_API_URL=http://127.0.0.1:8000
RAG_INTERNAL_API_KEY=
JSON_BODY_LIMIT=1mb
MAX_UPLOAD_MB=15
STORE_RAW_TEXT=true
RAW_TEXT_MAX_CHARS=50000
AUTH_RATE_LIMIT_MAX=25
CHAT_RATE_LIMIT_MAX=20
UPLOAD_RATE_LIMIT_MAX=10
# Optional — Gmail API OAuth2. Missing config never crashes;
# verification links are also printed to backend logs.
EMAIL_USER=
EMAIL_PASSWORD=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REFRESH_TOKEN=| Variable | Required | Meaning |
|---|---|---|
ACCESS_TOKEN_SECRET |
Yes (≥ 32 chars) | JWT signing key. setup_local.sh generates one. |
MONGO_DB_URI |
Prod yes | Empty locally → in-memory Mongo. Production must be a real URI. |
RAG_API_URL |
Yes | FastAPI base URL. |
RAG_INTERNAL_API_KEY |
Prod yes | Shared with rag_model/.env. Empty = RAG endpoints are open. |
CORS_ORIGINS / FRONTEND_URL |
Yes | Exact browser origin, no trailing slash, never *. |
TRUST_PROXY |
Behind Render | Must be the string true or rate-limit breaks on X-Forwarded-For. |
REQUIRE_EMAIL_VERIFICATION |
No | false for local / judge day. |
DEMO_MODE_ENABLED |
No | Default on. Set false wherever real patient data lives. |
Verify Mongo without booting the API:
cd Backend && npm run check:dbCommon Atlas copy-paste bugs: & instead of &, leftover <password>, unencoded @ in the password (@ → %40).
# Empty = deterministic / template chat. Extraction still works.
GROQ_API_KEY=
GROQ_MODEL=llama-3.3-70b-versatile
GROQ_REQUEST_TIMEOUT=20
GROQ_MODEL_DISCOVERY_TIMEOUT=15
GROQ_WARMUP_TIMEOUT=15
ENABLE_SELF_CONSISTENCY=true
ENABLE_TEMPLATE_EXPLANATIONS=true
ENABLE_FOLLOWUP_STATE=true
ENABLE_MULTI_ENGINE_OCR=true
LLM_CONFIDENCE_CAP=0.60
CLINICAL_DATA_DISCLAIMER_MODE=governance
RAG_CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
MAX_UPLOAD_MB=15
STORE_RAW_TEXT=true
RAW_TEXT_MAX_CHARS=50000
ENABLE_OCR=true
ENABLE_VECTOR_MEMORY=false
AUTO_GENERATE_HEALTH_PROFILE=false
RAG_INTERNAL_API_KEY=
# MUST match Backend/.env when using a real cluster.
MONGO_DB_URI=mongodb://127.0.0.1:27017/medreport
MONGO_DB_NAME=medreport
FDA_API_KEY=
OPENFDA_API_KEY=
ENABLE_RXNAV=true
ENABLE_OPENFDA=true
# Optional heavier engines (need extra pip packages).
VISION_FALLBACK_ENABLED=false
VISION_FALLBACK_PROVIDER=openai
VISION_FALLBACK_API_KEY=
HANDWRITING_ENABLED=false| Variable | Default | Meaning |
|---|---|---|
GROQ_API_KEY |
empty | Enables fluent LLM chat. |
GROQ_MODEL |
llama-3.3-70b-versatile |
Use llama-3.1-8b-instant for cheapest / fastest. |
RAG_INTERNAL_API_KEY |
empty | Same value as Backend. Middleware only enforces when non-empty. |
MONGO_DB_URI |
local | Same cluster as the backend. |
ENABLE_OCR |
true |
false on lite deploys (text-layer PDFs only). |
ENABLE_MULTI_ENGINE_OCR |
true |
Second engine (Tesseract). Single-engine results are marked uncorroborated. |
ENABLE_TEMPLATE_EXPLANATIONS |
true |
Deterministic “what does my X mean?”. |
ENABLE_SELF_CONSISTENCY |
true |
Two LLM calls. Flip false if Groq 429s on demo day. |
ENABLE_FOLLOWUP_STATE |
true |
Structured pronoun resolution. |
LLM_CONFIDENCE_CAP |
0.60 |
Displayed ceiling for generative answers. |
ENABLE_VECTOR_MEMORY |
false |
Optional Chroma index. Not a source of truth. |
AUTO_GENERATE_HEALTH_PROFILE |
false locally |
true in production if you want a profile after every save. |
ENABLE_RXNAV / ENABLE_OPENFDA |
true |
Free drug APIs. Curated tables still run if they are down. |
Used only by docker-compose.yml.
GROQ_API_KEY=
RAG_INTERNAL_API_KEY=
DATABASE_URL=mongodb://mongo:27017/medreport
MONGO_DB_NAME=medreport
JWT_SECRET=change-this-to-a-unique-secret-of-at-least-32-characters
ACCESS_TOKEN_SECRET=change-this-to-a-unique-secret-of-at-least-32-characters
MAX_UPLOAD_MB=15
STORE_RAW_TEXT=true
ENABLE_OCR=true
ENABLE_VECTOR_MEMORY=false
AUTO_GENERATE_HEALTH_PROFILE=true
FDA_API_KEY=
ENABLE_RXNAV=true
ENABLE_OPENFDA=true
STORE_ORIGINAL_FILES=true
SYNC_WORKER_ENABLED=true
SYNC_WORKER_INTERVAL_SECONDS=60
VISION_FALLBACK_ENABLED=false
HANDWRITING_ENABLED=falseJWT_SECRET in this file is mapped to ACCESS_TOKEN_SECRET inside the backend container.
Generate production secrets:
openssl rand -base64 48 # ACCESS_TOKEN_SECRET
openssl rand -base64 48 # RAG_INTERNAL_API_KEY (paste into BOTH services)| Situation | What you must set |
|---|---|
| Browse the demo only | Nothing. ./setup_local.sh && ./run_local.sh |
| Fluent chat locally | GROQ_API_KEY in rag_model/.env |
| Shared local Mongo | Same MONGO_DB_URI in Backend + RAG |
| Production | MONGO_DB_URI, ACCESS_TOKEN_SECRET, RAG_INTERNAL_API_KEY (both services), RAG_API_URL, CORS_ORIGINS, FRONTEND_URL, VITE_API_URL, TRUST_PROXY=true |
| Email verification | Gmail OAuth quartet on the backend |
| Scanned PDFs on a small host | ENABLE_OCR=true and Tesseract in the image; EasyOCR needs more RAM |
A longer production walkthrough lives in docs/PRODUCTION_ENV_SETUP.md.
cp .env.example .env
# set JWT_SECRET / ACCESS_TOKEN_SECRET (and GROQ_API_KEY if you want LLM chat)
docker compose up --build| Service | Host port |
|---|---|
| Frontend (nginx) | http://localhost:3000 |
| Backend | http://localhost:5000 |
| RAG | http://localhost:8000 |
| MongoDB 7 | internal mongo:27017 |
Compose waits for Mongo and RAG health checks before starting the backend.
./run_local.sh
# open http://localhost:5173/demoOr load every scenario from the CLI:
curl -X POST http://localhost:4000/api/demo/load-all | python3 -m json.tool| Scenario | What it proves |
|---|---|
| Warfarin + aspirin + ibuprofen | Cross-drug interaction |
| Penicillin allergy → amoxicillin | Allergy + cross-reactivity |
| Creatinine 4.5 + metformin | Renal contraindication |
| K 5.8 + ARB + KCl | Drug–lab contradiction |
| Mixed-unit glucose | Unit normalisation before trends |
| Troponin absent | Says “not found” instead of inventing it |
Embedded SYSTEM INSTRUCTION |
Prompt-injection defence |
Demo patients are namespaced demo__* and cannot touch real records. Disable wherever real data is stored:
DEMO_MODE_ENABLED=false# RAG / clinical suite
cd rag_model && source .venv/bin/activate
pytest -q
# Backend syntax + unit tests
cd Backend && npm test
# Frontend
cd medreport_dashboard && npm testPersistence tests use a real MongoDB if TEST_MONGO_URI / MONGO_DB_URI is reachable, otherwise mongomock. Live Groq hallucination tests are skipped until GROQ_API_KEY is set.
Free-tier path (verified in-repo):
- MongoDB Atlas M0 — create a cluster, DB user, allow
0.0.0.0/0, copy the SRV URI. - Render Blueprint —
render.yamlcreatesygc-backendandygc-rag. - Set
MONGO_DB_URI,RAG_INTERNAL_API_KEY,GROQ_API_KEYon both Render services. - On
ygc-backendsetRAG_API_URL=https://<ygc-rag>.onrender.comandCORS_ORIGINS/FRONTEND_URLto the Vercel origin. - Vercel — import
medreport_dashboard/, setVITE_API_URL=https://<ygc-backend>.onrender.com. - Confirm
GET https://<ygc-rag>/patient_summary/testreturns 401/403 (key is actually enforced). - Optional: Gmail OAuth for mail; UptimeRobot on the backend
/healthevery 5 minutes.
Render’s free workspace is 750 hours/month. Keep only the backend awake; let RAG cold-start (30–60 s) or move it to the Oracle Always Free VM (deploy/oracle/README.md).
Step-by-step: docs/PRODUCTION_ENV_SETUP.md · docs/deploy-free-tier.md.
- Not a medical device. Findings are informational. Confirm medication and lab decisions with a clinician or pharmacist.
- Curated tables (
dose_limits,specialist_modules,trend_thresholds, …) are taggedpending_clinician_reviewuntil a licensed clinician signs them off. Seedocs/CLINICIAN_REVIEW.md. demo_assets/is synthetic only. Real reports belong inprivate_demo_reports/(gitignored). Never commit PHI.RAG_INTERNAL_API_KEYmust be set in any environment that holds real patient data.- Set
DEMO_MODE_ENABLED=falsenext to real records. - Account deletion removes the user’s Mongo reports and RAG clinical / memory data.
| Doc | Topic |
|---|---|
docs/MONGODB_ONLY.md |
Collections, why there is no SQLite |
docs/ZERO_HALLUCINATION_ARCHITECTURE.md |
Design blueprint for grounded answers |
docs/DEGRADATION_PATHS.md |
Behaviour when Groq / Mongo / RxNav die |
docs/DEMO_MODE.md |
Judge harness |
docs/PRODUCTION_ENV_SETUP.md |
Production env, step by step |
docs/deploy-free-tier.md |
Vercel + Render + Atlas |
docs/CLINICIAN_REVIEW.md |
Signing off curated tables |
deploy/oracle/README.md |
Full-OCR RAG on Oracle Always Free |
private_demo_reports/README.md |
Testing with private reports |
YGC Health — understand the report, verify the source, decide with a clinician.