diff --git a/README.md b/README.md index 1046e97b..660f9312 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Ped-AI -Ped-AI is a pediatric clinical documentation, education, and bedside decision-support app. This fork has moved well beyond the original scribe app: it now combines encounter documentation, clinical workflows, Learning Hub CMS, admin controls, MCP-backed clinical assistant integration, Redis-backed operational state, and hardened deployment defaults. +Ped-AI is a pediatric clinical documentation, education, and bedside decision-support app. This fork has moved well beyond the original scribe app: it now combines encounter documentation, clinical workflows, private teaching material, admin controls, MCP-backed clinical assistant integration, Redis-backed operational state, and hardened deployment defaults. The app runs as an authenticated Express/Postgres service with a browser frontend and optional integrations for LiteLLM, AWS, OpenAI-compatible APIs, Nextcloud WebDAV, S3-compatible storage, OpenBao, Redis, OIDC, TOTP, and Cloudflare Turnstile. @@ -24,14 +24,6 @@ The app runs as an authenticated Express/Postgres service with a browser fronten - Mobile-friendly PWA layout for bedside use. - Per-user phone extension and pager directory with soft-delete, search, ZIP export, and JSON/ZIP import for handoff between users. -### Learning Hub - -- CMS for articles, clinical pearls, quizzes, and presentations. -- Tiptap article editor, quiz builder, category management, and draft/publish flow. -- AI-assisted content generation from topic text, uploaded files, or connected Nextcloud WebDAV files. -- Marp slide editing with preview and PPTX export. -- Keyword, semantic, and hybrid search using Postgres/pgvector where configured. - ### My Resources - Private teaching material any signed-in user can generate for themselves — nobody else sees it. @@ -180,7 +172,6 @@ Primary references: - `docs/embeddings-setup.md` — embedding model configuration. - `docs/global-prompt-administration.md` — prompt overrides and the conversation budget. - `docs/speech.md` — STT, TTS, recording, and audio backups. -- `docs/learning-hub.md` — the CMS and education workflow. - `docs/my-resources.md` — private teaching material, the slide renderer, and search sources. - `docs/deployment.md` — production deployment. - `docs/scaling.md` — scaling priorities and readiness work. diff --git a/assets/learning/slides-reference.pptx b/assets/deck/slides-reference.pptx similarity index 100% rename from assets/learning/slides-reference.pptx rename to assets/deck/slides-reference.pptx diff --git a/docs/database.md b/docs/database.md index 3981951d..64805066 100644 --- a/docs/database.md +++ b/docs/database.md @@ -180,11 +180,6 @@ Metadata for files in S3-compatible object storage. File bytes stay in S3. | description | TEXT | | | created_at | TIMESTAMPTZ DEFAULT NOW() | | -### `learning_categories`, `learning_content`, `learning_questions`, `learning_options`, `learning_progress` - -Learning Hub CMS tables. `learning_content.embedding` is `VECTOR(768)` for -semantic search (pgvector IVFFLAT index). See `docs/learning-hub.md`. - ### `developmental_milestones` AAP-aligned pediatric milestone reference data. Age group + domain keyed. diff --git a/docs/features-explained.md b/docs/features-explained.md index 7ec5ad19..aa4c2de7 100644 --- a/docs/features-explained.md +++ b/docs/features-explained.md @@ -26,19 +26,9 @@ Browser Whisper and browser-local model workers are removed. Do not expect a pre The voice preview button calls LiteLLM TTS and plays the returned audio in the browser. If preview is silent, check that a LiteLLM voice is selected, the gateway is configured, the user is authenticated, and browser autoplay has not blocked playback. -## Learning Hub - -Learning Hub is both a learner-facing content area and an admin/moderator CMS. - -- Articles and pearls render sanitized content. -- Quizzes support single-answer, multi-select, and true/false questions. -- Presentations use Marp-style markdown with preview and PPTX export. -- AI generation can use topic text, uploaded source files, or connected Nextcloud WebDAV files. -- Categories can organize content without deleting the content when category assignments change. - ## Nextcloud WebDAV -Users can connect a Nextcloud account with an app password. Learning Hub AI generation can browse files from the connected WebDAV account, and users can set a default browse path to avoid repeatedly navigating to the same clinical content folder. +Users can connect a Nextcloud account with an app password and export generated notes to it. ## Documents And S3 @@ -79,7 +69,7 @@ deliberately never cached. Full detail in ## Admin Panel -Admins can manage users, roles, registration, security settings, model defaults, prompts, logs, and Learning Hub content. Production deployments should enable SSO/2FA and restrict admin access. +Admins can manage users, roles, registration, security settings, model defaults, prompts and logs. Production deployments should enable SSO/2FA and restrict admin access. ## Feature Status @@ -89,7 +79,6 @@ Admins can manage users, roles, registration, security settings, model defaults, | Server transcription | Active | Routed through LiteLLM; the upstream vendor is a gateway choice. | | Browser Web Speech preview | Optional | Explicit opt-in only. | | 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. | @@ -107,4 +96,4 @@ Admins can manage users, roles, registration, security settings, model defaults, - Check `docker logs pediatric-ai-scribe -f` for backend errors. - Check `/api/health` for service status. - Check provider credentials and model names before debugging UI state. -- For Learning Hub file import failures, verify Nextcloud URL, username, app password, and folder path. +- For Nextcloud export failures, verify the URL, username, app password, and folder path. diff --git a/docs/learning-hub.md b/docs/learning-hub.md deleted file mode 100644 index c9b84d03..00000000 --- a/docs/learning-hub.md +++ /dev/null @@ -1,101 +0,0 @@ -# Learning Hub - -A CMS + content-delivery module for clinical education material inside the -app. Supports articles, clinical pearls, quizzes, and Marp-rendered -presentations with PPTX export. Quiz questions are stored alongside article -content and can optionally be generated by AI from uploaded source material. - -## Content types - -| Type | Description | -|---|---| -| `article` | Rich HTML body with an optional attached quiz | -| `pearl` | Short clinical snippet (no quiz, no heavy media) | -| `quiz` | Standalone quiz (no article body) | -| `presentation` | Marp markdown rendered as slides; PPTX export supported | - -## User-facing features - -- Browse by category. -- Three search modes: - - **Keyword** — Postgres full-text. - - **Semantic** — pgvector cosine similarity on the embedding column. - - **Hybrid** — weighted merge of both result sets. -- Articles render with sanitized HTML (DOMPurify, loaded via SRI-pinned cdnjs). -- Quizzes: multiple-choice, multi-select, true/false. Score computed on submit, - per-question explanations revealed after. -- Presentation viewer: modal with keyboard / swipe navigation. -- Progress: `learning_progress` stores per-attempt score + total. - -## CMS (moderator / admin) - -- Tiptap rich-text editor for article body. -- Draft / published toggle. -- Category assignment. -- Quiz builder: add/remove questions, add/remove options, mark correct, enter - explanation. -- Marp editor for presentations with live preview. - -## AI content generation - -`POST /api/admin/learning/generate` takes one of: - -| Input | Notes | -|---|---| -| `topic` | Plain-text description of the topic | -| Uploaded files | PDF / DOCX / PPTX / ODT / EPUB / TXT / MD / HTML / CSV / JSON, ≤ 10 MB each, max 10 files. The declared type must be in the allowlist *and* match the extension, and the bytes are sniffed before anything parses them. | -| WebDAV path | Pulled from the user's connected Nextcloud instance | - -Parameters: `model` (from the provider whitelist), `slideCount` for -presentations, `wordCount` for articles. - -File uploads pass the `src/utils/fileType.js` magic-byte check so a -mismatched extension is rejected before it reaches the parser. - -## Marp → PPTX export - -`POST /api/admin/learning/generate-pptx` writes the markdown to a temp -directory and runs `pandoc` against -`assets/learning/slides-reference.pptx`. The reference deck carries the fonts, -palette and slide layouts, so restyling the export means editing that file in -PowerPoint — not changing code. - -Images are handled before pandoc sees the markdown. Each -`/api/generated-images/{id}` link is resolved through the ownership check and -written beside the deck under a name this route chooses; any link that does not -resolve to one of those is dropped. Pandoc resolves image links against the -filesystem, so passing an arbitrary local path through would embed that file -into the deck. - -This is the Learning Hub's own path and is separate from My Resources, which -renders decks with python-pptx from a typed deck rather than from markdown — -see [`my-resources.md`](my-resources.md). - -## Semantic search - -| | | -|---|---| -| Store | `pgvector` on `learning_content.embedding VECTOR(768)` | -| Index | IVFFLAT, cosine distance | -| Primary model | `vertex/text-embedding-005` (768 dims), served through LiteLLM | -| Fallback model | OpenAI `text-embedding-3-small` (truncated to 768 to match the column) | - -Embeddings are generated on content publish + on every edit. If the embedding -provider is unreachable, the content still saves — keyword search remains -available. - -## Tables - -| Table | Purpose | -|---|---| -| `learning_categories` | Top-level groupings | -| `learning_content` | Articles / pearls / quizzes / presentations. Body + `embedding` vector. | -| `learning_questions` | Quiz question prompts (FK to content) | -| `learning_options` | Answer options (FK to question) | -| `learning_progress` | Per-user attempt history | - -## Retrieval sizing - -How many corpus excerpts the Clinical Assistant, the Learning Hub and My -Resources each receive, and the reranker cap that overrides all three: -[retrieval-tuning.md](retrieval-tuning.md). diff --git a/docs/logic/auth-admin-learning.md b/docs/logic/auth-admin-learning.md index 08821a68..bb907e53 100644 --- a/docs/logic/auth-admin-learning.md +++ b/docs/logic/auth-admin-learning.md @@ -43,7 +43,7 @@ generation can use topic text, uploaded files, or connected Nextcloud/WebDAV sources. Semantic search uses pgvector embeddings on `learning_content` when an embedding provider is configured. -See [`../learning-hub.md`](../learning-hub.md) and +See [`../embeddings-setup.md`](../embeddings-setup.md). ## Security Rules diff --git a/migrations/1780800000000_retire-learning-hub.js b/migrations/1780800000000_retire-learning-hub.js new file mode 100644 index 00000000..b1e66dd3 --- /dev/null +++ b/migrations/1780800000000_retire-learning-hub.js @@ -0,0 +1,39 @@ +// Learning Hub is retired. My Resources generates better slides from a typed +// deck, and the articles and quizzes live in the quiz app. +// +// The content was exported before this ran — every article as markdown plus a +// full SQL dump of all five tables — to ops-backups/learning-hub-export-*. +// That export is the restore path; this migration is not reversible in any +// useful sense, because down() can recreate the shape but never the rows. +// +// generated_image_links goes with it: it existed only to say which Learning Hub +// content an image was published in, and it was the sole reason a generated +// image could ever be read by someone who did not make it. Images are now +// owner-only, which is both simpler and stricter. + +exports.up = async function (pgm) { + // Order matters only for readability — CASCADE handles the dependencies. + pgm.sql('DROP TABLE IF EXISTS generated_image_links CASCADE'); + pgm.sql('DROP TABLE IF EXISTS learning_progress CASCADE'); + pgm.sql('DROP TABLE IF EXISTS learning_options CASCADE'); + pgm.sql('DROP TABLE IF EXISTS learning_questions CASCADE'); + pgm.sql('DROP TABLE IF EXISTS learning_content CASCADE'); + pgm.sql('DROP TABLE IF EXISTS learning_categories CASCADE'); + + // The workflow enum on image jobs. Any rows for the retired workflow go with + // it: those images belonged to Learning Hub content that no longer exists. + pgm.sql("DELETE FROM generated_image_jobs WHERE workflow = 'learning_hub'"); + pgm.sql('ALTER TABLE generated_image_jobs DROP CONSTRAINT IF EXISTS generated_image_jobs_workflow_check'); + pgm.sql("ALTER TABLE generated_image_jobs ADD CONSTRAINT generated_image_jobs_workflow_check " + + "CHECK (workflow = ANY (ARRAY['clinical_assistant'::text, 'my_resources'::text]))"); + + // Settings that only ever addressed Learning Hub. + pgm.sql("DELETE FROM app_settings WHERE key LIKE 'learning_hub.%'"); +}; + +exports.down = async function (pgm) { + // The shape, not the content. Restoring the rows means loading the export. + pgm.sql('ALTER TABLE generated_image_jobs DROP CONSTRAINT IF EXISTS generated_image_jobs_workflow_check'); + pgm.sql("ALTER TABLE generated_image_jobs ADD CONSTRAINT generated_image_jobs_workflow_check " + + "CHECK (workflow = ANY (ARRAY['clinical_assistant'::text, 'learning_hub'::text, 'my_resources'::text]))"); +}; diff --git a/public/components/cms.html b/public/components/cms.html deleted file mode 100644 index 6b4fb536..00000000 --- a/public/components/cms.html +++ /dev/null @@ -1,327 +0,0 @@ -
-

Content Manager

-

Create and manage Learning Hub content, quizzes, and categories

-
- - -
-
-Published
-
-Drafts
-
-Categories
-
-Quizzes
-
-Attempts
-
- - -
- - - - - -
- - -
-
-
- - - - -
-
- - -
-
- -
-
- Title - Category - Type - Status - Updated - -
-
-
Loading...
-
-
-
- - - - -
-
diff --git a/public/components/learning.html b/public/components/learning.html deleted file mode 100644 index 4f494663..00000000 --- a/public/components/learning.html +++ /dev/null @@ -1,64 +0,0 @@ -
-

Learning Hub

-

Pediatric education, clinical pearls, and self-assessment quizzes

-
- - -
-
- - -
-
- - -
- - -
- - - diff --git a/public/index.html b/public/index.html index 8c3a089d..c3eea3a2 100644 --- a/public/index.html +++ b/public/index.html @@ -288,14 +288,6 @@ My Resources - -