// ======================================== // FRAMEWORK DE COMPARAISON ADVERSARIAL // Responsabilité: Comparer pipelines normales vs adversariales // Utilisation: A/B testing et validation efficacité anti-détection // ======================================== const { logSh } = require('../ErrorReporting'); const { tracer } = require('../trace'); // Pipelines à comparer const { generateSimple } = require('../selective-enhancement/SelectiveUtils'); // Pipeline normale const { generateWithAdversarialContext, compareAdversarialStrategies } = require('./ContentGenerationAdversarial'); // Pipeline adversariale /** * MAIN ENTRY POINT - COMPARAISON A/B PIPELINE * Compare pipeline normale vs adversariale sur même input */ async function compareNormalVsAdversarial(input, options = {}) { return await tracer.run('ComparisonFramework.compareNormalVsAdversarial()', async () => { const { hierarchy, csvData, adversarialConfig = {}, runBothPipelines = true, analyzeContent = true } = input; const { detectorTarget = 'general', intensity = 1.0, iterations = 1 } = options; await tracer.annotate({ comparisonType: 'normal_vs_adversarial', detectorTarget, intensity, iterations, elementsCount: Object.keys(hierarchy).length }); const startTime = Date.now(); logSh(`🆚 COMPARAISON A/B: Pipeline normale vs adversariale`, 'INFO'); logSh(` 🎯 Détecteur cible: ${detectorTarget} | Intensité: ${intensity} | Itérations: ${iterations}`, 'INFO'); const results = { normal: null, adversarial: null, comparison: null, iterations: [] }; try { for (let i = 0; i < iterations; i++) { logSh(`🔄 Itération ${i + 1}/${iterations}`, 'INFO'); const iterationResults = { iteration: i + 1, normal: null, adversarial: null, metrics: {} }; // ======================================== // PIPELINE NORMALE // ======================================== if (runBothPipelines) { logSh(` 📊 Génération pipeline normale...`, 'DEBUG'); const normalStartTime = Date.now(); try { const normalResult = await generateSimple(hierarchy, csvData); iterationResults.normal = { success: true, content: normalResult, duration: Date.now() - normalStartTime, elementsCount: Object.keys(normalResult).length }; logSh(` ✅ Pipeline normale: ${iterationResults.normal.elementsCount} éléments (${iterationResults.normal.duration}ms)`, 'DEBUG'); } catch (error) { iterationResults.normal = { success: false, error: error.message, duration: Date.now() - normalStartTime }; logSh(` ❌ Pipeline normale échouée: ${error.message}`, 'ERROR'); } } // ======================================== // PIPELINE ADVERSARIALE // ======================================== logSh(` 🎯 Génération pipeline adversariale...`, 'DEBUG'); const adversarialStartTime = Date.now(); try { const adversarialResult = await generateWithAdversarialContext({ hierarchy, csvData, adversarialConfig: { detectorTarget, intensity, enableAllSteps: true, ...adversarialConfig } }); iterationResults.adversarial = { success: true, content: adversarialResult.content, stats: adversarialResult.stats, adversarialMetrics: adversarialResult.adversarialMetrics, duration: Date.now() - adversarialStartTime, elementsCount: Object.keys(adversarialResult.content).length }; logSh(` ✅ Pipeline adversariale: ${iterationResults.adversarial.elementsCount} éléments (${iterationResults.adversarial.duration}ms)`, 'DEBUG'); logSh(` 📊 Score efficacité: ${adversarialResult.adversarialMetrics.effectivenessScore.toFixed(2)}%`, 'DEBUG'); } catch (error) { iterationResults.adversarial = { success: false, error: error.message, duration: Date.now() - adversarialStartTime }; logSh(` ❌ Pipeline adversariale échouée: ${error.message}`, 'ERROR'); } // ======================================== // ANALYSE COMPARATIVE ITÉRATION // ======================================== if (analyzeContent && iterationResults.normal?.success && iterationResults.adversarial?.success) { iterationResults.metrics = analyzeContentComparison( iterationResults.normal.content, iterationResults.adversarial.content ); logSh(` 📈 Diversité: Normal=${iterationResults.metrics.diversity.normal.toFixed(2)}% | Adversarial=${iterationResults.metrics.diversity.adversarial.toFixed(2)}%`, 'DEBUG'); } results.iterations.push(iterationResults); } // ======================================== // CONSOLIDATION RÉSULTATS // ======================================== const totalDuration = Date.now() - startTime; // Prendre les meilleurs résultats ou derniers si une seule itération const lastIteration = results.iterations[results.iterations.length - 1]; results.normal = lastIteration.normal; results.adversarial = lastIteration.adversarial; // Analyse comparative globale results.comparison = generateGlobalComparison(results.iterations, options); logSh(`🆚 COMPARAISON TERMINÉE: ${iterations} itérations (${totalDuration}ms)`, 'INFO'); if (results.comparison.winner) { logSh(`🏆 Gagnant: ${results.comparison.winner} (score: ${results.comparison.bestScore.toFixed(2)})`, 'INFO'); } await tracer.event('Comparaison A/B terminée', { iterations, winner: results.comparison.winner, totalDuration }); return results; } catch (error) { const duration = Date.now() - startTime; logSh(`❌ COMPARAISON A/B ÉCHOUÉE après ${duration}ms: ${error.message}`, 'ERROR'); throw new Error(`ComparisonFramework failed: ${error.message}`); } }, input); } /** * COMPARAISON MULTI-DÉTECTEURS */ async function compareMultiDetectors(hierarchy, csvData, detectorTargets = ['general', 'gptZero', 'originality']) { logSh(`🎯 COMPARAISON MULTI-DÉTECTEURS: ${detectorTargets.length} stratégies`, 'INFO'); const results = {}; const startTime = Date.now(); for (const detector of detectorTargets) { logSh(` 🔍 Test détecteur: ${detector}`, 'DEBUG'); try { const comparison = await compareNormalVsAdversarial({ hierarchy, csvData, adversarialConfig: { detectorTarget: detector } }, { detectorTarget: detector, intensity: 1.0, iterations: 1 }); results[detector] = { success: true, comparison, effectivenessGain: comparison.adversarial?.adversarialMetrics?.effectivenessScore || 0 }; logSh(` ✅ ${detector}: +${results[detector].effectivenessGain.toFixed(2)}% efficacité`, 'DEBUG'); } catch (error) { results[detector] = { success: false, error: error.message, effectivenessGain: 0 }; logSh(` ❌ ${detector}: Échec - ${error.message}`, 'ERROR'); } } // Analyse du meilleur détecteur const bestDetector = Object.keys(results).reduce((best, current) => { if (!results[best]?.success) return current; if (!results[current]?.success) return best; return results[current].effectivenessGain > results[best].effectivenessGain ? current : best; }); const totalDuration = Date.now() - startTime; logSh(`🎯 MULTI-DÉTECTEURS TERMINÉ: Meilleur=${bestDetector} (${totalDuration}ms)`, 'INFO'); return { results, bestDetector, bestScore: results[bestDetector]?.effectivenessGain || 0, totalDuration }; } /** * BENCHMARK PERFORMANCE */ async function benchmarkPerformance(hierarchy, csvData, configurations = []) { const defaultConfigs = [ { name: 'Normal', type: 'normal' }, { name: 'Simple Adversarial', type: 'adversarial', detectorTarget: 'general', intensity: 0.5 }, { name: 'Intense Adversarial', type: 'adversarial', detectorTarget: 'gptZero', intensity: 1.0 }, { name: 'Max Adversarial', type: 'adversarial', detectorTarget: 'originality', intensity: 1.5 } ]; const configs = configurations.length > 0 ? configurations : defaultConfigs; logSh(`⚡ BENCHMARK PERFORMANCE: ${configs.length} configurations`, 'INFO'); const results = []; for (const config of configs) { logSh(` 🔧 Test: ${config.name}`, 'DEBUG'); const startTime = Date.now(); try { let result; if (config.type === 'normal') { result = await generateSimple(hierarchy, csvData); } else { const adversarialResult = await generateWithAdversarialContext({ hierarchy, csvData, adversarialConfig: { detectorTarget: config.detectorTarget || 'general', intensity: config.intensity || 1.0 } }); result = adversarialResult.content; } const duration = Date.now() - startTime; results.push({ name: config.name, type: config.type, success: true, duration, elementsCount: Object.keys(result).length, performance: Object.keys(result).length / (duration / 1000) // éléments par seconde }); logSh(` ✅ ${config.name}: ${Object.keys(result).length} éléments (${duration}ms)`, 'DEBUG'); } catch (error) { results.push({ name: config.name, type: config.type, success: false, error: error.message, duration: Date.now() - startTime }); logSh(` ❌ ${config.name}: Échec - ${error.message}`, 'ERROR'); } } // Analyser les résultats const successfulResults = results.filter(r => r.success); const fastest = successfulResults.reduce((best, current) => current.duration < best.duration ? current : best, successfulResults[0]); const mostEfficient = successfulResults.reduce((best, current) => current.performance > best.performance ? current : best, successfulResults[0]); logSh(`⚡ BENCHMARK TERMINÉ: Fastest=${fastest?.name} | Most efficient=${mostEfficient?.name}`, 'INFO'); return { results, fastest, mostEfficient, summary: { totalConfigs: configs.length, successful: successfulResults.length, failed: results.length - successfulResults.length } }; } // ============= HELPER FUNCTIONS ============= /** * Analyser différences de contenu entre normal et adversarial */ function analyzeContentComparison(normalContent, adversarialContent) { const metrics = { diversity: { normal: analyzeDiversityScore(Object.values(normalContent).join(' ')), adversarial: analyzeDiversityScore(Object.values(adversarialContent).join(' ')) }, length: { normal: Object.values(normalContent).join(' ').length, adversarial: Object.values(adversarialContent).join(' ').length }, elementsCount: { normal: Object.keys(normalContent).length, adversarial: Object.keys(adversarialContent).length }, differences: compareContentElements(normalContent, adversarialContent) }; return metrics; } /** * Score de diversité lexicale */ function analyzeDiversityScore(content) { if (!content || typeof content !== 'string') return 0; const words = content.split(/\s+/).filter(w => w.length > 2); if (words.length === 0) return 0; const uniqueWords = [...new Set(words.map(w => w.toLowerCase()))]; return (uniqueWords.length / words.length) * 100; } /** * Comparer éléments de contenu */ function compareContentElements(normalContent, adversarialContent) { const differences = { modified: 0, identical: 0, totalElements: Math.max(Object.keys(normalContent).length, Object.keys(adversarialContent).length) }; const allTags = [...new Set([...Object.keys(normalContent), ...Object.keys(adversarialContent)])]; allTags.forEach(tag => { if (normalContent[tag] && adversarialContent[tag]) { if (normalContent[tag] === adversarialContent[tag]) { differences.identical++; } else { differences.modified++; } } }); differences.modificationRate = differences.totalElements > 0 ? (differences.modified / differences.totalElements) * 100 : 0; return differences; } /** * Générer analyse comparative globale */ function generateGlobalComparison(iterations, options) { const successfulIterations = iterations.filter(it => it.normal?.success && it.adversarial?.success); if (successfulIterations.length === 0) { return { winner: null, bestScore: 0, summary: 'Aucune itération réussie' }; } // Moyenner les métriques const avgMetrics = { diversity: { normal: 0, adversarial: 0 }, performance: { normal: 0, adversarial: 0 } }; successfulIterations.forEach(iteration => { if (iteration.metrics) { avgMetrics.diversity.normal += iteration.metrics.diversity.normal; avgMetrics.diversity.adversarial += iteration.metrics.diversity.adversarial; } avgMetrics.performance.normal += iteration.normal.elementsCount / (iteration.normal.duration / 1000); avgMetrics.performance.adversarial += iteration.adversarial.elementsCount / (iteration.adversarial.duration / 1000); }); const iterCount = successfulIterations.length; avgMetrics.diversity.normal /= iterCount; avgMetrics.diversity.adversarial /= iterCount; avgMetrics.performance.normal /= iterCount; avgMetrics.performance.adversarial /= iterCount; // Déterminer le gagnant const diversityGain = avgMetrics.diversity.adversarial - avgMetrics.diversity.normal; const performanceLoss = avgMetrics.performance.normal - avgMetrics.performance.adversarial; // Score composite (favorise diversité avec pénalité performance) const adversarialScore = diversityGain * 2 - (performanceLoss * 0.5); return { winner: adversarialScore > 5 ? 'adversarial' : 'normal', bestScore: Math.max(avgMetrics.diversity.normal, avgMetrics.diversity.adversarial), diversityGain, performanceLoss, avgMetrics, summary: `Diversité: +${diversityGain.toFixed(2)}%, Performance: ${performanceLoss > 0 ? '-' : '+'}${Math.abs(performanceLoss).toFixed(2)} elem/s` }; } module.exports = { compareNormalVsAdversarial, // ← MAIN ENTRY POINT compareMultiDetectors, benchmarkPerformance, analyzeContentComparison, analyzeDiversityScore };