RxCheck is a production-grade drug interaction checker with PostgreSQL-backed clinical rules, automated FDA data ingestion, multi-provider LLM router, and intelligent prescription image analysis. Built for healthcare professionals and patients who need reliable medication safety screening with complete privacy.
Python Flask PostgreSQL License
Home Screen
Clean interface with dual input modes
Drug Input
Manual entry with autocomplete
Prescription Upload
Gemini Vision-powered OCR
Interaction Results
Risk categorization dashboard
Generic Alternatives
Cost savings with PMBJP options
Emergency Warning
High-risk interaction alerts
- Server-side interaction rules evaluation (never exposed to client)
- PostgreSQL connection pooling with thread-safe operations
- High-precision trigram search prevents false-positive matches
- RxNorm API proxy prevents direct client exposure
- PostgreSQL-backed rules engine with curated interaction patterns
- openFDA SPL data ingestion with automated ETL pipeline
- Multi-provider LLM router (Groq, Cerebras, Gemini, OpenRouter, HuggingFace) with automatic failover
- Trigram similarity matching (threshold 0.85) eliminates dangerous substring matches
- Levenshtein fuzzy matching for Indian brand name normalization
- RxNorm integration for standardized drug nomenclature
- Client-side image upload with Gemini 2.5 Flash vision analysis
- Automatic image preprocessing and clinical OCR
- Indian prescription shorthand extraction (OD, BD, TDS, etc.)
- Multi-line prescription text grouping for broken OCR output
- Support for single-letter vitamins (A, C, D, E, K) and numeric compounds (Omega 3, D3)
- 34 common Indian brand-to-generic mappings
- PMBJP (Jan Aushadhi) generic alternatives highlighted
- Approximate MRP pricing with regional disclaimers
- Automatic prescription matching and savings calculation
- Medical disclaimer modal with localStorage persistence
- Emergency escalation warnings for high-risk interactions
- WhatsApp sharing and clipboard export
- Fully responsive mobile-first design
- Dual input modes: manual entry and prescription text parsing
app.py
βββ GET / β Render SPA shell
βββ GET /api/rxnorm/<path> β Transparent RxNorm proxy
βββ GET /api/local-data β Serve safe catalog data (brand_hints.json + price_catalog.json)
βββ POST /api/normalize-drug β Server-side brandβgeneric matching
βββ POST /api/check-interactions β PostgreSQL interaction query
βββ POST /api/extract-vision β Gemini Vision image analysis endpoint
build_catgory.py
βββ INPUT β updated_indian_medicine_data.csv
βββ CLEAN β Strip dosage forms, normalize salts
βββ EXTRACT β Brand families (e.g., "Ascoril LS" β "ascoril ls" + "ascoril")
βββ OUTPUT β brand_hints.json (brandβgeneric mappings)
βββ OUTPUT β price_catalog.json (genericβpricing + alternatives)
etl.py
βββ SEED β Bootstrap from hardcoded INTERACTION_RULES
βββ CHECKPOINT β Resume from last successful batch
βββ FETCH β Download FDA drug labels (openFDA API)
βββ PARSE β Extract drug names and interaction text
βββ LLM β Structure free-text with multi-provider router
βββ LOAD β Upsert drugs and interactions to PostgreSQL
βββ COMMIT β Atomic checkpoint update
llm_router.py
βββ Groq (llama-3.3-70b-versatile) β Primary
βββ Cerebras (gpt-oss-120b) β Fallback 1
βββ Gemini (gemini-2.5-flash) β Fallback 2
βββ OpenRouter (free tier) β Fallback 3
βββ HuggingFace (Meta-Llama-3-8B) β Fallback 4
-- High-precision drug resolution CREATE TABLE drugs ( id BIGSERIAL PRIMARY KEY, generic_name CITEXT UNIQUE NOT NULL, brand_names JSONB DEFAULT '[]'::jsonb ); CREATE INDEX idx_drugs_name_trgm ON drugs USING gin (generic_name gin_trgm_ops); -- Interaction rules storage CREATE TABLE interactions ( drug_a_id BIGINT REFERENCES drugs(id), drug_b_id BIGINT REFERENCES drugs(id), severity TEXT CHECK (severity IN ('high', 'medium', 'low')), description TEXT, action_text TEXT, source TEXT, PRIMARY KEY (drug_a_id, drug_b_id), CHECK (drug_a_id < drug_b_id) ); -- ETL checkpoint for fault-tolerance CREATE TABLE etl_sync_state ( id INT PRIMARY KEY DEFAULT 1 CHECK (id = 1), last_successful_skip INT DEFAULT 0, updated_at TIMESTAMPTZ DEFAULT NOW() );
- No framework dependencies β pure JavaScript
- Gemini Vision API for prescription image analysis
- RxNorm proxy calls β all API traffic routed through Flask
- LocalStorage for disclaimer acceptance tracking
- Dual input modes β manual entry and prescription text parsing
graph TB
subgraph Client["π₯οΈ Client Browser"]
UI["SPA Interface<br/>(Vanilla JS)"]
IMG["Image Upload<br/>Module"]
end
subgraph Flask["βοΈ Flask Backend (app.py)"]
API["REST API<br/>Endpoints"]
NORM["Drug Normalizer<br/>(Levenshtein)"]
VISION["Vision Proxy<br/>(Gemini)"]
end
subgraph Database["ποΈ PostgreSQL"]
DRUGS[("drugs<br/>table")]
INTER[("interactions<br/>table")]
ETL_STATE[("etl_sync_state<br/>table")]
end
subgraph External["π External APIs"]
RXNORM["RxNorm API<br/>(NLM)"]
GEMINI["Gemini Vision<br/>(Google)"]
FDA["openFDA API<br/>(Drug Labels)"]
end
subgraph ETL["π ETL Pipeline (etl.py)"]
FETCH["FDA Fetcher"]
LLM["LLM Router<br/>(Multi-Provider)"]
LOADER["DB Loader"]
end
UI -->|"1. Drug Names"| API
IMG -->|"2. Prescription Image"| VISION
API -->|"3. Normalize"| NORM
API -->|"4. Query Interactions"| INTER
NORM -->|"5. Fuzzy Match"| DRUGS
VISION -->|"6. OCR Request"| GEMINI
API -->|"7. Proxy Request"| RXNORM
FETCH -->|"8. Fetch Labels"| FDA
FETCH -->|"9. Extract Text"| LLM
LLM -->|"10. Structured Data"| LOADER
LOADER -->|"11. Upsert"| DRUGS
LOADER -->|"12. Upsert"| INTER
LOADER -->|"13. Checkpoint"| ETL_STATE
style Client fill:#e1f5ff
style Flask fill:#fff4e1
style Database fill:#e8f5e9
style External fill:#fce4ec
style ETL fill:#f3e5f5
sequenceDiagram
participant User
participant Browser
participant Flask
participant RxNorm
participant PostgreSQL
User->>Browser: Enter drug names
Browser->>Flask: POST /api/normalize-drug
Flask->>Flask: Levenshtein fuzzy match<br/>(Indian brands)
Flask-->>Browser: Normalized generic names
Browser->>Flask: GET /api/rxnorm/...
Flask->>RxNorm: Proxy request
RxNorm-->>Flask: RxCUI + ingredients
Flask-->>Browser: Drug metadata
Browser->>Flask: POST /api/check-interactions
Flask->>PostgreSQL: SELECT with trigram search<br/>(threshold 0.85)
PostgreSQL-->>Flask: Matching drug IDs
Flask->>PostgreSQL: Query interactions table<br/>(Cartesian product)
PostgreSQL-->>Flask: Interaction rules
Flask-->>Browser: Risk categorization
Browser->>User: Display results dashboard
flowchart TD
START(["Start ETL"]) --> SEED{"Seed Mode?"}
SEED -->|Yes| HARDCODE["Load INTERACTION_RULES<br/>from data.py"]
SEED -->|No| CHECKPOINT["Read etl_sync_state<br/>last_successful_skip"]
HARDCODE --> UPSERT_SEED["Upsert seed data<br/>to PostgreSQL"]
UPSERT_SEED --> END(["End"])
CHECKPOINT --> FETCH["Fetch batch from openFDA<br/>(skip + limit)"]
FETCH --> PARSE["Extract drug names<br/>and interaction text"]
PARSE --> LLM_ROUTE["LLM Router<br/>(Multi-Provider)"]
LLM_ROUTE --> GROQ{"Groq API"}
GROQ -->|Success| STRUCTURE["Structured JSON"]
GROQ -->|Fail| CEREBRAS{"Cerebras API"}
CEREBRAS -->|Success| STRUCTURE
CEREBRAS -->|Fail| GEMINI{"Gemini API"}
GEMINI -->|Success| STRUCTURE
GEMINI -->|Fail| OPENROUTER{"OpenRouter API"}
OPENROUTER -->|Success| STRUCTURE
OPENROUTER -->|Fail| HF{"HuggingFace API"}
HF -->|Success| STRUCTURE
HF -->|Fail| ERROR["Log error & skip batch"]
STRUCTURE --> UPSERT["Upsert drugs & interactions<br/>(idempotent)"]
UPSERT --> UPDATE_CHECKPOINT["Update etl_sync_state<br/>(atomic transaction)"]
UPDATE_CHECKPOINT --> MORE{"More batches?"}
MORE -->|Yes| FETCH
MORE -->|No| END
ERROR --> END
style START fill:#4caf50,color:#fff
style END fill:#f44336,color:#fff
style STRUCTURE fill:#2196f3,color:#fff
style ERROR fill:#ff9800,color:#fff
sequenceDiagram
participant User
participant Browser
participant Flask
participant Gemini
User->>Browser: Upload prescription image
Browser->>Browser: Client-side preprocessing<br/>(resize, compress)
Browser->>Flask: POST /api/extract-vision<br/>(multipart/form-data)
Flask->>Gemini: Vision API request<br/>(Gemini 2.5 Flash)
Gemini->>Gemini: Clinical OCR<br/>(Indian shorthand aware)
Gemini-->>Flask: Extracted text
Flask-->>Browser: Drug names + dosages
Browser->>Browser: Parse multi-line text<br/>(group broken OCR)
Browser->>Browser: Extract vitamins<br/>(A, C, D, E, K)
Browser->>Browser: Normalize compounds<br/>(Omega 3, D3)
Browser->>User: Populate drug input fields
erDiagram
DRUGS ||--o{ INTERACTIONS : "drug_a_id"
DRUGS ||--o{ INTERACTIONS : "drug_b_id"
DRUGS {
bigserial id PK
citext generic_name UK
jsonb brand_names
}
INTERACTIONS {
bigint drug_a_id FK
bigint drug_b_id FK
text severity
text description
text action_text
text source
}
ETL_SYNC_STATE {
int id PK
int last_successful_skip
timestamptz updated_at
}
flowchart LR
Start([Start]) --> Groq["Groq<br/>llama-3.3-70b-versatile<br/>(Primary)"]
Groq -->|200 OK| Success([Success])
Groq -->|429/Quota| Cerebras["Cerebras<br/>gpt-oss-120b<br/>(Fallback 1)"]
Cerebras -->|200 OK| Success
Cerebras -->|429/Quota| Gemini["Gemini<br/>gemini-2.5-flash<br/>(Fallback 2)"]
Gemini -->|200 OK| Success
Gemini -->|429/Quota| OpenRouter["OpenRouter<br/>free tier<br/>(Fallback 3)"]
OpenRouter -->|200 OK| Success
OpenRouter -->|429/Quota| HuggingFace["HuggingFace<br/>Meta-Llama-3-8B<br/>(Fallback 4)"]
HuggingFace -->|200 OK| Success
HuggingFace -->|All exhausted| Failure([Failure])
style Start fill:#4caf50,color:#fff
style Success fill:#2196f3,color:#fff
style Failure fill:#f44336,color:#fff
style Groq fill:#e3f2fd
style Cerebras fill:#f3e5f5
style Gemini fill:#fff3e0
style OpenRouter fill:#e8f5e9
style HuggingFace fill:#fce4ec
graph LR
subgraph Public["π Public Zone"]
USER["User Browser"]
end
subgraph DMZ["π‘οΈ Application Zone"]
FLASK["Flask API<br/>(Port 5000)"]
PROXY["RxNorm Proxy<br/>(No API key exposure)"]
end
subgraph Secure["π Secure Zone"]
DB[("PostgreSQL<br/>(Connection Pool)")]
RULES["Interaction Rules<br/>(Server-side only)"]
end
subgraph External["βοΈ External Services"]
RXNORM_API["RxNorm API"]
GEMINI_API["Gemini API"]
FDA_API["openFDA API"]
end
USER -->|"HTTPS"| FLASK
FLASK -->|"Proxy"| PROXY
PROXY -->|"API Key Hidden"| RXNORM_API
FLASK -->|"Encrypted"| DB
FLASK -->|"Load Rules"| RULES
FLASK -->|"API Key Hidden"| GEMINI_API
FLASK -->|"Public API"| FDA_API
USER -.->|"β Never Exposed"| RULES
USER -.->|"β Never Exposed"| DB
style Public fill:#ffebee
style DMZ fill:#fff3e0
style Secure fill:#e8f5e9
style External fill:#e3f2fd
- Python 3.8+
- PostgreSQL 12+ with
pg_trgmandcitextextensions - pip package manager
- At least one LLM API key (Groq, Cerebras, Gemini, OpenRouter, or HuggingFace)
- Google Gemini API key (for prescription image analysis)
-
Clone the repository
git clone https://github.com/indiser/Rxcheck.git cd Rxcheck -
Create virtual environment
python -m venv env source env/bin/activate # Windows: env\Scripts\activate
-
Install dependencies
pip install -r requirements.txt
Dependencies include:
- Flask β Web framework
- psycopg2-binary β PostgreSQL adapter
- python-dotenv β Environment variables
- requests β HTTP client
- groq β Groq LLM API
- google-genai β Gemini API (Vision + LLM)
- openai β OpenAI-compatible clients
- huggingface_hub β HuggingFace API
- pillow β Image processing
- gunicorn, uvicorn β Production servers
-
Set up PostgreSQL database
createdb rxcheck psql rxcheck -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" psql rxcheck -c "CREATE EXTENSION IF NOT EXISTS citext;"
-
Configure environment variables
# Create .env file cat > .env << EOF DATABASE_URL=postgresql://postgres:password@localhost:5432/rxcheck DB_POOL_MIN=1 DB_POOL_MAX=10 # At least one LLM API key required for ETL GROQ_API_KEY=your_groq_key_here CEREBRAS_API_KEY=your_cerebras_key_here GEMINI_API_KEY=your_gemini_key_here OPENROUTER_API_KEY=your_openrouter_key_here HUGGING_FACE_API_KEY=your_hf_key_here EOF
-
Build catalog from CSV dataset (optional)
# Generates brand_hints.json and price_catalog.json from CSV python build_catgory.pyNote: Pre-built JSON files are included in the repository. Only run this if you have an updated CSV dataset.
-
Initialize database schema
python etl.py --seed-only
-
Run the ETL pipeline (optional)
# Fetch 100 FDA records and populate database python etl.py --limit 100 # The pipeline automatically resumes from checkpoint on subsequent runs python etl.py --limit 100 # Fetches next 100 records
-
Start the Flask application
python app.py
-
Open in browser
http://localhost:5000
rxcheck/
βββ app.py # Flask REST API with connection pooling
βββ data.py # Business data (interaction rules, savings lookup)
βββ etl.py # openFDA ingestion pipeline with checkpointing
βββ llm_router.py # Multi-provider LLM failover router + Gemini Vision
βββ schema.sql # PostgreSQL DDL (drugs, interactions, etl_sync_state)
βββ build_catgory.py # CSVβJSON catalog builder (brand hints + pricing)
βββ brand_hints.json # Auto-generated brandβgeneric mappings
βββ price_catalog.json # Auto-generated generic drug pricing catalog
βββ updated_indian_medicine_data.csv # Source dataset for Indian medicines
βββ .env # Environment configuration (not in repo)
βββ .gitignore # Git ignore patterns
βββ requirements.txt # Python dependencies
βββ images/ # Website screenshots for README
β βββ home-screen.png
β βββ drug-input.png
β βββ prescription-upload.png
β βββ interaction-results.png
β βββ generic-alternatives.png
β βββ emergency-warning.png
βββ templates/
β βββ index.html # SPA shell with disclaimer modal
βββ static/
β βββ app.js # Client-side logic (image analysis, UI, API calls)
β βββ styles.css # Design system with emergency warning styles
βββ README.md # This file
The build_catgory.py script processes a CSV dataset of Indian medicines to generate optimized JSON catalogs for fast server-side lookups.
File: updated_indian_medicine_data.csv
Columns used:
nameβ Brand name (e.g., "Ascoril LS Syrup 100ml")short_composition1β Primary active ingredientshort_composition2β Secondary active ingredient (if combination drug)priceβ MRP in rupeespack_size_labelβ Packaging description
-
Salt Normalization
- Combines composition1 + composition2
- Strips dosage information in parentheses
- Converts to lowercase for case-insensitive matching
-
Brand Family Extraction
- Removes dosage forms (tablet, syrup, injection, etc.)
- Strips numeric dosages (500mg, 10ml, etc.)
- Preserves letter suffixes (LS, AT, CV, SR, etc.)
- Example: "Ascoril LS Syrup 100ml" β "ascoril ls"
-
Dual-Key Strategy
- Full brand family:
"ascoril ls" β "ambroxol + levosalbutamol + guaifenesin" - Base word:
"ascoril" β "ambroxol + levosalbutamol + guaifenesin" - Prevents false-positive overwrites (base word only saved if not already mapped)
- Full brand family:
brand_hints.json
{
"ascoril ls": "ambroxol + levosalbutamol + guaifenesin",
"ascoril": "ambroxol + levosalbutamol + guaifenesin",
"glycomet": "metformin",
"glycomet gp": "metformin + glimepiride"
}price_catalog.json
{
"metformin": {
"display": "Metformin",
"alternatives": ["Glycomet 500mg Tablet"],
"range": "Rs. 18 (strip of 10 tablets)"
}
}# Generate catalogs from CSV python build_catgory.py # Output: # Success! Built high-precision catalog. # - brand_hints.json (thousands of brandβgeneric mappings) # - price_catalog.json (generic drug pricing database)
Note: Pre-built JSON files are included in the repository. Only regenerate if you have an updated CSV dataset.
User Input β Server-side Levenshtein Matching β RxNorm API Lookup β PostgreSQL Trigram Search
Two-tier precision strategy:
- Tier 1: Exact
CITEXTmatch (zero false positives) - Tier 2: Trigram similarity with threshold 0.85 (blocks "folic acid" β "valproic acid")
Resolved Drugs β PostgreSQL Query β Interaction Rules β Risk Categorization
Server-side evaluation:
- All interaction rules stored in
data.py(never sent to client) - PostgreSQL query with canonical drug pair ordering
- Cartesian product resolution for multi-ingredient drugs
Checkpoint Read β FDA API Fetch β Text Extraction β LLM Parsing β PostgreSQL Upsert β Checkpoint Write
Fault-tolerance guarantees:
- Atomic checkpoint updates (same transaction as data writes)
- Idempotent upserts (replaying batches creates no duplicates)
- Multi-provider LLM failover (automatic quota/rate-limit handling)
- Crash recovery (resumes from last successful batch)
Photo Upload β Gemini Vision API β Text Extraction β Drug Name Parsing
Processing pipeline:
- Client-side image upload to
/api/extract-vision - Gemini 2.5 Flash vision model for clinical OCR
- Automatic drug name extraction with confidence scoring
- Indian prescription shorthand recognition
- Risk Grid: Safe / Caution / Dangerous counters
- Interaction Cards: Severity badges with clinical guidance
- Generic Alternatives: RxNorm-normalized names with Indian pricing
- Save Money Table: Brand-to-generic comparison with PMBJP options
- Flask 3.1+ β Lightweight WSGI web framework
- psycopg2-binary β PostgreSQL adapter with connection pooling
- python-dotenv β Environment variable management
- requests β HTTP library for RxNorm proxy and FDA API
- gunicorn β Production WSGI server
- uvicorn β ASGI server for async workloads
- Groq β Primary LLM provider (llama-3.3-70b-versatile)
- Cerebras β Fallback LLM (gpt-oss-120b)
- Google Gemini (google-genai) β Fallback LLM (gemini-2.5-flash) + Vision API
- OpenRouter (openai) β Free tier fallback
- HuggingFace (huggingface_hub) β Final fallback (Meta-Llama-3-8B)
- Pillow β Image preprocessing for prescription uploads
- PostgreSQL 12+ β Relational database with advanced text search
- pg_trgm extension β Trigram similarity matching
- citext extension β Case-insensitive text type
- Vanilla JavaScript β No framework overhead
- Gemini Vision API β Cloud-based prescription image analysis
- Custom CSS β Design system with CSS variables
- Google Fonts β Syne, IBM Plex Mono, IBM Plex Sans
- pandas β CSV data processing (build_catgory.py)
- JSON β Catalog storage format (brand_hints.json, price_catalog.json)
- CSV β Indian medicine dataset (updated_indian_medicine_data.csv)
Transparent proxy to https://rxnav.nlm.nih.gov/REST/<path>.
Example:
curl http://localhost:5000/api/rxnorm/rxcui.json?name=metforminServe safe catalog data (brand hints, price catalog, savings lookup).
Response:
{
"brand_hints": {"glycomet": "metformin", ...},
"price_catalog": {"metformin": {...}, ...},
"brand_savings_lookup": [...]
}Server-side brandβgeneric normalization with Levenshtein fuzzy matching.
Request:
{
"name": "Glycomet 500"
}Response:
{
"normalized": "metformin",
"catalog": {
"display": "Metformin",
"alternatives": ["Metformin immediate-release", "Metformin sustained-release"],
"range": "Rs. 18-110 per strip of 10 tablets"
}
}Query PostgreSQL for interactions between resolved drugs.
Request:
{
"resolved": [
{
"rawName": "Warfarin",
"genericName": "warfarin",
"ingredients": ["warfarin"]
},
{
"rawName": "Aspirin",
"genericName": "aspirin",
"ingredients": ["aspirin"]
}
]
}Response:
{
"findings": [
{
"level": "high",
"pair": ["Warfarin", "Aspirin"],
"message": "Higher bleeding risk when warfarin is combined with antiplatelet drugs or NSAIDs.",
"action": "Avoid casual co-use and check INR/bleeding plan with the prescriber.",
"source": "Internal Clinical Rules Engine"
}
]
}Send a prescription image to Gemini Vision for clinical OCR.
Request:
multipart/form-data
- image: <binary image file>
Response:
{
"text": "Ecosprin 75mg\nCombiflam\nMetformin 500mg SR"
}CREATE TABLE drugs ( id BIGSERIAL PRIMARY KEY, generic_name CITEXT UNIQUE NOT NULL, brand_names JSONB DEFAULT '[]'::jsonb ); CREATE INDEX idx_drugs_name_trgm ON drugs USING gin (generic_name gin_trgm_ops); CREATE INDEX idx_drugs_brands ON drugs USING gin (brand_names jsonb_path_ops);
CREATE TABLE interactions ( drug_a_id BIGINT NOT NULL REFERENCES drugs(id) ON DELETE CASCADE, drug_b_id BIGINT NOT NULL REFERENCES drugs(id) ON DELETE CASCADE, severity TEXT NOT NULL CHECK (severity IN ('high', 'medium', 'low')), description TEXT NOT NULL DEFAULT '', action_text TEXT NOT NULL DEFAULT '', source TEXT NOT NULL DEFAULT 'openFDA SPL', PRIMARY KEY (drug_a_id, drug_b_id), CHECK (drug_a_id < drug_b_id) ); CREATE INDEX idx_interactions_drug_a ON interactions (drug_a_id); CREATE INDEX idx_interactions_drug_b ON interactions (drug_b_id);
CREATE TABLE etl_sync_state ( id INT PRIMARY KEY DEFAULT 1 CHECK (id = 1), last_successful_skip INT NOT NULL DEFAULT 0, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() );
# Seed database from hardcoded rules only python etl.py --seed-only # Fetch 100 FDA records (resumes from checkpoint automatically) python etl.py --limit 100 # Dry-run (fetch + parse, no database writes) python etl.py --dry-run --limit 10 # Reset checkpoint to 0 (use with caution!) python etl.py --reset-checkpoint # Manual skip override (only if checkpoint is 0) python etl.py --skip 500 --limit 100
- SEED β Bootstrap from
data.pyINTERACTION_RULES - CHECKPOINT β Read
etl_sync_state.last_successful_skip - FETCH β Download batch from openFDA API
- PARSE β Extract drug names and interaction text
- LLM β Structure free-text with multi-provider router
- LOAD β Upsert drugs and interactions to PostgreSQL
- COMMIT β Atomic checkpoint update
- Idempotent upserts β Replaying batches creates no duplicates
- Atomic checkpoints β Checkpoint and data committed in same transaction
- Crash recovery β Resumes from last successful batch
- Multi-provider failover β Automatic LLM quota/rate-limit handling
The ETL pipeline uses a multi-provider LLM router for fault-tolerance. Configure API keys in .env:
# Primary (fastest, best for JSON extraction) GROQ_API_KEY=your_groq_key_here # Fallbacks (tried in order when primary fails) CEREBRAS_API_KEY=your_cerebras_key_here GEMINI_API_KEY=your_gemini_key_here OPENROUTER_API_KEY=your_openrouter_key_here HUGGING_FACE_API_KEY=your_hf_key_here
Provider Selection Logic:
- Groq (llama-3.3-70b-versatile) β Primary
- Cerebras (gpt-oss-120b) β Fallback on quota/rate-limit
- Gemini (gemini-2.5-flash) β Fallback on Cerebras failure
- OpenRouter (free tier) β Fallback on Gemini failure
- HuggingFace (Meta-Llama-3-8B) β Final fallback
Automatic Failover Triggers:
- 429 (rate limit exceeded)
- Quota exhausted
- Capacity/overload errors
- Transient 502/504 errors (retried once)
RxCheck is an educational screening tool, not a substitute for professional medical advice.
This application:
- β Cannot replace clinical judgment or pharmacist consultation
- β May miss rare interactions, allergies, or contraindications
- β Does not account for patient-specific factors (age, weight, renal function, pregnancy)
- β Cannot verify local brand formulations or bioequivalence
Always confirm medication decisions with a qualified healthcare professional.
- Interaction rules never leave the server
- PostgreSQL connection pooling prevents race conditions
- High-precision matching eliminates false positives
- RxNorm proxy prevents client-side API key exposure
- Trigram threshold tuned to block dangerous substring matches
- Server-side Levenshtein matching for Indian brand names
- Emergency escalation warnings for high-risk interactions
- Medical disclaimer modal with persistent acceptance
- Atomic ETL checkpoints (crash-safe)
- Idempotent upserts (replay-safe)
- Multi-provider LLM failover (quota-safe)
- Canonical drug pair ordering (duplicate-safe)
- No user accounts or authentication
- No prescription data storage
- Client-side image upload (processed server-side)
- LocalStorage only for disclaimer acceptance
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/clinical-rule-update) - Commit your changes (
git commit -m 'Add new interaction rule for X+Y') - Push to the branch (
git push origin feature/clinical-rule-update) - Open a Pull Request
- Additional interaction rules with clinical references
- More Indian brand-to-generic mappings
- Prescription image analysis accuracy improvements
- Internationalization (Hindi, Tamil, Bengali)
- Database migration scripts
- Additional LLM providers in router
This project is licensed under the MIT License β see the LICENSE file for details.
- National Library of Medicine β RxNorm and RxNav APIs
- U.S. FDA β openFDA drug label database
- Google Gemini β Vision API for prescription image analysis
- PostgreSQL β Advanced open-source database
- Google Fonts β Syne, IBM Plex Mono, IBM Plex Sans typefaces
- PMBJP (Jan Aushadhi) β Generic medicine pricing data
- Groq, Cerebras, Google, OpenRouter, HuggingFace β LLM API providers
For questions, bug reports, or feature requests:
- GitHub Issues: Report a bug or request a feature
- Email: indiser01@gmail.com
If you find RxCheck useful, please consider giving it a β on GitHub!
Built with β€οΈ for safer medication management in India