diff --git a/README.md b/README.md index 0e4359c..4d7d5c4 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ The app runs as an authenticated Express/Postgres service with a browser fronten - Live encounter capture with structured pediatric HPI generation. - Dictation cleanup for narrative notes. - SOAP, sick visit, well visit, hospital course, chart review, precharting, and ED encounter workflows. +- Parent-facing education handouts generated from clinician notes, with diagnosis, medication, emergency-care guidance, and preferred-language support. - Pediatric developmental milestone tooling. - Templates, physician memory, and per-tab model overrides. - Server-side speech-to-text routing through configured providers. diff --git a/docs/api-reference.md b/docs/api-reference.md index 3d16a4d..81d8a0c 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -436,19 +436,23 @@ Generate a SHADESS (Strengths, Home, Activities, Drugs, Emotions, Sexuality, Saf --- -### POST /api/generate-sick-visit +### POST /api/sick-visit/note -Generate a sick visit note from a transcript, incorporating diagnosis information. +Generate a sick visit note from chief complaint, transcript/dictation, ROS/PE, and diagnosis context. - **Auth required:** Yes - **Request body:** ```json { - "transcript": "string", + "chiefComplaint": "string", + "transcript": "string (optional)", + "dictation": "string (optional)", "patientAge": "string", "patientGender": "string", "model": "string", - "diagnoses": "string | array", + "ros": "string (optional)", + "physicalExam": "string (optional)", + "diagnoses": "string (optional)", "physicianMemories": "string (optional)" } ``` @@ -456,6 +460,34 @@ Generate a sick visit note from a transcript, incorporating diagnosis informatio --- +### POST /api/patient-education + +Generate a parent-facing education handout from an existing clinician note. The frontend exposes this as the Handout action beside generated notes. + +- **Auth required:** Yes +- **Request body:** + ```json + { + "noteText": "string", + "diagnosis": "string (optional)", + "medications": "string (optional)", + "patientAge": "string (optional)", + "language": "string (optional, defaults to English)", + "readingLevel": "string (optional)", + "model": "string (optional)" + } + ``` +- **Response:** + ```json + { + "success": true, + "handout": "plain-text parent handout", + "model": "string" + } + ``` + +--- + ### POST /api/generate-milestone-narrative Generate a developmental milestone narrative from milestone data. @@ -498,8 +530,9 @@ Refine existing clinical text according to provided instructions. - **Request body:** ```json { - "text": "string", + "currentDocument": "string", "instructions": "string", + "sourceContext": "string (optional)", "model": "string" } ``` @@ -515,7 +548,7 @@ Shorten a block of clinical text while preserving key information. - **Request body:** ```json { - "text": "string", + "document": "string", "model": "string" } ``` @@ -531,7 +564,8 @@ Improve clarity and readability of clinical text. - **Request body:** ```json { - "text": "string", + "document": "string", + "context": "string (optional)", "model": "string" } ``` @@ -550,7 +584,7 @@ Check whether speech-to-text transcription is available and which provider is co ```json { "available": true, - "provider": "whisper | deepgram | browser" + "provider": "google | aws | litellm | openai | local | none" } ``` diff --git a/docs/architecture.md b/docs/architecture.md index a9ca6ea..e4d609f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -50,7 +50,7 @@ src/ models.js, prompts.js, ai.js # AI provider + model + prompt management embeddings.js # Vertex / LiteLLM / OpenAI embeddings transcribe*.js, tts*.js # STT / TTS provider clients - routes/ # 27 Express routers (auth, hpi, soap, …) + routes/ # Express routers (auth, hpi, soap, patient education, …) public/ # SPA index.html # shell, loads components on demand @@ -82,7 +82,7 @@ request → express.json (10 MB cap) → rate limiters (general 200 req/min, per-endpoint tighter on auth) → static (public/ with no-cache on HTML, 1h on JS/CSS; ?v=BUILD_ID busts cache per deploy) - → route (27 routers under /api/*) + → route (feature routers under /api/*) → authMiddleware (on protected routes: JWT, DB session check, 24h idle, last_activity update) → handler → response @@ -121,6 +121,12 @@ per-feature HTML fragments under `public/components/` fetched on demand. JS modules talk via `window` globals and `CustomEvent` on `document` — no bundler, no framework. Loader order is fixed in `index.html`. +Post-note helpers such as billing suggestions, don't-miss review, and patient +education handouts are reusable browser-side actions backed by authenticated +JSON APIs. The patient education helper generates a parent-facing plain-text +draft from the edited note and keeps the clinician in the review loop before +copying or sharing. + `authFetch.js` installs a global `fetch` interceptor that treats any 401 on an authenticated request as a signal to clear local session state and redirect to login. A `BroadcastChannel('pedscribe-auth')` pushes that signal to sibling diff --git a/docs/developer-guide-extended.md b/docs/developer-guide-extended.md index 94694b3..12cf9cd 100644 --- a/docs/developer-guide-extended.md +++ b/docs/developer-guide-extended.md @@ -134,7 +134,7 @@ The `database.js` file exports a helper object (`db`) with convenience methods: │ │ ├── wellVisit.js # Well-visit note generation (ROS/PE/ICD-10) │ │ ├── sickVisit.js # Sick visit note generation │ │ ├── refine.js # Refine/shorten any generated document -│ │ ├── transcribe.js # Whisper audio transcription +│ │ ├── transcribe.js # Server-side audio transcription provider router │ │ ├── tts.js # Text-to-speech (if configured) │ │ ├── nextcloud.js # Nextcloud WebDAV connect/export/disconnect │ │ ├── learningHub.js # User-facing: feed, content, quiz submission @@ -170,9 +170,9 @@ The `database.js` file exports a helper object (`db`) with convenience methods: │ │ │ # resend verification link handler, 2FA code input │ │ ├── admin.js # Admin panel: user management, site settings, SMTP config, │ │ │ # model enable/disable, prompt editor, announcement banner -│ │ ├── liveEncounter.js # MediaRecorder → Whisper transcription → AI HPI generation. +│ │ ├── liveEncounter.js # MediaRecorder → server STT → AI HPI generation. │ │ │ # Handles start/stop recording, timer, save/load encounters -│ │ ├── voiceDictation.js # Web Speech API (real-time) or Whisper (recorded) dictation +│ │ ├── voiceDictation.js # Web Speech preview plus server-STT-backed dictation │ │ ├── hospitalCourse.js # Paste/dictate hospital course → AI summary │ │ ├── chartReview.js # Paste/dictate chart data → AI outpatient review │ │ ├── soap.js # Paste/dictate → AI SOAP note diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 5fc7efe..0971246 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -40,7 +40,7 @@ src/ embeddings.js Vertex / LiteLLM / OpenAI embeddings notify.js ntfy push transcribe*.js, tts*.js STT / TTS provider clients - routes/ 27 routers + routes/ Express routers for auth, AI workflows, education, logs, and user data public/ index.html SPA shell, version-stamped asset refs @@ -49,7 +49,7 @@ public/ js/ 24 vanilla JS modules (no bundler) components/ per-tab HTML fragments loaded on demand css/styles.css - models/ bundled Whisper WASM + template-guide.md downloadable user template guide mobile/ Capacitor 6 wrapper (Android + iOS) .github/workflows/ CI (auto-version, APK, docker) diff --git a/docs/features-explained.md b/docs/features-explained.md index 5bb30eb..2f84ede 100644 --- a/docs/features-explained.md +++ b/docs/features-explained.md @@ -8,6 +8,12 @@ Ped-AI generates pediatric clinical notes from typed input, dictation, or record Model selection is available per task where the UI exposes a tab-level selector. Admin defaults provide the baseline model and user/task choices can override that baseline. +Generated notes can expose post-note helper panels. Billing suggestions and don't-miss review are clinician-facing. Patient education handouts are parent-facing drafts generated from the edited note, with optional diagnosis, medication, and preferred-language context. The clinician must verify the handout before sharing it. + +## Phone Extensions And Pagers + +The bedside tools include a per-user phone extension and pager directory. Entries support active/trash views, search, soft delete/restore, permanent purge, ZIP export, and JSON/ZIP import. Import preview flags exact active duplicates, exact trashed matches that can be restored, and possible duplicates before committing changes. + ## Speech Final transcription is server-side. Configure Google/Gemini, AWS Transcribe, LiteLLM, or OpenAI Whisper according to your deployment requirements. @@ -61,6 +67,8 @@ Admins can manage users, roles, registration, security settings, model defaults, | Browser Whisper | Removed | No public worker or model download path. | | Learning Hub CMS | Active | Articles, pearls, quizzes, presentations. | | Nextcloud WebDAV | Active | Used for file browsing/content import. | +| Patient handouts | Active | Parent-facing, note-derived, preferred-language draft. | +| Extension transfer | Active | ZIP export plus JSON/ZIP import preview. | | Audio backups | Active | Failure recovery only. | | TTS preview | Active | Depends on configured provider. | diff --git a/docs/improvements.md b/docs/improvements.md index 78630d9..ffcac75 100644 --- a/docs/improvements.md +++ b/docs/improvements.md @@ -93,9 +93,9 @@ This would help administrators optimize model selection and identify training op ### 9. Patient Education Materials -**Current state:** The Learning Hub serves educational content to physicians. +**Current state:** Patient education handouts are active as post-note helpers. Generated notes can open a Handout panel that creates a parent-facing plain-text draft from the clinician note, with optional diagnosis, medication, patient age, and preferred language context. The Learning Hub remains the physician-facing education/CMS area. -**Improvement:** Add a patient-facing education module that generates age-appropriate handouts based on the diagnosis. For example, after generating a note for a child with asthma, the app could produce a parent-friendly handout explaining the diagnosis, medications, and when to seek emergency care — in the parent's preferred language. +**Further improvement:** Add handout templates, saved handout history, institution-approved language libraries, and printable/PDF export. ### 10. Multi-Language Support diff --git a/docs/logic/architecture.md b/docs/logic/architecture.md index 1fd4a97..4f72a44 100644 --- a/docs/logic/architecture.md +++ b/docs/logic/architecture.md @@ -560,17 +560,16 @@ it top-to-bottom and you have the whole boot sequence. `req.ip` is the real client IP for rate-limit + audit log. 5. **`server.js:21–54`** — Helmet with a hand-crafted CSP. CSP allows: - `'self'` everywhere by default. - - `'wasm-unsafe-eval'` and `'unsafe-eval'` because in-browser - Whisper (`@xenova/transformers`) needs both. - - `cdn.jsdelivr.net` for the transformers worker. - - `huggingface.co` + the cdn-lfs subdomains for Whisper model - downloads. + - CDN origins used by the current frontend vendor assets. - `https://challenges.cloudflare.com` for Turnstile. - `connectSrc` extended to `clinicaltables.nlm.nih.gov` for ICD autocomplete. - `upgradeInsecureRequests: null` so the e2e container can be hit over plain HTTP. Production has Caddy in front, mixed content is a non-issue. + +Browser Whisper, Transformers.js browser STT, public Whisper workers, and +browser-local model downloads are intentionally not part of the runtime or CSP. 6. **`server.js:59–80`** — CORS, scoped to `/api/*` only. Static assets must not be CORS-checked because ES-module scripts always send an Origin header. In production, fail-closed: if neither @@ -738,7 +737,7 @@ From `server.js:229–303`: | `routes/chartReview.js` | `/api` | Chart review summarization (multi-note input) | | `routes/milestones.js` | `/api` | Read milestones, run developmental check | | `routes/peGuide.js` | `/api` | PE guide AI prompts (audio findings → narrative) | -| `routes/extensions.js` | `/api` | CRUD on `user_phone_extensions` | +| `routes/extensions.js` | `/api` | CRUD, ZIP export, JSON/ZIP import, duplicate preview for `user_phone_extensions` | | `routes/soap.js` | `/api` | SOAP note generation | | `routes/tts.js` | `/api` | Text-to-speech proxy (Google / ElevenLabs / LiteLLM) | | `routes/nextcloud.js` | `/api` | WebDAV upload of finished notes | @@ -755,6 +754,7 @@ From `server.js:229–303`: | `routes/sickVisit.js` | `/api` | Sick-visit AI note pipeline | | `routes/edEncounters.js` | `/api` | ED encounter pipeline | | `routes/dontMiss.js` | `/api` | "Don't miss" differential / red-flag prompt | +| `routes/patientEducation.js` | `/api` | Parent-facing education handouts generated from clinician notes | | `routes/userPreferences.js` | `/api/user` | User-level preferences (STT model, TTS voice, etc.) | | `routes/learningAI.js` | `/api/admin/learning` | AI-assisted Learning Hub authoring (generate quiz from content, embed) | @@ -1519,12 +1519,6 @@ RUN apk add --no-cache --virtual .build-deps python3 make g++ \ COPY . . RUN chmod +x /app/docker-entrypoint.sh RUN mkdir -p /app/data/logs -RUN mkdir -p /app/public/models/Xenova/whisper-tiny.en/onnx && \ - cd /app/public/models && \ - curl -sL -o transformers.min.js https://cdn.jsdelivr.net/npm/@xenova/transformers@2.0.0/dist/transformers.min.js && \ - cd Xenova/whisper-tiny.en && \ - curl -sL -o config.json https://huggingface.co/Xenova/whisper-tiny.en/resolve/main/config.json && \ - ... EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=20s \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1 @@ -1543,6 +1537,8 @@ Notes: Final image stays slim (~220 MB). - **`npm install --omit=dev`** — devDependencies (jsdom, dompurify for tests) are not in the runtime image. +- **No browser Whisper download step.** Browser-local Whisper assets and + Transformers.js browser STT are intentionally absent from the runtime image. - **Healthcheck.** Polls `/api/health` every 30 s. Container marked unhealthy after 5 failures. diff --git a/docs/logic/clinical-notes.md b/docs/logic/clinical-notes.md index 34132aa..122eed0 100644 --- a/docs/logic/clinical-notes.md +++ b/docs/logic/clinical-notes.md @@ -42,8 +42,9 @@ But every tab sits on top of the same scaffolding: `src/routes/`. 5. An **output card** that hides until generation succeeds, then reveals the AI's note plus a `Refine` and `Shorten` bar. -6. After generation, a few of the tabs auto-call **billing-codes** and - **don't-miss** helpers that mount additional cards beneath the note. +6. After generation, shared helpers can mount additional cards beneath + the note, including billing suggestions, don't-miss review, and + parent-facing patient education handouts. Why a tab per note type? Each note type has different inputs (e.g. Sick Visit needs CC + ROS + @@ -133,16 +134,18 @@ react. `transcribeAudio(blob)` in `public/js/app.js:739–795` is the single entry point for STT: -- If browser-Whisper is enabled in user settings (and supported), it - runs locally — no network call, no PHI leaves the device. -- Otherwise it POSTs the Blob to `/api/transcribe` (handled by +- It POSTs the Blob to `/api/transcribe` (handled by `src/routes/transcribe.js` — out of scope here). The server picks whichever provider the admin configured: Gemini, AWS Transcribe - Medical, OpenAI Whisper, Local Whisper, or LiteLLM. + Medical, OpenAI Whisper, local server Whisper, or LiteLLM. - On any failure with a non-empty Blob, the audio is auto-saved via `saveAudioBackup(blob, 'failed-transcription')` (`audioBackup.js`) so the user can retry from the Settings page. +Browser Whisper and browser-local model downloads are removed from the +runtime. Browser-native Web Speech remains an explicit opt-in live preview +when supported by the browser, not the final transcription path. + A startup probe (`checkTranscribeStatus`, `app.js:722`) queries `/api/transcribe/status` and stores the result in `window._transcribeAvailable`. If the server reports `false`, every tab @@ -188,6 +191,10 @@ After the note is rendered, several tabs trigger optional follow-ups: - `suggestDontMiss(outputId, noteText, noteType, age, cc)` (`app.js:894`) — POSTs to `/api/dont-miss` and mounts an orange-bordered card with up to 5 high-yield "don't miss" items. +- `attachPatientEducation(outputId, { patientAge })` — adds a Handout + button that opens a parent-facing education panel. Generate POSTs the + edited note to `/api/patient-education` with optional diagnosis, + medication, and parent-language fields. Refine and Shorten use the same pattern: `refineDocument(outputId, inputId)` (`app.js:946`) and `shortenDocument(outputId)` (`app.js:971`) diff --git a/mobile/README.md b/mobile/README.md index 9b1462c..5fee377 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -1,10 +1,10 @@ # PedScribe Mobile App -Native mobile wrapper for Pediatric AI Scribe using Capacitor. Provides background audio recording, push notifications, haptic feedback, deep linking, and share intent support on both iOS and Android. +Capacitor mobile wrapper for the hosted Ped-AI web app. The app defaults to `https://app.pedshub.com`, lets users choose a self-hosted server URL, and keeps clinical workflows API-backed through the same Express service as the browser app. ## Features -- Background recording that survives screen lock (foreground service on Android, background audio on iOS) +- Hosted web workflow inside a native WebView; server updates reach mobile clients without app-store releases - Configurable server URL (supports self-hosted instances) - Haptic feedback on recording start/stop - Keep screen awake during recording @@ -15,7 +15,7 @@ Native mobile wrapper for Pediatric AI Scribe using Capacitor. Provides backgrou stored in iOS Keychain / Android Keystore, gated by OS biometric. Enrolled on first password sign-in (opt-in prompt). 2FA still applies on top — biometric replaces the password step only. -- App Store and Play Store ready +- Android and iOS project scaffolds for store builds ## Prerequisites diff --git a/public/js/app.js b/public/js/app.js index fdf3ede..0731d83 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -817,6 +817,112 @@ function suggestDontMiss(outputElementId, noteText, noteType, patientAge, chiefC }); } +// ── Patient education handout helper ──────────────────────── +// Adds a reusable "Handout" action beside generated clinical notes. The actual +// handout is generated only when the physician clicks Generate in the panel. +function attachPatientEducation(outputElementId, opts) { + var outputEl = document.getElementById(outputElementId); + if (!outputEl) return; + opts = opts || {}; + var card = outputEl.closest('.card, .output-card'); + if (!card) return; + var actions = card.querySelector('.output-actions'); + var prefix = outputElementId.replace(/[^a-zA-Z0-9_-]/g, ''); + + if (actions && !document.getElementById(prefix + '-patient-ed-btn')) { + var btn = document.createElement('button'); + btn.id = prefix + '-patient-ed-btn'; + btn.className = 'btn-sm btn-ghost'; + btn.type = 'button'; + btn.innerHTML = ' Handout'; + btn.addEventListener('click', function() { + var panel = ensurePatientEducationPanel(outputElementId, opts); + panel.classList.remove('hidden'); + panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + }); + actions.appendChild(btn); + } +} + +function ensurePatientEducationPanel(outputElementId, opts) { + var outputEl = document.getElementById(outputElementId); + var prefix = outputElementId.replace(/[^a-zA-Z0-9_-]/g, ''); + var panelId = prefix + '-patient-ed'; + var panel = document.getElementById(panelId); + if (panel) return panel; + + panel = document.createElement('div'); + panel.id = panelId; + panel.className = 'card patient-ed-card hidden'; + panel.style.cssText = 'margin-top:10px;border-left:3px solid #0ea5e9;'; + panel.innerHTML = + '