// Patient Take Home — a plain-language, citation-free summary of the latest // clinical answer, generated on explicit user request only. This is a // separate synchronous model call; it never touches conversation history, // budgets, sources, or saved chats. const { PATIENT_TAKEHOME_BEHAVIOR } = require('./clinicalPrompts'); const { assistantGenerationOptions } = require('./clinicalAnswer'); const MAX_ANSWER_CHARS = 20000; const MAX_RESULT_CHARS = 12000; function stripCitationTokens(text) { // Defensive scrub: the prompt forbids citations, but never trust the model. return String(text || '') .replace(/\[(?:[0-9]+(?:\s*,\s*[0-9]+)*)\]|\[[0-9]+(?:\s*,\s*[0-9]+)*\]/g, '') .replace(/\((?:source|ref)[^)]*\)/gi, '') .replace(/\n\s*(#{1,3}\s*)?(sources?|references)\s*:?\s*$/im, '') .replace(/ {2,}/g, ' ') .trim(); } async function generatePatientTakehome(opts) { var answer = String(opts.answer || '').trim(); if (!answer) { var e = new Error('No answer to summarize'); e.statusCode = 400; throw e; } if (answer.length > MAX_ANSWER_CHARS) { e = new Error('Answer too long for a take-home sheet'); e.statusCode = 413; throw e; } var behavior = String(opts.behavior || '').trim() || PATIENT_TAKEHOME_BEHAVIOR; var messages = [ { role: 'system', content: behavior }, { role: 'user', content: 'Clinical answer:\n' + answer + '\n\nWrite the patient take-home sheet now.' } ]; var ai; try { ai = await opts.callAI(messages, assistantGenerationOptions({ model: opts.model || undefined, temperature: 0.15, maxTokens: 1800 })); } catch (err) { err = err || new Error('Take-home generation failed'); if (!err.statusCode && !err.response) err.statusCode = 502; throw err; } var text = stripCitationTokens(ai && ai.content || '').slice(0, MAX_RESULT_CHARS); if (!text) { e = new Error('The model returned an empty take-home sheet'); e.statusCode = 502; throw e; } return { text: text, model: ai.model || null, provider: ai.provider || null }; } module.exports = { MAX_ANSWER_CHARS, MAX_RESULT_CHARS, generatePatientTakehome, stripCitationTokens };