Added plan.md with complete architecture for format-agnostic content generation: - Support for Markdown, HTML, Plain Text, JSON formats - New FormatExporter module with neutral data structure - Integration strategy with existing ContentAssembly and ArticleStorage - Bonus features: SEO metadata generation, readability scoring, WordPress Gutenberg format - Implementation roadmap with 4 phases (6h total estimated) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
413 lines
14 KiB
JavaScript
413 lines
14 KiB
JavaScript
// ========================================
|
|
// ADVERSARIAL LAYERS - COUCHES MODULAIRES
|
|
// Responsabilité: Couches adversariales composables et réutilisables
|
|
// Architecture: Fonction pipeline |> layer1 |> layer2 |> layer3
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
const { tracer } = require('../trace');
|
|
const { applyAdversarialLayer } = require('./AdversarialCore');
|
|
|
|
/**
|
|
* COUCHE ANTI-GPTZEERO - Spécialisée contre GPTZero
|
|
*/
|
|
async function applyAntiGPTZeroLayer(content, options = {}) {
|
|
return await applyAdversarialLayer(content, {
|
|
detectorTarget: 'gptZero',
|
|
intensity: options.intensity || 1.0,
|
|
method: options.method || 'regeneration',
|
|
...options
|
|
});
|
|
}
|
|
|
|
/**
|
|
* COUCHE ANTI-ORIGINALITY - Spécialisée contre Originality.ai
|
|
*/
|
|
async function applyAntiOriginalityLayer(content, options = {}) {
|
|
return await applyAdversarialLayer(content, {
|
|
detectorTarget: 'originality',
|
|
intensity: options.intensity || 1.1,
|
|
method: options.method || 'hybrid',
|
|
...options
|
|
});
|
|
}
|
|
|
|
/**
|
|
* COUCHE ANTI-WINSTON - Spécialisée contre Winston AI
|
|
*/
|
|
async function applyAntiWinstonLayer(content, options = {}) {
|
|
return await applyAdversarialLayer(content, {
|
|
detectorTarget: 'winston',
|
|
intensity: options.intensity || 0.9,
|
|
method: options.method || 'enhancement',
|
|
...options
|
|
});
|
|
}
|
|
|
|
/**
|
|
* COUCHE GÉNÉRALE - Protection généraliste multi-détecteurs
|
|
*/
|
|
async function applyGeneralAdversarialLayer(content, options = {}) {
|
|
return await applyAdversarialLayer(content, {
|
|
detectorTarget: 'general',
|
|
intensity: options.intensity || 0.8,
|
|
method: options.method || 'hybrid',
|
|
...options
|
|
});
|
|
}
|
|
|
|
/**
|
|
* COUCHE LÉGÈRE - Modifications subtiles pour préserver qualité
|
|
*/
|
|
async function applyLightAdversarialLayer(content, options = {}) {
|
|
return await applyAdversarialLayer(content, {
|
|
detectorTarget: options.detectorTarget || 'general',
|
|
intensity: 0.5,
|
|
method: 'enhancement',
|
|
preserveStructure: true,
|
|
...options
|
|
});
|
|
}
|
|
|
|
/**
|
|
* COUCHE INTENSIVE - Maximum anti-détection
|
|
*/
|
|
async function applyIntensiveAdversarialLayer(content, options = {}) {
|
|
return await applyAdversarialLayer(content, {
|
|
detectorTarget: options.detectorTarget || 'gptZero',
|
|
intensity: 1.5,
|
|
method: 'regeneration',
|
|
preserveStructure: false,
|
|
...options
|
|
});
|
|
}
|
|
|
|
/**
|
|
* PIPELINE COMPOSABLE - Application séquentielle de couches
|
|
*/
|
|
async function applyLayerPipeline(content, layers = [], globalOptions = {}) {
|
|
return await tracer.run('AdversarialLayers.applyLayerPipeline()', async () => {
|
|
await tracer.annotate({
|
|
layersPipeline: true,
|
|
layersCount: layers.length,
|
|
elementsCount: Object.keys(content).length
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
logSh(`🔄 PIPELINE COUCHES ADVERSARIALES: ${layers.length} couches`, 'INFO');
|
|
|
|
let currentContent = content;
|
|
const pipelineStats = {
|
|
layers: [],
|
|
totalDuration: 0,
|
|
totalModifications: 0,
|
|
success: true
|
|
};
|
|
|
|
try {
|
|
for (let i = 0; i < layers.length; i++) {
|
|
const layer = layers[i];
|
|
const layerStartTime = Date.now();
|
|
|
|
logSh(` 🎯 Couche ${i + 1}/${layers.length}: ${layer.name || layer.type || 'anonyme'}`, 'DEBUG');
|
|
|
|
try {
|
|
const layerResult = await applyLayerByConfig(currentContent, layer, globalOptions);
|
|
|
|
currentContent = layerResult.content;
|
|
|
|
const layerStats = {
|
|
name: layer.name || `layer_${i + 1}`,
|
|
type: layer.type,
|
|
duration: Date.now() - layerStartTime,
|
|
modificationsCount: layerResult.stats?.elementsModified || 0,
|
|
success: true
|
|
};
|
|
|
|
pipelineStats.layers.push(layerStats);
|
|
pipelineStats.totalModifications += layerStats.modificationsCount;
|
|
|
|
logSh(` ✅ ${layerStats.name}: ${layerStats.modificationsCount} modifs (${layerStats.duration}ms)`, 'DEBUG');
|
|
|
|
} catch (error) {
|
|
logSh(` ❌ Couche ${i + 1} échouée: ${error.message}`, 'ERROR');
|
|
|
|
pipelineStats.layers.push({
|
|
name: layer.name || `layer_${i + 1}`,
|
|
type: layer.type,
|
|
duration: Date.now() - layerStartTime,
|
|
success: false,
|
|
error: error.message
|
|
});
|
|
|
|
// Continuer avec le contenu précédent si une couche échoue
|
|
if (!globalOptions.stopOnError) {
|
|
continue;
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
pipelineStats.totalDuration = Date.now() - startTime;
|
|
pipelineStats.success = pipelineStats.layers.every(layer => layer.success);
|
|
|
|
logSh(`🔄 PIPELINE TERMINÉ: ${pipelineStats.totalModifications} modifs totales (${pipelineStats.totalDuration}ms)`, 'INFO');
|
|
|
|
await tracer.event('Pipeline couches terminé', pipelineStats);
|
|
|
|
return {
|
|
content: currentContent,
|
|
stats: pipelineStats,
|
|
original: content
|
|
};
|
|
|
|
} catch (error) {
|
|
pipelineStats.totalDuration = Date.now() - startTime;
|
|
pipelineStats.success = false;
|
|
|
|
logSh(`❌ PIPELINE COUCHES ÉCHOUÉ après ${pipelineStats.totalDuration}ms: ${error.message}`, 'ERROR');
|
|
throw error;
|
|
}
|
|
}, { layers: layers.map(l => l.name || l.type), content: Object.keys(content) });
|
|
}
|
|
|
|
/**
|
|
* COUCHES PRÉDÉFINIES - Configurations courantes
|
|
*/
|
|
const PREDEFINED_LAYERS = {
|
|
// Stack défensif léger
|
|
lightDefense: [
|
|
{ type: 'general', name: 'General Light', intensity: 0.6, method: 'enhancement' },
|
|
{ type: 'anti-gptZero', name: 'GPTZero Light', intensity: 0.5, method: 'enhancement' }
|
|
],
|
|
|
|
// Stack défensif standard
|
|
standardDefense: [
|
|
{ type: 'general', name: 'General Standard', intensity: 0.8, method: 'hybrid' },
|
|
{ type: 'anti-gptZero', name: 'GPTZero Standard', intensity: 0.9, method: 'enhancement' },
|
|
{ type: 'anti-originality', name: 'Originality Standard', intensity: 0.8, method: 'enhancement' }
|
|
],
|
|
|
|
// Stack défensif intensif
|
|
heavyDefense: [
|
|
{ type: 'general', name: 'General Heavy', intensity: 1.0, method: 'regeneration' },
|
|
{ type: 'anti-gptZero', name: 'GPTZero Heavy', intensity: 1.2, method: 'regeneration' },
|
|
{ type: 'anti-originality', name: 'Originality Heavy', intensity: 1.1, method: 'hybrid' },
|
|
{ type: 'anti-winston', name: 'Winston Heavy', intensity: 1.0, method: 'enhancement' }
|
|
],
|
|
|
|
// Stack ciblé GPTZero
|
|
gptZeroFocused: [
|
|
{ type: 'anti-gptZero', name: 'GPTZero Primary', intensity: 1.3, method: 'regeneration' },
|
|
{ type: 'general', name: 'General Support', intensity: 0.7, method: 'enhancement' }
|
|
],
|
|
|
|
// Stack ciblé Originality
|
|
originalityFocused: [
|
|
{ type: 'anti-originality', name: 'Originality Primary', intensity: 1.4, method: 'hybrid' },
|
|
{ type: 'general', name: 'General Support', intensity: 0.8, method: 'enhancement' }
|
|
]
|
|
};
|
|
|
|
/**
|
|
* APPLIQUER STACK PRÉDÉFINI
|
|
*/
|
|
async function applyPredefinedStack(content, stackName, options = {}) {
|
|
const stack = PREDEFINED_LAYERS[stackName];
|
|
|
|
if (!stack) {
|
|
throw new Error(`Stack prédéfini inconnu: ${stackName}. Disponibles: ${Object.keys(PREDEFINED_LAYERS).join(', ')}`);
|
|
}
|
|
|
|
logSh(`📦 APPLICATION STACK PRÉDÉFINI: ${stackName}`, 'INFO');
|
|
|
|
return await applyLayerPipeline(content, stack, options);
|
|
}
|
|
|
|
/**
|
|
* COUCHES ADAPTATIVES - S'adaptent selon le contenu
|
|
*/
|
|
async function applyAdaptiveLayers(content, options = {}) {
|
|
const {
|
|
targetDetectors = ['gptZero', 'originality'],
|
|
maxIntensity = 1.0,
|
|
analysisMode = true
|
|
} = options;
|
|
|
|
logSh(`🧠 COUCHES ADAPTATIVES: Analyse + adaptation auto`, 'INFO');
|
|
|
|
// 1. Analyser le contenu pour détecter les risques
|
|
const contentAnalysis = analyzeContentRisks(content);
|
|
|
|
// 2. Construire pipeline adaptatif selon l'analyse
|
|
const adaptiveLayers = [];
|
|
|
|
// Niveau de base selon risque global
|
|
const baseIntensity = Math.min(maxIntensity, contentAnalysis.globalRisk * 1.2);
|
|
|
|
if (baseIntensity > 0.3) {
|
|
adaptiveLayers.push({
|
|
type: 'general',
|
|
name: 'Adaptive Base',
|
|
intensity: baseIntensity,
|
|
method: baseIntensity > 0.7 ? 'hybrid' : 'enhancement'
|
|
});
|
|
}
|
|
|
|
// Couches spécifiques selon détecteurs ciblés
|
|
targetDetectors.forEach(detector => {
|
|
const detectorRisk = contentAnalysis.detectorRisks[detector] || 0;
|
|
|
|
if (detectorRisk > 0.4) {
|
|
const intensity = Math.min(maxIntensity * 1.1, detectorRisk * 1.5);
|
|
adaptiveLayers.push({
|
|
type: `anti-${detector}`,
|
|
name: `Adaptive ${detector}`,
|
|
intensity,
|
|
method: intensity > 0.8 ? 'regeneration' : 'enhancement'
|
|
});
|
|
}
|
|
});
|
|
|
|
logSh(` 🎯 ${adaptiveLayers.length} couches adaptatives générées`, 'DEBUG');
|
|
|
|
if (adaptiveLayers.length === 0) {
|
|
logSh(` ✅ Contenu déjà optimal, aucune couche nécessaire`, 'INFO');
|
|
return { content, stats: { adaptive: true, layersApplied: 0 }, original: content };
|
|
}
|
|
|
|
return await applyLayerPipeline(content, adaptiveLayers, options);
|
|
}
|
|
|
|
// ============= HELPER FUNCTIONS =============
|
|
|
|
/**
|
|
* Appliquer couche selon configuration
|
|
*/
|
|
async function applyLayerByConfig(content, layerConfig, globalOptions = {}) {
|
|
const { type, intensity, method, ...layerOptions } = layerConfig;
|
|
const options = { ...globalOptions, ...layerOptions, intensity, method };
|
|
|
|
switch (type) {
|
|
case 'general':
|
|
return await applyGeneralAdversarialLayer(content, options);
|
|
case 'anti-gptZero':
|
|
return await applyAntiGPTZeroLayer(content, options);
|
|
case 'anti-originality':
|
|
return await applyAntiOriginalityLayer(content, options);
|
|
case 'anti-winston':
|
|
return await applyAntiWinstonLayer(content, options);
|
|
case 'light':
|
|
return await applyLightAdversarialLayer(content, options);
|
|
case 'intensive':
|
|
return await applyIntensiveAdversarialLayer(content, options);
|
|
default:
|
|
throw new Error(`Type de couche inconnu: ${type}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Analyser risques du contenu pour adaptation
|
|
*/
|
|
function analyzeContentRisks(content) {
|
|
const analysis = {
|
|
globalRisk: 0,
|
|
detectorRisks: {},
|
|
riskFactors: []
|
|
};
|
|
|
|
const allContent = Object.values(content).join(' ');
|
|
|
|
// Risques génériques
|
|
let riskScore = 0;
|
|
|
|
// 1. Mots typiques IA
|
|
const aiWords = ['optimal', 'comprehensive', 'seamless', 'robust', 'leverage', 'cutting-edge', 'furthermore', 'moreover'];
|
|
const aiWordCount = aiWords.filter(word => allContent.toLowerCase().includes(word)).length;
|
|
|
|
if (aiWordCount > 2) {
|
|
riskScore += 0.3;
|
|
analysis.riskFactors.push(`mots_ia: ${aiWordCount}`);
|
|
}
|
|
|
|
// 2. Structure uniforme
|
|
const contentLengths = Object.values(content).map(c => c.length);
|
|
const avgLength = contentLengths.reduce((a, b) => a + b, 0) / contentLengths.length;
|
|
const variance = contentLengths.reduce((sum, len) => sum + Math.pow(len - avgLength, 2), 0) / contentLengths.length;
|
|
const uniformity = 1 - (Math.sqrt(variance) / Math.max(avgLength, 1));
|
|
|
|
if (uniformity > 0.8) {
|
|
riskScore += 0.2;
|
|
analysis.riskFactors.push(`uniformité: ${uniformity.toFixed(2)}`);
|
|
}
|
|
|
|
// 3. Connecteurs répétitifs
|
|
const repetitiveConnectors = ['par ailleurs', 'en effet', 'de plus', 'cependant'];
|
|
const connectorCount = repetitiveConnectors.filter(conn =>
|
|
(allContent.match(new RegExp(conn, 'gi')) || []).length > 1
|
|
).length;
|
|
|
|
if (connectorCount > 2) {
|
|
riskScore += 0.2;
|
|
analysis.riskFactors.push(`connecteurs_répétitifs: ${connectorCount}`);
|
|
}
|
|
|
|
analysis.globalRisk = Math.min(1, riskScore);
|
|
|
|
// Risques spécifiques par détecteur
|
|
analysis.detectorRisks = {
|
|
gptZero: analysis.globalRisk + (uniformity > 0.7 ? 0.3 : 0),
|
|
originality: analysis.globalRisk + (aiWordCount > 3 ? 0.4 : 0),
|
|
winston: analysis.globalRisk + (connectorCount > 2 ? 0.2 : 0)
|
|
};
|
|
|
|
return analysis;
|
|
}
|
|
|
|
/**
|
|
* Obtenir informations sur les stacks disponibles
|
|
*/
|
|
function getAvailableStacks() {
|
|
return Object.keys(PREDEFINED_LAYERS).map(stackName => ({
|
|
name: stackName,
|
|
layersCount: PREDEFINED_LAYERS[stackName].length,
|
|
description: getStackDescription(stackName),
|
|
layers: PREDEFINED_LAYERS[stackName]
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Description des stacks prédéfinis
|
|
*/
|
|
function getStackDescription(stackName) {
|
|
const descriptions = {
|
|
lightDefense: 'Protection légère préservant la qualité',
|
|
standardDefense: 'Protection équilibrée multi-détecteurs',
|
|
heavyDefense: 'Protection maximale tous détecteurs',
|
|
gptZeroFocused: 'Optimisation spécifique anti-GPTZero',
|
|
originalityFocused: 'Optimisation spécifique anti-Originality.ai'
|
|
};
|
|
|
|
return descriptions[stackName] || 'Stack personnalisé';
|
|
}
|
|
|
|
module.exports = {
|
|
// Couches individuelles
|
|
applyAntiGPTZeroLayer,
|
|
applyAntiOriginalityLayer,
|
|
applyAntiWinstonLayer,
|
|
applyGeneralAdversarialLayer,
|
|
applyLightAdversarialLayer,
|
|
applyIntensiveAdversarialLayer,
|
|
|
|
// Pipeline et stacks
|
|
applyLayerPipeline, // ← MAIN ENTRY POINT PIPELINE
|
|
applyPredefinedStack, // ← MAIN ENTRY POINT STACKS
|
|
applyAdaptiveLayers, // ← MAIN ENTRY POINT ADAPTATIF
|
|
|
|
// Utilitaires
|
|
getAvailableStacks,
|
|
analyzeContentRisks,
|
|
PREDEFINED_LAYERS
|
|
}; |