Purpose: the first of the app's practical AI examples. It shows how a
practitioner uses the radiography AI features we have built — sovereign
pgvector "similar historical X-ray" matching plus a live AI diagnostic and
note-drafting engine — working on real ingested films.Written by OpenCode 2026-08-12. Intended for Hermes to use as the demo script.
SIAAS status (updated 2026-08-12, deployed): the client tenant is fully
sovereign — tenant PostgreSQL, MinIO object store, Orthanc DICOM receiver and
the token-authenticated sensor ingest all run on the client VPS (DB via
172.18.0.1:5436, MinIO:5438, Orthanc:8043). Only the shareddental
tenant still uses primary infrastructure for its own internal pipeline. See
§6 for the full three-tier picture.
A dentist is treating patient.tom (Lower Right, tooth 46). The surgery has
just ingested several new radiographs through the real DICOM sensor path
(storescu → Orthanc → Celery → MinIO). During the check-up the dentist:
DentalClinicalNote in the patient's record.All of this runs on the tenant-isolated client instance: the app on the primary,
patient data, the MinIO image store and Orthanc on the client VPS via the
encrypted SSH tunnel (172.18.0.1:5436 DB, :5438 MinIO, :8043 Orthanc).
| Feature | Backing | Where |
|---|---|---|
| AI risk flags on each film | analyze_radiograph (rule-based at ingest) |
Radiograph detail page "AI Analysis" card |
| Similar historical X-rays | pgvector get_historical_vector_matches (512-dim embedding, HNSW index, in-tenant PostgreSQL) |
Surgery workspace "X-Ray Historical Matching" slide-out |
| Diagnostic synthesis | Live LLM via the DENTAL_CLINICIAN role agent (DeepSeek deepseek-chat) using the 3-layer prompt (prompt_builder) |
Same slide-out |
| Structured note draft | AI/rule-based draft engine | Surgery workspace right pane |
| Signed-off note write | Sovereign Approve & Commit → DentalClinicalNote |
Surgery workspace |
https://test-client-dental.dental.veripath.co.uk/dental/surgery/345/ (appointment 345 = patient.tom CHECKUP, ATTENDED)dentist.test-client-dental / test123 (DENTAL_CLINICIAN, Keycloak realm test-client-dental)/dental/radiographs/86/ (tom, tooth 46, periapical — the walkthrough anchor film)/static/radgallery/If the demo is being run against the primary stack before the 8010 move-over,
the subdomain may be temporarily pointed at127.0.0.1:8011; the VPS2 tunnel
serves it otherwise.Access control (DSPT Cat 3, added 2026-08-12): the surgery workspace page
and its live WebSocket are clinician-only — DENTAL_ADMIN,
DENTAL_PRACTICE_MANAGER, DENTAL_CLINICIAN, DENTAL_HYGIENIST. Anonymous
visitors, receptionists and patients get HTTP 403 on/dental/surgery/<pk>/
and the WebSocket handshake is rejected (close 4403 → 403). A clinician can
only open and commit notes for appointments in their own practice.
dentist.test-client-dental / test123./dental/surgery/345/).
PERIAPICAL #91 — 96.2% similarPANORAMIC #87 — 89.2% similarBITE_WING #85 — 83.2% similarCO: Patient reports sensitivity on the lower right. O/E: On examination tooth 46 shows deep distal caries. Rx: Caries excavated and composite filling placed on tooth 46 distal. Advice: Advised soft diet and review in 3 months.
DentalClinicalNote row)./dental/radiographs/86/) to see the AIDentalClinicalNote (id observable in the UI; the workspace showsAccurate as deployed on 2026-08-12. Three tiers, not two:
test_client_dental DB, reached via the172.18.0.1:5438; Orthanc REST on172.18.0.1:8043). Patient records, the raw .dcm archives, the viewableX-Tenant-Token)dentalDENTAL_CLINICIAN role agent (DeepSeekdeepseek-chat). The matching context passed to the LLM is limited to thetenant.config['allow_llm'] (default on; the UI degrades to the rule-basedAccess control on the workspace & WebSocket (2026-08-12 DSPT Cat 3 fix):
/ws/surgery/stream/ and /dental/surgery/<pk>/ are clinician-only. The
WS route is wrapped in SessionMiddlewareStack (dsp_clinic/asgi.py) and the
consumer resolves the user from the session inside the tenant context — a
stock AuthMiddlewareStack is deliberately NOT used because it resolves users
with no tenant context and would misreport client-tenant users as anonymous.
connect() closes with 4403 (→ HTTP 403 handshake) when the caller is
anonymous, is not a clinician (DA/DPM/DC/DH), ?tenant= is missing or not an
active tenant, or the appointment belongs to another practice. The signed-off
note write re-verifies user + practice immediately before the commit. Verified
2026-08-12: dentist OK (tooth-46 → matches + LLM diagnostic + committed note);
anonymous / reception.test-client-dental / patient.tom → 403; cross-practice
commit rejected.
tenant.config['allow_llm'], §6).DENTAL_CLINICIAN role agent (id 12 Clinical Note Transcriber,pgvector_xray_match, nhs_fp17_audit).teeth_targeted set so#91 was ingested through the realteeth_targeted was backfilled on the films so tooth-level history matchingseed_dicom_samples --tenant test-client-dental --ingest.dental/consumers.py — SurgeryStreamConsumer (/ws/surgery/stream/); auth + tenant + practice checks in connect()/_create_clinical_notedsp_clinic/asgi.py — SessionMiddlewareStack on the WS route (user resolved inside tenant context)dental/radiograph_ai.py — get_historical_vector_matches, embed_radiographdental/services/prompt_builder.py — 3-layer prompt assemblydental/services/dicom_ingest.py — DICOM → MinIO → record → AI hookdental/templates/dental/surgery_workspace.html — the workspace UI