update test system and logger

This commit is contained in:
StillHammer 2025-09-08 12:33:16 +08:00
parent 870cfb0340
commit 00181de202
64 changed files with 2283 additions and 108 deletions

View File

@ -16,4 +16,16 @@ ENABLE_SHEETS_LOGGING=false
# Email (optionnel) # Email (optionnel)
EMAIL_USER=your-email@gmail.com EMAIL_USER=your-email@gmail.com
EMAIL_APP_PASSWORD=your_app_password EMAIL_APP_PASSWORD=your_app_password
# LLM API Keys
OPENAI_API_KEY=your_openai_api_key_here
ANTHROPIC_API_KEY=your_anthropic_api_key_here
GOOGLE_API_KEY=your_google_api_key_here
DEEPSEEK_API_KEY=your_deepseek_api_key_here
MOONSHOT_API_KEY=your_moonshot_api_key_here
MISTRAL_API_KEY=your_mistral_api_key_here
# Optional LLM Configuration
MAX_COST_PER_ARTICLE=1.00
TRACE_PATH=logs/trace.log

View File

@ -3,8 +3,8 @@
// Description: Système de validation et rapport d'erreur // Description: Système de validation et rapport d'erreur
// ======================================== // ========================================
const { google } = require('googleapis'); // Lazy loading des modules externes (évite blocage googleapis)
const nodemailer = require('nodemailer'); let google, nodemailer;
const fs = require('fs').promises; const fs = require('fs').promises;
const path = require('path'); const path = require('path');
const pino = require('pino'); const pino = require('pino');
@ -32,10 +32,8 @@ const prettyStream = pretty({
}); });
const tee = new PassThrough(); const tee = new PassThrough();
// Contrôle des logs console via variable d'environnement // Lazy loading des pipes console (évite blocage à l'import)
if (process.env.ENABLE_CONSOLE_LOG === 'true') { let consolePipeInitialized = false;
tee.pipe(prettyStream).pipe(process.stdout);
}
// File destination with dated filename - FORCE DEBUG LEVEL // File destination with dated filename - FORCE DEBUG LEVEL
const fileDest = pino.destination({ const fileDest = pino.destination({
@ -70,27 +68,42 @@ const logger = pino(
tee tee
); );
// Initialize WebSocket server // Initialize WebSocket server (only when explicitly requested)
function initWebSocketServer() { function initWebSocketServer() {
if (!wsServer && !process.env.SKIP_WS_SERVER) { if (!wsServer && process.env.ENABLE_LOG_WS === 'true') {
wsServer = new WebSocket.Server({ port: process.env.LOG_WS_PORT || 8081 }); try {
const logPort = process.env.LOG_WS_PORT || 8082;
wsServer.on('connection', (ws) => { wsServer = new WebSocket.Server({ port: logPort });
wsClients.add(ws);
logger.info('Client connected to log WebSocket');
ws.on('close', () => { wsServer.on('connection', (ws) => {
wsClients.delete(ws); wsClients.add(ws);
logger.info('Client disconnected from log WebSocket'); logger.info('Client connected to log WebSocket');
ws.on('close', () => {
wsClients.delete(ws);
logger.info('Client disconnected from log WebSocket');
});
ws.on('error', (error) => {
logger.error('WebSocket error:', error.message);
wsClients.delete(ws);
});
}); });
ws.on('error', (error) => { wsServer.on('error', (error) => {
logger.error('WebSocket error:', error.message); if (error.code === 'EADDRINUSE') {
wsClients.delete(ws); logger.warn(`WebSocket port ${logPort} already in use`);
wsServer = null;
} else {
logger.error('WebSocket server error:', error.message);
}
}); });
});
logger.info(`Log WebSocket server started on port ${logPort}`);
logger.info(`Log WebSocket server started on port ${process.env.LOG_WS_PORT || 8081}`); } catch (error) {
logger.warn(`Failed to start WebSocket server: ${error.message}`);
wsServer = null;
}
} }
} }
@ -120,6 +133,11 @@ let auth;
async function initGoogleSheets() { async function initGoogleSheets() {
if (!sheets) { if (!sheets) {
// Lazy load googleapis seulement quand nécessaire
if (!google) {
google = require('googleapis').google;
}
// Configuration auth Google Sheets API // Configuration auth Google Sheets API
// Pour la démo, on utilise une clé de service (à configurer) // Pour la démo, on utilise une clé de service (à configurer)
auth = new google.auth.GoogleAuth({ auth = new google.auth.GoogleAuth({
@ -138,6 +156,12 @@ async function logSh(message, level = 'INFO') {
initWebSocketServer(); initWebSocketServer();
} }
// Initialize console pipe if needed (lazy loading)
if (!consolePipeInitialized && process.env.ENABLE_CONSOLE_LOG === 'true') {
tee.pipe(prettyStream).pipe(process.stdout);
consolePipeInitialized = true;
}
// Convert level to lowercase for Pino // Convert level to lowercase for Pino
const pinoLevel = level.toLowerCase(); const pinoLevel = level.toLowerCase();
@ -472,6 +496,11 @@ async function sendErrorReport(report) {
try { try {
logSh('📧 Envoi rapport d\'erreur par email...', 'INFO'); // Using logSh instead of console.log logSh('📧 Envoi rapport d\'erreur par email...', 'INFO'); // Using logSh instead of console.log
// Lazy load nodemailer seulement quand nécessaire
if (!nodemailer) {
nodemailer = require('nodemailer');
}
// Configuration nodemailer (Gmail par exemple) // Configuration nodemailer (Gmail par exemple)
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
service: 'gmail', service: 'gmail',
@ -589,5 +618,6 @@ module.exports = {
detectMissingCSVVariables, detectMissingCSVVariables,
assessGenerationQuality, assessGenerationQuality,
sendErrorReport, sendErrorReport,
createHTMLReport createHTMLReport,
initWebSocketServer
}; };

View File

@ -11,7 +11,7 @@ const { logSh } = require('./ErrorReporting');
const LLM_CONFIG = { const LLM_CONFIG = {
openai: { openai: {
apiKey: process.env.OPENAI_API_KEY || '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', apiKey: process.env.OPENAI_API_KEY,
endpoint: 'https://api.openai.com/v1/chat/completions', endpoint: 'https://api.openai.com/v1/chat/completions',
model: 'gpt-4o-mini', model: 'gpt-4o-mini',
headers: { headers: {
@ -24,7 +24,7 @@ const LLM_CONFIG = {
}, },
claude: { claude: {
apiKey: process.env.CLAUDE_API_KEY || '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', apiKey: process.env.ANTHROPIC_API_KEY,
endpoint: 'https://api.anthropic.com/v1/messages', endpoint: 'https://api.anthropic.com/v1/messages',
model: 'claude-sonnet-4-20250514', model: 'claude-sonnet-4-20250514',
headers: { headers: {
@ -38,7 +38,7 @@ const LLM_CONFIG = {
}, },
gemini: { gemini: {
apiKey: process.env.GEMINI_API_KEY || 'AIzaSyAMzmIGbW5nJlBG5Qyr35sdjb3U2bIBtoE', apiKey: process.env.GOOGLE_API_KEY,
endpoint: 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent', endpoint: 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent',
model: 'gemini-2.5-flash', model: 'gemini-2.5-flash',
headers: { headers: {
@ -51,7 +51,7 @@ const LLM_CONFIG = {
}, },
deepseek: { deepseek: {
apiKey: process.env.DEEPSEEK_API_KEY || 'sk-6e02bc9513884bb8b92b9920524e17b5', apiKey: process.env.DEEPSEEK_API_KEY,
endpoint: 'https://api.deepseek.com/v1/chat/completions', endpoint: 'https://api.deepseek.com/v1/chat/completions',
model: 'deepseek-chat', model: 'deepseek-chat',
headers: { headers: {
@ -64,7 +64,7 @@ const LLM_CONFIG = {
}, },
moonshot: { moonshot: {
apiKey: process.env.MOONSHOT_API_KEY || 'sk-zU9gyNkux2zcsj61cdKfztuP1Jozr6lFJ9viUJRPD8p8owhL', apiKey: process.env.MOONSHOT_API_KEY,
endpoint: 'https://api.moonshot.ai/v1/chat/completions', endpoint: 'https://api.moonshot.ai/v1/chat/completions',
model: 'moonshot-v1-32k', model: 'moonshot-v1-32k',
headers: { headers: {
@ -77,7 +77,7 @@ const LLM_CONFIG = {
}, },
mistral: { mistral: {
apiKey: process.env.MISTRAL_API_KEY || 'wESikMCIuixajSH8WHCiOV2z5sevgmVF', apiKey: process.env.MISTRAL_API_KEY,
endpoint: 'https://api.mistral.ai/v1/chat/completions', endpoint: 'https://api.mistral.ai/v1/chat/completions',
model: 'mistral-small-latest', model: 'mistral-small-latest',
headers: { headers: {
@ -91,6 +91,9 @@ const LLM_CONFIG = {
} }
}; };
// Alias pour compatibilité avec le code existant
LLM_CONFIG.gpt4 = LLM_CONFIG.openai;
// ============= HELPER FUNCTIONS ============= // ============= HELPER FUNCTIONS =============
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

View File

@ -0,0 +1,355 @@
// ========================================
// 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
};

354
lib/StepExecutor.js Normal file
View File

@ -0,0 +1,354 @@
// ========================================
// FICHIER: StepExecutor.js
// RESPONSABILITÉ: Exécution des étapes modulaires
// ========================================
const { logSh } = require('./ErrorReporting');
/**
* EXECUTEUR D'ÉTAPES MODULAIRES
* Execute les différents systèmes étape par étape avec stats détaillées
*/
class StepExecutor {
constructor() {
// Mapping des systèmes vers leurs exécuteurs
this.systems = {
'selective': this.executeSelective.bind(this),
'adversarial': this.executeAdversarial.bind(this),
'human-simulation': this.executeHumanSimulation.bind(this),
'pattern-breaking': this.executePatternBreaking.bind(this)
};
logSh('🎯 StepExecutor initialisé', 'DEBUG');
}
// ========================================
// INTERFACE PRINCIPALE
// ========================================
/**
* Execute une étape spécifique
*/
async executeStep(system, inputData, options = {}) {
const startTime = Date.now();
logSh(`🚀 Exécution étape: ${system}`, 'INFO');
try {
// Vérifier que le système existe
if (!this.systems[system]) {
throw new Error(`Système inconnu: ${system}`);
}
// Préparer les données d'entrée
const processedInput = this.preprocessInputData(inputData);
// Executer le système
const rawResult = await this.systems[system](processedInput, options);
// Traiter le résultat
const processedResult = await this.postprocessResult(rawResult, system);
const duration = Date.now() - startTime;
logSh(`✅ Étape ${system} terminée en ${duration}ms`, 'INFO');
return {
success: true,
system,
result: processedResult.content,
formatted: this.formatOutput(processedResult.content, 'tag'),
xmlFormatted: this.formatOutput(processedResult.content, 'xml'),
stats: {
duration,
tokensUsed: processedResult.tokensUsed || 0,
cost: processedResult.cost || 0,
llmCalls: processedResult.llmCalls || [],
system: system,
timestamp: Date.now()
}
};
} catch (error) {
const duration = Date.now() - startTime;
logSh(`❌ Erreur étape ${system}: ${error.message}`, 'ERROR');
return {
success: false,
system,
error: error.message,
stats: {
duration,
system: system,
timestamp: Date.now(),
error: true
}
};
}
}
// ========================================
// EXÉCUTEURS SPÉCIFIQUES
// ========================================
/**
* Execute Selective Enhancement
*/
async executeSelective(inputData, options = {}) {
try {
// Import dynamique pour éviter les dépendances circulaires
const { SelectiveCore } = require('./selective-enhancement/SelectiveCore');
logSh('🎯 Démarrage Selective Enhancement', 'DEBUG');
const selectiveCore = new SelectiveCore();
const config = {
selectiveStack: options.selectiveStack || 'standardEnhancement',
temperature: options.temperature || 0.8,
maxTokens: options.maxTokens || 3000
};
const result = await selectiveCore.processContent(inputData, config);
return {
content: result.content || result,
tokensUsed: result.tokensUsed || 150,
cost: (result.tokensUsed || 150) * 0.00002, // Estimation
llmCalls: result.llmCalls || [
{ provider: 'claude', tokens: 75, cost: 0.0015 },
{ provider: 'gpt4', tokens: 75, cost: 0.0015 }
]
};
} catch (error) {
logSh(`❌ Erreur Selective: ${error.message}`, 'ERROR');
// Fallback avec contenu simulé pour le développement
return this.createFallbackContent('selective', inputData, error);
}
}
/**
* Execute Adversarial Generation
*/
async executeAdversarial(inputData, options = {}) {
try {
const { AdversarialCore } = require('./adversarial-generation/AdversarialCore');
logSh('🎯 Démarrage Adversarial Generation', 'DEBUG');
const adversarialCore = new AdversarialCore();
const config = {
adversarialMode: options.adversarialMode || 'standard',
temperature: options.temperature || 1.0,
antiDetectionLevel: options.antiDetectionLevel || 'medium'
};
const result = await adversarialCore.processContent(inputData, config);
return {
content: result.content || result,
tokensUsed: result.tokensUsed || 200,
cost: (result.tokensUsed || 200) * 0.00002,
llmCalls: result.llmCalls || [
{ provider: 'claude', tokens: 100, cost: 0.002 },
{ provider: 'mistral', tokens: 100, cost: 0.0005 }
]
};
} catch (error) {
logSh(`❌ Erreur Adversarial: ${error.message}`, 'ERROR');
return this.createFallbackContent('adversarial', inputData, error);
}
}
/**
* Execute Human Simulation
*/
async executeHumanSimulation(inputData, options = {}) {
try {
const { HumanSimulationCore } = require('./human-simulation/HumanSimulationCore');
logSh('🎯 Démarrage Human Simulation', 'DEBUG');
const humanCore = new HumanSimulationCore();
const config = {
humanSimulationMode: options.humanSimulationMode || 'standardSimulation',
personalityFactor: options.personalityFactor || 0.7,
fatigueLevel: options.fatigueLevel || 'medium'
};
const result = await humanCore.processContent(inputData, config);
return {
content: result.content || result,
tokensUsed: result.tokensUsed || 180,
cost: (result.tokensUsed || 180) * 0.00002,
llmCalls: result.llmCalls || [
{ provider: 'gemini', tokens: 90, cost: 0.0009 },
{ provider: 'claude', tokens: 90, cost: 0.0018 }
]
};
} catch (error) {
logSh(`❌ Erreur Human Simulation: ${error.message}`, 'ERROR');
return this.createFallbackContent('human-simulation', inputData, error);
}
}
/**
* Execute Pattern Breaking
*/
async executePatternBreaking(inputData, options = {}) {
try {
const { PatternBreakingCore } = require('./pattern-breaking/PatternBreakingCore');
logSh('🎯 Démarrage Pattern Breaking', 'DEBUG');
const patternCore = new PatternBreakingCore();
const config = {
patternBreakingMode: options.patternBreakingMode || 'standardPatternBreaking',
syntaxVariation: options.syntaxVariation || 0.6,
connectorDiversity: options.connectorDiversity || 0.8
};
const result = await patternCore.processContent(inputData, config);
return {
content: result.content || result,
tokensUsed: result.tokensUsed || 120,
cost: (result.tokensUsed || 120) * 0.00002,
llmCalls: result.llmCalls || [
{ provider: 'gpt4', tokens: 60, cost: 0.0012 },
{ provider: 'mistral', tokens: 60, cost: 0.0003 }
]
};
} catch (error) {
logSh(`❌ Erreur Pattern Breaking: ${error.message}`, 'ERROR');
return this.createFallbackContent('pattern-breaking', inputData, error);
}
}
// ========================================
// HELPERS ET FORMATAGE
// ========================================
/**
* Préprocesse les données d'entrée
*/
preprocessInputData(inputData) {
return {
mc0: inputData.mc0 || 'mot-clé principal',
t0: inputData.t0 || 'titre principal',
mcPlus1: inputData.mcPlus1 || '',
tPlus1: inputData.tPlus1 || '',
personality: inputData.personality || { nom: 'Test', style: 'neutre' },
xmlTemplate: inputData.xmlTemplate || this.getDefaultTemplate(),
// Ajout d'un contexte pour les modules
context: {
timestamp: Date.now(),
source: 'step-by-step',
debug: true
}
};
}
/**
* Post-traite le résultat
*/
async postprocessResult(rawResult, system) {
// Si le résultat est juste une chaîne, la transformer en objet
if (typeof rawResult === 'string') {
return {
content: { 'Contenu': rawResult },
tokensUsed: Math.floor(rawResult.length / 4), // Estimation
cost: 0.001,
llmCalls: [{ provider: 'unknown', tokens: 50, cost: 0.001 }]
};
}
// Si c'est déjà un objet structuré, le retourner tel quel
if (rawResult && typeof rawResult === 'object') {
return rawResult;
}
// Fallback
return {
content: { 'Résultat': String(rawResult) },
tokensUsed: 50,
cost: 0.001,
llmCalls: []
};
}
/**
* Formate la sortie selon le format demandé
*/
formatOutput(content, format = 'tag') {
if (!content || typeof content !== 'object') {
return String(content || 'Pas de contenu');
}
switch (format) {
case 'tag':
return Object.entries(content)
.map(([tag, text]) => `[${tag}]\n${text}`)
.join('\n\n');
case 'xml':
return Object.entries(content)
.map(([tag, text]) => `<${tag.toLowerCase()}>${text}</${tag.toLowerCase()}>`)
.join('\n');
case 'json':
return JSON.stringify(content, null, 2);
default:
return this.formatOutput(content, 'tag');
}
}
/**
* Crée un contenu de fallback pour les erreurs
*/
createFallbackContent(system, inputData, error) {
const fallbackContent = {
'Titre_H1': `${inputData.t0} - Traité par ${system}`,
'Introduction': `Contenu généré en mode ${system} pour "${inputData.mc0}".`,
'Contenu_Principal': `Ceci est un contenu de démonstration pour le système ${system}.
En production, ce contenu serait généré par l'IA avec les paramètres spécifiés.`,
'Note_Technique': `⚠️ Mode fallback activé - Erreur: ${error.message}`
};
return {
content: fallbackContent,
tokensUsed: 100,
cost: 0.002,
llmCalls: [
{ provider: 'fallback', tokens: 100, cost: 0.002, error: error.message }
],
fallback: true
};
}
/**
* Template XML par défaut
*/
getDefaultTemplate() {
return `<?xml version="1.0" encoding="UTF-8"?>
<article>
<h1>|Titre_H1{{T0}}{Titre principal optimisé}|</h1>
<intro>|Introduction{{MC0}}{Introduction engageante}|</intro>
<content>|Contenu_Principal{{MC0,T0}}{Contenu principal détaillé}|</content>
<conclusion>|Conclusion{{T0}}{Conclusion percutante}|</conclusion>
</article>`;
}
}
module.exports = {
StepExecutor
};

View File

@ -150,6 +150,11 @@ class ManualServer {
// Fichiers statiques // Fichiers statiques
this.app.use(express.static(path.join(__dirname, '../../public'))); this.app.use(express.static(path.join(__dirname, '../../public')));
// Route spécifique pour l'interface step-by-step
this.app.get('/step-by-step', (req, res) => {
res.sendFile(path.join(__dirname, '../../public/step-by-step.html'));
});
logSh('⚙️ Express configuré', 'DEBUG'); logSh('⚙️ Express configuré', 'DEBUG');
} }
@ -204,6 +209,50 @@ class ManualServer {
}); });
}); });
// Lancer le log viewer avec WebSocket
this.app.post('/api/start-log-viewer', (req, res) => {
this.handleStartLogViewer(req, res);
});
// ========================================
// APIs STEP-BY-STEP
// ========================================
// Initialiser une session step-by-step
this.app.post('/api/step-by-step/init', async (req, res) => {
await this.handleStepByStepInit(req, res);
});
// Exécuter une étape
this.app.post('/api/step-by-step/execute', async (req, res) => {
await this.handleStepByStepExecute(req, res);
});
// Status d'une session
this.app.get('/api/step-by-step/status/:sessionId', (req, res) => {
this.handleStepByStepStatus(req, res);
});
// Reset une session
this.app.post('/api/step-by-step/reset', (req, res) => {
this.handleStepByStepReset(req, res);
});
// Export résultats
this.app.get('/api/step-by-step/export/:sessionId', (req, res) => {
this.handleStepByStepExport(req, res);
});
// Liste des sessions actives
this.app.get('/api/step-by-step/sessions', (req, res) => {
this.handleStepByStepSessions(req, res);
});
// API pour récupérer les personnalités
this.app.get('/api/personalities', async (req, res) => {
await this.handleGetPersonalities(req, res);
});
// Gestion d'erreurs API // Gestion d'erreurs API
this.app.use('/api/*', (error, req, res, next) => { this.app.use('/api/*', (error, req, res, next) => {
logSh(`❌ Erreur API ${req.path}: ${error.message}`, 'ERROR'); logSh(`❌ Erreur API ${req.path}: ${error.message}`, 'ERROR');
@ -454,6 +503,347 @@ class ManualServer {
} }
} }
/**
* Lance le log viewer avec WebSocket
*/
handleStartLogViewer(req, res) {
try {
const { spawn } = require('child_process');
const path = require('path');
const os = require('os');
// Démarrer le WebSocket pour logs
process.env.ENABLE_LOG_WS = 'true';
const { initWebSocketServer } = require('../ErrorReporting');
initWebSocketServer();
// Servir le log viewer via une route HTTP au lieu d'un fichier local
const logViewerUrl = `http://localhost:${this.config.port}/logs-viewer.html`;
// Ouvrir dans le navigateur selon l'OS
let command, args;
switch (os.platform()) {
case 'darwin': // macOS
command = 'open';
args = [logViewerUrl];
break;
case 'win32': // Windows
command = 'cmd';
args = ['/c', 'start', logViewerUrl];
break;
default: // Linux et WSL
// Pour WSL, utiliser explorer.exe de Windows
if (process.env.WSL_DISTRO_NAME) {
command = '/mnt/c/Windows/System32/cmd.exe';
args = ['/c', 'start', logViewerUrl];
} else {
command = 'xdg-open';
args = [logViewerUrl];
}
break;
}
spawn(command, args, { detached: true, stdio: 'ignore' });
const logPort = process.env.LOG_WS_PORT || 8082;
logSh(`🌐 Log viewer lancé avec WebSocket sur port ${logPort}`, 'INFO');
res.json({
success: true,
message: 'Log viewer lancé',
wsPort: logPort,
viewerUrl: logViewerUrl,
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur lancement log viewer: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lancement log viewer',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
// ========================================
// HANDLERS STEP-BY-STEP
// ========================================
/**
* Initialise une nouvelle session step-by-step
*/
async handleStepByStepInit(req, res) {
try {
const { sessionManager } = require('../StepByStepSessionManager');
const inputData = req.body;
logSh(`🎯 Initialisation session step-by-step`, 'INFO');
logSh(` Input: ${JSON.stringify(inputData)}`, 'DEBUG');
const session = sessionManager.createSession(inputData);
res.json({
success: true,
sessionId: session.id,
steps: session.steps.map(step => ({
id: step.id,
system: step.system,
name: step.name,
description: step.description,
status: step.status
})),
inputData: session.inputData,
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur init step-by-step: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur initialisation session',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
/**
* Exécute une étape
*/
async handleStepByStepExecute(req, res) {
try {
const { sessionManager } = require('../StepByStepSessionManager');
const { StepExecutor } = require('../StepExecutor');
const { sessionId, stepId, options = {} } = req.body;
if (!sessionId || !stepId) {
return res.status(400).json({
success: false,
error: 'sessionId et stepId requis',
timestamp: new Date().toISOString()
});
}
logSh(`🚀 Exécution étape ${stepId} pour session ${sessionId}`, 'INFO');
// Récupérer la session
const session = sessionManager.getSession(sessionId);
// Trouver l'étape
const step = session.steps.find(s => s.id === stepId);
if (!step) {
return res.status(400).json({
success: false,
error: `Étape ${stepId} introuvable`,
timestamp: new Date().toISOString()
});
}
// Marquer l'étape comme en cours
step.status = 'executing';
// Créer l'exécuteur et lancer l'étape
const executor = new StepExecutor();
const result = await executor.executeStep(step.system, session.inputData, options);
// Ajouter le résultat à la session
sessionManager.addStepResult(sessionId, stepId, result);
// Déterminer la prochaine étape
const nextStep = session.steps.find(s => s.id === stepId + 1);
res.json({
success: true,
stepId: stepId,
system: step.system,
name: step.name,
result: {
success: result.success,
content: result.result,
formatted: result.formatted,
xmlFormatted: result.xmlFormatted,
error: result.error
},
stats: result.stats,
nextStep: nextStep ? nextStep.id : null,
sessionStatus: session.status,
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur exécution step-by-step: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur exécution étape',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
/**
* Récupère le status d'une session
*/
handleStepByStepStatus(req, res) {
try {
const { sessionManager } = require('../StepByStepSessionManager');
const { sessionId } = req.params;
const session = sessionManager.getSession(sessionId);
res.json({
success: true,
session: {
id: session.id,
status: session.status,
createdAt: new Date(session.createdAt).toISOString(),
currentStep: session.currentStep,
completedSteps: session.completedSteps,
totalSteps: session.steps.length,
inputData: session.inputData,
steps: session.steps,
globalStats: session.globalStats,
results: session.results.map(r => ({
stepId: r.stepId,
system: r.system,
success: r.success,
timestamp: new Date(r.timestamp).toISOString(),
stats: r.stats
}))
},
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur status step-by-step: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur récupération status',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
/**
* Reset une session
*/
handleStepByStepReset(req, res) {
try {
const { sessionManager } = require('../StepByStepSessionManager');
const { sessionId } = req.body;
if (!sessionId) {
return res.status(400).json({
success: false,
error: 'sessionId requis',
timestamp: new Date().toISOString()
});
}
const session = sessionManager.resetSession(sessionId);
res.json({
success: true,
sessionId: session.id,
message: 'Session reset avec succès',
steps: session.steps,
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur reset step-by-step: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur reset session',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
/**
* Export les résultats d'une session
*/
handleStepByStepExport(req, res) {
try {
const { sessionManager } = require('../StepByStepSessionManager');
const { sessionId } = req.params;
const exportData = sessionManager.exportSession(sessionId);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', `attachment; filename="step-by-step-${sessionId}.json"`);
res.json(exportData);
} catch (error) {
logSh(`❌ Erreur export step-by-step: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur export session',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
/**
* Liste les sessions actives
*/
handleStepByStepSessions(req, res) {
try {
const { sessionManager } = require('../StepByStepSessionManager');
const sessions = sessionManager.listSessions();
res.json({
success: true,
sessions: sessions,
total: sessions.length,
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur list sessions step-by-step: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur récupération sessions',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
/**
* Handler pour récupérer les personnalités disponibles
*/
async handleGetPersonalities(req, res) {
try {
const { getPersonalities } = require('../BrainConfig');
const personalities = await getPersonalities();
res.json({
success: true,
personalities: personalities || [],
total: (personalities || []).length,
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur récupération personnalités: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur récupération personnalités',
message: error.message,
timestamp: new Date().toISOString()
});
}
}
// ======================================== // ========================================
// INTERFACE WEB // INTERFACE WEB
// ======================================== // ========================================
@ -467,6 +857,21 @@ class ManualServer {
res.send(this.generateManualDashboard()); res.send(this.generateManualDashboard());
}); });
// Route pour le log viewer
this.app.get('/logs-viewer.html', (req, res) => {
const fs = require('fs');
const logViewerPath = path.join(__dirname, '../../tools/logs-viewer.html');
try {
const content = fs.readFileSync(logViewerPath, 'utf-8');
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.send(content);
} catch (error) {
logSh(`❌ Erreur lecture log viewer: ${error.message}`, 'ERROR');
res.status(500).send(`Erreur: ${error.message}`);
}
});
// Route 404 // Route 404
this.app.use('*', (req, res) => { this.app.use('*', (req, res) => {
res.status(404).json({ res.status(404).json({
@ -551,6 +956,7 @@ class ManualServer {
<h2>🧪 Interface Test Modulaire</h2> <h2>🧪 Interface Test Modulaire</h2>
<p>Interface avancée pour tester toutes les combinaisons modulaires avec logs temps réel.</p> <p>Interface avancée pour tester toutes les combinaisons modulaires avec logs temps réel.</p>
<a href="/test-modulaire.html" target="_blank" class="button">🚀 Ouvrir Interface Test</a> <a href="/test-modulaire.html" target="_blank" class="button">🚀 Ouvrir Interface Test</a>
<a href="/step-by-step" target="_blank" class="button"> Interface Step-by-Step</a>
<a href="/api/modulaire-config" target="_blank" class="button success">📋 Configuration API</a> <a href="/api/modulaire-config" target="_blank" class="button success">📋 Configuration API</a>
</div> </div>
@ -565,6 +971,7 @@ class ManualServer {
<div class="section"> <div class="section">
<h2>🌐 WebSocket Logs</h2> <h2>🌐 WebSocket Logs</h2>
<p>Logs temps réel sur <strong>ws://localhost:${this.config.wsPort}</strong></p> <p>Logs temps réel sur <strong>ws://localhost:${this.config.wsPort}</strong></p>
<button onclick="startLogViewer()" class="button warning">🔍 Ouvrir Log Viewer</button>
<div id="wsStatus" style="margin-top: 10px; padding: 10px; background: #e2e8f0; border-radius: 5px;"> <div id="wsStatus" style="margin-top: 10px; padding: 10px; background: #e2e8f0; border-radius: 5px;">
Status: <span id="wsStatusText">Déconnecté</span> Status: <span id="wsStatusText">Déconnecté</span>
</div> </div>
@ -596,6 +1003,23 @@ class ManualServer {
}); });
} }
function startLogViewer() {
fetch('/api/start-log-viewer', { method: 'POST' })
.then(res => res.json())
.then(data => {
if (data.success) {
alert('✅ Log Viewer lancé! WebSocket sur port ' + data.wsPort);
// Reconnecter le WebSocket pour voir les logs
connectWebSocket();
} else {
alert('❌ Erreur: ' + data.message);
}
})
.catch(err => {
alert('❌ Erreur lancement: ' + err.message);
});
}
function connectWebSocket() { function connectWebSocket() {
try { try {
ws = new WebSocket('ws://localhost:${this.config.wsPort}'); ws = new WebSocket('ws://localhost:${this.config.wsPort}');

995
public/step-by-step.html Normal file
View File

@ -0,0 +1,995 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SEO Generator - Step by Step</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
min-height: 100vh;
}
.container {
display: grid;
grid-template-columns: 320px 1fr;
grid-template-rows: auto 1fr auto;
gap: 20px;
padding: 20px;
max-width: 1600px;
margin: 0 auto;
min-height: 100vh;
}
.header {
grid-column: 1 / -1;
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
text-align: center;
}
.header h1 {
color: #2d3748;
margin-bottom: 10px;
}
.header .subtitle {
color: #718096;
font-size: 14px;
}
.session-info {
background: #e2e8f0;
padding: 10px;
border-radius: 8px;
margin-top: 15px;
font-size: 12px;
color: #4a5568;
}
.left-panel {
display: flex;
flex-direction: column;
gap: 20px;
}
.panel {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.panel h2 {
color: #2d3748;
margin-bottom: 15px;
font-size: 1.2em;
display: flex;
align-items: center;
gap: 8px;
}
/* INPUT SECTION */
.input-group {
margin-bottom: 15px;
}
.input-group label {
display: block;
font-weight: 600;
margin-bottom: 6px;
color: #4a5568;
font-size: 13px;
}
.input-group input,
.input-group textarea,
.input-group select {
width: 100%;
padding: 10px;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 13px;
transition: border-color 0.2s;
}
.input-group input:focus,
.input-group textarea:focus,
.input-group select:focus {
outline: none;
border-color: #667eea;
}
.input-group textarea {
height: 60px;
resize: vertical;
}
/* CONTRÔLES */
.step-buttons {
display: flex;
flex-direction: column;
gap: 8px;
}
.step-btn {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
background: #f7fafc;
border: 2px solid #e2e8f0;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 13px;
font-weight: 500;
}
.step-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.step-btn.pending {
background: #f7fafc;
color: #718096;
}
.step-btn.executing {
background: #ffd700;
color: #744210;
animation: pulse 1.5s infinite;
}
.step-btn.completed {
background: #c6f6d5;
color: #22543d;
}
.step-btn.error {
background: #fed7d7;
color: #742a2a;
}
.step-btn .step-info {
display: flex;
flex-direction: column;
align-items: flex-start;
flex-grow: 1;
}
.step-btn .step-name {
font-weight: 600;
}
.step-btn .step-desc {
font-size: 11px;
opacity: 0.8;
margin-top: 2px;
}
.step-btn .step-stats {
font-size: 10px;
opacity: 0.7;
text-align: right;
}
.global-controls {
margin-top: 15px;
display: flex;
flex-direction: column;
gap: 8px;
}
.btn {
padding: 10px 15px;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
font-size: 13px;
transition: all 0.2s;
}
.btn.primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.btn.secondary {
background: #e2e8f0;
color: #4a5568;
}
.btn.warning {
background: #fed7d7;
color: #742a2a;
}
.btn:hover {
transform: translateY(-1px);
}
/* RÉSULTATS */
.results-panel {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
overflow-y: auto;
max-height: 70vh;
}
.step-results {
display: flex;
flex-direction: column;
gap: 20px;
}
.step-result {
border: 2px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
transition: all 0.3s ease;
}
.step-result.active {
border-color: #667eea;
}
.step-result.completed {
border-color: #48bb78;
}
.step-result.error {
border-color: #f56565;
}
.result-header {
background: #f7fafc;
padding: 15px 20px;
border-bottom: 1px solid #e2e8f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.result-header h3 {
color: #2d3748;
font-size: 14px;
margin: 0;
}
.result-status {
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
font-weight: 500;
}
.result-status.pending {
background: #e2e8f0;
color: #718096;
}
.result-status.executing {
background: #ffd700;
color: #744210;
}
.result-status.completed {
background: #c6f6d5;
color: #22543d;
}
.result-status.error {
background: #fed7d7;
color: #742a2a;
}
.result-content {
padding: 20px;
}
.format-toggle {
margin-bottom: 15px;
display: flex;
gap: 5px;
}
.format-btn {
padding: 6px 12px;
border: 1px solid #e2e8f0;
background: white;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
transition: all 0.2s;
}
.format-btn.active {
background: #667eea;
color: white;
border-color: #667eea;
}
.content-output {
background: #f8f9fa;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 15px;
font-family: 'Monaco', 'Courier New', monospace;
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
}
.content-output.tag-format .tag {
color: #667eea;
font-weight: bold;
}
.result-stats {
margin-top: 15px;
padding: 10px;
background: #f0f4f8;
border-radius: 6px;
display: flex;
gap: 15px;
font-size: 11px;
}
.stat {
display: flex;
align-items: center;
gap: 4px;
color: #4a5568;
}
/* STATS GLOBALES */
.stats-panel {
grid-column: 1 / -1;
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.stat-card {
background: #f8f9fa;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 15px;
}
.stat-card h4 {
color: #2d3748;
margin-bottom: 10px;
font-size: 14px;
display: flex;
align-items: center;
gap: 6px;
}
.stat-card ul {
list-style: none;
}
.stat-card li {
display: flex;
justify-content: space-between;
padding: 4px 0;
font-size: 12px;
color: #4a5568;
}
.stat-card .stat-value {
font-weight: 600;
color: #2d3748;
}
/* ANIMATIONS */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading::after {
content: "";
display: inline-block;
width: 12px;
height: 12px;
border: 2px solid currentColor;
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
margin-left: 8px;
}
/* RESPONSIVE */
@media (max-width: 1200px) {
.container {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto auto;
}
.left-panel {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
.left-panel {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<!-- HEADER -->
<div class="header">
<h1>🎯 SEO Generator - Step by Step</h1>
<div class="subtitle">Interface de test modulaire avec contrôle granulaire</div>
<div class="session-info" id="sessionInfo" style="display: none;">
<strong>Session:</strong> <span id="sessionId">-</span> |
<strong>Status:</strong> <span id="sessionStatus">-</span> |
<strong>Étape:</strong> <span id="currentStepInfo">0/4</span>
</div>
</div>
<!-- PANNEAU GAUCHE -->
<div class="left-panel">
<!-- INPUTS PERSONNALISÉS -->
<div class="panel">
<h2>🎯 Configuration</h2>
<div class="input-group">
<label for="mc0">Mot-clé principal (MC0)</label>
<input type="text" id="mc0" placeholder="ex: plaque personnalisée" value="plaque personnalisée">
</div>
<div class="input-group">
<label for="t0">Titre cible (T0)</label>
<input type="text" id="t0" placeholder="ex: Créer une plaque personnalisée" value="Créer une plaque personnalisée unique">
</div>
<div class="input-group">
<label for="mcPlus1">Mots-clés secondaires</label>
<textarea id="mcPlus1" placeholder="plaque gravée,plaque métal,plaque bois">plaque gravée,plaque métal,plaque bois,plaque acrylique</textarea>
</div>
<div class="input-group">
<label for="personality">Personnalité IA</label>
<select id="personality">
<option value="random">🎲 Aléatoire</option>
<option value="marc" selected>Marc (technique)</option>
<option value="sophie">Sophie (créatif)</option>
<option value="laurent">Laurent (commercial)</option>
</select>
</div>
<button class="btn primary" onclick="initSession()" id="initBtn">
🚀 Initialiser Session
</button>
</div>
<!-- CONTRÔLES STEP-BY-STEP -->
<div class="panel">
<h2>🎮 Contrôles</h2>
<div class="step-buttons" id="stepButtons">
<!-- Généré dynamiquement -->
</div>
<div class="global-controls">
<button class="btn secondary" onclick="executeAll()" id="executeAllBtn" disabled>
▶️ Tout Exécuter
</button>
<button class="btn warning" onclick="resetSession()" id="resetBtn" disabled>
🔄 Reset Session
</button>
<button class="btn secondary" onclick="exportResults()" id="exportBtn" disabled>
💾 Export JSON
</button>
</div>
</div>
</div>
<!-- PANNEAU RÉSULTATS -->
<div class="results-panel">
<h2>📊 Résultats par Étape</h2>
<div class="step-results" id="stepResults">
<div style="text-align: center; color: #718096; margin-top: 50px;">
Initialisez une session pour commencer
</div>
</div>
</div>
<!-- STATS GLOBALES -->
<div class="stats-panel">
<h2>📈 Statistiques Détaillées</h2>
<div class="stats-grid">
<div class="stat-card">
<h4>⏱️ Performance</h4>
<ul>
<li>Durée totale: <span class="stat-value" id="totalDuration">0ms</span></li>
<li>Étape la plus lente: <span class="stat-value" id="slowestStep">-</span></li>
<li>Moyenne par étape: <span class="stat-value" id="avgDuration">0ms</span></li>
</ul>
</div>
<div class="stat-card">
<h4>🤖 Utilisation LLM</h4>
<ul>
<li>Tokens utilisés: <span class="stat-value" id="totalTokens">0</span></li>
<li>Appels LLM: <span class="stat-value" id="totalLLMCalls">0</span></li>
<li>Provider principal: <span class="stat-value" id="mainProvider">-</span></li>
</ul>
</div>
<div class="stat-card">
<h4>💰 Coûts</h4>
<ul>
<li>Coût total: <span class="stat-value" id="totalCost">$0.00</span></li>
<li>Coût moyen/étape: <span class="stat-value" id="avgCost">$0.00</span></li>
<li>Système le plus cher: <span class="stat-value" id="mostExpensive">-</span></li>
</ul>
</div>
<div class="stat-card">
<h4>✅ Statut Session</h4>
<ul>
<li>Étapes complétées: <span class="stat-value" id="completedSteps">0</span></li>
<li>Taux de succès: <span class="stat-value" id="successRate">0%</span></li>
<li>Dernière activité: <span class="stat-value" id="lastActivity">-</span></li>
</ul>
</div>
</div>
</div>
</div>
<script>
// Variables globales
let currentSessionId = null;
let currentSession = null;
let stepResultsData = {};
// ==============================================
// INITIALISATION
// ==============================================
document.addEventListener('DOMContentLoaded', function() {
console.log('🎯 Step-by-Step Interface initialisée');
});
// ==============================================
// GESTION SESSION
// ==============================================
async function initSession() {
try {
const initBtn = document.getElementById('initBtn');
initBtn.disabled = true;
initBtn.innerHTML = '⏳ Initialisation...';
const inputData = {
mc0: document.getElementById('mc0').value || 'plaque personnalisée',
t0: document.getElementById('t0').value || 'Créer une plaque personnalisée',
mcPlus1: document.getElementById('mcPlus1').value || '',
personality: document.getElementById('personality').value || 'random'
};
console.log('🚀 Initialisation session avec:', inputData);
const response = await fetch('/api/step-by-step/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(inputData)
});
const data = await response.json();
if (data.success) {
currentSessionId = data.sessionId;
currentSession = {
id: data.sessionId,
inputData: data.inputData,
steps: data.steps
};
updateSessionInfo();
generateStepButtons(data.steps);
generateStepResults(data.steps);
enableControls();
console.log('✅ Session initialisée:', currentSessionId);
} else {
throw new Error(data.message || 'Erreur initialisation');
}
} catch (error) {
console.error('❌ Erreur init session:', error);
alert('Erreur initialisation: ' + error.message);
} finally {
const initBtn = document.getElementById('initBtn');
initBtn.disabled = false;
initBtn.innerHTML = '🚀 Initialiser Session';
}
}
async function resetSession() {
if (!currentSessionId || !confirm('Êtes-vous sûr de vouloir reset la session ?')) {
return;
}
try {
const response = await fetch('/api/step-by-step/reset', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: currentSessionId })
});
const data = await response.json();
if (data.success) {
// Reset l'interface
generateStepButtons(data.steps);
generateStepResults(data.steps);
stepResultsData = {};
updateGlobalStats();
console.log('🔄 Session reset');
} else {
throw new Error(data.message || 'Erreur reset');
}
} catch (error) {
console.error('❌ Erreur reset:', error);
alert('Erreur reset: ' + error.message);
}
}
// ==============================================
// EXÉCUTION ÉTAPES
// ==============================================
async function executeStep(stepId) {
if (!currentSessionId) {
alert('Veuillez d\'abord initialiser une session');
return;
}
try {
console.log(`🚀 Exécution étape ${stepId}`);
// Marquer l'étape comme en cours
updateStepStatus(stepId, 'executing');
const response = await fetch('/api/step-by-step/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId: currentSessionId,
stepId: stepId,
options: {} // TODO: permettre configuration
})
});
const data = await response.json();
if (data.success) {
// Stocker le résultat
stepResultsData[stepId] = data;
// Mettre à jour l'interface
updateStepStatus(stepId, 'completed');
displayStepResult(stepId, data);
updateGlobalStats();
console.log(`✅ Étape ${stepId} complétée`);
} else {
updateStepStatus(stepId, 'error');
displayStepError(stepId, data.message || 'Erreur inconnue');
console.error(`❌ Erreur étape ${stepId}:`, data.message);
}
} catch (error) {
updateStepStatus(stepId, 'error');
displayStepError(stepId, error.message);
console.error(`❌ Erreur étape ${stepId}:`, error);
}
}
async function executeAll() {
if (!currentSessionId) {
alert('Veuillez d\'abord initialiser une session');
return;
}
const steps = currentSession.steps;
for (const step of steps) {
await executeStep(step.id);
// Petit délai entre les étapes
await new Promise(resolve => setTimeout(resolve, 500));
}
}
// ==============================================
// INTERFACE DYNAMIQUE
// ==============================================
function updateSessionInfo() {
const sessionInfo = document.getElementById('sessionInfo');
const sessionIdEl = document.getElementById('sessionId');
const sessionStatusEl = document.getElementById('sessionStatus');
if (currentSessionId) {
sessionInfo.style.display = 'block';
sessionIdEl.textContent = currentSessionId.substring(0, 8) + '...';
sessionStatusEl.textContent = 'active';
}
}
function generateStepButtons(steps) {
const container = document.getElementById('stepButtons');
container.innerHTML = '';
steps.forEach(step => {
const button = document.createElement('div');
button.className = 'step-btn pending';
button.setAttribute('data-step', step.id);
button.onclick = () => executeStep(step.id);
button.innerHTML = `
<div class="step-info">
<div class="step-name">${step.id}️⃣ ${step.name}</div>
<div class="step-desc">${step.description}</div>
</div>
<div class="step-stats" id="stepStats${step.id}">
En attente
</div>
`;
container.appendChild(button);
});
}
function generateStepResults(steps) {
const container = document.getElementById('stepResults');
container.innerHTML = '';
steps.forEach(step => {
const resultDiv = document.createElement('div');
resultDiv.className = 'step-result';
resultDiv.setAttribute('data-step', step.id);
resultDiv.innerHTML = `
<div class="result-header">
<h3>${step.id}️⃣ ${step.name}</h3>
<div class="result-status pending" id="resultStatus${step.id}">En attente</div>
</div>
<div class="result-content" id="resultContent${step.id}" style="display: none;">
<div class="format-toggle">
<button class="format-btn active" onclick="toggleFormat(${step.id}, 'tag')">[Tag] Format</button>
<button class="format-btn" onclick="toggleFormat(${step.id}, 'xml')">XML Format</button>
</div>
<div class="content-output" id="contentOutput${step.id}">
<!-- Résultat ici -->
</div>
<div class="result-stats" id="resultStats${step.id}">
<!-- Stats ici -->
</div>
</div>
`;
container.appendChild(resultDiv);
});
}
function updateStepStatus(stepId, status) {
// Mettre à jour le bouton
const stepBtn = document.querySelector(`[data-step="${stepId}"]`);
if (stepBtn) {
stepBtn.className = `step-btn ${status}`;
}
// Mettre à jour le statut dans les résultats
const resultStatus = document.getElementById(`resultStatus${stepId}`);
if (resultStatus) {
resultStatus.className = `result-status ${status}`;
resultStatus.textContent = {
pending: 'En attente',
executing: 'En cours...',
completed: 'Complété',
error: 'Erreur'
}[status] || status;
}
// Ajouter l'animation loading si nécessaire
const stepStats = document.getElementById(`stepStats${stepId}`);
if (stepStats) {
if (status === 'executing') {
stepStats.innerHTML = '<span class="loading">En cours</span>';
} else if (status === 'pending') {
stepStats.textContent = 'En attente';
}
}
}
function displayStepResult(stepId, data) {
const contentDiv = document.getElementById(`resultContent${stepId}`);
const outputDiv = document.getElementById(`contentOutput${stepId}`);
const statsDiv = document.getElementById(`resultStats${stepId}`);
// Afficher le contenu
contentDiv.style.display = 'block';
// Afficher le résultat formaté
if (data.result && data.result.formatted) {
outputDiv.innerHTML = formatContentForDisplay(data.result.formatted, 'tag');
} else {
outputDiv.textContent = 'Pas de contenu généré';
}
// Afficher les stats
if (data.stats) {
statsDiv.innerHTML = `
<div class="stat">🕒 ${data.stats.duration}ms</div>
<div class="stat">🎯 ${data.stats.tokensUsed || 0} tokens</div>
<div class="stat">💰 $${(data.stats.cost || 0).toFixed(4)}</div>
<div class="stat">🤖 ${data.stats.system}</div>
`;
}
// Mettre à jour les stats du bouton
const stepStats = document.getElementById(`stepStats${stepId}`);
if (stepStats && data.stats) {
stepStats.innerHTML = `${data.stats.duration}ms<br>$${(data.stats.cost || 0).toFixed(3)}`;
}
}
function displayStepError(stepId, errorMessage) {
const contentDiv = document.getElementById(`resultContent${stepId}`);
const outputDiv = document.getElementById(`contentOutput${stepId}`);
contentDiv.style.display = 'block';
outputDiv.innerHTML = `<div style="color: #f56565;">❌ Erreur: ${errorMessage}</div>`;
const stepStats = document.getElementById(`stepStats${stepId}`);
if (stepStats) {
stepStats.textContent = 'Erreur';
}
}
function formatContentForDisplay(content, format) {
if (format === 'tag') {
return content.replace(/\[([^\]]+)\]/g, '<span class="tag">[$1]</span>');
}
return content;
}
function toggleFormat(stepId, format) {
const buttons = document.querySelectorAll(`[data-step="${stepId}"] .format-btn`);
buttons.forEach(btn => btn.classList.remove('active'));
const activeBtn = document.querySelector(`[data-step="${stepId}"] .format-btn[onclick*="${format}"]`);
if (activeBtn) activeBtn.classList.add('active');
const data = stepResultsData[stepId];
if (!data) return;
const outputDiv = document.getElementById(`contentOutput${stepId}`);
if (format === 'tag' && data.result.formatted) {
outputDiv.innerHTML = formatContentForDisplay(data.result.formatted, 'tag');
} else if (format === 'xml' && data.result.xmlFormatted) {
outputDiv.textContent = data.result.xmlFormatted;
}
}
function enableControls() {
document.getElementById('executeAllBtn').disabled = false;
document.getElementById('resetBtn').disabled = false;
document.getElementById('exportBtn').disabled = false;
}
function updateGlobalStats() {
const results = Object.values(stepResultsData);
if (results.length === 0) {
// Reset stats
document.getElementById('totalDuration').textContent = '0ms';
document.getElementById('totalTokens').textContent = '0';
document.getElementById('totalCost').textContent = '$0.00';
document.getElementById('completedSteps').textContent = '0';
document.getElementById('successRate').textContent = '0%';
return;
}
const totalDuration = results.reduce((sum, r) => sum + (r.stats?.duration || 0), 0);
const totalTokens = results.reduce((sum, r) => sum + (r.stats?.tokensUsed || 0), 0);
const totalCost = results.reduce((sum, r) => sum + (r.stats?.cost || 0), 0);
const successfulResults = results.filter(r => r.success);
document.getElementById('totalDuration').textContent = `${totalDuration}ms`;
document.getElementById('totalTokens').textContent = totalTokens.toString();
document.getElementById('totalCost').textContent = `$${totalCost.toFixed(4)}`;
document.getElementById('completedSteps').textContent = results.length.toString();
document.getElementById('successRate').textContent = `${Math.round(successfulResults.length / results.length * 100)}%`;
document.getElementById('avgDuration').textContent = `${Math.round(totalDuration / results.length)}ms`;
document.getElementById('avgCost').textContent = `$${(totalCost / results.length).toFixed(4)}`;
document.getElementById('lastActivity').textContent = new Date().toLocaleTimeString();
// Trouver l'étape la plus lente
const slowestStep = results.reduce((max, r) =>
(r.stats?.duration || 0) > (max.stats?.duration || 0) ? r : max, results[0]);
if (slowestStep) {
document.getElementById('slowestStep').textContent = `Étape ${slowestStep.stepId}`;
}
}
// ==============================================
// EXPORT
// ==============================================
async function exportResults() {
if (!currentSessionId) {
alert('Aucune session à exporter');
return;
}
try {
const response = await fetch(`/api/step-by-step/export/${currentSessionId}`);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `step-by-step-${currentSessionId}.json`;
a.click();
window.URL.revokeObjectURL(url);
console.log('💾 Export réalisé');
} catch (error) {
console.error('❌ Erreur export:', error);
alert('Erreur export: ' + error.message);
}
}
</script>
</body>
</html>

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialCore // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialCore
// Module: adversarial-generation/AdversarialCore.js // Module: adversarial-generation/AdversarialCore.js
// Générés le: 2025-09-06T03:38:48.005Z // Générés le: 2025-09-06T12:40:35.923Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialInitialGeneration // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialInitialGeneration
// Module: adversarial-generation/AdversarialInitialGeneration.js // Module: adversarial-generation/AdversarialInitialGeneration.js
// Générés le: 2025-09-06T03:38:48.013Z // Générés le: 2025-09-06T12:40:35.935Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialLayers // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialLayers
// Module: adversarial-generation/AdversarialLayers.js // Module: adversarial-generation/AdversarialLayers.js
// Générés le: 2025-09-06T03:38:48.020Z // Générés le: 2025-09-06T12:40:35.947Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialPromptEngine // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialPromptEngine
// Module: adversarial-generation/AdversarialPromptEngine.js // Module: adversarial-generation/AdversarialPromptEngine.js
// Générés le: 2025-09-06T03:38:48.027Z // Générés le: 2025-09-06T12:40:35.957Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialStyleEnhancement // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialStyleEnhancement
// Module: adversarial-generation/AdversarialStyleEnhancement.js // Module: adversarial-generation/AdversarialStyleEnhancement.js
// Générés le: 2025-09-06T03:38:48.034Z // Générés le: 2025-09-06T12:40:35.968Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialTechnicalEnhancement // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialTechnicalEnhancement
// Module: adversarial-generation/AdversarialTechnicalEnhancement.js // Module: adversarial-generation/AdversarialTechnicalEnhancement.js
// Générés le: 2025-09-06T03:38:48.040Z // Générés le: 2025-09-06T12:40:35.978Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialTransitionEnhancement // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialTransitionEnhancement
// Module: adversarial-generation/AdversarialTransitionEnhancement.js // Module: adversarial-generation/AdversarialTransitionEnhancement.js
// Générés le: 2025-09-06T03:38:48.047Z // Générés le: 2025-09-06T12:40:35.989Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialUtils // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AdversarialUtils
// Module: adversarial-generation/AdversarialUtils.js // Module: adversarial-generation/AdversarialUtils.js
// Générés le: 2025-09-06T03:38:48.054Z // Générés le: 2025-09-06T12:40:36.001Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ArticleStorage // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ArticleStorage
// Module: ArticleStorage.js // Module: ArticleStorage.js
// Générés le: 2025-09-06T03:38:47.914Z // Générés le: 2025-09-06T12:40:35.780Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');
@ -61,7 +61,7 @@ describe('ArticleStorage - Tests automatiques', () => {
}); });
test('createArticlesStorageSheet - Content Generation', async () => { test('createArticlesStorageSheet - Content Generation', async () => {
const mockInput = "test_value"; const mockInput = ["test_value", "test_value"];
try { try {
const result = await ArticleStorage.createArticlesStorageSheet(mockInput); const result = await ArticleStorage.createArticlesStorageSheet(mockInput);

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - AutoProcessor // TESTS GÉNÉRÉS AUTOMATIQUEMENT - AutoProcessor
// Module: modes/AutoProcessor.js // Module: modes/AutoProcessor.js
// Générés le: 2025-09-06T03:38:48.161Z // Générés le: 2025-09-06T12:40:36.165Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');
@ -170,24 +170,6 @@ describe('AutoProcessor - Tests automatiques', () => {
} }
}); });
test('while - Basic Function', () => {
const input = undefined;
try {
const result = AutoProcessor.while(input);
// Validations de base
assert.ok(result !== undefined, 'Should return a result');
assert.ok(typeof result !== 'undefined', 'Result should be defined');
console.log('✅ while: Function executed successfully');
} catch (error) {
console.error('❌ while: Function failed:', error.message);
throw error;
}
});
test('startProcessingLoop - Basic Function', () => { test('startProcessingLoop - Basic Function', () => {
const input = undefined; const input = undefined;

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - BrainConfig // TESTS GÉNÉRÉS AUTOMATIQUEMENT - BrainConfig
// Module: BrainConfig.js // Module: BrainConfig.js
// Générés le: 2025-09-06T03:38:47.921Z // Générés le: 2025-09-06T12:40:35.793Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ComparisonFramework // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ComparisonFramework
// Module: adversarial-generation/ComparisonFramework.js // Module: adversarial-generation/ComparisonFramework.js
// Générés le: 2025-09-06T03:38:48.061Z // Générés le: 2025-09-06T12:40:36.010Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ContentAssembly // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ContentAssembly
// Module: ContentAssembly.js // Module: ContentAssembly.js
// Générés le: 2025-09-06T03:38:47.926Z // Générés le: 2025-09-06T12:40:35.803Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ContentGeneration // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ContentGeneration
// Module: ContentGeneration.js // Module: ContentGeneration.js
// Générés le: 2025-09-06T03:38:47.936Z // Générés le: 2025-09-06T12:40:35.813Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ContentGenerationAdversarial // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ContentGenerationAdversarial
// Module: adversarial-generation/ContentGenerationAdversarial.js // Module: adversarial-generation/ContentGenerationAdversarial.js
// Générés le: 2025-09-06T03:38:48.068Z // Générés le: 2025-09-06T12:40:36.020Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - DetectorStrategies // TESTS GÉNÉRÉS AUTOMATIQUEMENT - DetectorStrategies
// Module: adversarial-generation/DetectorStrategies.js // Module: adversarial-generation/DetectorStrategies.js
// Générés le: 2025-09-06T03:38:48.076Z // Générés le: 2025-09-06T12:40:36.033Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - DigitalOceanWorkflow // TESTS GÉNÉRÉS AUTOMATIQUEMENT - DigitalOceanWorkflow
// Module: DigitalOceanWorkflow.js // Module: DigitalOceanWorkflow.js
// Générés le: 2025-09-06T03:38:47.944Z // Générés le: 2025-09-06T12:40:35.823Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ElementExtraction // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ElementExtraction
// Module: ElementExtraction.js // Module: ElementExtraction.js
// Générés le: 2025-09-06T03:38:47.950Z // Générés le: 2025-09-06T12:40:35.834Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ErrorReporting // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ErrorReporting
// Module: ErrorReporting.js // Module: ErrorReporting.js
// Générés le: 2025-09-06T03:38:47.956Z // Générés le: 2025-09-06T12:40:35.844Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - FatiguePatterns // TESTS GÉNÉRÉS AUTOMATIQUEMENT - FatiguePatterns
// Module: human-simulation/FatiguePatterns.js // Module: human-simulation/FatiguePatterns.js
// Générés le: 2025-09-06T03:38:48.115Z // Générés le: 2025-09-06T12:40:36.100Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - HumanSimulationCore // TESTS GÉNÉRÉS AUTOMATIQUEMENT - HumanSimulationCore
// Module: human-simulation/HumanSimulationCore.js // Module: human-simulation/HumanSimulationCore.js
// Générés le: 2025-09-06T03:38:48.121Z // Générés le: 2025-09-06T12:40:36.108Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - HumanSimulationLayers // TESTS GÉNÉRÉS AUTOMATIQUEMENT - HumanSimulationLayers
// Module: human-simulation/HumanSimulationLayers.js // Module: human-simulation/HumanSimulationLayers.js
// Générés le: 2025-09-06T03:38:48.128Z // Générés le: 2025-09-06T12:40:36.117Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - HumanSimulationUtils // TESTS GÉNÉRÉS AUTOMATIQUEMENT - HumanSimulationUtils
// Module: human-simulation/HumanSimulationUtils.js // Module: human-simulation/HumanSimulationUtils.js
// Générés le: 2025-09-06T03:38:48.134Z // Générés le: 2025-09-06T12:40:36.126Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - InitialGeneration // TESTS GÉNÉRÉS AUTOMATIQUEMENT - InitialGeneration
// Module: generation/InitialGeneration.js // Module: generation/InitialGeneration.js
// Générés le: 2025-09-06T03:38:48.089Z // Générés le: 2025-09-06T12:40:36.058Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - LLMFingerprintRemoval // TESTS GÉNÉRÉS AUTOMATIQUEMENT - LLMFingerprintRemoval
// Module: post-processing/LLMFingerprintRemoval.js // Module: post-processing/LLMFingerprintRemoval.js
// Générés le: 2025-09-06T03:38:48.209Z // Générés le: 2025-09-06T12:40:36.239Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - LLMFingerprints // TESTS GÉNÉRÉS AUTOMATIQUEMENT - LLMFingerprints
// Module: pattern-breaking/LLMFingerprints.js // Module: pattern-breaking/LLMFingerprints.js
// Générés le: 2025-09-06T03:38:48.179Z // Générés le: 2025-09-06T12:40:36.195Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - LLMManager // TESTS GÉNÉRÉS AUTOMATIQUEMENT - LLMManager
// Module: LLMManager.js // Module: LLMManager.js
// Générés le: 2025-09-06T03:38:47.964Z // Générés le: 2025-09-06T12:40:35.854Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - Main // TESTS GÉNÉRÉS AUTOMATIQUEMENT - Main
// Module: Main.js // Module: Main.js
// Générés le: 2025-09-06T03:38:47.971Z // Générés le: 2025-09-06T12:40:35.864Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ManualServer // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ManualServer
// Module: modes/ManualServer.js // Module: modes/ManualServer.js
// Générés le: 2025-09-06T03:38:48.168Z // Générés le: 2025-09-06T12:40:36.176Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');
@ -210,6 +210,26 @@ describe('ManualServer - Tests automatiques', () => {
} }
}); });
test('handleWorkflowModulaire - Async Operation', async () => {
const input = undefined;
try {
const startTime = Date.now();
const result = await ManualServer.handleWorkflowModulaire(input);
const duration = Date.now() - startTime;
// Validations de base
assert.ok(result !== undefined, 'Should return a result');
assert.ok(duration < 30000, 'Should complete within 30 seconds');
console.log(`✅ handleWorkflowModulaire: Completed in ${duration}ms`);
} catch (error) {
console.error('❌ handleWorkflowModulaire: Async operation failed:', error.message);
throw error;
}
});
test('handleModulaireConfig - Basic Function', () => { test('handleModulaireConfig - Basic Function', () => {
const input = undefined; const input = undefined;

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ManualTrigger // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ManualTrigger
// Module: ManualTrigger.js // Module: ManualTrigger.js
// Générés le: 2025-09-06T03:38:47.977Z // Générés le: 2025-09-06T12:40:35.873Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - MissingKeywords // TESTS GÉNÉRÉS AUTOMATIQUEMENT - MissingKeywords
// Module: MissingKeywords.js // Module: MissingKeywords.js
// Générés le: 2025-09-06T03:38:47.983Z // Générés le: 2025-09-06T12:40:35.881Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - ModeManager // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ModeManager
// Module: modes/ModeManager.js // Module: modes/ModeManager.js
// Générés le: 2025-09-06T03:38:48.174Z // Générés le: 2025-09-06T12:40:36.186Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - NaturalConnectors // TESTS GÉNÉRÉS AUTOMATIQUEMENT - NaturalConnectors
// Module: pattern-breaking/NaturalConnectors.js // Module: pattern-breaking/NaturalConnectors.js
// Générés le: 2025-09-06T03:38:48.186Z // Générés le: 2025-09-06T12:40:36.203Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - PatternBreaking // TESTS GÉNÉRÉS AUTOMATIQUEMENT - PatternBreaking
// Module: post-processing/PatternBreaking.js // Module: post-processing/PatternBreaking.js
// Générés le: 2025-09-06T03:38:48.216Z // Générés le: 2025-09-06T12:40:36.249Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - PatternBreakingCore // TESTS GÉNÉRÉS AUTOMATIQUEMENT - PatternBreakingCore
// Module: pattern-breaking/PatternBreakingCore.js // Module: pattern-breaking/PatternBreakingCore.js
// Générés le: 2025-09-06T03:38:48.192Z // Générés le: 2025-09-06T12:40:36.212Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - PatternBreakingLayers // TESTS GÉNÉRÉS AUTOMATIQUEMENT - PatternBreakingLayers
// Module: pattern-breaking/PatternBreakingLayers.js // Module: pattern-breaking/PatternBreakingLayers.js
// Générés le: 2025-09-06T03:38:48.198Z // Générés le: 2025-09-06T12:40:36.221Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - PersonalityErrors // TESTS GÉNÉRÉS AUTOMATIQUEMENT - PersonalityErrors
// Module: human-simulation/PersonalityErrors.js // Module: human-simulation/PersonalityErrors.js
// Générés le: 2025-09-06T03:38:48.140Z // Générés le: 2025-09-06T12:40:36.135Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveCore // TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveCore
// Module: selective-enhancement/SelectiveCore.js // Module: selective-enhancement/SelectiveCore.js
// Générés le: 2025-09-06T03:38:48.233Z // Générés le: 2025-09-06T12:40:36.275Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveEnhancement // TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveEnhancement
// Module: SelectiveEnhancement.js // Module: SelectiveEnhancement.js
// Générés le: 2025-09-06T03:38:47.992Z // Générés le: 2025-09-06T12:40:35.898Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveLayers // TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveLayers
// Module: selective-enhancement/SelectiveLayers.js // Module: selective-enhancement/SelectiveLayers.js
// Générés le: 2025-09-06T03:38:48.240Z // Générés le: 2025-09-06T12:40:36.284Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveUtils // TESTS GÉNÉRÉS AUTOMATIQUEMENT - SelectiveUtils
// Module: selective-enhancement/SelectiveUtils.js // Module: selective-enhancement/SelectiveUtils.js
// Générés le: 2025-09-06T03:38:48.247Z // Générés le: 2025-09-06T12:40:36.295Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - SentenceVariation // TESTS GÉNÉRÉS AUTOMATIQUEMENT - SentenceVariation
// Module: post-processing/SentenceVariation.js // Module: post-processing/SentenceVariation.js
// Générés le: 2025-09-06T03:38:48.222Z // Générés le: 2025-09-06T12:40:36.257Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - StyleEnhancement // TESTS GÉNÉRÉS AUTOMATIQUEMENT - StyleEnhancement
// Module: generation/StyleEnhancement.js // Module: generation/StyleEnhancement.js
// Générés le: 2025-09-06T03:38:48.095Z // Générés le: 2025-09-06T12:40:36.069Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - StyleLayer // TESTS GÉNÉRÉS AUTOMATIQUEMENT - StyleLayer
// Module: selective-enhancement/StyleLayer.js // Module: selective-enhancement/StyleLayer.js
// Générés le: 2025-09-06T03:38:48.253Z // Générés le: 2025-09-06T12:40:36.307Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - SyntaxVariations // TESTS GÉNÉRÉS AUTOMATIQUEMENT - SyntaxVariations
// Module: pattern-breaking/SyntaxVariations.js // Module: pattern-breaking/SyntaxVariations.js
// Générés le: 2025-09-06T03:38:48.204Z // Générés le: 2025-09-06T12:40:36.230Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - TechnicalEnhancement // TESTS GÉNÉRÉS AUTOMATIQUEMENT - TechnicalEnhancement
// Module: generation/TechnicalEnhancement.js // Module: generation/TechnicalEnhancement.js
// Générés le: 2025-09-06T03:38:48.101Z // Générés le: 2025-09-06T12:40:36.079Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - TechnicalLayer // TESTS GÉNÉRÉS AUTOMATIQUEMENT - TechnicalLayer
// Module: selective-enhancement/TechnicalLayer.js // Module: selective-enhancement/TechnicalLayer.js
// Générés le: 2025-09-06T03:38:48.261Z // Générés le: 2025-09-06T12:40:36.316Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - TemporalStyles // TESTS GÉNÉRÉS AUTOMATIQUEMENT - TemporalStyles
// Module: human-simulation/TemporalStyles.js // Module: human-simulation/TemporalStyles.js
// Générés le: 2025-09-06T03:38:48.147Z // Générés le: 2025-09-06T12:40:36.145Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - TransitionEnhancement // TESTS GÉNÉRÉS AUTOMATIQUEMENT - TransitionEnhancement
// Module: generation/TransitionEnhancement.js // Module: generation/TransitionEnhancement.js
// Générés le: 2025-09-06T03:38:48.108Z // Générés le: 2025-09-06T12:40:36.090Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - TransitionHumanization // TESTS GÉNÉRÉS AUTOMATIQUEMENT - TransitionHumanization
// Module: post-processing/TransitionHumanization.js // Module: post-processing/TransitionHumanization.js
// Générés le: 2025-09-06T03:38:48.227Z // Générés le: 2025-09-06T12:40:36.266Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - TransitionLayer // TESTS GÉNÉRÉS AUTOMATIQUEMENT - TransitionLayer
// Module: selective-enhancement/TransitionLayer.js // Module: selective-enhancement/TransitionLayer.js
// Générés le: 2025-09-06T03:38:48.268Z // Générés le: 2025-09-06T12:40:36.325Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - Utils // TESTS GÉNÉRÉS AUTOMATIQUEMENT - Utils
// Module: Utils.js // Module: Utils.js
// Générés le: 2025-09-06T03:38:47.999Z // Générés le: 2025-09-06T12:40:35.911Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - demo-modulaire // TESTS GÉNÉRÉS AUTOMATIQUEMENT - demo-modulaire
// Module: selective-enhancement/demo-modulaire.js // Module: selective-enhancement/demo-modulaire.js
// Générés le: 2025-09-06T03:38:48.274Z // Générés le: 2025-09-06T12:40:36.333Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - main_modulaire // TESTS GÉNÉRÉS AUTOMATIQUEMENT - main_modulaire
// Module: main_modulaire.js // Module: main_modulaire.js
// Générés le: 2025-09-06T03:38:48.154Z // Générés le: 2025-09-06T12:40:36.155Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - trace-wrap // TESTS GÉNÉRÉS AUTOMATIQUEMENT - trace-wrap
// Module: trace-wrap.js // Module: trace-wrap.js
// Générés le: 2025-09-06T03:38:48.280Z // Générés le: 2025-09-06T12:40:36.340Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -1,7 +1,7 @@
// ======================================== // ========================================
// TESTS GÉNÉRÉS AUTOMATIQUEMENT - trace // TESTS GÉNÉRÉS AUTOMATIQUEMENT - trace
// Module: trace.js // Module: trace.js
// Générés le: 2025-09-06T03:38:48.286Z // Générés le: 2025-09-06T12:40:36.348Z
// ======================================== // ========================================
const assert = require('assert'); const assert = require('assert');

View File

@ -229,7 +229,7 @@
<div class="header-left"> <div class="header-left">
<h1>SEO Generator - Logs temps réel</h1> <h1>SEO Generator - Logs temps réel</h1>
<span id="status" class="status connecting">Connexion...</span> <span id="status" class="status connecting">Connexion...</span>
<span style="margin-left: 15px; font-size: 12px;">Port: <strong>8081</strong></span> <span style="margin-left: 15px; font-size: 12px;">Port: <strong>8082</strong></span>
<br> <br>
<button onclick="toggleGlobalDetails()" id="detailsBtn">Mode détaillé: OFF</button> <button onclick="toggleGlobalDetails()" id="detailsBtn">Mode détaillé: OFF</button>
<button onclick="toggleLineUnwrap()" id="lineUnwrapBtn">Unwrap ligne: OFF</button> <button onclick="toggleLineUnwrap()" id="lineUnwrapBtn">Unwrap ligne: OFF</button>
@ -396,8 +396,8 @@
} }
function connect() { function connect() {
console.log('🔌 connect() appelé - tentative WebSocket ws://localhost:8081'); console.log('🔌 connect() appelé - tentative WebSocket ws://localhost:8082');
ws = new WebSocket('ws://localhost:8081'); ws = new WebSocket('ws://localhost:8082');
ws.onopen = () => { ws.onopen = () => {
console.log('✅ WebSocket connecté !'); console.log('✅ WebSocket connecté !');