fix: Streaming-Navigation-Bugs (doppelte Messages, client_disconnected), Tool-Result ohne Truncation
- _chatAbandoned-Flag statt AbortController: Backend-Stream läuft bei Navigation weiter, Frontend ignoriert Events - stopHistoryPolling() während Streaming-Poll aktiv, verhindert parallele Polls - _pollLock in pollHistory() gegen Race-Conditions bei gleichzeitigen Poll-Aufrufen - Tool-Result truncate(300) im Live-Chat entfernt, in History beibehalten - Dashboard Badge-Styling verfeinert
This commit is contained in:
1 parent
6f800e2d50
commit
7c0e5d180a
5 files changed
+30
-7
No files matched your search
@@ -75,10 +75,12 @@
|
|||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
padding: var(--badge-padding);
|
padding: var(--badge-padding);
|
||||||
background: var(--f1-bg);
|
background: var(--f2-bg);
|
||||||
border: var(--border-width) solid var(--f1-border);
|
border: var(--border-width) solid var(--f2-border);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
font-size: var(--font-size);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
#newAgentCard,
|
#newAgentCard,
|
||||||
|
|||||||
+8
-1
@@ -33,6 +33,7 @@ let isStreaming = false;
|
|||||||
let pendingFiles = []; // [{ name, type, base64 }]
|
let pendingFiles = []; // [{ name, type, base64 }]
|
||||||
let pendingRefs = []; // [{ path, from, to }] - Workspace-Dateireferenzen
|
let pendingRefs = []; // [{ path, from, to }] - Workspace-Dateireferenzen
|
||||||
let _lastDoneChainData = null; // Chain-Daten vom letzten done-Event
|
let _lastDoneChainData = null; // Chain-Daten vom letzten done-Event
|
||||||
|
let _chatAbandoned = false; // true wenn User weg-navigiert während Stream läuft
|
||||||
|
|
||||||
async function clearChat() {
|
async function clearChat() {
|
||||||
const archive = await apiArchive(getCurrentSessionId());
|
const archive = await apiArchive(getCurrentSessionId());
|
||||||
@@ -102,6 +103,8 @@ async function sendMessage() {
|
|||||||
let fullText = '';
|
let fullText = '';
|
||||||
let currentToolBox = null;
|
let currentToolBox = null;
|
||||||
|
|
||||||
|
_chatAbandoned = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await apiChat(messages, getCurrentSessionId());
|
const resp = await apiChat(messages, getCurrentSessionId());
|
||||||
const reader = resp.body.getReader();
|
const reader = resp.body.getReader();
|
||||||
@@ -111,7 +114,7 @@ async function sendMessage() {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
if (done) break;
|
if (done || _chatAbandoned) break;
|
||||||
|
|
||||||
buffer += decoder.decode(value, { stream: true });
|
buffer += decoder.decode(value, { stream: true });
|
||||||
const lines = buffer.split('\n');
|
const lines = buffer.split('\n');
|
||||||
@@ -184,6 +187,8 @@ async function sendMessage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// Bei Navigation nichts tun – der neue Page-Init übernimmt
|
||||||
|
if (_chatAbandoned) return;
|
||||||
endStreamBubble();
|
endStreamBubble();
|
||||||
// Prüfen ob Backend noch streamt (z.B. bei Netzwerkfehler)
|
// Prüfen ob Backend noch streamt (z.B. bei Netzwerkfehler)
|
||||||
try {
|
try {
|
||||||
@@ -197,6 +202,8 @@ async function sendMessage() {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
addErrorBubble('Netzwerk-Fehler: ' + e.message);
|
addErrorBubble('Netzwerk-Fehler: ' + e.message);
|
||||||
} finally {
|
} finally {
|
||||||
|
// Bei Navigation nichts aufräumen – der neue Page-Init übernimmt
|
||||||
|
if (_chatAbandoned) return;
|
||||||
if (!isStreaming || !_streamingPollInterval) {
|
if (!isStreaming || !_streamingPollInterval) {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
isStreaming = false;
|
isStreaming = false;
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ async function loadLatestArchive() {
|
|||||||
if (lastToolBox && !lastToolBox.querySelector('.tool-result')) {
|
if (lastToolBox && !lastToolBox.querySelector('.tool-result')) {
|
||||||
const rd = document.createElement('div');
|
const rd = document.createElement('div');
|
||||||
rd.className = 'tool-result' + (msg.isError ? ' tool-error' : '');
|
rd.className = 'tool-result' + (msg.isError ? ' tool-error' : '');
|
||||||
rd.textContent = truncate(msg.content, 300);
|
rd.textContent = truncate(msg.content || '', 300);
|
||||||
lastToolBox.appendChild(rd);
|
lastToolBox.appendChild(rd);
|
||||||
}
|
}
|
||||||
continue; // kein separates div
|
continue; // kein separates div
|
||||||
@@ -183,6 +183,8 @@ async function loadLatestArchive() {
|
|||||||
|
|
||||||
// ── Polling ──
|
// ── Polling ──
|
||||||
|
|
||||||
|
let _pollLock = false;
|
||||||
|
|
||||||
function startHistoryPolling(intervalMs = 3000) {
|
function startHistoryPolling(intervalMs = 3000) {
|
||||||
if (historyPollInterval) return;
|
if (historyPollInterval) return;
|
||||||
historyPollInterval = setInterval(pollHistory, intervalMs);
|
historyPollInterval = setInterval(pollHistory, intervalMs);
|
||||||
@@ -197,6 +199,8 @@ function stopHistoryPolling() {
|
|||||||
|
|
||||||
async function pollHistory(force = false) {
|
async function pollHistory(force = false) {
|
||||||
if (isStreaming && !force) return;
|
if (isStreaming && !force) return;
|
||||||
|
if (_pollLock) return;
|
||||||
|
_pollLock = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newMessages = await apiHistoryPoll(getCurrentSessionId(), lastKnownMessageId);
|
const newMessages = await apiHistoryPoll(getCurrentSessionId(), lastKnownMessageId);
|
||||||
@@ -206,5 +210,7 @@ async function pollHistory(force = false) {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('History poll fehlgeschlagen:', e);
|
console.error('History poll fehlgeschlagen:', e);
|
||||||
|
} finally {
|
||||||
|
_pollLock = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@ let _recoveryChainId = null;
|
|||||||
|
|
||||||
function startStreamingPoll(sessionId, sendBtn, chainId) {
|
function startStreamingPoll(sessionId, sendBtn, chainId) {
|
||||||
_recoveryChainId = chainId;
|
_recoveryChainId = chainId;
|
||||||
|
// History-Polling stoppen – Streaming-Poll übernimmt
|
||||||
|
stopHistoryPolling();
|
||||||
if (_streamingPollInterval) clearInterval(_streamingPollInterval);
|
if (_streamingPollInterval) clearInterval(_streamingPollInterval);
|
||||||
_streamingPollInterval = setInterval(async () => {
|
_streamingPollInterval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -29,6 +31,9 @@ function startStreamingPoll(sessionId, sendBtn, chainId) {
|
|||||||
}
|
}
|
||||||
setMeta('Bereit');
|
setMeta('Bereit');
|
||||||
_recoveryChainId = null;
|
_recoveryChainId = null;
|
||||||
|
|
||||||
|
// History-Polling wieder starten
|
||||||
|
startHistoryPolling();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Streaming-Poll fehlgeschlagen:', e);
|
console.error('Streaming-Poll fehlgeschlagen:', e);
|
||||||
@@ -274,6 +279,9 @@ registerPage('chat', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
// Stream nicht abbrechen (Backend soll weiterlaufen),
|
||||||
|
// aber Frontend-Processing stoppen
|
||||||
|
_chatAbandoned = true;
|
||||||
if (this._closeMenu) {
|
if (this._closeMenu) {
|
||||||
document.removeEventListener('click', this._closeMenu);
|
document.removeEventListener('click', this._closeMenu);
|
||||||
this._closeMenu = null;
|
this._closeMenu = null;
|
||||||
|
|||||||
+3
-3
@@ -238,7 +238,7 @@ function renderToolBoxContent(div, name, inputJson, result, isError) {
|
|||||||
|
|
||||||
const resultDiv = document.createElement('div');
|
const resultDiv = document.createElement('div');
|
||||||
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
|
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
|
||||||
resultDiv.textContent = result ? truncate(result, 300) : '';
|
resultDiv.textContent = result || '';
|
||||||
resultDiv.hidden = true;
|
resultDiv.hidden = true;
|
||||||
div.appendChild(resultDiv);
|
div.appendChild(resultDiv);
|
||||||
foldTarget = resultDiv;
|
foldTarget = resultDiv;
|
||||||
@@ -340,12 +340,12 @@ function appendToolResult(result, isError) {
|
|||||||
|
|
||||||
const existing = lastBox.querySelector('.tool-result');
|
const existing = lastBox.querySelector('.tool-result');
|
||||||
if (existing) {
|
if (existing) {
|
||||||
existing.textContent = truncate(result, 300);
|
existing.textContent = result || '';
|
||||||
if (isError) existing.classList.add('tool-error');
|
if (isError) existing.classList.add('tool-error');
|
||||||
} else {
|
} else {
|
||||||
const resultDiv = document.createElement('div');
|
const resultDiv = document.createElement('div');
|
||||||
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
|
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
|
||||||
resultDiv.textContent = truncate(result, 300);
|
resultDiv.textContent = result || '';
|
||||||
resultDiv.hidden = true;
|
resultDiv.hidden = true;
|
||||||
lastBox.appendChild(resultDiv);
|
lastBox.appendChild(resultDiv);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user