use indexed assistant prompt examples
This commit is contained in:
parent
c2ac647e29
commit
dfaf8f15db
6 changed files with 24 additions and 116 deletions
|
|
@ -485,7 +485,7 @@
|
|||
<script type="module" src="/js/chartReview.js"></script>
|
||||
<script type="module" src="/js/soap.js"></script>
|
||||
<script type="module" src="/js/milestones.js"></script>
|
||||
<script defer src="/js/peGuide.js"></script>
|
||||
<script type="module" src="/js/peGuide.js"></script>
|
||||
<script type="module" src="/js/extensions.js"></script>
|
||||
<script type="module" src="/js/notes.js"></script>
|
||||
<script type="module" src="/js/diagrams.js"></script>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
// step array and produces a Technique + Findings narrative.
|
||||
// ============================================================
|
||||
|
||||
(function () {
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// GRADING SCALES — universal reference shown at top of each system
|
||||
// ────────────────────────────────────────────────────────────
|
||||
|
|
@ -1710,5 +1708,3 @@
|
|||
if (s == null) return '';
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -44,73 +44,6 @@ var GREETING_RE = /^(hi|hello|hey|yo|good\s+(morning|afternoon|evening)|thanks|t
|
|||
var MAX_SAVED_CHATS_PER_USER = 100;
|
||||
var MAX_SAVED_CHAT_PAYLOAD = 250000;
|
||||
var MAX_SAVED_CHAT_TITLE = 160;
|
||||
var EXAMPLE_CANDIDATES = [
|
||||
{ label: 'Status asthma escalation', prompt: 'In a 4-year-old with acute wheeze, when should magnesium sulfate be considered and what dose is recommended?' },
|
||||
{ label: 'Febrile neonate', prompt: 'What initial workup and empiric antibiotics are recommended for a well-appearing febrile neonate?' },
|
||||
{ label: 'Sepsis vasoactives', prompt: 'In pediatric septic shock, when should fluid boluses be limited and vasoactives started?' },
|
||||
{ label: 'Toxic ingestion', prompt: 'How should an unknown pediatric ingestion be initially assessed and when is activated charcoal appropriate?' },
|
||||
{ label: 'Meningitis antibiotics', prompt: 'What empiric antibiotics are recommended for suspected bacterial meningitis by pediatric age group?' },
|
||||
{ label: 'Sickle fever', prompt: 'How should fever in a child with sickle cell disease be evaluated and treated?' },
|
||||
{ label: 'Head injury CT', prompt: 'Which pediatric head injury features should prompt CT imaging or ED observation?' },
|
||||
{ label: 'Anaphylaxis IM epi', prompt: 'What is the recommended IM epinephrine dose for pediatric anaphylaxis and when should it be repeated?' },
|
||||
{ label: 'Dehydration fluids', prompt: 'How should dehydration severity guide oral rehydration versus IV fluids in children?' },
|
||||
{ label: 'Neonatal jaundice', prompt: 'Which neonatal jaundice risk factors change phototherapy thresholds or urgency?' },
|
||||
{ label: 'UTI imaging', prompt: 'When should children with febrile UTI receive renal ultrasound or further imaging?' },
|
||||
{ label: 'Kawasaki workup', prompt: 'What clinical and laboratory features support incomplete Kawasaki disease evaluation?' },
|
||||
{ label: 'Intussusception', prompt: 'What are the red flags, diagnostic steps, and reduction approach for suspected intussusception?' },
|
||||
{ label: 'NAT red flags', prompt: 'What injury patterns and history features should raise concern for non-accidental trauma?' },
|
||||
{ label: 'Status epilepticus', prompt: 'Outline first- and second-line treatment for pediatric status epilepticus with typical dosing.' },
|
||||
{ label: 'Croup escalation', prompt: 'When should a child with croup receive nebulized epinephrine, and how long should they be observed?' },
|
||||
{ label: 'Bilious vomiting', prompt: 'What are the red flags for bilious vomiting in neonates, and what immediate workup is recommended?' },
|
||||
{ label: 'Bronchiolitis oxygen', prompt: 'What supportive care and oxygen/admission criteria are recommended for infant bronchiolitis?' },
|
||||
{ label: 'DKA cerebral edema', prompt: 'What warning signs suggest cerebral edema during pediatric DKA treatment, and what immediate treatment is recommended?' },
|
||||
{ label: 'Neonatal resuscitation', prompt: 'Summarize the first minute steps in neonatal resuscitation and when positive pressure ventilation is indicated.' }
|
||||
];
|
||||
var TOPIC_SUGGESTIONS = {
|
||||
asthma: [
|
||||
'How should acute pediatric asthma exacerbation severity guide SABA frequency, ipratropium, steroids, magnesium sulfate, and disposition?',
|
||||
'When should magnesium sulfate, continuous albuterol, noninvasive ventilation, ICU transfer, or intubation be considered in status asthmaticus?',
|
||||
'How can bronchiolitis, viral-induced wheeze, and early asthma be distinguished in infants and preschool children?',
|
||||
'What discharge criteria, observation period, relapse risks, and follow-up are recommended after an ED asthma exacerbation?',
|
||||
'When should controller therapy be started or stepped up after an ED asthma visit?'
|
||||
],
|
||||
bronchiolitis: [
|
||||
'What supportive care, suctioning, hydration, oxygen targets, and feeding guidance are recommended for infant bronchiolitis?',
|
||||
'When is a bronchodilator trial reasonable in bronchiolitis, and when should it be stopped?',
|
||||
'What apnea risk, work of breathing, oxygen saturation, hydration, and social factors support admission for bronchiolitis?',
|
||||
'How can bronchiolitis be distinguished from early asthma, pneumonia, cardiac disease, or foreign body aspiration?',
|
||||
'When should high-flow nasal cannula, CPAP, ICU transfer, or escalation be considered in bronchiolitis?'
|
||||
],
|
||||
fever: [
|
||||
'How should fever without source be evaluated by age group in neonates, young infants, and older children?',
|
||||
'What cultures, inflammatory markers, LP decisions, empiric antibiotics, and disposition are recommended for febrile neonates?',
|
||||
'Which clinical and laboratory features stratify risk for serious bacterial infection in febrile infants?',
|
||||
'When is lumbar puncture indicated or avoidable in febrile infants by age and risk category?',
|
||||
'How should fever be approached in immunocompromised children, central lines, or incomplete immunization?'
|
||||
],
|
||||
seizure: [
|
||||
'What is the timed pediatric status epilepticus algorithm including benzodiazepine dosing and second-line choices?',
|
||||
'How should simple versus complex febrile seizures be evaluated, treated, and explained to caregivers?',
|
||||
'When should levetiracetam, fosphenytoin, phenobarbital, valproate, anesthetic infusion, or ICU transfer be used?',
|
||||
'Which seizure presentations require neuroimaging, labs, toxicology, LP, EEG, or admission?',
|
||||
'What common seizure mimics in children should be considered and how are they distinguished?'
|
||||
],
|
||||
croup: [
|
||||
'How should croup severity guide dexamethasone, nebulized epinephrine, observation, and disposition?',
|
||||
'When is nebulized epinephrine indicated for croup and how long should the child be observed afterward?',
|
||||
'What dexamethasone dose and route are recommended for mild, moderate, and severe croup?',
|
||||
'What discharge, admission, ICU, and airway escalation criteria apply after croup treatment?',
|
||||
'What alternative diagnoses should be considered in a child with stridor or atypical croup?'
|
||||
],
|
||||
dehydration: [
|
||||
'Which clinical signs best estimate mild, moderate, and severe dehydration in children?',
|
||||
'What oral rehydration protocol, volumes, antiemetic use, and reassessment are recommended for gastroenteritis?',
|
||||
'When are IV boluses, maintenance fluids, deficit replacement, glucose, or electrolyte checks indicated?',
|
||||
'How should hypernatremic dehydration be recognized and corrected safely?',
|
||||
'What discharge, admission, and follow-up criteria apply after pediatric gastroenteritis or dehydration?'
|
||||
]
|
||||
};
|
||||
|
||||
var promptPool = createClinicalPromptPool({
|
||||
redisCache: redisCache,
|
||||
callAI: callAI,
|
||||
|
|
@ -118,9 +51,7 @@ var promptPool = createClinicalPromptPool({
|
|||
semanticSearch: semanticSearch,
|
||||
dedupeSources: dedupeSources,
|
||||
normalizeMcpSearchResponse: normalizeMcpSearchResponse,
|
||||
getIndexedTopicExamples: getIndexedTopicExamples,
|
||||
exampleCandidates: EXAMPLE_CANDIDATES,
|
||||
topicSuggestions: TOPIC_SUGGESTIONS
|
||||
getIndexedTopicExamples: getIndexedTopicExamples
|
||||
});
|
||||
|
||||
function positiveInt(value, fallback) {
|
||||
|
|
@ -386,18 +317,6 @@ async function prepareAssistantChat(body) {
|
|||
}
|
||||
|
||||
var history = Array.isArray(body.history) ? body.history.slice(-8) : [];
|
||||
var contextualHistory = priorClinicalHistory(history, message);
|
||||
var suggestions = getTopicSuggestions(message);
|
||||
if (!contextualHistory.length && suggestions.length > 0 && message.split(/\s+/).length < 4) {
|
||||
return { direct: {
|
||||
success: true,
|
||||
answer: buildSuggestionAnswer(message, suggestions),
|
||||
suggestions: suggestions,
|
||||
sources: [],
|
||||
model: null,
|
||||
search: { skipped: true, reason: 'topic_suggestions' }
|
||||
} };
|
||||
}
|
||||
|
||||
var searchQuery = await rewriteSearchQuery(message, history, chatModel).catch(function(e) {
|
||||
console.warn('[clinical-assistant] query rewrite skipped:', e.message);
|
||||
|
|
@ -503,13 +422,6 @@ function needsContextualRewrite(message) {
|
|||
return words.length <= 8 || /\b(it|this|that|they|them|he|she|dose|dosing|how much|what about|next|admit|discharge|criteria|side effects?|contraindications?|monitor|monitoring)\b/i.test(text);
|
||||
}
|
||||
|
||||
function priorClinicalHistory(history, message) {
|
||||
var latest = String(message || '').trim();
|
||||
return (Array.isArray(history) ? history : []).filter(function(m) {
|
||||
return m && (m.role === 'user' || m.role === 'assistant') && m.content && String(m.content).trim() !== latest;
|
||||
});
|
||||
}
|
||||
|
||||
function deterministicFollowupQuery(message, history) {
|
||||
var text = String(message || '').trim().toLowerCase();
|
||||
if (!/^(dose|dosing|what dose|dose\?|how much|med dose|medication dose)$/i.test(text)) return '';
|
||||
|
|
@ -716,22 +628,4 @@ function assistantErrorMessage(e) {
|
|||
return msg || 'Assistant request failed';
|
||||
}
|
||||
|
||||
function getTopicSuggestions(message) {
|
||||
var normalized = String(message || '').toLowerCase().replace(/[^a-z0-9\s-]/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
if (!normalized) return [];
|
||||
var direct = TOPIC_SUGGESTIONS[normalized];
|
||||
if (direct) return direct;
|
||||
var keys = Object.keys(TOPIC_SUGGESTIONS);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (normalized.indexOf(keys[i]) !== -1) return TOPIC_SUGGESTIONS[keys[i]];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function buildSuggestionAnswer(topic, suggestions) {
|
||||
return 'I can look up **' + topic + '**. Pick a specific clinical question so I retrieve the right cited pages instead of giving a broad summary:\n\n' +
|
||||
suggestions.map(function(s) { return '- ' + s; }).join('\n') +
|
||||
'\n\nOr type your own specific question, for example: "management of acute asthma exacerbation in a 6-year-old".';
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -41,10 +41,7 @@ function createClinicalPromptPool(opts) {
|
|||
|
||||
refreshIfNeeded(false).catch(function() {});
|
||||
|
||||
var examples = [];
|
||||
rotateExamples(opts.exampleCandidates || [], now).slice(0, 9).forEach(function(item) {
|
||||
examples.push({ label: item.label, prompt: item.prompt });
|
||||
});
|
||||
var examples = await fallbackIndexedExamples();
|
||||
|
||||
memoryCache = { expiresAt: now + cacheMs, examples: examples };
|
||||
return examples;
|
||||
|
|
|
|||
20
test/clinical-assistant-prompt-pool.test.js
Normal file
20
test/clinical-assistant-prompt-pool.test.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const root = path.join(__dirname, '..');
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(root, relativePath), 'utf8');
|
||||
}
|
||||
|
||||
test('clinical assistant starter prompts are Redis or indexed-source backed', () => {
|
||||
const route = read('src/routes/clinicalAssistant.js');
|
||||
const pool = read('src/utils/clinicalPromptPool.js');
|
||||
assert.doesNotMatch(route, /EXAMPLE_CANDIDATES|TOPIC_SUGGESTIONS|topic_suggestions|buildSuggestionAnswer/);
|
||||
assert.match(route, /createClinicalPromptPool\(\{[\s\S]*redisCache: redisCache/);
|
||||
assert.match(pool, /opts\.redisCache\.getJson\(redisKey\)/);
|
||||
assert.match(pool, /opts\.redisCache\.setJson\(redisKey, payload, ttlSeconds\)/);
|
||||
assert.match(pool, /var examples = await fallbackIndexedExamples\(\)/);
|
||||
});
|
||||
|
|
@ -20,6 +20,7 @@ const moduleEntrypoints = [
|
|||
'public/js/milestones.js',
|
||||
'public/js/nextcloud.js',
|
||||
'public/js/notes.js',
|
||||
'public/js/peGuide.js',
|
||||
'public/js/sickVisit.js',
|
||||
'public/js/soap.js',
|
||||
'public/js/secureStorage.js',
|
||||
|
|
|
|||
Loading…
Reference in a new issue