From ffffe17b30185b616a2868c59d07a5ea5a73f06a Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 8 May 2026 07:10:00 +0200 Subject: [PATCH] tighten chart review source coverage --- public/js/chartReview.js | 8 +++++++- src/routes/chartReview.js | 16 +++++++++++++--- src/utils/prompts.js | 16 ++++++++++++---- test/clinical-notes-entrypoints.test.js | 16 ++++++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/public/js/chartReview.js b/public/js/chartReview.js index 3e7e94b..07cfb4e 100644 --- a/public/js/chartReview.js +++ b/public/js/chartReview.js @@ -13,6 +13,12 @@ var visitIndex = 1; + function getVisitContent(card) { + var el = card.querySelector('.visit-content'); + if (!el) return ''; + return (el.innerText || el.textContent || '').trim(); + } + function resetChartReview() { // Clear demographics and fields ['cr-age', 'cr-pmh', 'cr-instructions', 'chart-label'].forEach(function(id) { @@ -91,7 +97,7 @@ var edVisits = []; visitsContainer.querySelectorAll('.visit-card').forEach(function(card) { - var content = card.querySelector('.visit-content').innerText.trim(); + var content = getVisitContent(card); if (!content) return; var vType = card.querySelector('.visit-type').value; var entry = { diff --git a/src/routes/chartReview.js b/src/routes/chartReview.js index 63e69fd..4e42ee1 100644 --- a/src/routes/chartReview.js +++ b/src/routes/chartReview.js @@ -35,18 +35,28 @@ router.post('/generate-chart-review', authMiddleware, async (req, res) => { 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 => { - clinicalData += `\n\n=== OUTPATIENT VISIT (${v.date}) ===\n` + wrapUserText('visit', v.content || ''); + 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 => { - clinicalData += `\n\n=== SUBSPECIALTY: ${(s.specialty || 'Subspecialty').toUpperCase()} — ${s.specialistName || 'Unknown'} (${s.date}) ===\n` + wrapUserText('consult', s.content || ''); + 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 => { - clinicalData += `\n\n=== ED VISIT (${v.date}) ===\n` + wrapUserText('ed_visit', v.content || ''); + 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); }); diff --git a/src/utils/prompts.js b/src/utils/prompts.js index 36e1217..dea8c31 100644 --- a/src/utils/prompts.js +++ b/src/utils/prompts.js @@ -135,34 +135,42 @@ ${CORE_RULES} // ======================== CHART REVIEW ======================== chartReviewOutpatient: `You are an expert at summarizing outpatient medical records for chart review/precharting. ${CORE_RULES} +Purpose: create a short guide for the next/current visit, not a full note rewrite. Format: - Start with: "[Name] is a [age] [gender] with [conditions] here today for [reason]" - Include relevant past medical/surgical history -- Last visit summary: date, what was discussed, plan, labs +- Visit timeline: briefly account for each provided visit/note in chronological order, but combine duplicative details - Recent labs with dates and values - Current medications - Subspecialty notes: date, specialist, findings, recommendations - Pending items -- Be concise — this is for quick precharting reference`, +- Prioritize details that guide today's assessment, follow-up, counseling, orders, referrals, labs, or safety checks +- Be concise — this is for quick precharting reference; avoid copying whole notes`, chartReviewSubspecialty: `You are an expert at summarizing subspecialty consultation notes. ${CORE_RULES} +Purpose: create a short guide for the next/current visit, not a full note rewrite. For each subspecialty note: - Date of visit - Specialist name/type - Key findings - Assessment - Recommendations/plan -- Follow-up plan`, +- Follow-up plan +- Pending tests, labs, referrals, medication changes, or action items +- Be concise and mention only details that affect ongoing care`, chartReviewED: `You are summarizing an ED visit for chart review/hospital course. ${CORE_RULES} +Purpose: create a short guide for the next/current visit, not a full note rewrite. Include: - Date and time of ED visit - Chief complaint - Key findings (vitals, exam, labs, imaging) - Interventions (IVF, medications, procedures) -- Disposition (admitted to where, discharge)`, +- Disposition (admitted to where, discharge) +- Pending results, return precautions, medication changes, and follow-up needs +- Be concise and mention only details that affect ongoing care`, // ======================== SOAP NOTE ======================== soapFull: `You are an expert medical scribe. Generate a complete SOAP note. diff --git a/test/clinical-notes-entrypoints.test.js b/test/clinical-notes-entrypoints.test.js index 1869a61..6a20148 100644 --- a/test/clinical-notes-entrypoints.test.js +++ b/test/clinical-notes-entrypoints.test.js @@ -121,3 +121,19 @@ test('clinical AI outputs are inserted as text or sanitized output helpers', () assert.match(read(file), pattern, `${file} should not inject generated clinical text as raw HTML`); } }); + +test('chart review preserves pasted note coverage cues', () => { + const route = read('src/routes/chartReview.js'); + const script = read('public/js/chartReview.js'); + const prompts = read('src/utils/prompts.js'); + assert.match(script, /function getVisitContent\(card\)/); + assert.match(script, /el\.innerText \|\| el\.textContent/); + assert.match(route, /SOURCE COVERAGE REQUIREMENT/); + assert.match(route, /Account for every numbered source note/); + assert.match(route, /keep the output short/); + assert.match(route, /guides the next\/current visit/); + assert.match(route, /SOURCE NOTE \$\{sourceIndex\} of \$\{noteCount\}/); + assert.match(prompts, /Purpose: create a short guide for the next\/current visit/); + assert.match(prompts, /avoid copying whole notes/); + assert.match(prompts, /Pending tests, labs, referrals, medication changes, or action items/); +});