No description
Find a file
Daniel 9ad34a7f35 feat(client): port PE Guide reference libraries + rescue untracked FAQ data
Two things in one commit because they're coupled by a gitignore fix:
(1) PE Guide minimum-viable port, (2) a pre-existing bug where
client/src/data/faq.ts was silently gitignored and never committed.

.gitignore — narrow scope
  Changed `data/` to `/data/`. The old rule matched every nested
  `data/` directory in the tree, including client/src/data/, which
  meant faq.ts from the FAQ port (commit c0038da) never landed in
  git — the Faq page built locally only because the file existed
  on the dev machine. A fresh clone or CI build would fail the
  Vite build at '@/data/faq'. The narrowed rule still ignores the
  runtime DB directory at repo root while allowing project data
  modules to be tracked. This commit also commits the missing
  faq.ts so the FAQ page is actually buildable from git again.

client/src/data/pe-guide.ts
  Verbatim port of lines 23-311 of public/js/peGuide.js — the stable
  reference content:
    • SCALES (12 scales: MRC, DTR, plantar, Beighton, ATR, RR, SpO2,
      Silverman, Westley, Levine murmur, pulse amp, cap refill)
    • SYSTEM_SCALES (per-body-system scale mapping)
    • APTM_LEGEND (5 cardiac auscultation points)
    • INNOCENT_MURMURS (5 benign childhood murmurs)
    • RESP_SOUNDS (7 entries with /audio/respiratory/*.ogg paths)
    • CARDIAC_SOUNDS (6 entries with /audio/cardiac/* paths)
  Counts preserved exactly. Audio files stay under public/audio/ and
  are served unchanged.

What is NOT in this commit — on purpose
  PE_DATA (the ~1000-line age-group × system × component × step
  hierarchy) stays in the vanilla app. The migration checkpoint memory
  explicitly warns about the class of bug where an LLM silently drops
  entries from long clinical arrays. PE_DATA porting needs its own
  session with per-entry counts + visual diff against the vanilla
  source. An amber banner at the top of the React page links to
  /#peGuide (the legacy checklist viewer) so users still reach the
  full exam-step checklist + Generate-Exam-Report flow.

  Also skipped: the big inline APTM_SVG chest diagram. The letter
  legend (A/P/E/T/M) carries the clinical content; the pictorial
  SVG can land later without content risk.

client/src/pages/PeGuide.tsx — viewer
  Grid-of-cards layout: one card per scale, per APTM point, per
  innocent-murmur, and per sound entry. Sound cards use native
  <audio controls> so the browser does the usual play/pause/seek —
  no custom player. data-testid hooks throughout for the spec.

e2e/tests/peguide-react.spec.js — four smoke tests:
  all 12 scales render (this is the count that would fail loudly if
  someone trimmed SCALES later), APTM has all 5 letters, sound
  libraries have the exact respiratory + cardiac keys, and the
  legacy-viewer link is present.

Client tsc -b + vite build clean. Bundle 452.91 kB / 129.35 kB gz.
2026-04-23 23:57:16 +02:00
.github/workflows ci: multi-arch Docker via native runners + Node 24 opt-in + PAT for tag-trigger chain 2026-04-15 00:05:34 +02:00
android Fix APK crash: replace XML splash with PNG, add real mipmap launcher icons 2026-03-29 11:07:10 +00:00
client feat(client): port PE Guide reference libraries + rescue untracked FAQ data 2026-04-23 23:57:16 +02:00
docs feat: neonatal calculator, DOCX/PPTX/ODT/EPUB support, gateway-agnostic URL helper, TTS/STT fixes 2026-04-19 02:17:06 +02:00
e2e feat(client): port PE Guide reference libraries + rescue untracked FAQ data 2026-04-23 23:57:16 +02:00
migrations Batch of security + scale fixes 2026-04-14 05:24:40 +02:00
mobile Release v6.31.0 2026-04-23 21:50:04 +00:00
monitoring Add Loki + Grafana monitoring stack, ntfy notifications, biometric auth 2026-04-11 03:00:52 +02:00
public feat(client): port PE Guide reference libraries + rescue untracked FAQ data 2026-04-23 23:57:16 +02:00
scripts test(lint): static reference linter — catches dead-code + orphan refs 2026-04-23 18:58:38 +02:00
shared feat(client): port Learning Hub to React 2026-04-23 23:49:56 +02:00
src feat: port Vaccine Schedule + Catch-Up Schedule (real tables, not stubs) 2026-04-23 22:25:41 +02:00
test feat: B — extract drug data to public/data/drugs.json (schema v1.0) 2026-04-20 04:23:24 +02:00
.dockerignore Add .dockerignore (exclude .env, .agent-config, node_modules, data) 2026-03-21 19:27:21 -04:00
.env.example feat(security): OpenBao-backed secret injection via entrypoint 2026-04-22 03:59:10 +02:00
.gitignore feat(client): port PE Guide reference libraries + rescue untracked FAQ data 2026-04-23 23:57:16 +02:00
.gitmessage docs: add CONTRIBUTING.md + .gitmessage template for conventional commits 2026-04-14 23:51:09 +02:00
.node-pg-migraterc.json Add node-pg-migrate for versioned schema changes + better mobile UA labels 2026-04-14 05:06:19 +02:00
admin-cli.js v3.0.0: Auth, admin panel, security fixes, per-tab model selector 2026-03-21 19:25:51 -04:00
BROWSER_WHISPER_SETUP.md Update docs for v3 truly self-hosted setup 2026-03-31 23:12:46 +00:00
BROWSER_WHISPER_TROUBLESHOOTING.md v16: Make Browser Whisper CDN failure graceful with clear warnings 2026-03-31 16:18:46 +00:00
CONTRIBUTING.md fix: verify auto-version → android + docker pipeline end-to-end 2026-04-15 00:10:35 +02:00
DEVELOPER_GUIDE.md Expand DEVELOPER_GUIDE with detailed architecture, DB layer, file descriptions 2026-03-24 19:30:01 -04:00
docker-compose.e2e.yml test(e2e): +120 tests across 8 new specs; baseline fixes 2026-04-23 18:58:38 +02:00
docker-compose.local.yml Feat: admin CMS, save/resume encounters, user templates, SSHADESS, well visit notes, sick visit, ROS/PE checklists, ICD-10 diagnoses 2026-03-22 16:43:39 -04:00
docker-compose.monitoring.yml Update bilirubin to exact AAP 2022 values, add exchange transfusion 2026-04-11 05:50:19 +02:00
docker-compose.yml Maintenance CLI + unpin postgres digest 2026-04-14 04:00:13 +02:00
docker-entrypoint.sh fix(entrypoint): docker-compose env overrides win over OpenBao-fetched values 2026-04-22 21:04:46 +02:00
Dockerfile feat(security): OpenBao-backed secret injection via entrypoint 2026-04-22 03:59:10 +02:00
EMBEDDINGS_SETUP.md Add embeddings setup documentation 2026-03-31 14:37:46 +00:00
FEATURES_EXPLAINED.md Fix TTS preview + Browser Whisper preload, add comprehensive docs 2026-03-31 15:13:53 +00:00
grafana-dashboard.json feat: neonatal calculator, DOCX/PPTX/ODT/EPUB support, gateway-agnostic URL helper, TTS/STT fixes 2026-04-19 02:17:06 +02:00
IMPROVEMENTS.md Add automatic ICD-10 and CPT billing code suggestions 2026-04-11 01:50:17 +02:00
knip.json build(ts): day 5 — Vitest + Zod + Knip tooling 2026-04-23 19:54:30 +02:00
OPENID_SETUP.md v4: Fix milestones display + add OpenID auth + 100MB PDF support 2026-04-01 17:59:51 +00:00
package-lock.json build(ts): day 5 — Vitest + Zod + Knip tooling 2026-04-23 19:54:30 +02:00
package.json Release v6.31.0 2026-04-23 21:50:04 +00:00
README.md docs: add Testing section (unit + Playwright e2e) 2026-04-21 02:14:54 +02:00
server.ts feat: day 7 — first tab ported to React (Extensions) + Express serves /app/* 2026-04-23 22:09:04 +02:00
TRANSCRIPTION_OPTIONS.md Add comprehensive transcription options documentation 2026-03-31 21:58:17 +00:00
tsconfig.json build(ts): day 2 — shared/types.ts + server.ts rename (no behavior 2026-04-23 19:21:22 +02:00
vitest.config.ts build(ts): day 5 — Vitest + Zod + Knip tooling 2026-04-23 19:54:30 +02:00

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

cp .env.example .env

Edit .env — at minimum set:

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

docker compose up -d

App runs on port 3552. First user to register becomes admin.

3. Admin CLI

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 for provider-specific guides.


Cloudflare Turnstile (Bot Protection)

Optional CAPTCHA on login, registration, and password reset forms.

TURNSTILE_SITE_KEY=0x4AAA...
TURNSTILE_SECRET_KEY=0x4AAA...

Email

Without SMTP, email verification is skipped and users are auto-verified.

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:

# 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

docker pull danielonyejesi/pediatric-ai-scribe-v3:latest

Minimal compose without building:

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/ directory for detailed documentation:


Development

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)

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.

# 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:

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.