Clinsight is an AI-powered clinical support platform that helps users understand their laboratory test results instantly. Users upload a laboratory report as an image or PDF, and Clinsight extracts th The MVP focuses on one core problem: people receive laboratory results but cannot understand what they mean without waiting for a doctor. Clinsight reduces that waiting time by giving users immediate
- Guest Upload: Upload + full AI interpretation without an account. Prompted to sign up after 3 responses.
- Authentication: Email/password signup with OTP verification. Google OAuth. Password reset. Session persistence.
- Lab Results Upload: Camera capture + file upload (JPG, PNG, PDF). Validation, loading, and error states included.
- OCR Extraction: Extracts test names, values, reference ranges. Preview screen before AI runs.
- AI Interpretation: Plain-language explanation. Summary + suggested questions. Medical disclaimer on every screen.
- Follow-Up Chat: Context-aware chat tied to each upload. Persistent thread per session. Suggested prompt questions for new users.
- History: Authenticated users can view, reopen, and delete past interpretations.
- Notifications & Settings: Push notification when interpretation is ready. Notification preferences, profile editing, email/password update, account deletion.
- Backend: Python FastAPI
- Authentication: OTP-based (email/phone), no passwords, Google OAuth
- OCR: Document/image text extraction
- Database: PostgreSQL
| Feature | User Input | System Output |
|---|---|---|
| Upload | Photo or file (JPG/PNG/PDF) | Validated file → OCR pipeline |
| OCR | Uploaded image / PDF | Test names, values, reference ranges extracted |
| AI Interpretation | Confirmed OCR values | Plain-language explanations, summary, follow-up questions |
| Chat | Free-text question, Suggested questions | Context-aware AI response using report data |
| Signup | Email + password or Google OAuthentication | Account created; OTP verification email sent |
| Guest Migration | Sign up after guest upload | Guest session linked to account; interpretation saved |
- Upload: Accepted: JPG, PNG, PDF. Unreadable/unsupported file → error state + reupload. OCR timeout at 15s → error + reupload.
- AI Engine: Classifies each value as Normal / Caution / Abnormal against reference. Disclaimer appended automatically. AI error → fallback message + retry.
- Guest flow: Call
POST /api/v1/guest-sessionwithX-Device-Fingerprintto obtain or reuse aguest_session_id(stored in Postgres, default TTL 1 hour). SendX-Guest-Session-Idon upload, c - Auth & session: JWT access token in
Authorization: Bearer; refresh token inrefresh_tokenHttpOnly cookie, backed byauth_sessionsper device.CurrentUserrequires verified email. `Sessi - Notifications: Push sent on interpretation complete. Suppressed if the user is actively on the result screen. Users who opt out can still access results in-app.
- Lab result uploaded in under 30 seconds: ≤ 30s
- OCR extracts values accurately: 85%+
- AI interpretation generated successfully: 90%+
- AI interpretation completes within 15 seconds: < 15s
- Chat produces context-aware responses: Pass
- Guest session migrates without data loss: Pass
- Authentication flows complete without errors: Pass
- Crash-free sessions across core flow: 95%+
- Medical disclaimer on every interpretation screen: 100%
- Users can view past interpretations: Pass
- Doctor/patient matching & telemedicine
- Doctor dashboard, onboarding, verification
- PDF export
- Long-term patient health tracking
- Multi-language support
- Hospital / lab system integrations
- AI learning loop & model retraining
- Admin analytics system
This is the target layout for the full MVP. Interns should follow this exactly — add files in the right domain folders rather than creating new top-level directories.
clinsights-be/
├── app/
│ ├── main.py # FastAPI app, global exception handlers
│ ├── core/
│ │ ├── config.py # Settings loaded from .env (pydantic-settings)
│ │ └── security.py # JWT + password hashing utilities
│ ├── api/
│ │ ├── deps.py # Shared dependencies: DBSession, CurrentUser, GuestSession
│ │ └── v1/
│ │ ├── router.py # Aggregates all v1 domain routers
│ │ └── endpoints/
│ │ ├── auth.py # /auth — signup, login, OTP verify, Google OAuth
│ │ ├── users.py # /users — profile, settings, account deletion
│ │ ├── upload.py # /upload — file upload (JPG, PNG, PDF)
│ │ ├── ocr.py # /ocr — trigger extraction, preview extracted values
│ │ ├── interpretation.py # /interpretation — AI result + risk classification
│ │ ├── chat.py # /chat — follow-up Q&A tied to an interpretation
│ │ ├── history.py # /history — list, view, delete past interpretations
│ │ └── notifications.py # /notifications — preferences, push settings
│ ├── db/
│ │ └── session.py # Async engine + session factory + get_session()
│ ├── models/
│ │ ├── __init__.py # Imports all models — required for Alembic discovery
│ │ ├── user.py # User ORM model
│ │ ├── otp.py # OTP codes
│ │ ├── lab_report.py # LabReport — file path, upload status, guest session link
│ │ ├── ocr_result.py # OCRResult — extracted test names, values, ranges
│ │ ├── interpretation.py # Interpretation — AI output, risk level, disclaimer
│ │ ├── chat.py # ChatSession + ChatMessage — per-report chat threads
│ │ └── notification.py # NotificationPreference — per-user push settings
│ ├── schemas/
│ │ ├── auth.py # Signup, Login, OTP request/response schemas
│ │ ├── user.py # User profile schemas
│ │ ├── upload.py # Upload response schemas
│ │ ├── ocr.py # OCR preview + confirm schemas
│ │ ├── interpretation.py # AI interpretation response schemas
│ │ ├── chat.py # Chat message request/response schemas
│ │ ├── history.py # History list/detail schemas
│ │ └── notification.py # Notification preference schemas
│ └── services/
│ ├── auth/ # Modular auth services
│ │ ├── email.py # Auth-specific email dispatch
│ │ ├── otp.py # OTP generation + validation logic
│ │ ├── service.py # Core auth business logic
│ │ └── tokens.py # JWT lifecycle logic
│ ├── email.py # General transactional email client (Resend)
│ ├── oauth.py # Google OAuth integration
│ ├── upload.py # File validation, storage (local/S3), size checks
│ ├── ocr.py # OCR extraction pipeline (PDF/image → structured data)
│ ├── ai.py # AI interpretation engine — prompting, risk classification
│ ├── chat.py # Chat service — context injection, response generation
│ ├── guest.py # Guest session helpers (Postgres)
│ ├── guest_sessions.py # GuestSessionManager (create, limits, migrate)
│ ├── auth_sessions.py # AuthSessionManager (per-device refresh tokens)
│ └── notification.py # Push notification dispatch + preference management
├── alembic/
│ ├── env.py # Wired to app.models.Base.metadata + settings
│ └── versions/ # Migration files — one per schema change
├── tests/ # Pytest suite
├── .env.example
├── alembic.ini
├── pyproject.toml
└── uv.lock
api/deps.py— all shared FastAPI dependencies live here. Routes importDBSession,CurrentUser, andGuestSessionfrom one place.models/schemas/servicessplit — DB shape, API shape, and business logic stay decoupled. They diverge faster than you'd think.db/session.pyseparate frommodels/— engine setup is infrastructure; models are domain.- One file per domain in
endpoints/,models/,schemas/,services/— makes it easy to find where any feature lives and keeps PRs focused.
- Python 3.12+
- uv (
curl -LsSf https://astral.sh/uv/install.sh | sh) - A running Postgres instance (local, Docker, or Supabase)
uv synccp .env.example .envFill in .env with your database credentials and secrets:
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/clinsights
JWT_SECRET=<generate: python3 -c "import secrets; print(secrets.token_urlsafe(64))">
JWT_ALGORITHM=HS256
OTP_PEPPER=<generate: python3 -c "import secrets; print(secrets.token_urlsafe(64))">Ensure your local PostgreSQL database is created (clinsights) and the proper roles exist. Open your psql terminal and execute:
ALTER SCHEMA public OWNER TO postgres;
GRANT ALL ON SCHEMA public TO postgres;
GRANT CREATE ON SCHEMA public TO postgres;
GRANT USAGE ON SCHEMA public TO postgres;
GRANT ALL PRIVILEGES ON DATABASE clinsights TO postgres;Then run migrations:
uv run alembic upgrade headuv run fastapi dev app/main.pyOr run via Uvicorn explicitly:
uv run uvicorn app.main:app --reload- Root API →
http://127.0.0.1:8000 - Swagger UI →
http://127.0.0.1:8000/docs - ReDoc →
http://127.0.0.1:8000/redoc
We use Alembic integrated with uv to manage database schema migrations.
# 1. Edit a model in app/models/
# 2. Generate a migration
uv run alembic revision --autogenerate -m "describe the change"
# 3. Review the generated file carefully before applying
# 4. Apply
uv run alembic upgrade headAlembic discovers models by importing them. If a model file is not imported in __init__.py, Alembic won't see it.
# app/models/__init__.py — every model must be listed here
from app.models.user import User
from app.models.otp import OTP- Add route handlers to the relevant file in
app/api/v1/endpoints/ - Import and register in
app/api/v1/router.py
- Create or extend a file in
app/models/ - Import the model in
app/models/__init__.pyso Alembic discovers it - Generate and apply a migration
Add Pydantic request/response models to app/schemas/. Keep them separate from ORM models.
Add it to app/services/. Routes should stay thin: validate input → call service → return response.
- Fork the repository and create your feature branch (
git checkout -b feature/your-feature) - Install dependencies:
uv sync - Run tests before committing:
uv run pytest - Install pre-commit hook (
uv run pre-commit install) - Commit your changes (
git commit -m 'Add feature') - Push to your branch (
git push origin feature/your-feature) - Open a Pull Request
- Follow existing code style and conventions
- Write clear, descriptive commit messages
- Include tests for new features
- Keep PRs focused and manageable in size
- Never commit secrets, keys, or credentials