UNPKG

@tehreet/conduit

Version:

LLM API gateway with intelligent routing, robust process management, and health monitoring

264 lines 8.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UsageStorage = void 0; const events_1 = require("events"); const promises_1 = require("fs/promises"); const path_1 = require("path"); const os_1 = require("os"); const log_1 = require("../utils/log"); /** * Storage layer for usage data */ class UsageStorage extends events_1.EventEmitter { constructor(config = {}) { super(); this.memoryStore = []; this.isInitialized = false; this.config = { type: 'memory', retention: 30, maxSize: 10000, ...config }; this.filePath = this.config.path || (0, path_1.join)((0, os_1.homedir)(), '.conduit', 'usage.json'); } /** * Initialize storage */ async initialize() { if (this.isInitialized) { return; } (0, log_1.log)('Initializing usage storage...'); if (this.config.type === 'file') { await this.initializeFileStorage(); } else if (this.config.type === 'database') { await this.initializeDatabaseStorage(); } this.isInitialized = true; this.emit('initialized'); (0, log_1.log)(`Usage storage initialized (type: ${this.config.type})`); } /** * Store usage data */ async store(usage) { if (!this.isInitialized) { throw new Error('Storage not initialized'); } try { switch (this.config.type) { case 'memory': await this.storeInMemory(usage); break; case 'file': await this.storeInFile(usage); break; case 'database': await this.storeInDatabase(usage); break; } this.emit('stored', usage); } catch (error) { (0, log_1.log)('Error storing usage data:', error); this.emit('error', error); throw error; } } /** * Query usage data */ async query(query = {}) { if (!this.isInitialized) { throw new Error('Storage not initialized'); } try { let data = []; switch (this.config.type) { case 'memory': data = await this.queryMemory(query); break; case 'file': data = await this.queryFile(query); break; case 'database': data = await this.queryDatabase(query); break; } return this.applyFilters(data, query); } catch (error) { (0, log_1.log)('Error querying usage data:', error); this.emit('error', error); throw error; } } /** * Cleanup old data */ async cleanup(cutoffDate) { if (!this.isInitialized) { return; } const cutoff = cutoffDate || new Date(Date.now() - (this.config.retention * 24 * 60 * 60 * 1000)); try { switch (this.config.type) { case 'memory': await this.cleanupMemory(cutoff); break; case 'file': await this.cleanupFile(cutoff); break; case 'database': await this.cleanupDatabase(cutoff); break; } this.emit('cleanup', { cutoffDate: cutoff }); } catch (error) { (0, log_1.log)('Error cleaning up usage data:', error); this.emit('error', error); } } /** * Update configuration */ async updateConfig(config) { this.config = { ...this.config, ...config }; if (this.config.path) { this.filePath = this.config.path; } this.emit('config-updated', this.config); } /** * Get storage status */ getStatus() { return { initialized: this.isInitialized, type: this.config.type, entryCount: this.memoryStore.length, filePath: this.config.type === 'file' ? this.filePath : undefined }; } // Memory storage implementation async storeInMemory(usage) { this.memoryStore.push(usage); // Auto-cleanup if max size exceeded if (this.memoryStore.length > this.config.maxSize) { const excess = this.memoryStore.length - this.config.maxSize; this.memoryStore.splice(0, excess); } } async queryMemory(_query) { return [...this.memoryStore]; } async cleanupMemory(cutoffDate) { const initialCount = this.memoryStore.length; this.memoryStore = this.memoryStore.filter(usage => usage.timestamp > cutoffDate); const removedCount = initialCount - this.memoryStore.length; if (removedCount > 0) { (0, log_1.log)(`Cleaned up ${removedCount} usage entries from memory`); } } // File storage implementation async initializeFileStorage() { const dir = this.filePath.substring(0, this.filePath.lastIndexOf('/')); try { await (0, promises_1.stat)(dir); } catch { await (0, promises_1.mkdir)(dir, { recursive: true }); } // Load existing data try { const data = await (0, promises_1.readFile)(this.filePath, 'utf8'); this.memoryStore = JSON.parse(data).map((item) => ({ ...item, timestamp: new Date(item.timestamp) })); } catch { // File doesn't exist or is invalid, start fresh this.memoryStore = []; } } async storeInFile(usage) { this.memoryStore.push(usage); // Auto-cleanup if max size exceeded if (this.memoryStore.length > this.config.maxSize) { const excess = this.memoryStore.length - this.config.maxSize; this.memoryStore.splice(0, excess); } await this.saveToFile(); } async queryFile(_query) { return [...this.memoryStore]; } async cleanupFile(cutoffDate) { const initialCount = this.memoryStore.length; this.memoryStore = this.memoryStore.filter(usage => usage.timestamp > cutoffDate); const removedCount = initialCount - this.memoryStore.length; if (removedCount > 0) { await this.saveToFile(); (0, log_1.log)(`Cleaned up ${removedCount} usage entries from file`); } } async saveToFile() { const data = JSON.stringify(this.memoryStore, null, 2); await (0, promises_1.writeFile)(this.filePath, data, 'utf8'); } // Database storage implementation (placeholder) async initializeDatabaseStorage() { // TODO: Implement database storage (0, log_1.log)('Database storage not implemented, falling back to memory'); this.config.type = 'memory'; } async storeInDatabase(usage) { // TODO: Implement database storage await this.storeInMemory(usage); } async queryDatabase(query) { // TODO: Implement database storage return this.queryMemory(query); } async cleanupDatabase(cutoffDate) { // TODO: Implement database storage await this.cleanupMemory(cutoffDate); } // Helper methods applyFilters(data, query) { let filtered = data; // Filter by project ID if (query.projectId) { filtered = filtered.filter(item => item.projectId === query.projectId); } // Filter by agent ID if (query.agentId) { filtered = filtered.filter(item => item.agentId === query.agentId); } // Filter by model if (query.model) { filtered = filtered.filter(item => item.model === query.model); } // Filter by time range if (query.timeRange) { filtered = filtered.filter(item => item.timestamp >= query.timeRange.start && item.timestamp <= query.timeRange.end); } // Sort by timestamp (newest first) filtered.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); // Apply pagination if (query.offset) { filtered = filtered.slice(query.offset); } if (query.limit) { filtered = filtered.slice(0, query.limit); } return filtered; } } exports.UsageStorage = UsageStorage; //# sourceMappingURL=UsageStorage.js.map