optimize technical truth

This commit is contained in:
team 1
2026-04-29 09:09:43 +02:00
parent bca015129c
commit d3d94d023e
3 changed files with 192 additions and 5 deletions

View File

@@ -8,6 +8,9 @@ document.addEventListener('DOMContentLoaded', () => {
const retriexCardsToggleEl = document.getElementById('toggle-retriex-cards');
const LAST_TURN_STORAGE_KEY = 'retriex:lastCompletedTurn';
const DETAIL_CARDS_STORAGE_KEY = 'retriex:showDetailCards';
const JOB_STATUS_POLL_INTERVAL_MS = 2500;
const JOB_COMPLETION_CATCHUP_GRACE_MS = 10000;
const JOB_CLIENT_STALE_GRACE_MS = 150000;
const state = {
abortRequested: false,
@@ -798,10 +801,13 @@ document.addEventListener('DOMContentLoaded', () => {
await new Promise((resolve, reject) => {
let finished = false;
let lastSseEventId = 0;
let completedStatusSeenAt = null;
let lastClientProgressAt = Date.now();
const source = new EventSource(`/ask-sse/${encodeURIComponent(jobId)}`);
state.eventSource = source;
let networkErrorTimer = null;
let jobStatusTimer = null;
const clearNetworkErrorTimer = () => {
if (!networkErrorTimer) {
@@ -812,13 +818,32 @@ document.addEventListener('DOMContentLoaded', () => {
networkErrorTimer = null;
};
const clearJobStatusTimer = () => {
if (!jobStatusTimer) {
return;
}
clearTimeout(jobStatusTimer);
jobStatusTimer = null;
};
const clearStreamTimers = () => {
clearNetworkErrorTimer();
clearJobStatusTimer();
};
const parsePositiveInteger = (value) => {
const parsed = Number.parseInt(String(value || ''), 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
};
const complete = () => {
if (finished) {
return;
}
finished = true;
clearNetworkErrorTimer();
clearStreamTimers();
finishEventStream();
resolve();
};
@@ -829,16 +854,107 @@ document.addEventListener('DOMContentLoaded', () => {
}
finished = true;
clearNetworkErrorTimer();
clearStreamTimers();
finishEventStream();
reject(err);
};
const completeFromStatus = (statusPayload) => {
if (finished || state.abortRequested) {
complete();
return;
}
const statusLastEventId = parsePositiveInteger(statusPayload?.lastEventId);
if (statusLastEventId > lastSseEventId) {
completedStatusSeenAt ??= Date.now();
if (Date.now() - completedStatusSeenAt < JOB_COMPLETION_CATCHUP_GRACE_MS) {
return;
}
}
finalizeStream(bubble, raw);
rememberCompletedTurn(prompt, raw);
complete();
};
const completeWithJobError = (message) => {
appendError(message || 'Der Antwort-Stream wurde beendet, bevor die Antwort abgeschlossen werden konnte.');
complete();
};
const pollJobStatus = async () => {
if (finished || state.abortRequested) {
return;
}
try {
const res = await fetch(`/ask-jobs/${encodeURIComponent(jobId)}`, {
method: 'GET',
cache: 'no-store',
headers: {'Accept': 'application/json'},
});
if (!res.ok) {
if (res.status === 404 && Date.now() - lastClientProgressAt > JOB_CLIENT_STALE_GRACE_MS) {
completeWithJobError('Der Antwort-Job wurde nicht mehr gefunden. Bitte sende die Anfrage erneut.');
}
return;
}
const statusPayload = await res.json();
const status = String(statusPayload?.status || '');
const message = String(statusPayload?.message || '').trim();
if (status === 'completed') {
completeFromStatus(statusPayload);
return;
}
if (status === 'failed') {
completeWithJobError(message || 'Der Antwort-Stream ist fehlgeschlagen. Bitte sende die Anfrage erneut.');
return;
}
if (status === 'interrupted') {
completeWithJobError(message || 'Der Antwort-Stream wurde durch einen Verbindungsabbruch unterbrochen. Bitte sende die Anfrage erneut, falls die Antwort unvollständig ist.');
return;
}
if (status === 'missing') {
completeWithJobError(message || 'Der Antwort-Job wurde nicht gefunden. Bitte sende die Anfrage erneut.');
return;
}
if (status === 'running') {
const updatedAt = parsePositiveInteger(statusPayload?.updatedAt);
const serverTime = parsePositiveInteger(statusPayload?.serverTime);
const staleAfterSeconds = parsePositiveInteger(statusPayload?.runningStaleAfterSeconds);
if (updatedAt > 0 && serverTime > 0 && staleAfterSeconds > 0 && serverTime - updatedAt > staleAfterSeconds + 15) {
completeWithJobError(message || 'Der Antwort-Job liefert seit längerer Zeit keine neuen Daten. Der Stream wurde beendet.');
return;
}
}
} catch (err) {
console.debug('Stream job status polling failed:', err);
} finally {
if (!finished && !state.abortRequested) {
jobStatusTimer = setTimeout(pollJobStatus, JOB_STATUS_POLL_INTERVAL_MS);
}
}
};
state.completeStream = complete;
state.failStream = fail;
jobStatusTimer = setTimeout(pollJobStatus, JOB_STATUS_POLL_INTERVAL_MS);
source.onopen = () => {
clearNetworkErrorTimer();
lastClientProgressAt = Date.now();
};
source.onmessage = (event) => {
@@ -848,6 +964,8 @@ document.addEventListener('DOMContentLoaded', () => {
}
clearNetworkErrorTimer();
lastClientProgressAt = Date.now();
completedStatusSeenAt = null;
if (event.data === undefined || event.data === null || event.data === '') {
return;
@@ -962,4 +1080,4 @@ document.addEventListener('DOMContentLoaded', () => {
chatEl.innerHTML = '';
addMessage('assistant', '<em>History cleared.</em>');
});
});
});