feat: complete real-time voice-ai engine upgrades and frontend integration guide
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
# CureSelect AI — Real-Time Voice Intake & EHR Frontend Integration Guide
|
||||
|
||||
**Version**: `2.0.0 (Production Release)`
|
||||
**Target Audience**: Frontend Engineers (React, Next.js, Vue, React Native, Flutter, Vanilla TypeScript)
|
||||
**Backend Protocol**: Full-Duplex WebSockets (`ws://`) & HTTP/1.1 REST
|
||||
**Authentication Header**: `X-API-Key: skyheal_secure_api_key_2026` or `Authorization: Bearer <TOKEN>`
|
||||
**Default Dashboard**: `http://localhost:8000/` or `http://localhost:8000/dashboard`
|
||||
|
||||
---
|
||||
|
||||
## 1. Architectural Overview
|
||||
|
||||
The **CureSelect AI Voice Engine** is a high-speed, full-duplex conversational health intake system. It processes real-time microphone audio or text turns, classifies speaker perspectives, detects emergency distress, checks RxNorm drug safety, redacts HIPAA/ABDM PII, and generates real-time HL7 FHIR R4 Bundles and Doctor SOAP Notes.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Frontend (React / Web / Mobile)
|
||||
participant WS as WebSocket Manager (ws/auto)
|
||||
participant Engine as AI Orchestrator & Multi-Models
|
||||
participant EHR as EHR Export (FHIR / SOAP Note)
|
||||
|
||||
Client->>WS: Connect (ws://localhost:8000/v1/voice-onboarding/ws/auto)
|
||||
WS-->>Client: {"type": "connected", "session_id": "VOICE-AUTO-8F12A9C4"}
|
||||
WS-->>Client: {"type": "ai_response", "reply": "Hello Patient, welcome to CureSelect..."}
|
||||
|
||||
Client->>WS: Stream Binary Mic Bytes (MediaRecorder WebM/PCM)
|
||||
WS-->>Client: {"type": "backchannel", "reply": "Mm-hmm..."} (< 15ms)
|
||||
WS-->>Client: {"type": "speculative_audio_chunk", "ttfa_ms": 180} (< 200ms)
|
||||
WS-->>Client: {"type": "transcript", "speaker_type": "caregiver", "speaker_label": "Caregiver (Mother)"}
|
||||
WS-->>Client: {"type": "ai_response", "reply": "I'm sorry to hear about your mother's headache...", "audio_base64": "..."}
|
||||
|
||||
Client->>EHR: GET /v1/voice-onboarding/soap-note/{session_id}?format=html
|
||||
EHR-->>Client: Render Printable Doctor SOAP Note Report
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. WebSocket Connection Lifecycle (`ws://localhost:8000/v1/voice-onboarding/ws/auto`)
|
||||
|
||||
### 2.1 Connection Setup
|
||||
Connect to the zero-effort auto-session WebSocket endpoint:
|
||||
```typescript
|
||||
const ws = new WebSocket("ws://localhost:8000/v1/voice-onboarding/ws/auto");
|
||||
```
|
||||
|
||||
### 2.2 Inbound WebSocket Event Schema
|
||||
|
||||
#### 1. Connection Initialized (`connected`)
|
||||
Sent immediately upon socket handshake:
|
||||
```json
|
||||
{
|
||||
"type": "connected",
|
||||
"session_id": "VOICE-AUTO-9DEC79EC",
|
||||
"message": "Connected to CureSelect Real-Time Voice Engine"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Speaker Diarized Transcript (`transcript`)
|
||||
Sent as soon as patient or caregiver speech is recognized:
|
||||
```json
|
||||
{
|
||||
"type": "transcript",
|
||||
"role": "patient",
|
||||
"speaker_type": "caregiver",
|
||||
"speaker_label": "Caregiver (Mother)",
|
||||
"text": "My mother has severe headache for 10 days and she is taking Metformin 500mg."
|
||||
}
|
||||
```
|
||||
*Note*: `speaker_type` is either `"patient"` or `"caregiver"`.
|
||||
|
||||
#### 3. Low-Latency Acoustic Backchannel (`backchannel`)
|
||||
Emitted in **< 15ms** during patient pauses to signal active listening:
|
||||
```json
|
||||
{
|
||||
"type": "backchannel",
|
||||
"reply": "Mm-hmm...",
|
||||
"audio_base64": "..."
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Speculative Audio Chunk (`speculative_audio_chunk`)
|
||||
Emitted in **< 200ms** on the first 3-4 words generated by the LLM:
|
||||
```json
|
||||
{
|
||||
"type": "speculative_audio_chunk",
|
||||
"clause_text": "I am sorry to hear that.",
|
||||
"audio_base64": "...",
|
||||
"ttfa_ms": 180
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. Complete AI Response & State Update (`ai_response`)
|
||||
Contains full reply, TTS audio, next stage, and updated clinical state:
|
||||
```json
|
||||
{
|
||||
"type": "ai_response",
|
||||
"reply": "Any known drug allergies?",
|
||||
"audio_id": "aud_771829",
|
||||
"audio_base64": "...",
|
||||
"next_stage": "symptom_collection",
|
||||
"completed": false,
|
||||
"state_updates": {
|
||||
"symptoms": [{"name": "headache", "duration": "10 days", "severity": "severe"}],
|
||||
"medications": [{"name": "Metformin", "dosage": "500mg daily"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. Emergency Nurse Escalation Alert (`nurse_escalation_alert`)
|
||||
Emitted when Pain Score >= 8/10 or Distress Score > 0.75:
|
||||
```json
|
||||
{
|
||||
"type": "nurse_escalation_alert",
|
||||
"session_id": "VOICE-AUTO-9DEC79EC",
|
||||
"distress_score": 0.85,
|
||||
"pain_score": 9,
|
||||
"message": "CRITICAL: Patient reported severe pain (9/10). Nurse escalation triggered!"
|
||||
}
|
||||
```
|
||||
|
||||
#### 7. RxNorm / FDA Drug Safety Warning (`drug_safety_warning`)
|
||||
Emitted when drug interactions or drug-allergy contraindications are flagged:
|
||||
```json
|
||||
{
|
||||
"type": "drug_safety_warning",
|
||||
"warnings": [
|
||||
{
|
||||
"severity": "CRITICAL",
|
||||
"category": "DRUG_INTERACTION",
|
||||
"description": "High risk of bleeding: Combined use of Warfarin and Aspirin."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Outbound Client Messages
|
||||
|
||||
### 3.1 Streaming Audio Bytes
|
||||
Stream raw WebM / PCM audio blobs from `MediaRecorder`:
|
||||
```javascript
|
||||
mediaRecorder.ondataavailable = (e) => {
|
||||
if (e.data.size > 0 && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(e.data); // Send raw Blob
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 3.2 Text Turn Fallback
|
||||
```json
|
||||
{
|
||||
"type": "text_turn",
|
||||
"text": "10 days-ah severe headache iruku, Metformin 500mg sapudren."
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Full-Duplex Barge-In Interruption
|
||||
Send when the user speaks or clicks "Interrupt AI" while AI audio is playing:
|
||||
```json
|
||||
{
|
||||
"type": "interrupt"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. REST API Integration Endpoints
|
||||
|
||||
All REST requests require the authentication header:
|
||||
`X-API-Key: skyheal_secure_api_key_2026`
|
||||
|
||||
### 4.1 HL7 FHIR R4 Bundle Export
|
||||
- **Endpoint**: `GET /v1/voice-onboarding/fhir/{session_id}`
|
||||
- **Response**: Production-grade HL7 FHIR R4 Bundle (`Patient`, `Encounter`, `Condition`, `MedicationStatement`, `AllergyIntolerance`).
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:8000/v1/voice-onboarding/fhir/VOICE-AUTO-9DEC79EC" \
|
||||
-H "X-API-Key: skyheal_secure_api_key_2026"
|
||||
```
|
||||
|
||||
### 4.2 Doctor SOAP Note & Printable PDF/HTML Report
|
||||
- **Endpoint**: `GET /v1/voice-onboarding/soap-note/{session_id}?format=json` (or `format=html`)
|
||||
- **Query Params**:
|
||||
- `format=json`: Returns structured Subjective, Objective, Assessment (ICD-10-CM & SNOMED-CT codes), and Plan JSON.
|
||||
- `format=html`: Renders a clean printable HTML document for 1-click doctor chart printing.
|
||||
|
||||
```bash
|
||||
# Get JSON SOAP Note with ICD-10 & SNOMED CT
|
||||
curl -X GET "http://localhost:8000/v1/voice-onboarding/soap-note/VOICE-AUTO-9DEC79EC?format=json" \
|
||||
-H "X-API-Key: skyheal_secure_api_key_2026"
|
||||
|
||||
# Get Printable HTML Report
|
||||
curl -X GET "http://localhost:8000/v1/voice-onboarding/soap-note/VOICE-AUTO-9DEC79EC?format=html" \
|
||||
-H "X-API-Key: skyheal_secure_api_key_2026"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. React / Next.js Custom Hook Example
|
||||
|
||||
```typescript
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
|
||||
export function useVoiceIntakeWebSocket(url: string = "ws://localhost:8000/v1/voice-onboarding/ws/auto") {
|
||||
const [sessionId, setSessionId] = useState<string>("");
|
||||
const [messages, setMessages] = useState<Array<any>>([]);
|
||||
const [clinicalState, setClinicalState] = useState<any>({});
|
||||
const [escalationAlert, setEscalationAlert] = useState<string | null>(null);
|
||||
const [drugWarnings, setDrugWarnings] = useState<Array<any>>([]);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ws = new WebSocket(url);
|
||||
wsRef.current = ws;
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === "connected") {
|
||||
setSessionId(data.session_id);
|
||||
} else if (data.type === "transcript") {
|
||||
setMessages(prev => [...prev, { role: data.role, text: data.text, label: data.speaker_label }]);
|
||||
} else if (data.type === "ai_response") {
|
||||
setMessages(prev => [...prev, { role: "assistant", text: data.reply, label: "AI Assistant" }]);
|
||||
if (data.audio_base64) {
|
||||
const audio = new Audio("data:audio/wav;base64," + data.audio_base64);
|
||||
audio.play().catch(console.error);
|
||||
}
|
||||
if (data.state_updates) {
|
||||
setClinicalState(data.state_updates);
|
||||
}
|
||||
} else if (data.type === "nurse_escalation_alert") {
|
||||
setEscalationAlert(data.message);
|
||||
} else if (data.type === "drug_safety_warning") {
|
||||
setDrugWarnings(data.warnings);
|
||||
}
|
||||
};
|
||||
|
||||
return () => ws.close();
|
||||
}, [url]);
|
||||
|
||||
const sendTextTurn = (text: string) => {
|
||||
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
||||
wsRef.current.send(JSON.stringify({ type: "text_turn", text }));
|
||||
}
|
||||
};
|
||||
|
||||
const interruptAI = () => {
|
||||
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
||||
wsRef.current.send(JSON.stringify({ type: "interrupt" }));
|
||||
}
|
||||
};
|
||||
|
||||
return { sessionId, messages, clinicalState, escalationAlert, drugWarnings, sendTextTurn, interruptAI };
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user