Scanseta is a prescription scanning system with:
- A React + TypeScript frontend for uploading prescription images and viewing extracted data.
- A FastAPI backend for OCR extraction, medication parsing, FDA verification, and PNDF enrichment.
frontend/: Vite + React + TypeScript application.backend/: FastAPI API and ML/scraper pipeline.setup.ps1: One-time setup script for backend + frontend.dev.ps1: Starts backend and frontend in separate PowerShell windows.
- Python 3.8+
- Node.js + npm
- Windows PowerShell (recommended for provided scripts)
From repo root:
.\setup.ps1
.\dev.ps1What this does:
- Creates
backend/.venv(if missing) - Installs backend requirements from
backend/requirements.txt - Installs Playwright Chromium for backend scrapers
- Installs frontend npm dependencies
- Creates
frontend/.env.localwithVITE_API_BASE_URL=http://localhost:8000(if missing) - Launches backend on
http://localhost:8000and frontend on Vite dev server (usuallyhttp://localhost:5173)
cd backend
python -m venv .venv
.\.venv\Scripts\pip.exe install -r requirements.txt
.\.venv\Scripts\python.exe -m playwright install chromium
.\.venv\Scripts\python.exe run_server.pycd frontend
npm install
Set-Content -Path .env.local -Value "VITE_API_BASE_URL=http://localhost:8000"
npm run devOptional config check:
cd frontend
npm run check-envVITE_API_BASE_URL(required)- Must start with
http://orhttps:// - Must not end with
/ - Local default:
http://localhost:8000
- Must start with
HF_BASE_MODEL(default:Qwen/Qwen2.5-VL-7B-Instruct)HF_ADAPTER_REPO(default:scanseta/qwen_prescription_model)HF_HOME(default:./hf_home)TRANSFORMERS_CACHE(default:./hf_cache)HF_OFFLOAD_DIR(default:./offload)- Post-processing tuning vars used in
backend/main.py:LEXICON_PATHMAX_EDIT_DISTANCEMIN_SIMILARITYNGRAM_NPLAUSIBILITY_THRESHOLDMAX_CANDIDATESMAX_LENGTH_DELTAMIN_SIMILARITY_FOR_EDITAMBIGUITY_MARGIN
- Enrichment/cache tuning:
PNDF_NEGATIVE_CACHE_TTL_SECONDSSCAN_RESULT_CACHE_TTL_SECONDSSCAN_RESULT_CACHE_MAX_ENTRIESENRICHMENT_JOB_TTL_SECONDSENRICHMENT_FDA_TIMEOUT_SECONDSENRICHMENT_PNDF_TIMEOUT_SECONDSENRICHMENT_MAX_DRUGSENRICHMENT_PERSIST_DEBOUNCE_SECONDS
- The backend tries structured model JSON parsing before falling back to OCR token parsing.
- Medication names are canonicalized against
backend/data/drug_lexicon.txtusing conservative fuzzy matching. - Ambiguous fuzzy matches abstain instead of forcing a correction.
OOVmedications are blocked from FDA/PNDF enrichment./scanreturns immediately and queues FDA/PNDF enrichment in a background job when valid medication candidates are found.
Run from backend/:
python scripts/build_drug_lexicon.pyNotes:
- Reads
backend/data/pndf_cache.json+backend/data/fda_cache.json - Merges optional
backend/data/drug_lexicon_overrides.txt - Writes
backend/data/drug_lexicon.txt - By default, preserves existing lexicon entries to avoid accidental shrink
OOVmedications are blocked from FDA/PNDF enrichment, so lexicon coverage directly affects recall- Use
--replace-outputfor strict rebuild from cache + overrides only
Base URL: http://localhost:8000
GET /- API status summaryGET /health- health status and model/device infoGET /model-status- detailed model statusGET /load-model- load/reload modelPOST /scan- scan one prescription image and queue background enrichment when possiblePOST /scan-batch- scan multiple prescription images (backend/manual; current frontend uses/scan)POST /enrich-medications- enrich manually provided medication names synchronouslyPOST /enrichment-jobs- create an async enrichment job for manually provided medication namesGET /enrichment-jobs/{job_id}- fetch current async enrichment job statusGET /enrichment-jobs/{job_id}/results- fetch current async enrichment job resultsPOST /enrichment-jobs/{job_id}/retry- retry an async enrichment job, optionally by source
Notes:
/scanreturns parsed medications immediately, with enrichment status fields such asenrichment_job_id,enrichment_status,fda_enrichment_status, andpndf_enrichment_status.fda_verificationandpndf_enrichedmay be empty on the initial/scanresponse if the background job has only been queued.POST /enrich-medicationsstill performs FDA + PNDF enrichment in the request/response cycle.
Examples:
curl http://localhost:8000/healthcurl "http://localhost:8000/load-model"curl -X POST "http://localhost:8000/enrich-medications" `
-H "Content-Type: application/json" `
-d "{\"drug_names\":[\"paracetamol\",\"ibuprofen\"]}"curl -X POST "http://localhost:8000/scan" `
-F "file=@C:\path\to\prescription.jpg"scanseta/
|- setup.ps1
|- dev.ps1
|- frontend/
| |- package.json
| |- .env.local (local)
| |- src/lib/config.ts
| `- src/lib/prescription-api.ts
`- backend/
|- main.py
|- run_server.py
|- requirements.txt
|- data/
`- src/
|- scrapers/
`- post_processing/
503 Model not loadedon/scan:- Call
GET /load-modelfirst, then retry.
- Call
- Frontend cannot call backend:
- Check
frontend/.env.localand ensureVITE_API_BASE_URLis set correctly. - Restart frontend dev server after changing env files.
- Check
- Playwright errors during enrichment:
- Reinstall browser:
.\.venv\Scripts\python.exe -m playwright install chromium
- Reinstall browser:
- Windows async/subprocess errors with Playwright:
- Use
python run_server.py(it sets the Windows event loop policy correctly).
- Use
- This project is for prescription text extraction and medication data enrichment workflows.
- Output should be reviewed by qualified professionals before clinical use.