diff --git a/public/js/assistant/images.js b/public/js/assistant/images.js index b5110122..da5d8976 100644 --- a/public/js/assistant/images.js +++ b/public/js/assistant/images.js @@ -285,7 +285,7 @@ async function imageSourceToBlob(src) { export function buildContextualImagePrompt(request, lastAnswer) { var prompt = String(request || '').trim(); if (!lastAnswer) return prompt; - var answer = String(lastAnswer || '').replace(/\[\d+(?:\s*,\s*\d+)*\]/g, '').slice(0, 3000); + var answer = String(lastAnswer || '').replace(/\\?\[(?:\^?\d+(?:\s*,\s*\^?\d+)*)\\?\]/g, '').slice(0, 3000); return prompt + '\n\nUse this clinical answer as the required context. Do not switch topics or introduce unrelated scenes such as gardening. Create a medical teaching visual faithful to the answer. Do not include citations, reference numbers, footnotes, source lists, or named organizations; do not invent references.\n\nAnswer:\n' + answer; } diff --git a/src/routes/clinicalAssistant.js b/src/routes/clinicalAssistant.js index 9e45e658..72e71d6d 100644 --- a/src/routes/clinicalAssistant.js +++ b/src/routes/clinicalAssistant.js @@ -66,6 +66,11 @@ function positiveInt(value, fallback) { return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback; } +function prepareClinicalImagePrompt(value) { + var prompt = String(value || '').replace(/\\?\[(?:\^?\d+(?:\s*,\s*\^?\d+)*)\\?\]/g, '').trim().slice(0, 4600); + return prompt + '\n\nDo not include citations, reference numbers, footnotes, source lists, named organizations, or invented references.'; +} + if (process.env.CLINICAL_ASSISTANT_MCP_WARMUP !== 'false') { setTimeout(function() { warmMcpSession().catch(function(e) { @@ -287,7 +292,7 @@ router.post('/clinical-assistant/image', async function(req, res) { try { var prompt = String(req.body.prompt || '').trim(); if (!prompt) return res.status(400).json({ error: 'Prompt is required' }); - if (prompt.length > 5000) prompt = prompt.substring(0, 5000); + prompt = prepareClinicalImagePrompt(prompt); var model = await getSetting('clinical_assistant.image_model', '') || process.env.CLINICAL_ASSISTANT_IMAGE_MODEL || 'openai-gpt-image-1'; var image = await generateImage(prompt, model); @@ -303,7 +308,7 @@ router.post('/clinical-assistant/image/jobs', async function(req, res) { try { var prompt = String(req.body.prompt || '').trim(); if (!prompt) return res.status(400).json({ error: 'Prompt is required' }); - if (prompt.length > 5000) prompt = prompt.substring(0, 5000); + prompt = prepareClinicalImagePrompt(prompt); var model = await getSetting('clinical_assistant.image_model', '') || process.env.CLINICAL_ASSISTANT_IMAGE_MODEL || 'openai-gpt-image-1'; var id = crypto.randomBytes(16).toString('hex'); diff --git a/test/assistant-image-intent.test.js b/test/assistant-image-intent.test.js index e61bae4e..7011f47a 100644 --- a/test/assistant-image-intent.test.js +++ b/test/assistant-image-intent.test.js @@ -13,6 +13,13 @@ test('assistant image intent does not intercept table retrieval requests', async assert.equal(isImageRequest('show me Table 13.1'), false); }); +test('clinical image prompts strip normal, escaped, and footnote citations', async () => { + const { buildContextualImagePrompt } = await imagesModule(); + const prompt = buildContextualImagePrompt('Create a teaching visual', 'Claim [1], escaped \\[2, 3\\], and footnote [^4].'); + assert.doesNotMatch(prompt, /\[1\]|\\\[2, 3\\\]|\[\^4\]/); + assert.match(prompt, /Do not include citations/); +}); + test('assistant image intent still handles explicit visual requests', async () => { const { isImageRequest } = await imagesModule(); assert.equal(isImageRequest('show me the diagram'), true);