import { logger } from "$lib/server/logger"; /** * Tracks active upstream generation requests so they can be cancelled on demand. * Multiple controllers can be registered per conversation (for threaded/background runs). */ export class AbortRegistry { private static instance: AbortRegistry; private controllers = new Map>(); public static getInstance(): AbortRegistry { if (!AbortRegistry.instance) { AbortRegistry.instance = new AbortRegistry(); } return AbortRegistry.instance; } public register(conversationId: string, controller: AbortController) { const key = conversationId.toString(); let set = this.controllers.get(key); if (!set) { set = new Set(); this.controllers.set(key, set); } set.add(controller); controller.signal.addEventListener( "abort", () => { this.unregister(key, controller); }, { once: true } ); } public abort(conversationId: string) { const set = this.controllers.get(conversationId); if (!set?.size) return; logger.debug({ conversationId }, "Aborting active generation via AbortRegistry"); for (const controller of set) { if (!controller.signal.aborted) { controller.abort(); } } this.controllers.delete(conversationId); } public unregister(conversationId: string, controller: AbortController) { const set = this.controllers.get(conversationId); if (!set) return; set.delete(controller); if (set.size === 0) { this.controllers.delete(conversationId); } } }