docs align memory and audio backup behavior

This commit is contained in:
Daniel 2026-05-08 23:32:39 +02:00
parent 4a020fb7b0
commit 19c9e8ba1e
8 changed files with 46 additions and 67 deletions

View file

@ -124,9 +124,11 @@ Applied to: `soap.js`, `hpi.js`, `refine.js`, `sickVisit.js`, `wellVisit.js`,
### Physician memories ### Physician memories
Saved corrections are injected into prompts as `[STYLE HINTS (low priority)]` Saved templates and prompt preferences are injected into prompts as
with 200-character snippets. The low-priority wording prevents smaller models `[STYLE HINTS (low priority)]` when they belong to AI-context categories. The
from hallucinating content from the correction examples into the current note. low-priority wording prevents smaller models from hallucinating content from a
stored template into the current note. `custom` memories and legacy
`correction_*` rows are not prompt context.
## API call logging ## API call logging

View file

@ -844,36 +844,15 @@ Delete a memory.
### GET /api/memories/context ### GET /api/memories/context
Retrieve all memories formatted for injection into AI generation prompts. Retrieve AI-context template/preference rows formatted for injection into AI
generation prompts. `custom` rows and legacy `correction_*` rows are excluded.
- **Auth required:** Yes - **Auth required:** Yes
- **Response:** - **Response:**
```json ```json
{ {
"context": "string" "success": true,
} "context": "string"
```
---
### POST /api/memories/correction
Automatically save a style correction as a memory. Used when the physician corrects AI output.
- **Auth required:** Yes
- **Request body:**
```json
{
"section": "string",
"original_snippet": "string",
"corrected_snippet": "string"
}
```
- **Response:**
```json
{
"success": true,
"id": "number"
} }
``` ```

View file

@ -138,20 +138,23 @@ Draft/complete encounter workspace. Auto-expires (default 7 d,
### `user_memories` ### `user_memories`
Per-user clinical-style hints injected into AI prompts. Per-user template and preference rows. Only selected categories are injected
into AI generation through `/api/memories/context`; `custom` rows are stored
for the user but not included in prompt context.
| Column | Type | Notes | | Column | Type | Notes |
|---|---|---| |---|---|---|
| id | SERIAL PK | | | id | SERIAL PK | |
| user_id | INTEGER FK users.id ON DELETE CASCADE | | | user_id | INTEGER FK users.id ON DELETE CASCADE | |
| category | TEXT NOT NULL DEFAULT 'custom' | `physical_exam`, `ros`, `encounter_format`, `custom`, `template_*`, `correction_*` | | category | TEXT NOT NULL DEFAULT 'custom' | Valid categories: `physical_exam`, `ros`, `encounter_format`, `family_history`, `assessment_plan`, `custom`, `template_soap`, `template_hpi`, `template_wellvisit`, `template_sickvisit`, `template_ed`. Legacy `correction_*` rows may exist but are filtered out. |
| name | TEXT NOT NULL | | | name | TEXT NOT NULL | Encrypted with `enc1:` for new rows |
| content | TEXT NOT NULL | | | content | TEXT NOT NULL | Encrypted with `enc1:` for new rows |
| created_at, updated_at | TIMESTAMPTZ DEFAULT NOW() | | | created_at, updated_at | TIMESTAMPTZ DEFAULT NOW() | |
### `audio_backups` ### `audio_backups`
Retry store for failed-transcription audio. Optional 24-hour encrypted recovery store for recordings when transcription
fails, so users can retry without re-recording.
| Column | Type | Notes | | Column | Type | Notes |
|---|---|---| |---|---|---|

View file

@ -278,7 +278,7 @@ Important keys:
Draft encounters (7-day auto-expiry). Columns: `label`, `enc_type`, `transcript`, `generated_note`, `partial_data` (JSON), `status`, `expires_at`. Draft encounters (7-day auto-expiry). Columns: `label`, `enc_type`, `transcript`, `generated_note`, `partial_data` (JSON), `status`, `expires_at`.
#### `user_memories` #### `user_memories`
User templates fed into AI generation. `category` is one of: `physical_exam`, `ros`, `encounter_format`, `family_history`, `assessment_plan`, `custom`. User templates and preferences. `/api/memories/context` injects only AI-context categories: `physical_exam`, `ros`, `encounter_format`, `family_history`, `assessment_plan`, `template_soap`, `template_hpi`, `template_wellvisit`, `template_sickvisit`, `template_ed`. `custom` rows are user-visible storage but are not prompt context. Legacy `correction_*` rows may exist but are filtered out.
### Learning Hub Tables ### Learning Hub Tables

View file

@ -243,24 +243,19 @@ docker exec -w /app pediatric-ai-scribe npm run migrate:new -- add_my_table
3. Admin-editable automatically through `PUT /api/admin/config` which accepts 3. Admin-editable automatically through `PUT /api/admin/config` which accepts
arbitrary keys. arbitrary keys.
## Physician memory / correction tracker ## Physician Templates And Preferences
1. On note generation, `trackAIOutput(elementId, text)` captures the original 1. Settings saves user templates/preferences through `/api/memories` into
output in memory. `user_memories`.
2. User edits the note in a contenteditable field. 2. New rows encrypt `name` and `content` with the shared `enc1:` string format.
3. On Save, `saveCorrection(elementId, section)` diffs current vs. original. 3. `GET /api/memories/context` decrypts rows and returns only AI-context
4. If changed by > 2 words or > 20 characters, `POST /api/memories/correction` categories: `physical_exam`, `ros`, `encounter_format`, `family_history`,
stores the before/after in `user_memories` with category `assessment_plan`, `template_soap`, `template_hpi`, `template_wellvisit`,
`correction_{section}`. `template_sickvisit`, and `template_ed`.
5. Next generation: `GET /api/memories/context` fetches the 10 most recent per 4. `custom` rows remain visible in settings but are not included in prompt
category and `src/utils/prompts.js` injects them as context.
`[STYLE HINTS (low priority)]` 200-character snippets. 5. Legacy `correction_*` rows from the removed correction-learning feature are
filtered out rather than deleted.
Tabs with correction capture: Live Encounter, SOAP, Dictation, Sick Visit,
Well Visit (Hospital Course and Chart Review save corrections when available
but don't always have a trackable single output element).
Maximum 20 corrections retained per category (oldest deleted).
## Route reference ## Route reference

View file

@ -916,16 +916,15 @@ signatures are simple and the provider branches are well-isolated.
A worked example of how the layers compose. Tab: Sick Visit. A worked example of how the layers compose. Tab: Sick Visit.
1. **Frontend collects inputs.** `sickVisit.js` reads: 1. **Frontend collects inputs.** `sickVisit.js` reads:
- The transcript area (which holds either browser-whisper output or - The transcript area, populated by the configured transcription API or
the result of `transcribeAudio` after recording stop). the explicit opt-in browser speech-recognition fallback.
- The structured ROS / PE radio buttons (NORMAL / ABNORMAL / NOT - The structured ROS / PE radio buttons (NORMAL / ABNORMAL / NOT
REVIEWED + free-text per-system notes). REVIEWED + free-text per-system notes).
- Patient age and chief complaint. - Patient age and chief complaint.
- The selected model (`getSelectedModel()` — tab-level dropdown wins - The selected model (`getSelectedModel()` — tab-level dropdown wins
over global). over global).
2. **Optional memory injection.** If user memories are enabled, 2. **Optional template injection.** `getUserMemoryContext()` pulls saved
`getUserMemoryContext()` pulls saved style hints (with the AI-context templates and preferences from `/api/memories/context`.
`[STYLE HINTS (low priority)]` framing — see `docs/ai-providers.md`).
3. **POST to /api/sick-visit.** The route: 3. **POST to /api/sick-visit.** The route:
- Wraps every user-derived chunk in `<UNTRUSTED_*>` blocks via - Wraps every user-derived chunk in `<UNTRUSTED_*>` blocks via
`wrapUserText`. `wrapUserText`.

View file

@ -744,7 +744,7 @@ From `server.js:229303`:
| `routes/refine.js` | `/api` | Take a draft note + correction prompt, return refined note | | `routes/refine.js` | `/api` | Take a draft note + correction prompt, return refined note |
| `routes/logs.js` | `/api` | Client-side error log ingest (`/api/logs/client-error`) | | `routes/logs.js` | `/api` | Client-side error log ingest (`/api/logs/client-error`) |
| `routes/encounters.js` | `/api` | **Sacred.** Save / load / list / delete `saved_encounters`. Optimistic lock + idempotency. See section 13 | | `routes/encounters.js` | `/api` | **Sacred.** Save / load / list / delete `saved_encounters`. Optimistic lock + idempotency. See section 13 |
| `routes/memories.js` | `/api` | CRUD on `user_memories` (style hints, templates) | | `routes/memories.js` | `/api` | CRUD on `user_memories` (templates and prompt preferences) |
| `routes/notes.js` | `/api` | CRUD on `personal_notes` + voice-to-AI-note + trash/restore | | `routes/notes.js` | `/api` | CRUD on `personal_notes` + voice-to-AI-note + trash/restore |
| `routes/documents.js` | `/api` | S3 doc upload / list / download / delete (metadata in `user_documents`) | | `routes/documents.js` | `/api` | S3 doc upload / list / download / delete (metadata in `user_documents`) |
| `routes/audioBackups.js` | `/api` | Server-side audio retry store (encrypted gzip, 24h TTL) | | `routes/audioBackups.js` | `/api` | Server-side audio retry store (encrypted gzip, 24h TTL) |
@ -979,9 +979,10 @@ Nextcloud or are exported.
Created via migration `1777003849000_add-personal-notes.js`, soft-delete Created via migration `1777003849000_add-personal-notes.js`, soft-delete
added via `1777090000000_notes-trash.js`. added via `1777090000000_notes-trash.js`.
**Distinct from `user_memories`.** Memories are AI prompt fuel **Distinct from `user_memories`.** Memories store user templates and
(templates / style hints) — they get injected into LLM calls. Notes preferences. Only the AI-context categories are injected into LLM calls;
are pure clinician text that never goes to an AI. Both are encrypted. `custom` rows are stored but excluded from prompt context. Notes are pure
clinician text that never goes to an AI. Both are encrypted.
### `user_memories` — AI prompt fuel ### `user_memories` — AI prompt fuel
@ -989,7 +990,7 @@ are pure clinician text that never goes to an AI. Both are encrypted.
| ------ | ---- | ----- | | ------ | ---- | ----- |
| `id` | SERIAL PK | | | `id` | SERIAL PK | |
| `user_id` | INTEGER FK (CASCADE) | | | `user_id` | INTEGER FK (CASCADE) | |
| `category` | TEXT NOT NULL DEFAULT 'custom' | `physical_exam`, `ros`, `encounter_format`, `family_history`, `assessment_plan`, `custom`, `template_soap`, `template_hpi`, `template_wellvisit`, `template_sickvisit`, `template_ed`, legacy `correction_*` (filtered out) | | `category` | TEXT NOT NULL DEFAULT 'custom' | `physical_exam`, `ros`, `encounter_format`, `family_history`, `assessment_plan`, `custom`, `template_soap`, `template_hpi`, `template_wellvisit`, `template_sickvisit`, `template_ed`. Legacy `correction_*` rows may exist but are filtered out. |
| `name` | TEXT NOT NULL | **Encrypted** | | `name` | TEXT NOT NULL | **Encrypted** |
| `content` | TEXT NOT NULL | **Encrypted** | | `content` | TEXT NOT NULL | **Encrypted** |
| `created_at`, `updated_at` | TIMESTAMPTZ | | | `created_at`, `updated_at` | TIMESTAMPTZ | |
@ -1030,7 +1031,7 @@ guardrail.
**Retention.** None — explicit. These tables grow forever. Disk **Retention.** None — explicit. These tables grow forever. Disk
should be sized accordingly. A future migration is on the roadmap. should be sized accordingly. A future migration is on the roadmap.
### `audio_backups`failed-transcription retry store ### `audio_backups`24-hour transcription recovery store
| Column | Type | Notes | | Column | Type | Notes |
| ------ | ---- | ----- | | ------ | ---- | ----- |
@ -1043,9 +1044,9 @@ should be sized accordingly. A future migration is on the roadmap.
| `created_at`, `expires_at` | TIMESTAMPTZ | 24h default | | `created_at`, `expires_at` | TIMESTAMPTZ | 24h default |
**Who writes.** `routes/audioBackups.js` from the frontend **Who writes.** `routes/audioBackups.js` from the frontend
`audioBackup.js` lifecycle when a transcription call fails — the `audioBackup.js` lifecycle when a transcription call fails. The client
client uploads the gzipped audio so the user can retry without uploads encrypted gzipped audio so the user can retry without re-recording;
re-recording. expired rows are deleted by the database cleanup job.
**Retention.** 24 hours via hourly cleanup. Audio is the most sensitive **Retention.** 24 hours via hourly cleanup. Audio is the most sensitive
PHI in the system; short retention is intentional. PHI in the system; short retention is intentional.

View file

@ -1404,8 +1404,8 @@ reference shape.
router.post('/<your-endpoint>', authMiddleware, async function(req, res) { router.post('/<your-endpoint>', authMiddleware, async function(req, res) {
try { try {
var { ..., physicianMemories, model } = req.body; var { ..., physicianMemories, model } = req.body;
// Validate inputs, build context with wrapUserText() per field. // Validate inputs, build context with wrapUserText() per field.
// Append style hints if physicianMemories. // Append template preferences if physicianMemories.
var result = await callAI([ var result = await callAI([
{ role: 'system', content: PROMPTS.<yourPromptKey> + INJECTION_GUARD }, { role: 'system', content: PROMPTS.<yourPromptKey> + INJECTION_GUARD },
{ role: 'user', content: context } { role: 'user', content: context }