- 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
71 lines
1.8 KiB
JavaScript
71 lines
1.8 KiB
JavaScript
const { EventEmitter } = require('node:events');
|
|
|
|
const events = new EventEmitter();
|
|
events.setMaxListeners(100);
|
|
|
|
let queue = [];
|
|
let running = false;
|
|
|
|
// Abort-Controller für laufende Jobs (für manuellen Abbruch)
|
|
const controllers = new Map();
|
|
|
|
/** Abort-Controller für eine Session registrieren (einmal pro Job). */
|
|
function registerController(sessionId) {
|
|
const controller = new AbortController();
|
|
controllers.set(sessionId, controller);
|
|
return controller;
|
|
}
|
|
|
|
/** Laufenden Job einer Session abbrechen. */
|
|
function abortRunning(sessionId) {
|
|
const controller = controllers.get(sessionId);
|
|
if (controller) controller.abort();
|
|
controllers.delete(sessionId);
|
|
}
|
|
|
|
/** Controller nach Job-Ende entfernen. */
|
|
function clearController(sessionId) {
|
|
controllers.delete(sessionId);
|
|
}
|
|
|
|
/** Einen Job anmelden. Läuft sofort, wenn nichts anderes läuft, sonst wartet er. */
|
|
function enqueue(job) {
|
|
queue.push(job);
|
|
events.emit('job-queued', job.sessionId);
|
|
processNext();
|
|
}
|
|
|
|
/** Job aus der Warteschlange holen und ausführen. */
|
|
async function processNext() {
|
|
if (running || queue.length === 0) return;
|
|
const job = queue.shift();
|
|
running = true;
|
|
try {
|
|
await job.run();
|
|
} catch (e) {
|
|
job.onError?.(e);
|
|
} finally {
|
|
running = false;
|
|
events.emit('job-finished', job.sessionId);
|
|
processNext();
|
|
}
|
|
}
|
|
|
|
function queuedBefore(sessionId) {
|
|
return queue.findIndex((j) => j.sessionId === sessionId);
|
|
}
|
|
|
|
function isRunning(sessionId) {
|
|
return running;
|
|
}
|
|
|
|
function cancelQueued(sessionId) {
|
|
queue = queue.filter((j) => j.sessionId !== sessionId);
|
|
}
|
|
|
|
function state() {
|
|
return { running, queued: queue.map((j) => j.sessionId) };
|
|
}
|
|
|
|
module.exports = { events, enqueue, queuedBefore, isRunning, cancelQueued, state, registerController, abortRunning, clearController };
|