CliniScan is a layered multimodal triage support app for hackathon/demo use. It combines typed or voice-captured symptom text, structured intake fields, optional image analysis, and similar-case retrieval to generate urgency guidance and a structured report.
Use CliniScan: https://cliniscan-hcbc.netlify.app/
The public product is the polished React website hosted on Netlify. It connects to the FastAPI analysis service hosted on Render at https://cliniscan-api.onrender.com.
CliniScan is not a diagnosis tool and not a replacement for licensed care.
Not a medical diagnosis. Always consult a licensed medical professional.
- Structured intake: symptom text, body location, duration, pain severity (1-10), age, known conditions, medications.
- Voice-enabled symptom entry: users can type symptoms manually or record audio in the browser. The backend transcribes audio with OpenAI Whisper and optionally formats the transcript into concise clinical language with Claude before filling the symptom field.
- Optional image input for visual evidence extraction:
- upload an existing
jpg/png/webpimage - open the device camera and capture a new photo
- reject unsupported, oversized, or medically irrelevant images with a safe fallback
- upload an existing
- Layered backend pipeline:
- safety override
- symptom structuring
- vision extraction
- deterministic evidence fusion
- pgvector RAG retrieval (similar-case grounding)
- quality gate
- clinical reasoning
- Results dashboard with:
- urgency badge
- possible conditions + confidence
- clinical assessment
- risk signals and red flags
- submitted-input toggle (shows uploaded image + entered fields)
- Report actions (top-right in Reports view):
- Download PDF
- Share (native share sheet, clipboard fallback)
- Privacy-minimal product analytics:
- anonymous page views and assessment funnel events
- image, camera, and voice feature-use flags without medical content
- hashed per-tab session identifiers
- protected aggregate summary endpoint
- Frontend: React + Vite
- Backend: FastAPI
- AI providers: OpenAI + Anthropic
- Audio transcription: OpenAI Whisper (
whisper-1) - Clinical note formatting: Claude Sonnet, when
ANTHROPIC_API_KEYis configured - Similar-case retrieval: PostgreSQL + pgvector
CliniScan/
backend/
main.py
requirements.txt
.env.example
cache/
db/
layers/
models/
demo/
demo.mp4
android/
settings.gradle
app/
ios/
project.yml
CliniScan/
CliniScanTests/
CliniScanUITests/
frontend/
package.json
src/
docs/android/
docs/ios/
tests/
README.md
requirements.txt
The native Android app lives in android/ and the native iOS scaffold lives in ios/. Both are intentionally separate from the React website in frontend/.
The production website uses React + Vite and is deployed on Netlify. The frontend build must receive the hosted API URL at build time:
VITE_API_URL=https://cliniscan-api.onrender.comDeployment details and verification steps are documented in
docs/web-deployment.md.
The repository also includes streamlit_app.py as a simplified prototype. It is not the
primary public UI and does not reproduce the polished React design.
Run it locally:
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python -m streamlit run streamlit_app.pyFor Streamlit Community Cloud, create an app from this repository and set the main file
path to streamlit_app.py. To use a different backend, add this app secret:
CLINISCAN_API_URL = "https://your-api.example.com"macOS/Linux:
cd backend
python3 -m venv ../.venv
../.venv/bin/python -m pip install -r requirements.txt
cp .env.example .envWindows PowerShell:
cd backend
python -m venv ..\.venv
..\.venv\Scripts\python -m pip install -r requirements.txt
copy .env.example .envSet API keys in backend/.env:
OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_key
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/cliniscan
DATABASE_URL_RAW=postgresql://postgres:postgres@localhost:5432/cliniscan# 1. Create the database
createdb cliniscan
# 2. Install pgvector extension (if not already)
psql cliniscan -c "CREATE EXTENSION IF NOT EXISTS vector;"
# 3. Run seed script
python db/seed.py
# 4. Start the server normally
uvicorn main:app --reloadRun backend:
macOS/Linux:
cd backend
../.venv/bin/python -m uvicorn main:app --reload --port 8000Windows PowerShell:
cd backend
..\.venv\Scripts\python -m uvicorn main:app --reload --port 8000cd frontend
npm install
npm run devDefault URLs:
- Backend:
http://localhost:8000 - Frontend:
http://localhost:3000
If needed, override frontend API URL:
macOS/Linux:
VITE_API_URL=http://localhost:8000 npm run devWindows PowerShell:
set VITE_API_URL=http://localhost:8000
npm run dev- The symptom field shows a microphone button only when the browser supports
MediaRecorderandgetUserMedia. - Microphone access must be allowed by the browser and operating system. If the in-app browser blocks access, open
http://127.0.0.1:3000/in Chrome or Safari and allow microphone permission. - Voice capture is optional. The textarea remains editable before and after transcription, and failed voice capture does not block normal form submission.
/transcriberequiresOPENAI_API_KEY. IfANTHROPIC_API_KEYis missing, transcription still works and the raw transcript is returned as the clinical note fallback.
- Upload an image accepts JPG, PNG, and WEBP files up to 10 MB.
- Open camera uses the browser
getUserMediaAPI and prefers the rear-facing camera on supported mobile devices. - Camera access requires HTTPS in production and explicit browser permission from the user.
- A captured photo is converted to JPEG and sent through the same
/analyzeimage pipeline as an uploaded file. - If an image is unavailable, unsupported, or not medically relevant, CliniScan continues in text-only mode and reports that limitation.
CliniScan records only allowlisted product events such as page_view,
assessment_started, and assessment_completed. It does not send symptom text,
images, transcripts, body location, age, medications, diagnoses, or API payloads to the
analytics table.
See docs/analytics-privacy.md for the event schema, privacy
boundaries, retention guidance, and protected summary API.
The project demo video is stored at demo/demo.mp4. The file is H.264/AAC MP4 and is kept under GitHub's 100 MB file limit.
Returns backend/provider status.
JSON body:
- Required:
symptom_text,body_location,duration_days,severity_score,provider - Optional:
age,known_conditions,medications,image_base64,image_mime,demo_scenario
Multipart form upload:
- Required field:
audio - Supported browser recording formats:
webmorwav
Returns:
{
"raw_transcript": "Patient's original spoken words...",
"clinical_note": "Patient reports symptoms in concise clinical language..."
}Failure behavior:
400ifOPENAI_API_KEYis not configured or the audio upload is empty.422if no speech is detected.502if Whisper transcription is unavailable.
Run tests:
macOS/Linux:
.venv/bin/python -m pytest -qWindows PowerShell:
python -m pytest -qBuild frontend:
cd frontend
npm run buildCurrent verified baseline:
- 23 backend tests passing
- frontend production build passing
- live Netlify website connected to the Render API
- live symptom-to-results workflow passing
- image payload, relevance gate, and text-only fallback passing
- desktop and mobile layouts passing browser QA