const { test } = require('node:test'); const assert = require('node:assert/strict'); const path = require('node:path'); const { pathToFileURL } = require('node:url'); async function imagesModule() { return import(pathToFileURL(path.join(__dirname, '..', 'public/js/assistant/images.js')).href); } test('assistant image intent does not intercept table retrieval requests', async () => { const { isImageRequest } = await imagesModule(); assert.equal(isImageRequest('can you show me the table?'), false); assert.equal(isImageRequest('show me Table 13.1'), false); }); test('assistant image intent still handles explicit visual requests', async () => { const { isImageRequest } = await imagesModule(); assert.equal(isImageRequest('show me the diagram'), true); assert.equal(isImageRequest('create an infographic for asthma'), true); }); // A run of image turns used to make every acknowledgement produce another image: // the model saw its own "I'll generate an educational image…" in the history and // repeated it for "Окей" and "Nice". The tool is withheld for short follow-ups // that say nothing about a picture, which does not depend on recognising an // acknowledgement in any particular language. function imageToolGuard() { const fs = require('node:fs'); const src = fs.readFileSync(path.join(__dirname, '..', 'src/routes/clinicalAssistant.js'), 'utf8'); const start = src.indexOf('const IMAGE_REQUEST_PATTERN'); const end = src.indexOf('async function dispatchImageRequestFallback'); assert.ok(start > 0 && end > start, 'image guard block located'); return new Function(src.slice(start, end) + '; return { withholdImageTool, mentionsImage };')(); } const afterImage = [ { role: 'user', content: 'Can you illustrate with an image' }, { role: 'assistant', content: "I'll generate an educational image illustrating the imaging findings of periventricular leukomalacia." } ]; const afterText = [ { role: 'user', content: 'periventricular leukomalacia' }, { role: 'assistant', content: 'PVL is a disorder of the periventricular white matter.' } ]; test('acknowledgements after an image turn do not get the image tool, in any language', () => { const { withholdImageTool } = imageToolGuard(); for (const ack of ['ok', 'okay', 'Nice', 'Perfect', 'thanks!', 'Окей', 'bien', 'ç', '👍', 'got it']) { assert.equal(withholdImageTool(ack, afterImage), true, JSON.stringify(ack) + ' must not trigger another image'); } }); test('real image follow-ups keep the tool, including terse repeat requests', () => { const { withholdImageTool } = imageToolGuard(); for (const ask of ['again', 'another one', 'ещё', 'redo', 'make it bigger with labels', 'now show the MRI diagram', 'can you add a figure for the cystic phase']) { assert.equal(withholdImageTool(ask, afterImage), false, JSON.stringify(ask) + ' is a real image request'); } }); test('the guard only applies after an image turn and never blocks a first request', () => { const { withholdImageTool } = imageToolGuard(); assert.equal(withholdImageTool('Nice', afterText), false, 'no preceding image turn, nothing to repeat'); assert.equal(withholdImageTool('Окей', []), false, 'an empty conversation cannot be repeating an image'); assert.equal(withholdImageTool('draw me a diagram of the airway', afterText), false); assert.equal(withholdImageTool('Explain the pathophysiology of PVL in preterm infants', afterImage), false, 'a substantive question is not a short acknowledgement'); }); test('the in-chat image is a clickable thumbnail, not a full-width picture', async () => { const fs = require('node:fs'); const root = path.join(__dirname, '..'); const { createAssistantImageStore } = await import(pathToFileURL(path.join(root, 'public/js/assistant/images.js')).href); const store = createAssistantImageStore({}); const html = store.renderGeneratedImage('/api/generated-images/synthetic.png', 'Generated teaching visual'); assert.match(html, /]*data-assistant-open-image="img-\d+"/, 'the image itself opens the full-resolution preview'); assert.match(html, /]*role="button"[^>]*>/, 'and announces itself as activatable'); assert.match(html, /data-assistant-open-image="img-\d+"><\/i> Preview/, 'the Preview button still works'); const css = fs.readFileSync(path.join(root, 'public/css/assistant.css'), 'utf8'); const rule = css.split('\n').find(line => line.startsWith('.assistant-generated-image img {')); assert.ok(rule, 'the generated-image rule exists'); assert.doesNotMatch(rule, /width:100%/, 'no longer stretched to the bubble width'); assert.match(rule, /max-width:min\(100%,320px\)/, 'capped to a thumbnail'); assert.match(rule, /max-height:240px/); assert.match(rule, /cursor:zoom-in/, 'the thumbnail invites the click'); }); test('acknowledgements ask for a clinical question instead of costing a generation', () => { const fs = require('node:fs'); const src = fs.readFileSync(path.join(__dirname, '..', 'src/routes/clinicalAssistant.js'), 'utf8'); const line = src.split('\n').find(l => l.startsWith('var GREETING_RE')); assert.ok(line, 'GREETING_RE located'); const RE = new Function(line + '; return GREETING_RE;')(); for (const ack of ['ok', 'Okay', 'Окей', 'ок', 'Nice', 'Perfect', 'cool', 'great', 'thanks!', 'Thank you', 'got it', 'noted', 'bien', 'gracias', 'merci', 'danke', 'спасибо']) { assert.equal(RE.test(ack), true, JSON.stringify(ack) + ' should get the "ask me something" reply'); } // Answers end with "would you like more detail?", so these must reach the model. for (const real of ['yes', 'sure', 'no', 'more', 'yes please', 'tell me more', 'amoxicillin dose', 'PVL', 'what is IVH', 'okay but what about grade 3']) { assert.equal(RE.test(real), false, JSON.stringify(real) + ' must still be answered'); } });