364 lines
9.1 KiB
JavaScript
364 lines
9.1 KiB
JavaScript
// ========================================
|
|
// 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: 'initial-generation',
|
|
name: 'Initial Generation',
|
|
description: 'Génération de contenu initial avec Claude',
|
|
status: 'pending',
|
|
duration: 0,
|
|
error: null
|
|
},
|
|
{
|
|
id: 2,
|
|
system: 'selective',
|
|
name: 'Selective Enhancement',
|
|
description: 'Amélioration sélective (Technique → Transitions → Style)',
|
|
status: 'pending',
|
|
duration: 0,
|
|
error: null
|
|
},
|
|
{
|
|
id: 3,
|
|
system: 'adversarial',
|
|
name: 'Adversarial Generation',
|
|
description: 'Génération adversariale anti-détection',
|
|
status: 'pending',
|
|
duration: 0,
|
|
error: null
|
|
},
|
|
{
|
|
id: 4,
|
|
system: 'human-simulation',
|
|
name: 'Human Simulation',
|
|
description: 'Simulation comportements humains',
|
|
status: 'pending',
|
|
duration: 0,
|
|
error: null
|
|
},
|
|
{
|
|
id: 5,
|
|
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
|
|
}; |