Problem · Demo · How It Works · System Design · AI Pipeline · Requirements · Judging · Setup
Click the card below to open the 200 Words Write-up PDF.
Click the card below to open the Supported Voice Commands guide PDF.
|
A full periodontal exam demands 192 measurements — pocket depths, bleeding flags, recession values — dictated at full conversational speed while both hands hold instruments. Every existing voice charting tool breaks in one of three ways: |
|
ToothStream solves all three — by design, not by accident.
YOU SPEAK CHART UPDATES
────────── ─────────────
"tooth fourteen Tooth #14 buccal
buccal three depths: [3,5,4]
five four bleeding dot: ●
bleeding" cursor → lingual
│ ▲
│ 16 kHz PCM · WebSocket · 20ms chunks │
▼ │
FastAPI /ws/audio ──► Deepgram Nova-3 ──► normalizer.py ──► parser.py
[< 20 ms STT] [keyterm-boosted] [homophones] [token extractor]
│
JSON PerioPayload ◄───┘
│
clinicalRules.ts ◄── confidence gate
│
┌─────────────────┴──────────────────┐
VERIFIED SUSPICIOUS
< 50 ms Whisper + DeepSeek
│ arbitration
▼ │
State Machine ◄────────────────────────────┘
commits entry
advances cursor
┌──────────────────────────────────────────────────────────────────────┐
│ BROWSER │
│ │
│ 🎙 Microphone │
│ │ Web AudioWorklet — resamples to 16 kHz, linear16 PCM │
│ │ 20 ms chunks, linear interpolation for non-44.1 kHz inputs │
│ ▼ │
│ useDeepgramTranscription.ts │
│ │ binary WebSocket frames (max 120 pending chunks buffered) │
│ ▼ │
│ ╔══════════════════════════════════════════════════════════════╗ │
│ ║ WebSocketProvider.tsx — global chart state + orchestration ║ │
│ ║ ║ │
│ ║ transcriptParser.ts ──► clinicalRules.ts ──► VERIFIED ──┐ ║ │
│ ║ │ (7 rule checks) │ ║ │
│ ║ │ suspicious? │ ║ │
│ ║ ▼ │ ║ │
│ ║ whisperVerification.ts │ ║ │
│ ║ + deepseekDecision.ts ──────────────────────────────────┘ ║ │
│ ║ (async AI fallback, only on escalation) ║ │
│ ║ │ ║ │
│ ║ ▼ ║ │
│ ║ Clinical State Machine ║ │
│ ║ tooth → surface → sites[3] ║ │
│ ║ chart-order cursor · per-tooth undo stack ║ │
│ ║ │ ║ │
│ ║ ▼ ║ │
│ ║ PerioChart SVG (live render, 32 teeth) ║ │
│ ╚══════════════════════════════════════════════════════════════╝ │
└──────────────────────────────────────────────────────────────────────┘
▲ JSON PerioPayload
│
│ binary PCM ▼
┌──────────────────────────────────────────────────────────────────────┐
│ FASTAPI BACKEND │
│ │
│ /ws/audio │
│ ├─ receive_browser_audio() asyncio coroutine │
│ ├─ stream_audio_to_deepgram() asyncio coroutine │
│ └─ relay_deepgram_messages() asyncio coroutine │
│ │ audio queue maxsize=240, drop-oldest on overflow │
│ ▼ │
│ Deepgram Nova-3 [keyterm-boosted dental vocabulary] │
│ │ raw transcript │
│ ▼ │
│ normalizer.py [homophone + dental alias corrections] │
│ │ │
│ ▼ │
│ parser.py [token-level extractor, consumed-index] │
│ │ structured PerioPayload JSON │
│ └─────────────────────────────────────────► Browser │
│ │
│ /api/whisper-verify Oxlo whisper-large-v3 │
│ /api/deepseek-decision DeepSeek v3.2 arbitration │
│ /api/generate-report DeepSeek clinical summary │
└──────────────────────────────────────────────────────────────────────┘
|
States Cursor position Chart traversal order |
Transitions
|
Step 1 AudioWorklet captures 20 ms PCM chunk
Step 2 useDeepgramTranscription sends binary frame over WebSocket
Step 3 FastAPI relay_deepgram_messages() receives Deepgram transcript
Step 4 normalizer.py corrects homophones → parser.py extracts PerioPayload
Step 5 JSON sent back to browser [ < 50 ms total ]
Step 6 transcriptParser.ts re-parses for frontend context
Step 7 clinicalRules.ts validates: confidence · tooth range · depth range ·
triplet completeness · surface · statistical outlier (Δ > 4 from avg)
┌──────────────┬──────────────────────────────────────────────────┐
Step 8a │ VERIFIED │ State machine commits · cursor advances │
Step 8b │ SUSPICIOUS │ Whisper re-transcribes audio · DeepSeek │
│ │ arbitrates with chart context · retry Step 7 │
└──────────────┴──────────────────────────────────────────────────┘
Three layers, ordered by cost. The happy path never touches Layer 3.
|
Homophone Normalizer
Runs before parsing even starts.
|
Clinical Rules Engine
7 deterministic rule checks:
|
Whisper + DeepSeek
Activated when Layer 2 flags suspicious:
|
Result: Happy path < 50 ms · 20× faster than the 1.0 s p95 target. Layer 3 fires only when data integrity demands it.
| # | Requirement |
|---|---|
FR-01 |
Accept live browser mic audio — 16 kHz, 16-bit PCM, binary WebSocket |
FR-02 |
Transcribe in real time with interim (live feedback) + final (committed) results |
FR-03 |
Parse spoken triplets "three five four" → [3,5,4] without misreading tooth numbers |
FR-04 |
Resolve all dental surface aliases: buccal / lingual / palatal / facial / labial |
FR-05 |
Track cursor across 32 teeth × 2 surfaces × 3 sites = 192 measurement positions |
FR-06 |
Record: pocket depth · bleeding · recession · furcation class · mobility · missing · implant |
FR-07 |
Handle compound one-breath commands: "tooth 14 buccal 3 5 4 bleeding" |
FR-08 |
Correct STT homophones before parsing reaches the rules engine |
FR-09 |
Validate every payload through clinical rules before chart commit |
FR-10 |
On suspicious payload, replay audio through Whisper and arbitrate via DeepSeek |
FR-11 |
Voice "undo" — per-tooth snapshot stack, no session restart |
FR-12 |
Auto-advance cursor in chart order; handle missing-tooth skip cleanly |
FR-13 |
Export completed chart as a structured PDF report |
FR-14 |
Auto-reconnect to Deepgram on stream drop — no data loss, no manual restart |
| # | Requirement | Threshold | How It's Met |
|---|---|---|---|
NFR-01 |
End-to-end latency p95 | ≤ 1.0 s | Achieved: < 50 ms — async pipeline, zero blocking I/O |
NFR-02 |
Hard latency ceiling | ≤ 2.0 s | Deepgram Nova-3 STT < 20 ms; rules engine synchronous |
NFR-03 |
Pocket depth accuracy | ≥ 98% | 3-layer pipeline; consumed-index extraction; rules gate |
NFR-04 |
Bleeding flag accuracy | ≥ 90–95% | Token-match + homophone correction; no triplet confusion |
NFR-05 |
State collapses per session | 0 | Formal state machine; explicit commit guards throughout |
NFR-06 |
Reconnection | Auto, no data loss | Exponential backoff 0.5 s → 5.0 s; chart state in browser |
NFR-07 |
Concurrency | Multi-client ready | 3 async coroutines per session; no shared mutable state |
NFR-08 |
Audio fidelity | 16 kHz linear16 | AudioWorklet resamples via linear interpolation |
NFR-09 |
Availability | Stateless, restartable | Zero server-side session state; all chart data in browser |
NFR-10 |
Correctness gate | Confidence ≥ 0.85 | Payloads below threshold escalate; never commit silently |
How:
|
How:
|
|||||||||||||||||||||||||||
|
Zero unrecovered state collapses · No ghost entries · No wrong-surface drift · No session restart needed
|
||||||||||||||||||||||||||||
|
🎙️ Voice & Real-Time
🦷 Clinical Completeness
|
🤖 AI Accuracy
⚙️ Reliability
|
| Spoken | Action |
|---|---|
"tooth fourteen" |
Jump cursor to tooth #14 |
"buccal" · "lingual" |
Switch active surface |
"three five four" |
Commit depth triplet — mesial / mid / distal |
"bleeding" |
Mark current site DP positive |
"recession two" |
Record 2 mm recession value |
"missing" |
Mark tooth absent, auto-advance cursor |
"implant" |
Mark tooth as implant |
"furcation buccal class 2" |
Furcation class with surface metadata |
"undo" |
Pop per-tooth snapshot stack |
"next tooth" |
Advance cursor in chart order |
ToothStream/
│
├── backend/
│ ├── main.py FastAPI · /ws/audio · /api/whisper-verify
│ │ /api/deepseek-decision · /api/generate-report
│ │ Three async coroutines per WS session
│ ├── parser.py Token-level extractor · consumed-index tracking
│ └── normalizer.py Homophone + dental alias corrections
│
└── frontend/src/
├── clinicalRules.ts 7-rule validation gate ◄ Layer 2
└── components/
├── WebSocketProvider.tsx Chart state + state machine ◄ core
├── transcriptParser.ts Voice → PerioPayload
├── clinicalRulesBridge.ts Rules context + DeepSeek bridge
├── whisperVerification.ts WAV encode + Whisper fallback ◄ Layer 3a
├── deepseekDecision.ts DeepSeek arbitration ◄ Layer 3b
├── useDeepgramTranscription.ts AudioWorklet → 16 kHz PCM → WS
├── PerioChart.tsx Full-arch chart orchestrator
└── EnhancedToothCard.tsx Per-tooth SVG + depth render
Prerequisites: Python 3.10+ · Node.js 18+ · Deepgram API key (free)
git clone https://github.com/A-VISHAL/ToothStream.git
cd ToothStream|
Windows — one command start.batInstalls everything, starts both servers. |
Backend cd backend
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
echo "DEEPGRAM_API_KEY=your_key" > .env
uvicorn main:app --port 8000 --reload |
# Frontend (new terminal)
cd frontend && npm install && PORT=3002 npm start| Service | URL |
|---|---|
| Frontend | http://localhost:3002 |
| Backend | http://127.0.0.1:8000 |
| WebSocket | ws://127.0.0.1:8000/ws/audio |
