sickVisit.ts — /api/sick-visit/note
wellVisit.ts — /api/well-visit/{shadess,note}
peGuide.ts — /api/generate-pe-narrative (with typed PeStep)
milestones.ts — /api/{milestones-data, generate-milestone-narrative, generate-milestone-summary}
chartReview.ts — /api/generate-chart-review (typed VisitEntry etc.)
All five follow the established pattern. Added a handful of inline
interfaces (PeStep, MilestoneItem, VisitEntry) where the existing code
was juggling anonymous object shapes — these will migrate into
shared/types.ts during Day 5 if reused elsewhere.
14 of 29 routes converted. Progress: 14/54 backend files.
tsc --noEmit green.
66 lines
2.7 KiB
TypeScript
66 lines
2.7 KiB
TypeScript
// ============================================================
|
|
// SICK VISIT ROUTE — sick visit note generation
|
|
// ============================================================
|
|
|
|
import express = require('express');
|
|
import type { Request, Response } from 'express';
|
|
const { callAI } = require('../utils/ai');
|
|
const PROMPTS = require('../utils/prompts');
|
|
const { authMiddleware } = require('../middleware/auth');
|
|
const logger = require('../utils/logger');
|
|
const { wrapUserText, INJECTION_GUARD } = require('../utils/promptSafe');
|
|
|
|
const router = express.Router();
|
|
|
|
router.post('/sick-visit/note', authMiddleware, async function (req: Request, res: Response) {
|
|
const start = Date.now();
|
|
try {
|
|
const {
|
|
patientAge, patientGender, chiefComplaint,
|
|
transcript, dictation,
|
|
ros, physicalExam, diagnoses,
|
|
physicianMemories,
|
|
model,
|
|
} = req.body;
|
|
|
|
if (!chiefComplaint) {
|
|
return res.status(400).json({ error: 'Chief complaint is required' });
|
|
}
|
|
|
|
let context = 'SICK VISIT\n';
|
|
context += 'Patient: ' + (patientAge || 'Unknown age') + ', ' + (patientGender || 'Unknown gender') + '\n';
|
|
context += 'Chief Complaint: ' + wrapUserText('chief_complaint', chiefComplaint) + '\n\n';
|
|
|
|
if (transcript) {
|
|
context += 'ENCOUNTER TRANSCRIPT/DICTATION:\n' + wrapUserText('transcript', transcript) + '\n\n';
|
|
} else if (dictation) {
|
|
context += 'PHYSICIAN DICTATION:\n' + wrapUserText('dictation', dictation) + '\n\n';
|
|
}
|
|
if (ros) context += wrapUserText('ros', ros) + '\n\n';
|
|
if (physicalExam) context += wrapUserText('physical_exam', physicalExam) + '\n\n';
|
|
if (diagnoses) context += wrapUserText('diagnoses', diagnoses) + '\n\n';
|
|
if (physicianMemories) {
|
|
context += '[STYLE HINTS (low priority — only apply if relevant to the current note, never copy content from these examples)]\n'
|
|
+ wrapUserText('style_hints', physicianMemories) + '\n[END STYLE HINTS]\n\n';
|
|
}
|
|
|
|
const result = await callAI([
|
|
{ role: 'system', content: PROMPTS.sickVisitNote + INJECTION_GUARD },
|
|
{ role: 'user', content: context },
|
|
], { model });
|
|
|
|
const dur = Date.now() - start;
|
|
logger.apiCall(req.user!.id, '/api/sick-visit/note', {
|
|
model: result.model, tokensInput: result.usage?.input_tokens,
|
|
tokensOutput: result.usage?.output_tokens, duration: dur, statusCode: 200,
|
|
});
|
|
|
|
res.json({ success: true, note: result.content, model: result.model });
|
|
logger.audit(req.user!.id, 'generate_sick_visit', 'Generated sick visit note', req, { category: 'clinical' });
|
|
} catch (e: any) {
|
|
logger.error('[SickVisit] Note generation failed', e.message);
|
|
res.status(500).json({ error: 'Request failed' });
|
|
}
|
|
});
|
|
|
|
export = router;
|