seogeneratorserver/lib/pattern-breaking/PatternBreakingCore.js
StillHammer dbf1a3de8c Add technical plan for multi-format export system
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>
2025-11-18 16:14:29 +08:00

280 lines
11 KiB
JavaScript

// ========================================
// FICHIER: PatternBreakingCore.js
// RESPONSABILITÉ: Orchestrateur principal Pattern Breaking
// Niveau 2: Casser les patterns syntaxiques typiques des LLMs
// ========================================
const { logSh } = require('../ErrorReporting');
const { tracer } = require('../trace');
const { varyStructures, splitLongSentences, mergeShorter } = require('./SyntaxVariations');
const { replaceLLMFingerprints, detectLLMPatterns } = require('./LLMFingerprints');
const { humanizeTransitions, replaceConnectors } = require('./NaturalConnectors');
/**
* CONFIGURATION PAR DÉFAUT PATTERN BREAKING
*/
const DEFAULT_CONFIG = {
syntaxVariationEnabled: true,
llmFingerprintReplacement: true,
naturalConnectorsEnabled: true,
intensityLevel: 0.5, // Intensité globale (0-1)
preserveReadability: true, // Maintenir lisibilité
maxModificationsPerElement: 4, // Limite modifications par élément
qualityThreshold: 0.6 // Seuil qualité minimum
};
/**
* ORCHESTRATEUR PRINCIPAL - Pattern Breaking Layer
* @param {object} content - Contenu généré à traiter
* @param {object} options - Options de pattern breaking
* @returns {object} - { content, stats, fallback }
*/
async function applyPatternBreakingLayer(content, options = {}) {
return await tracer.run('PatternBreakingCore.applyPatternBreakingLayer()', async () => {
const startTime = Date.now();
await tracer.annotate({
contentKeys: Object.keys(content).length,
intensityLevel: options.intensityLevel,
personality: options.csvData?.personality?.nom
});
logSh(`🔧 PATTERN BREAKING - Début traitement`, 'INFO');
logSh(` 📊 ${Object.keys(content).length} éléments | Intensité: ${options.intensityLevel || DEFAULT_CONFIG.intensityLevel}`, 'DEBUG');
try {
// Configuration fusionnée
const config = { ...DEFAULT_CONFIG, ...options };
// Stats de pattern breaking
const patternStats = {
elementsProcessed: 0,
syntaxModifications: 0,
llmFingerprintReplacements: 0,
connectorReplacements: 0,
totalModifications: 0,
fallbackUsed: false,
patternsDetected: 0
};
// Contenu traité
let processedContent = { ...content };
// ========================================
// TRAITEMENT PAR ÉLÉMENT
// ========================================
for (const [elementKey, elementContent] of Object.entries(content)) {
await tracer.run(`PatternBreaking.processElement(${elementKey})`, async () => {
logSh(` 🎯 Traitement élément: ${elementKey}`, 'DEBUG');
let currentContent = elementContent;
let elementModifications = 0;
try {
// 1. Détection patterns LLM
const detectedPatterns = detectLLMPatterns(currentContent);
patternStats.patternsDetected += detectedPatterns.count;
if (detectedPatterns.count > 0) {
logSh(` 🔍 ${detectedPatterns.count} patterns LLM détectés: ${detectedPatterns.patterns.slice(0, 3).join(', ')}`, 'DEBUG');
}
// 2. Variation syntaxique
if (config.syntaxVariationEnabled) {
const syntaxResult = await applySyntaxVariation(currentContent, config);
currentContent = syntaxResult.content;
elementModifications += syntaxResult.modifications;
patternStats.syntaxModifications += syntaxResult.modifications;
logSh(` 📝 Syntaxe: ${syntaxResult.modifications} variations appliquées`, 'DEBUG');
}
// 3. Remplacement fingerprints LLM
if (config.llmFingerprintReplacement && detectedPatterns.count > 0) {
const fingerprintResult = await applyLLMFingerprints(currentContent, config);
currentContent = fingerprintResult.content;
elementModifications += fingerprintResult.modifications;
patternStats.llmFingerprintReplacements += fingerprintResult.modifications;
logSh(` 🤖 LLM Fingerprints: ${fingerprintResult.modifications} remplacements`, 'DEBUG');
}
// 4. Connecteurs naturels
if (config.naturalConnectorsEnabled) {
const connectorResult = await applyNaturalConnectors(currentContent, config);
currentContent = connectorResult.content;
elementModifications += connectorResult.modifications;
patternStats.connectorReplacements += connectorResult.modifications;
logSh(` 🔗 Connecteurs: ${connectorResult.modifications} humanisés`, 'DEBUG');
}
// 5. Validation qualité
const qualityCheck = validatePatternBreakingQuality(elementContent, currentContent, config.qualityThreshold);
if (qualityCheck.acceptable) {
processedContent[elementKey] = currentContent;
patternStats.elementsProcessed++;
patternStats.totalModifications += elementModifications;
logSh(` ✅ Élément traité: ${elementModifications} modifications totales`, 'DEBUG');
} else {
// Fallback: garder contenu original
processedContent[elementKey] = elementContent;
patternStats.fallbackUsed = true;
logSh(` ⚠️ Qualité insuffisante, fallback vers contenu original`, 'WARNING');
}
} catch (elementError) {
logSh(` ❌ Erreur pattern breaking élément ${elementKey}: ${elementError.message}`, 'WARNING');
processedContent[elementKey] = elementContent; // Fallback
patternStats.fallbackUsed = true;
}
}, { elementKey, originalLength: elementContent?.length });
}
// ========================================
// RÉSULTATS FINAUX
// ========================================
const duration = Date.now() - startTime;
const success = patternStats.elementsProcessed > 0 && !patternStats.fallbackUsed;
logSh(`🔧 PATTERN BREAKING - Terminé (${duration}ms)`, 'INFO');
logSh(`${patternStats.elementsProcessed}/${Object.keys(content).length} éléments traités`, 'INFO');
logSh(` 📊 ${patternStats.syntaxModifications} syntaxe | ${patternStats.llmFingerprintReplacements} fingerprints | ${patternStats.connectorReplacements} connecteurs`, 'INFO');
logSh(` 🎯 Patterns détectés: ${patternStats.patternsDetected} | Fallback: ${patternStats.fallbackUsed ? 'OUI' : 'NON'}`, 'INFO');
await tracer.event('Pattern Breaking terminé', {
success,
duration,
stats: patternStats
});
return {
content: processedContent,
stats: patternStats,
fallback: patternStats.fallbackUsed,
duration
};
} catch (error) {
const duration = Date.now() - startTime;
logSh(`❌ PATTERN BREAKING ÉCHOUÉ (${duration}ms): ${error.message}`, 'ERROR');
await tracer.event('Pattern Breaking échoué', {
error: error.message,
duration,
contentKeys: Object.keys(content).length
});
// Fallback complet
return {
content,
stats: { fallbackUsed: true, error: error.message },
fallback: true,
duration
};
}
}, {
contentElements: Object.keys(content).length,
intensityLevel: options.intensityLevel
});
}
/**
* APPLICATION VARIATION SYNTAXIQUE
*/
async function applySyntaxVariation(content, config) {
const syntaxResult = varyStructures(content, config.intensityLevel, {
preserveReadability: config.preserveReadability,
maxModifications: Math.floor(config.maxModificationsPerElement / 2)
});
return {
content: syntaxResult.content,
modifications: syntaxResult.modifications || 0
};
}
/**
* APPLICATION REMPLACEMENT LLM FINGERPRINTS
*/
async function applyLLMFingerprints(content, config) {
const fingerprintResult = replaceLLMFingerprints(content, {
intensity: config.intensityLevel,
preserveContext: true,
maxReplacements: Math.floor(config.maxModificationsPerElement / 2)
});
return {
content: fingerprintResult.content,
modifications: fingerprintResult.replacements || 0
};
}
/**
* APPLICATION CONNECTEURS NATURELS
*/
async function applyNaturalConnectors(content, config) {
const connectorResult = humanizeTransitions(content, {
intensity: config.intensityLevel,
preserveMeaning: true,
maxReplacements: Math.floor(config.maxModificationsPerElement / 2)
});
return {
content: connectorResult.content,
modifications: connectorResult.replacements || 0
};
}
/**
* VALIDATION QUALITÉ PATTERN BREAKING
*/
function validatePatternBreakingQuality(originalContent, processedContent, qualityThreshold) {
if (!originalContent || !processedContent) {
return { acceptable: false, reason: 'Contenu manquant' };
}
// Métriques de base
const lengthDiff = Math.abs(processedContent.length - originalContent.length) / originalContent.length;
const wordCountOriginal = originalContent.split(/\s+/).length;
const wordCountProcessed = processedContent.split(/\s+/).length;
const wordCountDiff = Math.abs(wordCountProcessed - wordCountOriginal) / wordCountOriginal;
// Vérifications qualité
const checks = {
lengthPreserved: lengthDiff < 0.3, // Pas plus de 30% de différence
wordCountPreserved: wordCountDiff < 0.2, // Pas plus de 20% de différence
noEmpty: processedContent.trim().length > 0, // Pas de contenu vide
readableStructure: processedContent.includes('.') // Structure lisible
};
const passedChecks = Object.values(checks).filter(Boolean).length;
const score = passedChecks / Object.keys(checks).length;
const acceptable = score >= qualityThreshold;
logSh(` 🎯 Validation Pattern Breaking: ${acceptable ? 'ACCEPTÉ' : 'REJETÉ'} (score: ${score.toFixed(2)})`, acceptable ? 'DEBUG' : 'WARNING');
return {
acceptable,
score,
checks,
reason: acceptable ? 'Qualité acceptable' : 'Score qualité insuffisant'
};
}
// ============= EXPORTS =============
module.exports = {
applyPatternBreakingLayer,
applySyntaxVariation,
applyLLMFingerprints,
applyNaturalConnectors,
validatePatternBreakingQuality,
DEFAULT_CONFIG
};