pediatric-ai-scribe-v3/src/routes/chartReview.js
Daniel 6c7d687dd0 Prompt-injection wrap: remaining AI routes
Applied the <UNTRUSTED_*> delimiter + INJECTION_GUARD pattern to:
  - src/routes/sickVisit.js  (chief complaint, transcript, dictation,
                               ROS, physical exam, diagnoses, style hints)
  - src/routes/wellVisit.js  (SSHADESS answers + full well-visit context)
  - src/routes/chartReview.js (PMH + all visit content + labs)
  - src/routes/hospitalCourse.js (all notes/H&P/ED + clarification
                                   & update endpoints)
  - src/routes/milestones.js (narrative + summary)

Each wraps patient-derived text in <UNTRUSTED_*>…</UNTRUSTED_*>
tags and appends the INJECTION_GUARD system instruction that tells
the model to treat wrapped content strictly as data. Operator-
supplied `additionalInstructions` stays unwrapped (trusted).
2026-04-14 05:31:54 +02:00

74 lines
3.4 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');
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;
}
// Include ALL visit data regardless of per-visit type —
// an outpatient chart review should include subspecialty consults too
(visits || []).forEach(v => {
clinicalData += `\n\n=== OUTPATIENT VISIT (${v.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 => {
clinicalData += `\n\n=== SUBSPECIALTY: ${(s.specialty || 'Subspecialty').toUpperCase()}${s.specialistName || 'Unknown'} (${s.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 => {
clinicalData += `\n\n=== ED VISIT (${v.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;