UNPKG

langpong

Version:
391 lines (390 loc) 14 kB
"use strict"; /** * ManagerStore.ts */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ManagerStore = void 0; const events_1 = require("events"); const winston_1 = __importDefault(require("winston")); const uuid_1 = require("uuid"); const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const langcode_1 = require("langcode"); // Bu projenin gerçeğinde muhtemelen .env / config üzerinden SECRET alacaksın const JWT_SECRET = "SUPER_SECRET_KEY"; // DEMO için sabit /** * Gelişmiş ManagerStore: * - Manager bazında run queue * - INITIALIZING durumunda gelen run'ları da kuyrukluyor. * - JWT tabanlı "session ownership" kontrolü örneği * - Faiss index i manager'a dahil etme */ class ManagerStore extends events_1.EventEmitter { static getInstance(opts) { if (!this.instance) { this.instance = new ManagerStore(opts); } return this.instance; } constructor(opts) { super(); this.managers = new Map(); this.currentGlobalRuns = 0; this.nextCleanupTimeout = null; const defaults = { maxConcurrentRunsPerManager: 3, maxConcurrentRunsGlobal: 10, maxIdleTimeMs: 10 * 60 * 1000, maxLifeTimeMs: 60 * 60 * 1000, cleanupIntervalMs: 60 * 1000, logsDir: "./logs", logLevel: "info", }; this.options = Object.assign(Object.assign({}, defaults), opts); this.logger = winston_1.default.createLogger({ level: this.options.logLevel, format: winston_1.default.format.json(), transports: [ new winston_1.default.transports.Console({ format: winston_1.default.format.simple(), }), new winston_1.default.transports.File({ filename: `${this.options.logsDir}/manager_store.log`, maxsize: 10 * 1024 * 1024, maxFiles: 5, }), ], }); this.on("runComplete", () => { this.processQueueAll(); // bir run tamamlanınca tekrar kuyruğu dene (boş slot açıldı) this.scheduleNextCleanup(); }); this.on("managerCreated", () => { this.scheduleNextCleanup(); }); this.on("managerReady", () => { // manager INITIALIZING'den READY'e geçtiyse, oradaki queue'daki işleri çalıştırabiliriz this.processQueueAll(); this.scheduleNextCleanup(); }); } /** * Basit JWT doğrulama örneği: * - request'ten token al * - decode et * - userId'yi döndür * Gerçekte try/catch ile de verify yapmalısınız. */ decodeJWT(token) { const payload = jsonwebtoken_1.default.verify(token, JWT_SECRET); return payload; } /** * Session'ın sahibi, bu userId mi? */ checkSessionOwnership(sessionId, userId) { const entry = this.managers.get(sessionId); if (!entry) { throw new Error(`No manager for sessionId=${sessionId}`); } } /** * createManagerAsync: FAISS index vb. gibi ağır init senaryolarını taklit ediyor */ async createManagerAsync(pluginConfigs) { let sessionId; do { sessionId = (0, uuid_1.v4)(); } while (this.managers.has(sessionId)); const now = Date.now(); const entry = { sessionId, manager: null, status: "INITIALIZING", createdAt: now, lastUsedAt: now, activeRunsCount: 0, taskQueue: [], chains: new Map(), chainOutputs: new Map(), sharedMemory: {}, faissIndex: null }; this.managers.set(sessionId, entry); this.logger.info(`ManagerEntry created (INITIALIZING): ${sessionId}`); try { const manager = await (0, langcode_1.langcode)(pluginConfigs, { debug: false, strict: true, logFile: "./logs/langcode.log" }); entry.faissIndex = { ready: true }; entry.manager = manager; entry.status = "READY"; this.logger.info(`Manager is READY now: ${sessionId}`); } catch (err) { this.logger.error(`Manager init failed: sessionId=${sessionId}, err=${String(err)}`); this.managers.delete(sessionId); console.log(err); throw err; } this.emit("managerCreated", sessionId); this.emit("managerReady", sessionId); return sessionId; } /** * getManager */ getManager(sessionId) { const entry = this.managers.get(sessionId); if (!entry) return undefined; entry.lastUsedAt = Date.now(); if (entry.status !== "READY" || !entry.manager) return undefined; return entry.manager; } /** * ManagerEntry'ye doğrudan erişmek istersen */ getManagerEntry(sessionId) { const entry = this.managers.get(sessionId); if (entry) { entry.lastUsedAt = Date.now(); } return entry; } /** * removeManager */ removeManager(sessionId) { const removed = this.managers.delete(sessionId); if (removed) { this.logger.info(`Manager removed: ${sessionId}`); } return removed; } async getPlugin(pluginName) { return await (0, langcode_1.getPlugin)(pluginName); } async getPlugins() { return await (0, langcode_1.getPlugins)(); } /** * Plugin run talebi -> Kuyruğa eklenir veya hemen işlenir */ async runPlugin(sessionId, pluginName, pluginParams) { // const { userId } = this.decodeJWT(userToken); // this.checkSessionOwnership(sessionId, userId); return new Promise((resolve, reject) => { const entry = this.managers.get(sessionId); if (!entry) { return reject({ error: `No manager found for sessionId=${sessionId}` }); } // Bir task oluştur const task = { type: "PLUGIN_RUN", pluginName, pluginParams, resolve, reject, }; // Kuyruğa ekle entry.taskQueue.push(task); // Kuyruk işlenmeye çalışılsın this.processQueue(sessionId); }); } /** * Chain run */ async runChain(sessionId, chainId, input) { // const { userId } = this.decodeJWT(userToken); // this.checkSessionOwnership(sessionId, userId); return new Promise((resolve, reject) => { const entry = this.managers.get(sessionId); if (!entry) { return reject(new Error(`No manager found for sessionId=${sessionId}`)); } const task = { type: "CHAIN_RUN", chainId, input, resolve, reject, }; entry.taskQueue.push(task); this.processQueue(sessionId); }); } /** * CHAIN oluşturma */ createChain(sessionId, chainId, config) { // const { userId } = this.decodeJWT(userToken); // this.checkSessionOwnership(sessionId, userId); const entry = this.managers.get(sessionId); if (!entry) { throw new Error(`No manager found for sessionId=${sessionId}`); } // Basit chain mock const chain = { async run(input) { // Burada manager veya faissIndex vs. erişilebilir // entry.faissIndex.search(...) vs. (örnek) return { chainType: config.type, input, }; }, }; entry.chains.set(chainId, chain); this.logger.info(`Chain created: ${chainId}, for session=${sessionId}`); return chain; } getChainOutput(sessionId, chainId) { const entry = this.managers.get(sessionId); if (!entry) return undefined; return entry.chainOutputs.get(chainId); } putSharedData(sessionId, key, data) { ; const entry = this.managers.get(sessionId); if (!entry) { throw new Error(`No manager found for sessionId=${sessionId}`); } entry.sharedMemory[key] = data; } getSharedData(sessionId, key) { const entry = this.managers.get(sessionId); if (!entry) return undefined; return entry.sharedMemory[key]; } /** * Kuyruktaki işleri işlemeye çalışır. * - Manager "READY" mi? * - Concurrency limitleri uygun mu? * Uygunsa 1 task çekip çalıştırır, bitince "runComplete" event'ine yol açar. * Tek seferde birden çok task'ı da (kullanımdaki concurrency limitine bağlı olarak) ilerletebilirsin. */ processQueue(sessionId) { const entry = this.managers.get(sessionId); if (!entry) return; // Manager READY değilse return (kuyrukta beklemeye devam) if (entry.status !== "READY" || !entry.manager) { return; } // Şu anda manager concurrency dolu mu? while (entry.taskQueue.length > 0 && entry.activeRunsCount < this.options.maxConcurrentRunsPerManager && this.currentGlobalRuns < this.options.maxConcurrentRunsGlobal) { // bir task çek const task = entry.taskQueue.shift(); // çalıştır this.executeTask(entry, task); } } /** * Bütün managerların kuyruğunu işleyebilir. * Bir run bittiğinde global concurrency düşer => başka managerlardaki kuyruklar da ilerleyebilir. */ processQueueAll() { for (const sessionId of this.managers.keys()) { this.processQueue(sessionId); } } /** * Tekil bir task'ı hayata geçirir. */ async executeTask(entry, task) { entry.lastUsedAt = Date.now(); // concurrency slot al entry.activeRunsCount++; this.currentGlobalRuns++; const sessionId = entry.sessionId; try { if (task.type === "PLUGIN_RUN") { this.logger.debug(`executeTask(PLUGIN_RUN) start: sessionId=${sessionId}, plugin=${task.pluginName}`); const result = await entry.manager.run(task.pluginName, task.pluginParams); this.logger.info(`executeTask(PLUGIN_RUN) success: sessionId=${sessionId}, plugin=${task.pluginName}`); task.resolve(result); } else if (task.type === "CHAIN_RUN") { this.logger.debug(`executeTask(CHAIN_RUN) start: sessionId=${sessionId}, chainId=${task.chainId}`); const chain = entry.chains.get(task.chainId); if (!chain) { throw new Error(`No chain found: chainId=${task.chainId}`); } const result = await chain.run(task.input); this.logger.info(`executeTask(CHAIN_RUN) success: sessionId=${sessionId}, chainId=${task.chainId}`); entry.chainOutputs.set(task.chainId, result); task.resolve(result); } } catch (err) { this.logger.error(`executeTask error: sessionId=${sessionId}, err=${(JSON.stringify(err, null, 2))}`); task.reject(err); } finally { entry.activeRunsCount--; this.currentGlobalRuns--; // Bir "run" bitti, store'a bildirelim this.emit("runComplete", sessionId); } } /** * Reaktif temizlik (benzer mantık): * Her runComplete ve managerReady sonrasında scheduleNextCleanup. */ scheduleNextCleanup() { if (this.nextCleanupTimeout) { clearTimeout(this.nextCleanupTimeout); } this.nextCleanupTimeout = setTimeout(() => { this.cleanupExpiredManagers(); this.nextCleanupTimeout = null; }, this.options.cleanupIntervalMs); } cleanupExpiredManagers() { const now = Date.now(); for (const [sessionId, entry] of this.managers.entries()) { const idleDuration = now - entry.lastUsedAt; const lifeDuration = now - entry.createdAt; if (idleDuration > this.options.maxIdleTimeMs || lifeDuration > this.options.maxLifeTimeMs) { // Manager'ı kapat this.managers.delete(sessionId); this.logger.info(`Manager cleaned up (expired): ${sessionId}, idleDuration=${idleDuration}, lifeDuration=${lifeDuration}`); } } } shutdownAllManagers() { this.managers.clear(); if (this.nextCleanupTimeout) { clearTimeout(this.nextCleanupTimeout); } this.logger.info("All managers have been shut down."); } /** * Debug: aktif manager listesi */ listActiveManagers() { const data = []; for (const [sessionId, entry] of this.managers.entries()) { data.push({ sessionId, status: entry.status, createdAt: entry.createdAt, lastUsedAt: entry.lastUsedAt, activeRunsCount: entry.activeRunsCount, queueLength: entry.taskQueue.length, chainCount: entry.chains.size, sharedMemoryKeys: Object.keys(entry.sharedMemory).length, faissLoaded: entry.faissIndex !== null, }); } return data; } } exports.ManagerStore = ManagerStore;