fix: reviewer findings — mhchem macro wrapping, regenerate history dedupe, strict safe image allowlist, stale docs
This commit is contained in:
parent
8b072496e2
commit
79de7ccddb
5 changed files with 12 additions and 12 deletions
|
|
@ -3,14 +3,12 @@
|
|||
`CLINICAL_ASSISTANT_CONVERSATION_CHARS` is the sole conversation budget source.
|
||||
Missing/empty uses 120000; nonempty values must validate as an integer from 1000
|
||||
through 1000000. Invalid configuration returns 503 before query rewrite,
|
||||
retrieval, handoff generation or chat provider calls. Counting is exactly
|
||||
JavaScript string length (UTF-16 code units), including all history plus draft,
|
||||
not model tokens. The old `clinical_assistant.conversation_chars` database value
|
||||
is ignored and its generic config PUT is rejected. Status retains
|
||||
`conversationChars`/`conversationUnit` and adds `conversationEnv`,
|
||||
`conversationSource`, `conversationMeasure`. Admin config exposes the same
|
||||
metadata as top-level `conversationBudget: {limit,unit,measure,env,source}`.
|
||||
Full-history storage, 8 MiB saves and explicit successful-only handoffs are unchanged.
|
||||
|
||||
## Catalogue and runtime use
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ export function safeImageUrl(src) {
|
|||
var base = (typeof window !== 'undefined' && window.location && window.location.href) || 'https://synthetic.invalid/';
|
||||
var url = new URL(s, base);
|
||||
if (url.protocol === 'http:' || url.protocol === 'https:') return url.origin === new URL(base).origin ? s : '';
|
||||
return s; // relative paths resolve to our own origin by construction
|
||||
if (url.protocol === 'file:' || url.protocol === 'javascript:' || url.protocol === 'data:') return '';
|
||||
return url.origin === new URL(base).origin ? s : ''; // relative paths resolve to our own origin only
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -398,8 +399,8 @@ export function renderLatexText(text, katex, hold) {
|
|||
if (dollars || brackets) return render(raw, dollars || brackets, display);
|
||||
if (inline !== undefined) return render(raw, inline, false);
|
||||
if (parens !== undefined) return render(raw, parens, false);
|
||||
if (ce !== undefined) return render(raw, ce, false);
|
||||
if (pu !== undefined) return render(raw, pu, false);
|
||||
if (ce !== undefined) return render(raw, '\\ce{' + ce + '}', false);
|
||||
if (pu !== undefined) return render(raw, '\\pu{' + pu + '}', false);
|
||||
return raw;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -939,7 +939,7 @@ import {
|
|||
if (typeof showToast === 'function') showToast('No question precedes this answer to regenerate.', 'error');
|
||||
return;
|
||||
}
|
||||
messages = messages.slice(0, index);
|
||||
messages = messages.slice(0, index - 1); // drop the answer AND its question; the question is replayed as the new message
|
||||
if (row && row.parentNode) row.remove();
|
||||
var input = document.getElementById('assistant-input');
|
||||
if (input) input.value = question.content;
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ test('mhchem inline delimiters render as KaTeX without eating ordinary backslash
|
|||
}
|
||||
};
|
||||
const html = renderAssistantMarkdown('Base deficit \\ce{HCO3-} and \\pu{37 C}; a plain \\ce without braces stays text.', [], { katex });
|
||||
assert.deepEqual(expressions, [{ expr: 'HCO3-', display: false }, { expr: '37 C', display: false }]);
|
||||
assert.match(html, /<span class="katex">HCO3-<\/span>/);
|
||||
assert.match(html, /<span class="katex">37 C<\/span>/);
|
||||
assert.deepEqual(expressions, [{ expr: '\\ce{HCO3-}', display: false }, { expr: '\\pu{37 C}', display: false }]);
|
||||
assert.match(html, /<span class="katex">\\ce{HCO3-}<\/span>/);
|
||||
assert.match(html, /<span class="katex">\\pu{37 C}<\/span>/);
|
||||
assert.match(html, /without braces stays text/);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -77,11 +77,12 @@ test('regenerate replays the preceding question for the latest answer and never
|
|||
secondRow.querySelector('[data-assistant-msg-regenerate]').click();
|
||||
assert.equal(app.document.getElementById('assistant-input').value, 'Second question', 'input refilled with the question');
|
||||
await new Promise(r => setImmediate(r)); await new Promise(r => setImmediate(r));
|
||||
assert.equal(c.messages.length, 4, 'old answer replaced by the regenerated one');
|
||||
assert.equal(c.messages[3].content, 'Regenerated answer.');
|
||||
assert.equal(c.messages.length, 3, 'the replayed question is sent as the new message, not duplicated in history');
|
||||
assert.equal(c.messages[2].content, 'Regenerated answer.');
|
||||
assert.equal(c.messages[0].content, 'First question', 'earlier turns untouched');
|
||||
assert.equal(c.messages[1].content, 'First answer.');
|
||||
assert.match(rows(app)[3].textContent, /Regenerated answer/);
|
||||
const all = rows(app);
|
||||
assert.match(all[all.length - 1].textContent, /Regenerated answer/);
|
||||
});
|
||||
|
||||
test('regenerate on a non-latest answer refuses honestly without touching the chat', t => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue