optimize msg flow
This commit is contained in:
@@ -764,6 +764,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
state.eventSource = source;
|
||||
|
||||
let networkErrorTimer = null;
|
||||
let completionWatchdogTimer = null;
|
||||
let completionWatchdogInFlight = false;
|
||||
|
||||
const clearNetworkErrorTimer = () => {
|
||||
if (!networkErrorTimer) {
|
||||
@@ -774,6 +776,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
networkErrorTimer = null;
|
||||
};
|
||||
|
||||
const clearCompletionWatchdog = () => {
|
||||
if (!completionWatchdogTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(completionWatchdogTimer);
|
||||
completionWatchdogTimer = null;
|
||||
};
|
||||
|
||||
const complete = () => {
|
||||
if (finished) {
|
||||
return;
|
||||
@@ -781,6 +792,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
finished = true;
|
||||
clearNetworkErrorTimer();
|
||||
clearCompletionWatchdog();
|
||||
finishEventStream();
|
||||
resolve();
|
||||
};
|
||||
@@ -792,10 +804,73 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
finished = true;
|
||||
clearNetworkErrorTimer();
|
||||
clearCompletionWatchdog();
|
||||
finishEventStream();
|
||||
reject(err);
|
||||
};
|
||||
|
||||
const finishFromStatus = (payload) => {
|
||||
if (finished || state.abortRequested || !payload || typeof payload !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const status = String(payload.status || '');
|
||||
const serverLastEventId = Number.parseInt(String(payload.lastEventId || '0'), 10) || 0;
|
||||
|
||||
// Only finalize from the side-channel when all numbered content chunks are already visible.
|
||||
// This prevents cutting off the final answer if the browser missed more than just the terminal done event.
|
||||
if (serverLastEventId > lastSseEventId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status === 'completed') {
|
||||
finalizeStream(bubble, raw);
|
||||
rememberCompletedTurn(prompt, raw);
|
||||
complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (status === 'failed' || status === 'interrupted') {
|
||||
appendError(payload.message || 'Der Antwort-Stream wurde beendet, ohne ein Abschluss-Signal an den Browser zu senden.');
|
||||
complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const checkCompletionStatus = async () => {
|
||||
if (finished || state.abortRequested || completionWatchdogInFlight) {
|
||||
return false;
|
||||
}
|
||||
|
||||
completionWatchdogInFlight = true;
|
||||
|
||||
try {
|
||||
const statusRes = await fetch(`/ask-jobs/${encodeURIComponent(jobId)}`, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
signal: state.abortController?.signal,
|
||||
});
|
||||
|
||||
if (!statusRes.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return finishFromStatus(await statusRes.json());
|
||||
} catch (err) {
|
||||
if (!state.abortRequested) {
|
||||
console.debug('Stream completion watchdog failed:', err);
|
||||
}
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
completionWatchdogInFlight = false;
|
||||
}
|
||||
};
|
||||
|
||||
completionWatchdogTimer = setInterval(checkCompletionStatus, 2500);
|
||||
|
||||
state.completeStream = complete;
|
||||
state.failStream = fail;
|
||||
|
||||
@@ -815,6 +890,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data === '[DONE]') {
|
||||
finalizeStream(bubble, raw);
|
||||
rememberCompletedTurn(prompt, raw);
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
|
||||
const numericEventId = Number.parseInt(event.lastEventId || '', 10);
|
||||
|
||||
if (Number.isFinite(numericEventId) && numericEventId > 0) {
|
||||
@@ -849,8 +931,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
void checkCompletionStatus();
|
||||
|
||||
if (source.readyState === EventSource.CLOSED) {
|
||||
fail(new Error('EventSource connection closed'));
|
||||
window.setTimeout(async () => {
|
||||
if (finished || state.abortRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const statusCompleted = await checkCompletionStatus();
|
||||
|
||||
if (!statusCompleted && !finished) {
|
||||
fail(new Error('EventSource connection closed'));
|
||||
}
|
||||
}, 250);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user