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>
280 lines
11 KiB
JavaScript
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
|
|
}; |