// An empty answer from the model is a failure. It used to be the only failure // that was never caught: the truncation check returns false for a zero-length // string, so nothing regenerated it and nothing raised — the empty turn was // saved into the chat and drawn as an empty bubble, then stayed in the history // where it degrades every answer after it. const test = require('node:test'); const assert = require('node:assert/strict'); const { finalizeAssistantAnswer } = require('../src/utils/clinicalAnswer'); function options(replies) { const calls = []; const queue = replies.slice(); return { calls, messages: [{ role: 'user', content: 'q' }], chatModel: 'synthetic', generationOptions: {}, callAI: async (messages, opts) => { calls.push({ messages, opts }); return { content: queue.length > 1 ? queue.shift() : queue[0], model: 'synthetic' }; } }; } test('an empty answer is asked for once more rather than returned', async () => { const o = options(['A real answer about croup.']); const out = await finalizeAssistantAnswer({ content: '' }, o); assert.equal(o.calls.length, 1, 'one retry, not none and not a loop'); assert.equal(out.answer, 'A real answer about croup.'); }); test('an answer that is empty twice raises instead of being presented as one', async () => { const o = options(['']); await assert.rejects( () => finalizeAssistantAnswer({ content: '' }, o), err => { assert.equal(err.code, 'empty_answer'); assert.equal(err.statusCode, 502); assert.match(err.message, /empty answer/i); return true; }); assert.equal(o.calls.length, 1, 'it does not keep paying for the same empty reply'); }); test('whitespace-only counts as empty — it renders as an empty bubble just the same', async () => { const o = options([' \n\n ']); await assert.rejects(() => finalizeAssistantAnswer({ content: '\n \t' }, o), err => err.code === 'empty_answer'); }); test('an answer that survives stripping is returned untouched and costs no retry', async () => { const o = options(['should not be called']); const out = await finalizeAssistantAnswer({ content: 'Croup is a viral illness.' }, o); assert.equal(out.answer, 'Croup is a viral illness.'); assert.equal(o.calls.length, 0); }); test('the caller is told to try again, because a repeat usually works', async () => { const o = options(['']); await assert.rejects(() => finalizeAssistantAnswer({ content: '' }, o), err => /try asking again|rephrase/i.test(err.message)); }); test('with no callAI to retry with, an empty answer still raises rather than returning empty', async () => { await assert.rejects( () => finalizeAssistantAnswer({ content: '' }, { messages: [] }), err => err.code === 'empty_answer'); });