352 lines
12 KiB
Markdown
352 lines
12 KiB
Markdown
# Pediatric AI Scribe v6
|
|
|
|
AI-powered clinical documentation platform for pediatric medicine. Generates HPIs, hospital courses, chart reviews, SOAP notes, well/sick visit notes, and developmental milestone assessments from voice recordings or dictation.
|
|
|
|
## Features
|
|
|
|
### Clinical Documentation
|
|
- **Live Encounter** — record doctor-patient conversations, AI generates structured OLDCARTS HPI
|
|
- **Voice Dictation** — dictate narrative, AI cleans and restructures
|
|
- **Hospital Course** — paste progress notes, generates prose, day-by-day, organ-system (ICU), or psych format
|
|
- **Chart Review / Precharting** — summarize outpatient, subspecialty, and ED notes
|
|
- **SOAP Notes** — full SOAP or subjective-only from dictation
|
|
- **Well Visit** — AAP 2025 Bright Futures periodicity with vaccines, screenings, billing codes, SSHADESS (12+), milestones
|
|
- **Sick Visit** — quick documentation with auto-suggested ROS and PE from chief complaint
|
|
- **Developmental Milestones** — AAP/Nelson tracker (birth-11y) with narrative/structured/summary output
|
|
|
|
### AI & Speech
|
|
- **5 AI Providers** — OpenRouter, AWS Bedrock, Azure OpenAI, Google Vertex AI, LiteLLM
|
|
- **5 STT Providers** — Google Gemini, Amazon Transcribe (Medical), OpenAI Whisper, Local Whisper, LiteLLM
|
|
- **3 TTS Providers** — Google Cloud TTS, LiteLLM (OpenAI), ElevenLabs
|
|
- **Browser Whisper** — fully offline in-browser transcription via WebAssembly (HIPAA-safe)
|
|
- **Per-tab model selector** — choose fast vs. smart vs. premium models per task
|
|
- **Physician memory system** — Dragon-like learning from your corrections
|
|
|
|
### Learning Hub
|
|
- **Content Management** — articles, clinical pearls, quizzes, presentations
|
|
- **AI Content Generation** — generate from topics, uploaded PDFs, or Nextcloud files
|
|
- **Marp Presentations** — slide editor with preview and PPTX export
|
|
- **Semantic Search** — vector-based search via pgvector embeddings
|
|
- **Quiz System** — MCQ, multi-select, true/false with scoring and progress tracking
|
|
|
|
### Platform
|
|
- **Multi-user with roles** — admin, moderator, user
|
|
- **OIDC/SSO** — Azure AD, Okta, Keycloak, PocketID, Google
|
|
- **2FA** — TOTP-based two-factor authentication
|
|
- **Cloudflare Turnstile** — bot protection on login, register, password reset
|
|
- **Email verification** — with customizable templates
|
|
- **Nextcloud integration** — WebDAV export
|
|
- **S3 Document Storage** — AWS S3, Backblaze B2, MinIO
|
|
- **PWA** — installable, works on mobile
|
|
- **Admin Panel** — user management, settings, prompt editor, model configuration, logs
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
### 1. Configure
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
Edit `.env` — at minimum set:
|
|
|
|
```env
|
|
AI_PROVIDER=litellm # or openrouter, bedrock, azure, vertex
|
|
LITELLM_API_BASE=https://your-litellm.example.com
|
|
LITELLM_API_KEY=sk-...
|
|
|
|
OPENAI_API_KEY=sk-... # for Whisper transcription (if not using LiteLLM STT)
|
|
|
|
JWT_SECRET=<64-char random> # openssl rand -hex 32
|
|
DB_PASSWORD=<strong password>
|
|
APP_URL=https://your-domain.com
|
|
```
|
|
|
|
### 2. Start
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
App runs on **port 3552**. First user to register becomes admin.
|
|
|
|
### 3. Admin CLI
|
|
|
|
```bash
|
|
docker exec pediatric-ai-scribe node admin-cli.js list-users
|
|
docker exec pediatric-ai-scribe node admin-cli.js create-admin admin@example.com password123 "Dr. Admin"
|
|
docker exec pediatric-ai-scribe node admin-cli.js make-admin user@example.com
|
|
docker exec pediatric-ai-scribe node admin-cli.js reset-password user@example.com newpassword
|
|
docker exec pediatric-ai-scribe node admin-cli.js toggle-registration
|
|
docker exec pediatric-ai-scribe node admin-cli.js stats
|
|
```
|
|
|
|
---
|
|
|
|
## AI Provider Configuration
|
|
|
|
Switch providers by setting `AI_PROVIDER` in `.env`. No code changes needed.
|
|
|
|
| Provider | HIPAA | Config |
|
|
|----------|-------|--------|
|
|
| **LiteLLM** | Depends on backend | `LITELLM_API_BASE`, `LITELLM_API_KEY` |
|
|
| **AWS Bedrock** | Yes (with BAA) | `AWS_BEDROCK_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` |
|
|
| **Azure OpenAI** | Yes (with BAA) | `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_API_KEY`, `AZURE_DEPLOYMENT_NAME` |
|
|
| **Google Vertex AI** | Yes (with BAA) | `GOOGLE_VERTEX_PROJECT`, `GOOGLE_VERTEX_LOCATION` |
|
|
| **OpenRouter** | No | `OPENROUTER_API_KEY` |
|
|
|
|
---
|
|
|
|
## Transcription (Speech-to-Text)
|
|
|
|
Set `TRANSCRIBE_PROVIDER` or let the app auto-detect.
|
|
|
|
| Provider | HIPAA | Config |
|
|
|----------|-------|--------|
|
|
| **Google Gemini** | Yes | `GOOGLE_VERTEX_PROJECT`, `GOOGLE_STT_MODEL` |
|
|
| **Amazon Transcribe** | Yes | AWS creds + `TRANSCRIBE_PROVIDER=aws` |
|
|
| **Amazon Transcribe Medical** | Yes | `AWS_TRANSCRIBE_MEDICAL=true`, `AWS_TRANSCRIBE_SPECIALTY=PRIMARYCARE` |
|
|
| **Local Whisper** | Yes (offline) | `TRANSCRIBE_PROVIDER=local`, `WHISPER_BINARY`, `WHISPER_MODEL_SIZE` |
|
|
| **OpenAI Whisper** | No | `OPENAI_API_KEY` |
|
|
| **LiteLLM** | Depends | `TRANSCRIBE_PROVIDER=litellm`, `LITELLM_STT_MODEL` |
|
|
| **Browser Whisper** | Yes (client-side) | No config needed — toggle in user settings |
|
|
|
|
---
|
|
|
|
## Text-to-Speech
|
|
|
|
| Provider | HIPAA | Config |
|
|
|----------|-------|--------|
|
|
| **Google Cloud TTS** | Yes | `GOOGLE_VERTEX_PROJECT`, `GOOGLE_TTS_VOICE` |
|
|
| **LiteLLM** | Depends | `LITELLM_TTS_MODEL`, `LITELLM_TTS_VOICE` |
|
|
| **ElevenLabs** | No | `ELEVENLABS_API_KEY` |
|
|
|
|
---
|
|
|
|
## OpenID Connect / SSO
|
|
|
|
Supports Azure AD, Okta, Keycloak, PocketID, Google, and any OIDC-compliant provider.
|
|
|
|
1. Register callback URL: `https://your-domain.com/api/auth/oidc/callback`
|
|
2. Admin Panel > Settings > Configure OIDC (Issuer URL, Client ID, Client Secret)
|
|
3. Users are auto-created and linked by email on first SSO login
|
|
|
|
See [OPENID_SETUP.md](OPENID_SETUP.md) for provider-specific guides.
|
|
|
|
---
|
|
|
|
## Cloudflare Turnstile (Bot Protection)
|
|
|
|
Optional CAPTCHA on login, registration, and password reset forms.
|
|
|
|
```env
|
|
TURNSTILE_SITE_KEY=0x4AAA...
|
|
TURNSTILE_SECRET_KEY=0x4AAA...
|
|
```
|
|
|
|
---
|
|
|
|
## Email
|
|
|
|
Without SMTP, email verification is skipped and users are auto-verified.
|
|
|
|
```env
|
|
SMTP_HOST=smtp.gmail.com
|
|
SMTP_PORT=587
|
|
SMTP_USER=your-email@gmail.com
|
|
SMTP_PASS=your-app-password
|
|
SMTP_FROM=noreply@yourdomain.com
|
|
```
|
|
|
|
---
|
|
|
|
## Maintenance CLI
|
|
|
|
After a Postgres image upgrade (major version bump or silent base-layer change),
|
|
btree indexes on text columns can become inconsistent with the new ICU/glibc
|
|
library. The app auto-detects this at startup and reindexes on drift, but you
|
|
can also trigger it manually:
|
|
|
|
```bash
|
|
# Health check — no writes
|
|
docker exec pediatric-ai-scribe npm run maint:check
|
|
|
|
# Rebuild all indexes + refresh collation + ANALYZE
|
|
docker exec pediatric-ai-scribe npm run maint:reindex
|
|
```
|
|
|
|
Run `maint:reindex` any time after:
|
|
|
|
- Upgrading the Postgres image (major or minor)
|
|
- Restoring from a dump created on a different Linux distro
|
|
- Seeing "invalid credentials" on credentials you know are correct
|
|
- Seeing `0 rows` returned from a lookup that should match
|
|
|
|
The reindex takes seconds on a small DB and a minute or two on larger ones.
|
|
Safe to run while the app is serving traffic, though queries may slow briefly.
|
|
|
|
---
|
|
|
|
## Docker Hub
|
|
|
|
```bash
|
|
docker pull danielonyejesi/pediatric-ai-scribe-v3:latest
|
|
```
|
|
|
|
Minimal compose without building:
|
|
|
|
```yaml
|
|
services:
|
|
app:
|
|
image: danielonyejesi/pediatric-ai-scribe-v3:latest
|
|
ports:
|
|
- "3552:3000"
|
|
env_file: .env
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
|
|
postgres:
|
|
image: pgvector/pgvector:pg16
|
|
environment:
|
|
POSTGRES_DB: pedscribe
|
|
POSTGRES_USER: pedscribe
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U pedscribe"]
|
|
interval: 10s
|
|
retries: 5
|
|
|
|
volumes:
|
|
pgdata:
|
|
```
|
|
|
|
---
|
|
|
|
## HIPAA Notice
|
|
|
|
This application processes data through third-party AI APIs.
|
|
|
|
- All connections use HTTPS/TLS
|
|
- Authentication required for all AI endpoints
|
|
- 2FA and SSO available
|
|
- Cloudflare Turnstile bot protection
|
|
- **AWS Bedrock**, **Azure OpenAI**, and **Google Vertex AI** offer BAAs
|
|
- **OpenRouter** and **ElevenLabs** do NOT offer BAAs
|
|
- **Browser Whisper** and **Local Whisper** keep audio fully private
|
|
|
|
**Do not use real PHI without executed BAAs with all providers in your deployment.**
|
|
|
|
---
|
|
|
|
## Documentation
|
|
|
|
See the [docs/](docs/) directory for detailed documentation:
|
|
|
|
- [Architecture Overview](docs/architecture.md)
|
|
- [API Reference](docs/api-reference.md)
|
|
- [Database Schema](docs/database.md)
|
|
- [Authentication & Security](docs/authentication.md)
|
|
- [AI Providers & Models](docs/ai-providers.md)
|
|
- [Speech (STT/TTS)](docs/speech.md)
|
|
- [Learning Hub & CMS](docs/learning-hub.md)
|
|
- [Configuration Reference](docs/configuration.md)
|
|
- [Deployment Guide](docs/deployment.md)
|
|
- [Developer Guide](docs/developer-guide.md)
|
|
|
|
---
|
|
|
|
## Development
|
|
|
|
```bash
|
|
npm install
|
|
cp .env.example .env # edit with your keys
|
|
# Requires PostgreSQL with pgvector
|
|
node server.js
|
|
```
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
Two layers, both zero-config after the initial setup.
|
|
|
|
### Unit tests — pure dose math (Node built-in)
|
|
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
Runs `node --test test/` against `public/js/calc-math.js` — pure functions for
|
|
APLS / Best Guess weight, Parkland, Holliday-Segar 4-2-1, PRAM, Westley,
|
|
epi (anaphylaxis vs arrest vs NRP, different concentrations), RSI drugs,
|
|
min SBP, ETT sizing, Lund-Browder TBSA. **36 assertions, no dependencies.**
|
|
|
|
### End-to-end tests — Playwright smoke suite
|
|
|
|
Runs a headless Chromium against the live app. **128 tests** covering every
|
|
calculator tab, every Bedside sub-pill + widget, auth-gated pages (encounter,
|
|
well visit, charts, vaccines, catch-up, learning hub, dictation, settings,
|
|
FAQ), at **both desktop and mobile (Pixel 5) viewports**.
|
|
|
|
```bash
|
|
# First-time setup: spin up the auth-less test container (port 3553)
|
|
docker compose -f docker-compose.yml -f docker-compose.e2e.yml up -d pediatric-scribe-e2e
|
|
|
|
# Then run the full suite (runs inside an official Playwright container)
|
|
npm run e2e
|
|
```
|
|
|
|
The runner script (`scripts/e2e.sh`) uses `mcr.microsoft.com/playwright` so you
|
|
don't need Node or browsers on the host.
|
|
|
|
**Test environment:**
|
|
|
|
- `pediatric-ai-scribe` (port 3552) — your normal app
|
|
- `pediatric-ai-scribe-e2e` (port 3553) — identical image, but with
|
|
`TURNSTILE_SECRET_KEY=""` and `SMTP_HOST=""` so Playwright can log in
|
|
without a bot challenge. Shares the same Postgres + pgdata volume.
|
|
- Test user: `e2e-user@ped-ai.test` (auto-verified on first register)
|
|
- Harness page: `public/e2e-harness.html` loads the calculators component
|
|
without the auth wall for smoke tests that don't need a logged-in session.
|
|
|
|
**Viewing failures** — Playwright writes `e2e/test-results/<test-name>/`
|
|
with:
|
|
|
|
- `test-failed-1.png` — screenshot at the point of failure
|
|
- `trace.zip` — full action trace (replay with `npx playwright show-trace`)
|
|
- `error-context.md` — DOM snapshot and console logs
|
|
|
|
Everything but the specs and config is gitignored under `e2e/`.
|
|
|
|
**Files:**
|
|
|
|
- `e2e/tests/bedside-smoke.spec.js` — 26 tests for the Bedside module
|
|
- `e2e/tests/top-calculators.spec.js` — 27 tests for BP / BMI / Growth /
|
|
Bili / Vitals / BSA / Dose / Resus / GCS / Equipment
|
|
- `e2e/tests/auth-gated-smoke.spec.js` — 11 tests for the auth-gated tabs
|
|
- `e2e/playwright.config.js` — runs all the above under both `chromium`
|
|
(Desktop Chrome) and `mobile-chrome` (Pixel 5) projects
|
|
|
|
**Writing a new test:**
|
|
|
|
```js
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
test('my new smoke test', async ({ page }) => {
|
|
await page.goto('/e2e-harness.html'); // bypasses auth for calculators
|
|
await page.waitForFunction(() => window.__harnessReady === true);
|
|
await page.click('button.calc-nav-pill[data-calc="bedside"]');
|
|
await expect(page.locator('#calc-bedside')).toBeVisible();
|
|
});
|
|
```
|
|
|
|
For auth-gated routes, use the login fixture in `auth-gated-smoke.spec.js`
|
|
as a template — it caches the token at module scope so you don't hit the
|
|
login rate-limit.
|