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');
}
});