langpong
Version:
langcode api server version
391 lines (390 loc) • 14 kB
JavaScript
"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;