const express = require('express'); const router = express.Router(); const { callAI } = require('../utils/ai'); const PROMPTS = require('../utils/prompts'); const { authMiddleware } = require('../middleware/auth'); var logger = require('../utils/logger'); var { wrapUserText, INJECTION_GUARD } = require('../utils/promptSafe'); // Generate chart review router.post('/generate-chart-review', authMiddleware, async (req, res) => { try { const { type, // 'outpatient' | 'subspecialty' | 'ed' patientAge, patientGender, pmh, visits, // Array of { date, type, content } subspecialty, // Array of { date, specialistName, specialty, content } edVisits, // Array of { date, content, labs } labs, // Array of { date, values } model, additionalInstructions } = req.body; const today = new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); let clinicalData = `Today's date: ${today}\nPatient: ${patientAge || 'Unknown'}, ${patientGender || 'Unknown'}`; if (pmh) clinicalData += `\nPMH: ${wrapUserText('pmh', pmh)}`; // Use the top-level review type to select the prompt — // the per-visit types only control data formatting, not the prompt. let prompt; if (type === 'ed') { prompt = PROMPTS.chartReviewED; } else if (type === 'subspecialty') { prompt = PROMPTS.chartReviewSubspecialty; } else { prompt = PROMPTS.chartReviewOutpatient; } const noteCount = (visits || []).length + (subspecialty || []).length + (edVisits || []).length; if (noteCount > 0) { prompt += `\n\nSOURCE COVERAGE REQUIREMENT:\nThe user provided ${noteCount} source note${noteCount === 1 ? '' : 's'}. Account for every numbered source note below, but keep the output short. Summarize each visit only to the extent it guides the next/current visit. Include important labs, medication changes, referrals, follow-up plans, and pending items. If a note is irrelevant or duplicative, say that briefly rather than silently ignoring it.`; clinicalData += `\n\nSource notes provided: ${noteCount}`; } let sourceIndex = 0; // Include ALL visit data regardless of per-visit type — // an outpatient chart review should include subspecialty consults too (visits || []).forEach(v => { sourceIndex += 1; clinicalData += `\n\n=== SOURCE NOTE ${sourceIndex} of ${noteCount}: OUTPATIENT VISIT (${v.date || 'no date'}) ===\n` + wrapUserText('visit', v.content || ''); if (v.labs && v.labs.trim()) clinicalData += `\n--- Labs from this visit (${v.date}) ---\n` + wrapUserText('labs', v.labs); }); (subspecialty || []).forEach(s => { sourceIndex += 1; clinicalData += `\n\n=== SOURCE NOTE ${sourceIndex} of ${noteCount}: SUBSPECIALTY: ${(s.specialty || 'Subspecialty').toUpperCase()} - ${s.specialistName || 'Unknown'} (${s.date || 'no date'}) ===\n` + wrapUserText('consult', s.content || ''); if (s.labs && s.labs.trim()) clinicalData += `\n--- Labs from this visit (${s.date}) ---\n` + wrapUserText('labs', s.labs); }); (edVisits || []).forEach(v => { sourceIndex += 1; clinicalData += `\n\n=== SOURCE NOTE ${sourceIndex} of ${noteCount}: ED VISIT (${v.date || 'no date'}) ===\n` + wrapUserText('ed_visit', v.content || ''); if (v.labs && v.labs.trim()) clinicalData += `\n--- Labs from this visit (${v.date}) ---\n` + wrapUserText('labs', v.labs); }); if (labs && labs.length > 0) { clinicalData += '\n\n=== ADDITIONAL LABS (not tied to a specific visit) ==='; labs.forEach(l => { clinicalData += `\n${l.date ? l.date + ': ' : ''}` + wrapUserText('labs', l.values || ''); }); } if (additionalInstructions) { prompt += `\n\nADDITIONAL INSTRUCTIONS (operator-supplied, trusted):\n${additionalInstructions}`; } const result = await callAI([ { role: 'system', content: prompt + INJECTION_GUARD }, { role: 'user', content: clinicalData } ], { model, maxTokens: 4000 }); res.json({ success: true, review: result.content, model: result.model }); logger.audit(req.user.id, 'generate_chart_review', 'Generated chart review', req, { category: 'clinical' }); } catch (err) { res.status(500).json({ error: 'Request failed' }); } }); module.exports = router;