Files
Khalid Abdi b29fdff1cb feat: ambient AI visit scribe (record/paste → reviewed SOAP note)
Record a clinician↔patient visit (or paste a transcript) on the patient
sheet; the backend transcribes it (OpenAI Whisper / Gemini), de-identifies
the transcript + context through Veil, and drafts a structured SOAP note
the clinician reviews and edits before saving — the same write-approval
gate as the chat agent.

Backend: POST /api/scribe/{transcribe,draft,save} (routes/scribe.ts,
services/ai/transcribe.ts), veil.redactText() free-text redactor,
appendEncounter service, audio MIME types on attachments. Gated by
patient:write + the clinic AI policy (reception/disabled-AI excluded).
Frontend: ScribeDialog + lib/scribe.ts, "Record visit" on the patient
detail, gated by clinical access + AI availability. New `scribe` locale
namespace across all five languages. Bumps to v0.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 18:33:30 +03:00

52 lines
1.7 KiB
TypeScript

// Client for the ambient AI visit-scribe API. The dialog records (or accepts a
// pasted transcript of) a clinician↔patient visit, transcribes it, drafts a
// structured encounter note, and — after the clinician reviews it — appends the
// note to the patient record (the same write-approval gate as the chat agent).
import { apiFetch } from "@/lib/api-client";
import type { Encounter, Patient } from "@/lib/patients";
export type ScribeVeil = {
active: boolean;
level: string;
classes: string[];
provider: string;
};
// Turn a stored audio attachment into a raw transcript (server streams it to the
// user's OpenAI/Gemini speech provider). Audio does NOT pass through Veil.
export function transcribeRecording(
attachmentId: string,
): Promise<{ transcript: string }> {
return apiFetch<{ transcript: string }>("/api/scribe/transcribe", {
method: "POST",
body: JSON.stringify({ attachmentId }),
});
}
// Draft an encounter note from a transcript. The transcript + patient context
// are de-identified through Veil before any external call; the draft is never
// saved automatically.
export function draftNote(input: {
fileNumber: string;
transcript: string;
visitType?: string;
date?: string;
}): Promise<{ draft: Encounter; veil: ScribeVeil }> {
return apiFetch<{ draft: Encounter; veil: ScribeVeil }>("/api/scribe/draft", {
method: "POST",
body: JSON.stringify(input),
});
}
// The approval step: append the reviewed note to the patient record.
export function saveNote(
fileNumber: string,
encounter: Encounter,
): Promise<Patient> {
return apiFetch<Patient>("/api/scribe/save", {
method: "POST",
body: JSON.stringify({ fileNumber, encounter }),
});
}