// ======================================== // FICHIER: StepByStepSessionManager.js // RESPONSABILITÉ: Gestion des sessions step-by-step // ======================================== // Pas besoin d'uuid externe, on utilise notre générateur simple const { logSh } = require('./ErrorReporting'); /** * GESTIONNAIRE DE SESSIONS STEP-BY-STEP * Gère les sessions de test modulaire pas-à-pas avec TTL */ class StepByStepSessionManager { constructor() { this.sessions = new Map(); this.TTL = 30 * 60 * 1000; // 30 minutes // Nettoyage automatique toutes les 5 minutes setInterval(() => this.cleanupExpiredSessions(), 5 * 60 * 1000); logSh('🎯 SessionManager initialisé', 'DEBUG'); } // ======================================== // GESTION DES SESSIONS // ======================================== /** * Crée une nouvelle session */ createSession(inputData) { const sessionId = this.generateUUID(); const session = { id: sessionId, createdAt: Date.now(), lastAccessedAt: Date.now(), inputData: this.validateInputData(inputData), currentStep: 0, completedSteps: [], results: [], globalStats: { totalDuration: 0, totalTokens: 0, totalCost: 0, llmCalls: [], startTime: Date.now(), endTime: null }, steps: this.generateStepsList(), status: 'initialized' }; this.sessions.set(sessionId, session); logSh(`✅ Session créée: ${sessionId}`, 'INFO'); return session; } /** * Récupère une session */ getSession(sessionId) { const session = this.sessions.get(sessionId); if (!session) { throw new Error(`Session introuvable: ${sessionId}`); } if (this.isSessionExpired(session)) { this.deleteSession(sessionId); throw new Error(`Session expirée: ${sessionId}`); } session.lastAccessedAt = Date.now(); return session; } /** * Met à jour une session */ updateSession(sessionId, updates) { const session = this.getSession(sessionId); Object.assign(session, updates); session.lastAccessedAt = Date.now(); logSh(`📝 Session mise à jour: ${sessionId}`, 'DEBUG'); return session; } /** * Supprime une session */ deleteSession(sessionId) { const deleted = this.sessions.delete(sessionId); if (deleted) { logSh(`🗑️ Session supprimée: ${sessionId}`, 'INFO'); } return deleted; } /** * Liste toutes les sessions actives */ listSessions() { const sessions = []; for (const [id, session] of this.sessions) { if (!this.isSessionExpired(session)) { sessions.push({ id: session.id, createdAt: session.createdAt, status: session.status, currentStep: session.currentStep, totalSteps: session.steps.length, inputData: { mc0: session.inputData.mc0, personality: session.inputData.personality } }); } } return sessions; } // ======================================== // GESTION DES ÉTAPES // ======================================== /** * Ajoute le résultat d'une étape */ addStepResult(sessionId, stepId, result) { const session = this.getSession(sessionId); // Marquer l'étape comme complétée if (!session.completedSteps.includes(stepId)) { session.completedSteps.push(stepId); } // Ajouter le résultat const stepResult = { stepId: stepId, system: result.system, timestamp: Date.now(), success: result.success, result: result.result || null, error: result.error || null, stats: result.stats || {}, formatted: result.formatted || null }; session.results.push(stepResult); // Mettre à jour les stats globales this.updateGlobalStats(session, result.stats || {}); // Mettre à jour le statut de l'étape const step = session.steps.find(s => s.id === stepId); if (step) { step.status = result.success ? 'completed' : 'error'; step.duration = (result.stats && result.stats.duration) || 0; step.error = result.error || null; } // Mettre à jour currentStep si nécessaire if (stepId > session.currentStep) { session.currentStep = stepId; } logSh(`📊 Résultat étape ${stepId} ajouté à session ${sessionId}`, 'DEBUG'); return session; } /** * Obtient le résultat d'une étape */ getStepResult(sessionId, stepId) { const session = this.getSession(sessionId); return session.results.find(r => r.stepId === stepId) || null; } /** * Reset une session */ resetSession(sessionId) { const session = this.getSession(sessionId); session.currentStep = 0; session.completedSteps = []; session.results = []; session.globalStats = { totalDuration: 0, totalTokens: 0, totalCost: 0, llmCalls: [], startTime: Date.now(), endTime: null }; session.steps = this.generateStepsList(); session.status = 'initialized'; logSh(`🔄 Session reset: ${sessionId}`, 'INFO'); return session; } // ======================================== // HELPERS PRIVÉS // ======================================== /** * Génère un UUID simple */ generateUUID() { return Date.now().toString(36) + Math.random().toString(36).substr(2); } /** * Valide les données d'entrée */ validateInputData(inputData) { const validated = { mc0: inputData.mc0 || 'mot-clé principal', t0: inputData.t0 || 'titre principal', mcPlus1: inputData.mcPlus1 || '', tPlus1: inputData.tPlus1 || '', personality: inputData.personality || 'random', tMinus1: inputData.tMinus1 || '', xmlTemplate: inputData.xmlTemplate || null }; return validated; } /** * Génère la liste des étapes */ generateStepsList() { return [ { id: 1, system: 'selective', name: 'Selective Enhancement', description: 'Amélioration sélective du contenu', status: 'pending', duration: 0, error: null }, { id: 2, system: 'adversarial', name: 'Adversarial Generation', description: 'Génération adversariale anti-détection', status: 'pending', duration: 0, error: null }, { id: 3, system: 'human-simulation', name: 'Human Simulation', description: 'Simulation comportements humains', status: 'pending', duration: 0, error: null }, { id: 4, system: 'pattern-breaking', name: 'Pattern Breaking', description: 'Cassage de patterns IA', status: 'pending', duration: 0, error: null } ]; } /** * Met à jour les statistiques globales */ updateGlobalStats(session, stepStats) { const global = session.globalStats; global.totalDuration += stepStats.duration || 0; global.totalTokens += stepStats.tokensUsed || 0; global.totalCost += stepStats.cost || 0; if (stepStats.llmCalls && Array.isArray(stepStats.llmCalls)) { global.llmCalls.push(...stepStats.llmCalls); } // Marquer la fin si toutes les étapes sont complétées if (session.completedSteps.length === session.steps.length) { global.endTime = Date.now(); session.status = 'completed'; } } /** * Vérifie si une session est expirée */ isSessionExpired(session) { return (Date.now() - session.lastAccessedAt) > this.TTL; } /** * Nettoie les sessions expirées */ cleanupExpiredSessions() { let cleaned = 0; for (const [id, session] of this.sessions) { if (this.isSessionExpired(session)) { this.sessions.delete(id); cleaned++; } } if (cleaned > 0) { logSh(`🧹 ${cleaned} sessions expirées nettoyées`, 'DEBUG'); } } // ======================================== // EXPORT/IMPORT // ======================================== /** * Exporte une session au format JSON */ exportSession(sessionId) { const session = this.getSession(sessionId); return { session: { id: session.id, createdAt: new Date(session.createdAt).toISOString(), inputData: session.inputData, results: session.results, globalStats: session.globalStats, steps: session.steps.map(step => ({ ...step, duration: step.duration ? `${step.duration}ms` : '0ms' })) }, exportedAt: new Date().toISOString(), version: '1.0.0' }; } } // Instance singleton const sessionManager = new StepByStepSessionManager(); module.exports = { StepByStepSessionManager, sessionManager };