AI-powered clinical decision support for licensed healthcare professionals.
InsightMD is a Next.js application that gives clinicians a conversational interface for differential diagnosis, drug interaction checking, triage guidance, SOAP note generation, and FHIR R4 EMR export — all backed by a large language model fine-tuned for clinical reasoning.
- Differential Diagnosis — Ranked differentials with ICD-10 codes, confidence levels, and red flags based on the patient presentation
- Triage Recommendations — Four-level triage system (
self-care/telehealth/urgent-care/er) with reasoning and timeframe - Clinical Scoring Tools — Automatic application of validated scores: CURB-65, Wells DVT/PE, HEART, CHADS₂-VASc, qSOFA, SOFA, PHQ-9, GAD-7, NIHSS, and more
- Drug Interaction Checking — Real-time pair-wise interaction checks powered by the OpenFDA drug label API
- Drug Lookup — Indications, warnings, and manufacturer information from FDA label data
- Clinical Tags — Structured condition cards with severity, medication recommendations (drug + dose + indication), and red flags
- SOAP Note Generation — Convert clinician dictation, interview transcripts, or pasted notes into structured SOAP format
- EMR Extraction — AI-driven extraction of patient info, vitals, diagnoses, labs, medications, and treatment plan from unstructured clinical text
- Document Upload — Attach PDF, DOCX, XLSX, TXT, CSV, and other file types; content is extracted and passed to the model for analysis
- Medical Image Analysis — Upload and analyze clinical images directly in chat
- FHIR R4 Export — Builds and sends a complete FHIR transaction Bundle (
Patient,Encounter,Condition,Observation,DocumentReference) to any sandbox FHIR endpoint - Auto-Export — Automatically export structured EMR data after each SOAP note is generated
- Connection Testing — Verify FHIR server connectivity from the settings UI
- AES-256-GCM Encryption — Bearer tokens stored in Supabase are encrypted at rest
- Voice Input — Dictate symptoms and notes via browser-based speech transcription (Hugging Face Whisper via direct transcription route)
- Persistent Chat History — All conversations saved to Supabase for authenticated users; switch between past consultations at any time
- Guest Mode — Full functionality without sign-in; chat history cached in
localStoragefor the current session - Dark / Light Mode — Theme switching with
next-themes - Animated Background — Subtle ambient animation on the landing/chat page
- Multilingual — Detects the user's language and responds entirely in that language
| Layer | Technology |
|---|---|
| Framework | Next.js 16, React 19, TypeScript |
| Styling | Tailwind CSS v4, Shadcn UI, Radix UI |
| AI Model | Featherless AI — Gemma 3 27B (text + vision) |
| AI SDK | Vercel AI SDK (ai, @ai-sdk/openai, @ai-sdk/react) |
| Authentication | Clerk (@clerk/nextjs) |
| Database | Supabase PostgreSQL (@supabase/supabase-js) |
| Voice | Hugging Face Whisper (via /api/speech/transcribe) |
| Drug Data | OpenFDA Drug Label API, RxNorm API |
| FHIR | HAPI FHIR R4 (sandbox) |
| Document Parsing | pdf-parse, mammoth (DOCX), xlsx (spreadsheets), jszip |
| Markdown Rendering | react-markdown, remark-gfm |
| Validation | Zod |
| Dev Tools | ESLint, concurrently, Docker Compose |
InsightMD/
├── app/
│ ├── layout.tsx # Root layout — Clerk, ThemeProvider, AnimatedBackground
│ ├── page.tsx # Main chat page
│ ├── globals.css # Global styles
│ ├── api/
│ │ ├── chat/route.ts # Streaming AI chat endpoint
│ │ ├── speech/transcribe/ # Voice transcription endpoint
│ │ └── emr/
│ │ ├── export/route.ts # FHIR R4 bundle export
│ │ ├── auto-export/route.ts# Automatic post-SOAP export
│ │ ├── settings/route.ts # Read/write EMR settings
│ │ └── test/route.ts # FHIR connection test
│ ├── settings/emr/ # EMR settings UI page
│ ├── sign-in/ # Clerk sign-in
│ └── sign-up/ # Clerk sign-up
│
├── components/
│ ├── chat/
│ │ ├── ChatInterface.tsx # Top-level chat shell with sidebar
│ │ ├── ChatInput.tsx # Message input with file/image/voice attach
│ │ ├── MessageList.tsx # Scrollable message history
│ │ ├── MessageBubble.tsx # Individual message with markdown rendering
│ │ ├── TriageBadge.tsx # Visual triage level indicator
│ │ ├── ClinicalTagsCard.tsx # Condition + medication + red flag cards
│ │ ├── DrugInteractionCard.tsx # Drug interaction results card
│ │ ├── ImagePreview.tsx # Inline image preview in chat
│ │ └── VoiceButton.tsx # Push-to-talk voice input button
│ ├── emr/
│ │ └── EMRPanel.tsx # EMR export panel shown after SOAP extraction
│ ├── ui/ # Shadcn UI primitives (button, card, badge, etc.)
│ ├── AnimatedBackground.tsx # Canvas/CSS background animation
│ └── ThemeProvider.tsx # next-themes provider wrapper
│
├── lib/
│ ├── ai/
│ │ ├── model.ts # Featherless model configuration
│ │ ├── system-prompt.ts # Full clinical system prompt (OPQRST, knowledge base)
│ │ ├── tools.ts # AI tool definitions (triage, drug check, EMR extract, etc.)
│ │ ├── medical-rag.ts # Lightweight medical reference retrieval
│ │ ├── emr-extractor.ts # AI-powered EMR/SOAP structured extraction
│ │ └── document-extractor.ts # File content extraction (PDF, DOCX, XLSX, etc.)
│ ├── api/
│ │ ├── openfda.ts # OpenFDA drug label + interaction queries
│ │ └── rxnorm.ts # RxNorm drug normalization
│ ├── emr/
│ │ ├── detection.ts # Detect SOAP payloads vs. generation requests
│ │ ├── fast-soap-parser.ts # Zero-AI regex SOAP parser for structured notes
│ │ ├── export-payload.ts # Build FHIR export payload from EMR entry
│ │ ├── send-export.ts # POST FHIR bundle to configured endpoint
│ │ └── settings-store.ts # Read/write EMR settings from Supabase
│ ├── fhir/
│ │ └── buildBundle.ts # Construct FHIR R4 transaction Bundle
│ ├── security/
│ │ └── encryption.ts # AES-256-GCM encrypt/decrypt for bearer tokens
│ ├── supabase/
│ │ ├── client.ts # Browser Supabase client
│ │ ├── server.ts # Server-side Supabase client (SSR)
│ │ ├── admin.ts # Service-role admin client
│ │ ├── chats.ts # Chat + message CRUD helpers
│ │ └── migrations.sql # Chat schema migration
│ └── utils/
│ ├── utils.ts # Tailwind cn() helper
│ └── image-compression.ts # Client-side image compression before upload
│
├── hooks/
│ └── useVoiceInput.ts # React hook for browser voice recording
│
├── types/
│ ├── chat.ts # Chat, Message, DrugInteraction, ClinicalTags types
│ └── emr.ts # EMREntry, VitalSigns, Diagnosis, SOAP types
│
├── supabase/migrations/
│ ├── 001_emr_connections.sql # EMR connections table
│ └── 002_emr_app_settings.sql # App-level EMR settings table
│
├── test-emr/ # Local EMR test harness (see below)
├── docs/
│ └── emr_integration.md # EMR integration reference
├── middleware.ts # Clerk auth middleware
├── next.config.ts
├── tsconfig.json
└── eslint.config.mjs
- Node.js 18+
- npm or yarn
- API keys: Featherless, Clerk, Supabase
- (Optional) Hugging Face API key for voice transcription
- (Optional) Docker for local FHIR testing
# Clone the repository
git clone <repo-url>
cd InsightMD
# Install dependencies
npm install
# Copy and fill in environment variables
cp .env.local.example .env.local
# Run the development server
npm run devOpen http://localhost:3000 to use the app.
Create a .env.local file in the project root.
# Featherless AI — https://featherless.ai/dashboard
FEATHERLESS_API_KEY=your_featherless_api_key# https://dashboard.clerk.com → API Keys
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...# https://supabase.com → Project Settings → API
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ... # server-side only, never expose to the browser# Hugging Face token for Whisper transcription
HUGGINGFACE_API_KEY=hf_...# Must decode to exactly 32 bytes — used for AES-256-GCM bearer token encryption
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
EMR_TOKEN_ENCRYPTION_KEY=your_base64_32_byte_keyApply the Supabase migrations in order:
-- 1. Chat history schema
-- Run: lib/supabase/migrations.sql
-- 2. EMR connections table
-- Run: supabase/migrations/001_emr_connections.sql
-- 3. EMR app settings table
-- Run: supabase/migrations/002_emr_app_settings.sqlOr via the Supabase SQL editor, paste and run each file.
- Access the app without signing in
- Chat history is stored in
localStoragefor the current browser session - Refreshing the page preserves your chat; opening a new chat clears it
- Sign in with Clerk (email/password or OAuth)
- All chats and messages are saved to Supabase
- Access your full history from the sidebar and switch between past consultations at any time
- Click the microphone button in the chat input to start recording
- Speech is transcribed and inserted as text into the message field
- Requires a Hugging Face API key configured in
.env.local
- Attach files directly in the chat input (PDF, DOCX, XLSX, TXT, CSV, images, and more)
- Document text is extracted server-side and passed to the model for analysis
- Images are sent directly to the vision model
The model has access to five built-in tools that are called automatically based on context:
| Tool | Trigger | Description |
|---|---|---|
set_triage_level |
Every clinical assessment | Sets the triage badge (self-care / telehealth / urgent-care / er) with reasoning and timeframe |
generate_clinical_tags |
Alongside triage | Produces structured condition cards with ICD-10, severity, medication recommendations, and red flags |
check_drug_interactions |
≥ 2 medications mentioned | Queries OpenFDA label data for pairwise drug interactions |
get_drug_info |
Specific drug question | Looks up indications, side effects, and warnings from FDA labels |
extract_emr |
Dictation / SOAP / document upload | Extracts a full structured EMR entry (SOAP format) from unstructured clinical text |
InsightMD supports sandbox-only FHIR R4 export. It is not connected to Epic, Cerner, or any production EMR system.
- Navigate to
/settings/emr - Enter your FHIR Base URL (e.g.
http://localhost:8080/fhir) - Optionally add a Bearer Token and enable Auto-Export
- Click Test Connection — this calls
GET {FHIR_BASE_URL}/metadata
When a SOAP note is generated, the app can export a FHIR R4 transaction Bundle containing:
PatientEncounterCondition(0–n)Observation(0–n, vitals extracted from note)DocumentReference(SOAP note + transcript as base64 attachment)
Manual export: click the export button in the EMR panel after a SOAP note is generated.
Auto-export: toggle in /settings/emr; the bundle is sent automatically after each extraction.
curl -i http://localhost:3000/api/emr/testThe test-emr/ directory contains a local test harness for validating the EMR integration end-to-end without a real FHIR server.
node test-emr/server.jsOpens a browser UI at http://localhost:8080 that displays every exported bundle in a readable layout as they arrive. Configure the app's FHIR Base URL to http://localhost:8080/fhir.
# Start with the app
npm run dev:emr
# Or start HAPI FHIR separately
docker compose -f test-emr/docker-compose.yml up -dFHIR metadata endpoint: http://localhost:8080/fhir/metadata
.\test-emr\quick-test.ps1
# Custom URLs
.\test-emr\quick-test.ps1 -AppUrl "http://localhost:3000" -FhirUrl "http://localhost:8080/fhir"docker compose -f test-emr/docker-compose.yml down
# or
npm run emr:down| Script | Description |
|---|---|
npm run dev |
Start the Next.js development server |
npm run dev:emr |
Start Next.js + HAPI FHIR (Docker) concurrently |
npm run dev:emr:detached |
Start HAPI FHIR in background, then Next.js |
npm run emr:down |
Stop the Docker FHIR server |
npm run build |
Production build |
npm run start |
Start the production server |
npm run lint |
Run ESLint |
| Method | Path | Description |
|---|---|---|
POST |
/api/chat |
Streaming AI chat with tool calling |
POST |
/api/speech/transcribe |
Voice audio → text transcription |
POST |
/api/emr/export |
Build and send FHIR R4 bundle |
POST |
/api/emr/auto-export |
Automatic export triggered post-SOAP |
GET/POST |
/api/emr/settings |
Read/write EMR configuration |
GET |
/api/emr/test |
Test FHIR server connectivity |
InsightMD is a clinical decision support tool intended exclusively for use by licensed healthcare professionals. It is not a replacement for clinical judgment.
- All AI-generated notes are drafts and require clinician review and attestation before inclusion in official medical records
- Medication recommendations reflect evidence-based guidelines; prescribing authority remains with the licensed clinician
- EMR export is for sandbox/demo environments only — not for use with production patient data
MIT