- Add local Whisper (whisper.cpp / faster-whisper) as transcription provider Set TRANSCRIBE_PROVIDER=local with configurable model size and binary path - Upgrade all refine/instruction inputs to resizable textareas across encounter, dictation, hospital course, chart review, well visit, sick visit - Make AI memory injection flexible: physician preferences and corrections are now actively applied (not just "formatting reference"), while still overridable by current prompt instructions
142 lines
5.2 KiB
JavaScript
142 lines
5.2 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const { callAI } = require('../utils/ai');
|
|
const PROMPTS = require('../utils/prompts');
|
|
const { authMiddleware } = require('../middleware/auth');
|
|
|
|
// Generate hospital course
|
|
router.post('/generate-hospital-course', authMiddleware, async (req, res) => {
|
|
try {
|
|
const {
|
|
notes, // Array of { date, type, content }
|
|
edNote, // { date, content, labs }
|
|
hAndP, // { date, content }
|
|
labs, // Array of { date, values }
|
|
patientAge, patientGender, pmh,
|
|
setting, // 'floor' | 'picu' | 'nicu' | 'psych'
|
|
los, // length of stay
|
|
model,
|
|
formatPreference, // 'auto' | 'prose' | 'dayByDay' | 'organSystem'
|
|
additionalInstructions,
|
|
physicianMemories
|
|
} = req.body;
|
|
|
|
if (!notes || notes.length === 0) {
|
|
return res.status(400).json({ error: 'No notes provided' });
|
|
}
|
|
|
|
// Determine format
|
|
let prompt;
|
|
let format = formatPreference || 'auto';
|
|
|
|
if (format === 'auto') {
|
|
if (setting === 'picu' || setting === 'nicu') {
|
|
format = 'organSystem';
|
|
} else if (setting === 'psych') {
|
|
format = 'psych';
|
|
} else if (los && parseInt(los) > 3) {
|
|
format = 'dayByDay';
|
|
} else {
|
|
format = 'prose';
|
|
}
|
|
}
|
|
|
|
switch (format) {
|
|
case 'organSystem': prompt = PROMPTS.hospitalCourseICU; break;
|
|
case 'dayByDay': prompt = PROMPTS.hospitalCourseLong; break;
|
|
case 'psych': prompt = PROMPTS.hospitalCoursePsych; break;
|
|
default: prompt = PROMPTS.hospitalCourseShort;
|
|
}
|
|
|
|
// Build the complete clinical data
|
|
let clinicalData = `Patient: ${patientAge || 'Unknown'}, ${patientGender || 'Unknown'}`;
|
|
if (pmh) clinicalData += `\nPMH: ${pmh}`;
|
|
if (setting) clinicalData += `\nSetting: ${setting}`;
|
|
if (los) clinicalData += `\nLength of Stay: ${los} days`;
|
|
clinicalData += `\nFormat: ${format}`;
|
|
|
|
if (edNote) {
|
|
clinicalData += `\n\n=== ED NOTE (${edNote.date || 'date unknown'}) ===\n${edNote.content}`;
|
|
if (edNote.labs) clinicalData += `\nED Labs: ${edNote.labs}`;
|
|
}
|
|
|
|
if (hAndP) {
|
|
clinicalData += `\n\n=== H&P (${hAndP.date || 'date unknown'}) ===\n${hAndP.content}`;
|
|
}
|
|
|
|
// Sort notes by date
|
|
const sortedNotes = [...notes].sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
sortedNotes.forEach((note, i) => {
|
|
clinicalData += `\n\n=== ${(note.type || 'Progress Note').toUpperCase()} - ${note.date || `Note ${i + 1}`} ===\n${note.content}`;
|
|
});
|
|
|
|
if (labs && labs.length > 0) {
|
|
clinicalData += '\n\n=== LABS ===';
|
|
labs.forEach(lab => {
|
|
clinicalData += `\n${lab.date}: ${lab.values}`;
|
|
});
|
|
}
|
|
|
|
if (additionalInstructions) {
|
|
prompt += `\n\nADDITIONAL INSTRUCTIONS FROM PHYSICIAN:\n${additionalInstructions}`;
|
|
}
|
|
|
|
if (physicianMemories) clinicalData += '\n\n[PHYSICIAN PREFERENCES & LEARNED PATTERNS — Apply these preferences to your output. These reflect how this physician writes notes, their preferred style, terminology, and corrections from past outputs. Adapt your response accordingly, but user instructions in the current prompt take priority if they conflict.]\n' + physicianMemories + '\n[END PREFERENCES]';
|
|
|
|
const result = await callAI([
|
|
{ role: 'system', content: prompt },
|
|
{ role: 'user', content: clinicalData }
|
|
], { model, maxTokens: 6000 });
|
|
|
|
res.json({
|
|
success: true,
|
|
hospitalCourse: result.content,
|
|
format: format,
|
|
model: result.model
|
|
});
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// Ask for clarification/missing info
|
|
router.post('/hospital-course-clarify', authMiddleware, async (req, res) => {
|
|
try {
|
|
const { currentDraft, notes } = req.body;
|
|
|
|
const result = await callAI([
|
|
{ role: 'system', content: PROMPTS.askClarification },
|
|
{ role: 'user', content: `Current draft:\n${currentDraft}\n\nSource notes:\n${JSON.stringify(notes)}` }
|
|
], { model: req.body.model });
|
|
|
|
res.json({ success: true, questions: result.content, model: result.model });
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// Add discharge day info
|
|
router.post('/hospital-course-update', authMiddleware, async (req, res) => {
|
|
try {
|
|
const { currentCourse, updates, model, instructions } = req.body;
|
|
if (!currentCourse) return res.status(400).json({ error: 'No current course' });
|
|
|
|
let userMsg = `CURRENT HOSPITAL COURSE:\n${currentCourse}\n\n`;
|
|
if (updates) userMsg += `NEW UPDATES TO ADD:\n${updates}\n\n`;
|
|
if (instructions) userMsg += `INSTRUCTIONS:\n${instructions}`;
|
|
|
|
const result = await callAI([
|
|
{ role: 'system', content: `You are updating a hospital course document with new information.
|
|
Add the new information in the appropriate place. Maintain the same format and style.
|
|
If adding discharge day information, add it at the end.
|
|
${PROMPTS.refine.split('\n').slice(0, -1).join('\n')}` },
|
|
{ role: 'user', content: userMsg }
|
|
], { model, maxTokens: 6000 });
|
|
|
|
res.json({ success: true, hospitalCourse: result.content, model: result.model });
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|