UNPKG

contextual-agent-sdk

Version:

SDK for building AI agents with seamless voice-text context switching

228 lines 8.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RedisStorageProvider = void 0; const redis_1 = require("redis"); class RedisStorageProvider { client; prefix = 'session:'; eventHandlers = new Set(); cleanupTimer; constructor(config) { const clientConfig = { url: config.url, username: config.username, password: config.password }; if (config.ssl) { clientConfig.socket = { tls: true, rejectUnauthorized: false }; } this.client = (0, redis_1.createClient)(clientConfig); this.client.on('error', (error) => { this.emitEvent(this.createEvent('error', error)); }); this.client.on('reconnecting', (retries) => { if (retries > (config.retryAttempts || 3)) { this.emitEvent(this.createEvent('error', new Error('Redis connection failed after max retries'))); } }); if (config.cleanupInterval && config.maxAge) { this.cleanupTimer = setInterval(() => { this.cleanup(config.maxAge).catch(error => { this.emitEvent(this.createEvent('error', error)); }); }, config.cleanupInterval); } } async createSession(sessionId, session) { const key = this.getKey(sessionId); await this.client.set(key, JSON.stringify(session)); if (session.metadata?.maxAge) { await this.client.expire(key, Math.floor(session.metadata.maxAge / 1000)); } this.emitEvent(this.createEvent('session_created', sessionId)); } async getSession(sessionId) { const data = await this.client.get(this.getKey(sessionId)); if (!data) return null; try { const session = JSON.parse(data); session.startTime = new Date(session.startTime); session.lastActivity = new Date(session.lastActivity); session.context.memoryBank = session.context.memoryBank.map(memory => ({ ...memory, timestamp: new Date(memory.timestamp) })); return session; } catch (error) { this.emitEvent(this.createEvent('error', error)); return null; } } async updateSession(sessionId, session) { const key = this.getKey(sessionId); await this.client.set(key, JSON.stringify(session)); if (session.metadata?.maxAge) { await this.client.expire(key, Math.floor(session.metadata.maxAge / 1000)); } this.emitEvent(this.createEvent('session_updated', sessionId)); } async deleteSession(sessionId) { const result = await this.client.del(this.getKey(sessionId)); const deleted = result === 1; if (deleted) { this.emitEvent(this.createEvent('session_deleted', sessionId)); } return deleted; } async getSessions(filter) { const keys = await this.client.keys(`${this.prefix}*`); const sessions = []; for (const key of keys) { const data = await this.client.get(key); if (!data) continue; try { const session = JSON.parse(data); if (this.matchesFilter(session, filter)) { session.startTime = new Date(session.startTime); session.lastActivity = new Date(session.lastActivity); session.context.memoryBank = session.context.memoryBank.map(memory => ({ ...memory, timestamp: new Date(memory.timestamp) })); sessions.push(session); } } catch (error) { this.emitEvent(this.createEvent('error', error)); } } return sessions; } async deleteSessions(filter) { const sessions = await this.getSessions(filter); let deleted = 0; for (const session of sessions) { const success = await this.deleteSession(session.sessionId); if (success) deleted++; } return deleted; } async cleanup(maxAge) { this.emitEvent(this.createEvent('cleanup_started')); const now = Date.now(); const sessions = await this.getSessions(); let deleted = 0; for (const session of sessions) { const age = now - session.lastActivity.getTime(); if (age > maxAge) { const success = await this.deleteSession(session.sessionId); if (success) deleted++; } } this.emitEvent(this.createEvent('cleanup_completed', deleted)); } async healthCheck() { try { await this.client.ping(); return true; } catch { return false; } } async getStats() { const sessions = await this.getSessions(); const now = Date.now(); const oneDayAgo = now - 24 * 60 * 60 * 1000; const activeSessions = sessions.filter(s => s.lastActivity.getTime() > oneDayAgo); const durations = sessions.map(s => s.lastActivity.getTime() - s.startTime.getTime()); const avgDuration = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0; const modalityCount = sessions.reduce((acc, s) => { acc[s.currentModality]++; return acc; }, { text: 0, voice: 0 }); const storageSize = sessions.reduce((size, session) => { return size + JSON.stringify(session).length; }, 0); return { totalSessions: sessions.length, activeSessionsLast24h: activeSessions.length, averageSessionDuration: avgDuration, modalityDistribution: modalityCount, storageSize }; } on(handler) { this.eventHandlers.add(handler); } off(handler) { this.eventHandlers.delete(handler); } getKey(sessionId) { return `${this.prefix}${sessionId}`; } emitEvent(event) { this.eventHandlers.forEach(handler => handler(event)); } createEvent(type, data) { switch (type) { case 'session_created': case 'session_updated': case 'session_deleted': return { type, sessionId: data }; case 'cleanup_completed': return { type, deletedCount: data }; case 'error': return { type, error: data }; default: return { type }; } } matchesFilter(session, filter) { if (!filter) return true; if (filter.userId && session.userId !== filter.userId) { return false; } if (filter.modality && session.currentModality !== filter.modality) { return false; } if (filter.startTime) { const startTime = new Date(session.startTime).getTime(); if (filter.startTime.from && startTime < filter.startTime.from.getTime()) { return false; } if (filter.startTime.to && startTime > filter.startTime.to.getTime()) { return false; } } if (filter.lastActivity) { const lastActivity = new Date(session.lastActivity).getTime(); if (filter.lastActivity.from && lastActivity < filter.lastActivity.from.getTime()) { return false; } if (filter.lastActivity.to && lastActivity > filter.lastActivity.to.getTime()) { return false; } } return true; } async shutdown() { if (this.cleanupTimer) { clearInterval(this.cleanupTimer); } await this.client.quit(); } } exports.RedisStorageProvider = RedisStorageProvider; //# sourceMappingURL=RedisStorageProvider.js.map