Recherche abbrechen + Chats löschen (mit Aufräumen) + Truncation-Fix
- Manueller Abbruch: POST /api/sessions/:id/abort (nur Owner), AbortController
für laufende Jobs (auch mitten im API-Stream), cancelQueued für wartende,
⛔-Button in der Activity-Bar, 'abgebrochen'-Event + History-Eintrag
- Chats löschen: 🗑-Button in der Sidebar mit Bestätigung, Ownership-Check
(vorher konnte jeder fremde Chats löschen), löscht jetzt auch Messages
und Dashboard-Dateien (HTML/GPX/tour.json) mit
- Truncation-Fix: finish_reason 'length' wird erkannt (sichtbarer Fehler
statt leerer Abschluss), max_tokens auf 131072 (GLM-Max), 10-Min-Timeout
gegen hängende Streams
This commit is contained in:
1 parent
0b51bb6142
commit
8a2bf46b09
8 files changed
+237
-39
No files matched your search
+59
-2
@@ -62,18 +62,52 @@ function renderSessions() {
|
||||
: s.status === 'done'
|
||||
? '<span class="w-2 h-2 rounded-full bg-emerald-400 inline-block mr-2"></span>'
|
||||
: '<span class="w-2 h-2 rounded-full bg-slate-500 inline-block mr-2"></span>';
|
||||
const row = document.createElement('div');
|
||||
row.className = 'group relative';
|
||||
const btn = document.createElement('button');
|
||||
btn.className = `w-full text-left px-3 py-2 rounded-lg text-sm flex items-start gap-1 ${
|
||||
active ? 'bg-blue-600/20 text-blue-300' : 'hover:bg-slate-800'
|
||||
}`;
|
||||
btn.innerHTML = `${dot}<span class="truncate">${escapeHtml(s.title)}</span>`;
|
||||
btn.innerHTML = `${dot}<span class="truncate flex-1">${escapeHtml(s.title)}</span>`;
|
||||
btn.onclick = () => openSession(s.id);
|
||||
list.appendChild(btn);
|
||||
const del = document.createElement('button');
|
||||
del.className =
|
||||
'absolute right-1 top-1/2 -translate-y-1/2 w-6 h-6 rounded text-xs text-slate-500 hover:text-red-400 hover:bg-slate-700 opacity-0 group-hover:opacity-100';
|
||||
del.textContent = '🗑';
|
||||
del.title = 'Chat löschen';
|
||||
del.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
deleteSession(s.id);
|
||||
};
|
||||
row.append(btn, del);
|
||||
list.appendChild(row);
|
||||
}
|
||||
const qs = state.sessions.find((s) => s.job === 'running' || s.job === 'queued');
|
||||
$('queueStatus').classList.toggle('hidden', !qs);
|
||||
}
|
||||
|
||||
async function deleteSession(id) {
|
||||
const s = state.sessions.find((x) => x.id === id);
|
||||
if (!confirm(`Chat „${s?.title || '…'}“ wirklich löschen? Das zugehörige Dashboard wird mit gelöscht.`)) return;
|
||||
const res = await fetch(`/api/sessions/${id}`, { method: 'DELETE' });
|
||||
if (!res.ok) {
|
||||
addToolLine('⚠️', 'Löschen fehlgeschlagen');
|
||||
return;
|
||||
}
|
||||
if (state.current?.id === id) {
|
||||
state.stream?.close();
|
||||
state.stream = null;
|
||||
state.streamingEl = null;
|
||||
state.current = null;
|
||||
hideActivity();
|
||||
const next = state.sessions.find((x) => x.id !== id);
|
||||
if (next) await openSession(next.id);
|
||||
else await newSession();
|
||||
} else {
|
||||
await loadSessions();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Chat-Rendering
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -187,6 +221,8 @@ function showActivity() {
|
||||
$('activityLog').classList.add('hidden');
|
||||
$('toggleLog').textContent = 'Details zeigen';
|
||||
state.activityShown = true;
|
||||
$('abortBtn').classList.remove('hidden');
|
||||
$('abortBtn').disabled = false;
|
||||
}
|
||||
|
||||
function setActivity(text, icon = '') {
|
||||
@@ -202,15 +238,29 @@ function logActivity(text) {
|
||||
|
||||
function hideActivity() {
|
||||
$('activityBar').classList.add('hidden');
|
||||
$('abortBtn').classList.add('hidden');
|
||||
state.activityShown = false;
|
||||
}
|
||||
|
||||
async function abortResearch() {
|
||||
if (!state.current) return;
|
||||
$('abortBtn').disabled = true;
|
||||
setActivity('Breche Recherche ab…', '⛔');
|
||||
try {
|
||||
await fetch(`/api/sessions/${state.current.id}/abort`, { method: 'POST' });
|
||||
} catch {
|
||||
$('abortBtn').disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
$('toggleLog').onclick = () => {
|
||||
const log = $('activityLog');
|
||||
const hidden = log.classList.toggle('hidden');
|
||||
$('toggleLog').textContent = hidden ? 'Details zeigen' : 'Details verbergen';
|
||||
};
|
||||
|
||||
$('abortBtn').onclick = abortResearch;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SSE-Stream
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -286,6 +336,13 @@ function connectStream(sessionId) {
|
||||
$('sendBtn').disabled = false;
|
||||
});
|
||||
|
||||
es.addEventListener('aborted', () => {
|
||||
addToolLine('⛔', 'Recherche abgebrochen.');
|
||||
hideActivity();
|
||||
loadSessions();
|
||||
$('sendBtn').disabled = false;
|
||||
});
|
||||
|
||||
es.addEventListener('error', (e) => {
|
||||
if (!e.data) return;
|
||||
const d = JSON.parse(e.data);
|
||||
|
||||
Reference in new issue
Block a user