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/chartReview.js"></script>
|
||||||
<script type="module" src="/js/soap.js"></script>
|
<script type="module" src="/js/soap.js"></script>
|
||||||
<script type="module" src="/js/milestones.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/extensions.js"></script>
|
||||||
<script type="module" src="/js/notes.js"></script>
|
<script type="module" src="/js/notes.js"></script>
|
||||||
<script type="module" src="/js/diagrams.js"></script>
|
<script type="module" src="/js/diagrams.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@
|
||||||
// step array and produces a Technique + Findings narrative.
|
// step array and produces a Technique + Findings narrative.
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
// ────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────
|
||||||
// GRADING SCALES — universal reference shown at top of each system
|
// GRADING SCALES — universal reference shown at top of each system
|
||||||
// ────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────
|
||||||
|
|
@ -1710,5 +1708,3 @@
|
||||||
if (s == null) return '';
|
if (s == null) return '';
|
||||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
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_CHATS_PER_USER = 100;
|
||||||
var MAX_SAVED_CHAT_PAYLOAD = 250000;
|
var MAX_SAVED_CHAT_PAYLOAD = 250000;
|
||||||
var MAX_SAVED_CHAT_TITLE = 160;
|
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({
|
var promptPool = createClinicalPromptPool({
|
||||||
redisCache: redisCache,
|
redisCache: redisCache,
|
||||||
callAI: callAI,
|
callAI: callAI,
|
||||||
|
|
@ -118,9 +51,7 @@ var promptPool = createClinicalPromptPool({
|
||||||
semanticSearch: semanticSearch,
|
semanticSearch: semanticSearch,
|
||||||
dedupeSources: dedupeSources,
|
dedupeSources: dedupeSources,
|
||||||
normalizeMcpSearchResponse: normalizeMcpSearchResponse,
|
normalizeMcpSearchResponse: normalizeMcpSearchResponse,
|
||||||
getIndexedTopicExamples: getIndexedTopicExamples,
|
getIndexedTopicExamples: getIndexedTopicExamples
|
||||||
exampleCandidates: EXAMPLE_CANDIDATES,
|
|
||||||
topicSuggestions: TOPIC_SUGGESTIONS
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function positiveInt(value, fallback) {
|
function positiveInt(value, fallback) {
|
||||||
|
|
@ -386,18 +317,6 @@ async function prepareAssistantChat(body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var history = Array.isArray(body.history) ? body.history.slice(-8) : [];
|
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) {
|
var searchQuery = await rewriteSearchQuery(message, history, chatModel).catch(function(e) {
|
||||||
console.warn('[clinical-assistant] query rewrite skipped:', e.message);
|
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);
|
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) {
|
function deterministicFollowupQuery(message, history) {
|
||||||
var text = String(message || '').trim().toLowerCase();
|
var text = String(message || '').trim().toLowerCase();
|
||||||
if (!/^(dose|dosing|what dose|dose\?|how much|med dose|medication dose)$/i.test(text)) return '';
|
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';
|
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;
|
module.exports = router;
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,7 @@ function createClinicalPromptPool(opts) {
|
||||||
|
|
||||||
refreshIfNeeded(false).catch(function() {});
|
refreshIfNeeded(false).catch(function() {});
|
||||||
|
|
||||||
var examples = [];
|
var examples = await fallbackIndexedExamples();
|
||||||
rotateExamples(opts.exampleCandidates || [], now).slice(0, 9).forEach(function(item) {
|
|
||||||
examples.push({ label: item.label, prompt: item.prompt });
|
|
||||||
});
|
|
||||||
|
|
||||||
memoryCache = { expiresAt: now + cacheMs, examples: examples };
|
memoryCache = { expiresAt: now + cacheMs, examples: examples };
|
||||||
return 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/milestones.js',
|
||||||
'public/js/nextcloud.js',
|
'public/js/nextcloud.js',
|
||||||
'public/js/notes.js',
|
'public/js/notes.js',
|
||||||
|
'public/js/peGuide.js',
|
||||||
'public/js/sickVisit.js',
|
'public/js/sickVisit.js',
|
||||||
'public/js/soap.js',
|
'public/js/soap.js',
|
||||||
'public/js/secureStorage.js',
|
'public/js/secureStorage.js',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue