tighten chart review source coverage

This commit is contained in:
Daniel 2026-05-08 07:10:00 +02:00
parent d48a19a891
commit ffffe17b30
4 changed files with 48 additions and 8 deletions

View file

@ -13,6 +13,12 @@
var visitIndex = 1; var visitIndex = 1;
function getVisitContent(card) {
var el = card.querySelector('.visit-content');
if (!el) return '';
return (el.innerText || el.textContent || '').trim();
}
function resetChartReview() { function resetChartReview() {
// Clear demographics and fields // Clear demographics and fields
['cr-age', 'cr-pmh', 'cr-instructions', 'chart-label'].forEach(function(id) { ['cr-age', 'cr-pmh', 'cr-instructions', 'chart-label'].forEach(function(id) {
@ -91,7 +97,7 @@
var edVisits = []; var edVisits = [];
visitsContainer.querySelectorAll('.visit-card').forEach(function(card) { visitsContainer.querySelectorAll('.visit-card').forEach(function(card) {
var content = card.querySelector('.visit-content').innerText.trim(); var content = getVisitContent(card);
if (!content) return; if (!content) return;
var vType = card.querySelector('.visit-type').value; var vType = card.querySelector('.visit-type').value;
var entry = { var entry = {

View file

@ -35,18 +35,28 @@ router.post('/generate-chart-review', authMiddleware, async (req, res) => {
prompt = PROMPTS.chartReviewOutpatient; 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 — // Include ALL visit data regardless of per-visit type —
// an outpatient chart review should include subspecialty consults too // an outpatient chart review should include subspecialty consults too
(visits || []).forEach(v => { (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); if (v.labs && v.labs.trim()) clinicalData += `\n--- Labs from this visit (${v.date}) ---\n` + wrapUserText('labs', v.labs);
}); });
(subspecialty || []).forEach(s => { (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); if (s.labs && s.labs.trim()) clinicalData += `\n--- Labs from this visit (${s.date}) ---\n` + wrapUserText('labs', s.labs);
}); });
(edVisits || []).forEach(v => { (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); if (v.labs && v.labs.trim()) clinicalData += `\n--- Labs from this visit (${v.date}) ---\n` + wrapUserText('labs', v.labs);
}); });

View file

@ -135,34 +135,42 @@ ${CORE_RULES}
// ======================== CHART REVIEW ======================== // ======================== CHART REVIEW ========================
chartReviewOutpatient: `You are an expert at summarizing outpatient medical records for chart review/precharting. chartReviewOutpatient: `You are an expert at summarizing outpatient medical records for chart review/precharting.
${CORE_RULES} ${CORE_RULES}
Purpose: create a short guide for the next/current visit, not a full note rewrite.
Format: Format:
- Start with: "[Name] is a [age] [gender] with [conditions] here today for [reason]" - Start with: "[Name] is a [age] [gender] with [conditions] here today for [reason]"
- Include relevant past medical/surgical history - 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 - Recent labs with dates and values
- Current medications - Current medications
- Subspecialty notes: date, specialist, findings, recommendations - Subspecialty notes: date, specialist, findings, recommendations
- Pending items - 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. chartReviewSubspecialty: `You are an expert at summarizing subspecialty consultation notes.
${CORE_RULES} ${CORE_RULES}
Purpose: create a short guide for the next/current visit, not a full note rewrite.
For each subspecialty note: For each subspecialty note:
- Date of visit - Date of visit
- Specialist name/type - Specialist name/type
- Key findings - Key findings
- Assessment - Assessment
- Recommendations/plan - 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. chartReviewED: `You are summarizing an ED visit for chart review/hospital course.
${CORE_RULES} ${CORE_RULES}
Purpose: create a short guide for the next/current visit, not a full note rewrite.
Include: Include:
- Date and time of ED visit - Date and time of ED visit
- Chief complaint - Chief complaint
- Key findings (vitals, exam, labs, imaging) - Key findings (vitals, exam, labs, imaging)
- Interventions (IVF, medications, procedures) - 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 ======================== // ======================== SOAP NOTE ========================
soapFull: `You are an expert medical scribe. Generate a complete SOAP note. soapFull: `You are an expert medical scribe. Generate a complete SOAP note.

View file

@ -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`); 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/);
});