fix: keep the sidebar generated image session-only, never stored in saved chats
This commit is contained in:
parent
a7fb45515f
commit
3a8dbbcce3
3 changed files with 9 additions and 13 deletions
|
|
@ -689,12 +689,12 @@ import {
|
|||
var titleEl = document.getElementById('assistant-save-title');
|
||||
var title = String(titleEl && titleEl.value || deriveChatTitle()).trim() || deriveChatTitle();
|
||||
setBusy(true, 'Saving chat...');
|
||||
// Sidebar image is session-only and is deliberately not saved with the chat.
|
||||
return saveAssistantChat({
|
||||
title: title,
|
||||
messages: messages,
|
||||
sources: lastSources,
|
||||
lastAnswer: lastAnswer,
|
||||
generatedImage: imageForSavedChatPayload(lastGeneratedImageSrc)
|
||||
lastAnswer: lastAnswer
|
||||
})
|
||||
.then(function (data) {
|
||||
setBusy(false, 'Ready');
|
||||
|
|
@ -882,7 +882,7 @@ import {
|
|||
|
||||
function downloadTranscript() {
|
||||
var payload = { version: 2, title: deriveChatTitle(), messages: messages, sources: lastSources,
|
||||
lastAnswer: lastAnswer, generatedImage: lastGeneratedImageSrc, savedAt: new Date().toISOString() };
|
||||
lastAnswer: lastAnswer, savedAt: new Date().toISOString() };
|
||||
var url = URL.createObjectURL(new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' }));
|
||||
var link = document.createElement('a');
|
||||
link.href = url;
|
||||
|
|
@ -925,11 +925,6 @@ import {
|
|||
.finally(function() { setBusy(false, 'Ready'); });
|
||||
}
|
||||
|
||||
function imageForSavedChatPayload(src) {
|
||||
src = String(src || '');
|
||||
return src; // Preserve inline generated images; the server validates supported image formats.
|
||||
}
|
||||
|
||||
function lastAssistantMessage(items) {
|
||||
for (var i = items.length - 1; i >= 0; i--) if (items[i].role === 'assistant') return items[i].content;
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -107,9 +107,10 @@ function savedChatPayload(body) {
|
|||
messages,
|
||||
sources: savedSources(body.sources),
|
||||
lastAnswer: body.lastAnswer || '',
|
||||
generatedImage: savedImage(body.generatedImage),
|
||||
savedAt: new Date().toISOString()
|
||||
};
|
||||
// Sidebar image is session-only: validated when supplied but never stored in saved chats.
|
||||
if (body.generatedImage !== undefined && body.generatedImage !== null && body.generatedImage !== '') savedImage(body.generatedImage);
|
||||
if (Buffer.byteLength(JSON.stringify(payload), 'utf8') > MAX_SAVED_CHAT_BYTES) {
|
||||
throw failure('Saved chat exceeds the 8 MiB storage limit. Nothing was saved or truncated; download the complete transcript instead.', 413, 'SAVED_CHAT_LIMIT');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ test('real accepted route retains early turns and late corrections in rewrite an
|
|||
assert.equal(long.statusCode, 200, 'The old independent 4,000-character clipping boundary is gone');
|
||||
});
|
||||
|
||||
test('real save/reopen keeps 101 turns, Markdown, Unicode, full source metadata and generated image', async () => {
|
||||
test('real save/reopen keeps 101 turns, Markdown, Unicode and full source metadata; the sidebar image is not stored', async () => {
|
||||
const app = server();
|
||||
const content = ' Preserve indent\n\n| Item | Unit |\n|---|---|\n| 0.25 | mg/kg |\n' + '保留'.repeat(6500);
|
||||
const sources = Array.from({ length: 31 }, (_, index) => ({ number: index + 1, title: 'Title ' + index, excerpt: content, resource: 'source-' + index, page: index + 1 }));
|
||||
|
|
@ -131,7 +131,7 @@ test('real save/reopen keeps 101 turns, Markdown, Unicode, full source metadata
|
|||
const reopened = await app.request('get', '/clinical-assistant/chats/:id', {});
|
||||
assert.deepEqual(JSON.parse(JSON.stringify(reopened.body.chat.payload.messages)), messages);
|
||||
assert.equal(reopened.body.chat.payload.lastAnswer, content);
|
||||
assert.equal(reopened.body.chat.payload.generatedImage, png);
|
||||
assert.equal(reopened.body.chat.payload.generatedImage, undefined, 'The sidebar image is session-only');
|
||||
assert.equal(reopened.body.chat.payload.sources.length, 31);
|
||||
assert.equal((await app.request('get', '/clinical-assistant/chats/:id', {}, 8)).statusCode, 404);
|
||||
const oversized = await app.request('post', '/clinical-assistant/chats', { messages: [{ role: 'user', content: 'x'.repeat(policy.MAX_SAVED_CHAT_BYTES) }] });
|
||||
|
|
@ -205,7 +205,7 @@ test('actual UI blocks over-budget input without clearing draft/history or autom
|
|||
ui.dom.window.close();
|
||||
});
|
||||
|
||||
test('actual UI sends complete prior history exactly once, preserves images on save, and offers explicit handoff', async () => {
|
||||
test('actual UI sends complete prior history exactly once, does not save the sidebar image, and offers explicit handoff', async () => {
|
||||
const ui = browserUI();
|
||||
const messages = Array.from({ length: 20 }, (_, i) => ({ role: i % 2 ? 'assistant' : 'user', content: 'Turn ' + i + (i === 0 ? 'x'.repeat(1500) : ''), sources: [] }));
|
||||
ui.context.restoreSavedChat({ messages, lastAnswer: 'Previous answer.', generatedImage: png });
|
||||
|
|
@ -217,7 +217,7 @@ test('actual UI sends complete prior history exactly once, preserves images on s
|
|||
assert.equal(ui.context.messages[20].content, 'New question');
|
||||
assert.equal(ui.document.getElementById('assistant-input').value, '');
|
||||
await ui.context.saveCurrentChat();
|
||||
assert.equal(ui.calls.save[0].generatedImage, png);
|
||||
assert.equal(ui.calls.save[0].generatedImage, undefined);
|
||||
assert.equal(ui.calls.save[0].messages.length, 22);
|
||||
await ui.context.requestHandoff();
|
||||
assert.equal(ui.calls.handoff.length, 1);
|
||||
|
|
|
|||
Loading…
Reference in a new issue