seogeneratorserver/lib/ContentGeneration.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

315 lines
12 KiB
JavaScript

// ========================================
// ORCHESTRATEUR GÉNÉRATION - ARCHITECTURE REFACTORISÉE
// Responsabilité: Coordonner les 4 étapes de génération
// ========================================
const { logSh } = require('./ErrorReporting');
const { tracer } = require('./trace');
// Import des 4 étapes séparées
const { generateInitialContent } = require('./generation/InitialGeneration');
const { enhanceTechnicalTerms } = require('./generation/TechnicalEnhancement');
const { enhanceTransitions } = require('./generation/TransitionEnhancement');
const { applyPersonalityStyle } = require('./generation/StyleEnhancement');
// Import Pattern Breaking (Niveau 2)
const { applyPatternBreaking } = require('./post-processing/PatternBreaking');
/**
* MAIN ENTRY POINT - GÉNÉRATION AVEC SELECTIVE ENHANCEMENT
* @param {Object} hierarchy - Hiérarchie des éléments extraits
* @param {Object} csvData - Données CSV avec personnalité
* @param {Object} options - Options de génération
* @returns {Object} - Contenu généré final
*/
async function generateWithContext(hierarchy, csvData, options = {}) {
return await tracer.run('ContentGeneration.generateWithContext()', async () => {
const startTime = Date.now();
const pipelineName = options.patternBreaking ? 'selective_enhancement_with_pattern_breaking' : 'selective_enhancement';
const totalSteps = options.patternBreaking ? 5 : 4;
await tracer.annotate({
pipeline: pipelineName,
elementsCount: Object.keys(hierarchy).length,
personality: csvData.personality?.nom,
mc0: csvData.mc0,
options,
totalSteps
});
logSh(`🚀 DÉBUT PIPELINE ${options.patternBreaking ? 'NIVEAU 2' : 'NIVEAU 1'}`, 'INFO');
logSh(` 🎭 Personnalité: ${csvData.personality?.nom} (${csvData.personality?.style})`, 'INFO');
logSh(` 📊 ${Object.keys(hierarchy).length} éléments à traiter`, 'INFO');
logSh(` 🔧 Options: ${JSON.stringify(options)}`, 'DEBUG');
try {
let pipelineResults = {
content: {},
stats: { stages: [], totalDuration: 0 },
debug: { pipeline: 'selective_enhancement', stages: [] }
};
// ÉTAPE 1: GÉNÉRATION INITIALE (Claude)
const step1Result = await generateInitialContent({
hierarchy,
csvData,
context: { step: 1, totalSteps, options }
});
pipelineResults.content = step1Result.content;
pipelineResults.stats.stages.push({ stage: 1, name: 'InitialGeneration', ...step1Result.stats });
pipelineResults.debug.stages.push(step1Result.debug);
// ÉTAPE 2: ENHANCEMENT TECHNIQUE (GPT-4) - Optionnel
if (!options.skipTechnical) {
const step2Result = await enhanceTechnicalTerms({
content: pipelineResults.content,
csvData,
context: { step: 2, totalSteps, options }
});
pipelineResults.content = step2Result.content;
pipelineResults.stats.stages.push({ stage: 2, name: 'TechnicalEnhancement', ...step2Result.stats });
pipelineResults.debug.stages.push(step2Result.debug);
} else {
logSh(`⏭️ ÉTAPE 2/4 IGNORÉE: Enhancement technique désactivé`, 'INFO');
}
// ÉTAPE 3: ENHANCEMENT TRANSITIONS (Gemini) - Optionnel
if (!options.skipTransitions) {
const step3Result = await enhanceTransitions({
content: pipelineResults.content,
csvData,
context: { step: 3, totalSteps, options }
});
pipelineResults.content = step3Result.content;
pipelineResults.stats.stages.push({ stage: 3, name: 'TransitionEnhancement', ...step3Result.stats });
pipelineResults.debug.stages.push(step3Result.debug);
} else {
logSh(`⏭️ ÉTAPE 3/4 IGNORÉE: Enhancement transitions désactivé`, 'INFO');
}
// ÉTAPE 4: ENHANCEMENT STYLE (Mistral) - Optionnel
if (!options.skipStyle) {
const step4Result = await applyPersonalityStyle({
content: pipelineResults.content,
csvData,
context: { step: 4, totalSteps, options }
});
pipelineResults.content = step4Result.content;
pipelineResults.stats.stages.push({ stage: 4, name: 'StyleEnhancement', ...step4Result.stats });
pipelineResults.debug.stages.push(step4Result.debug);
} else {
logSh(`⏭️ ÉTAPE 4/${totalSteps} IGNORÉE: Enhancement style désactivé`, 'INFO');
}
// ÉTAPE 5: PATTERN BREAKING (NIVEAU 2) - Optionnel
if (options.patternBreaking) {
const step5Result = await applyPatternBreaking({
content: pipelineResults.content,
csvData,
options: options.patternBreakingConfig || {}
});
pipelineResults.content = step5Result.content;
pipelineResults.stats.stages.push({ stage: 5, name: 'PatternBreaking', ...step5Result.stats });
pipelineResults.debug.stages.push(step5Result.debug);
} else if (totalSteps === 5) {
logSh(`⏭️ ÉTAPE 5/5 IGNORÉE: Pattern Breaking désactivé`, 'INFO');
}
// RÉSULTATS FINAUX
const totalDuration = Date.now() - startTime;
pipelineResults.stats.totalDuration = totalDuration;
const totalProcessed = pipelineResults.stats.stages.reduce((sum, stage) => sum + (stage.processed || 0), 0);
const totalEnhanced = pipelineResults.stats.stages.reduce((sum, stage) => sum + (stage.enhanced || 0), 0);
logSh(`✅ PIPELINE TERMINÉ: ${Object.keys(pipelineResults.content).length} éléments générés`, 'INFO');
logSh(` ⏱️ Durée totale: ${totalDuration}ms`, 'INFO');
logSh(` 📈 Enhancements: ${totalEnhanced} sur ${totalProcessed} éléments traités`, 'INFO');
// Log détaillé par étape
pipelineResults.stats.stages.forEach(stage => {
const enhancementRate = stage.processed > 0 ? Math.round((stage.enhanced / stage.processed) * 100) : 0;
logSh(` ${stage.stage}. ${stage.name}: ${stage.enhanced}/${stage.processed} (${enhancementRate}%) en ${stage.duration}ms`, 'DEBUG');
});
await tracer.event(`Pipeline ${pipelineName} terminé`, {
totalElements: Object.keys(pipelineResults.content).length,
totalEnhanced,
totalDuration,
stagesExecuted: pipelineResults.stats.stages.length
});
// Retourner uniquement le contenu pour compatibilité
return pipelineResults.content;
} catch (error) {
const totalDuration = Date.now() - startTime;
logSh(`❌ PIPELINE ÉCHOUÉ après ${totalDuration}ms: ${error.message}`, 'ERROR');
logSh(`❌ Stack trace: ${error.stack}`, 'DEBUG');
await tracer.event(`Pipeline ${pipelineName} échoué`, {
error: error.message,
duration: totalDuration
});
throw new Error(`ContentGeneration pipeline failed: ${error.message}`);
}
}, { hierarchy, csvData, options });
}
/**
* GÉNÉRATION SIMPLE (ÉTAPE 1 UNIQUEMENT)
* Pour tests ou fallback rapide
*/
async function generateSimple(hierarchy, csvData) {
logSh(`🔥 GÉNÉRATION SIMPLE: Claude uniquement`, 'INFO');
const result = await generateInitialContent({
hierarchy,
csvData,
context: { step: 1, totalSteps: 1, simple: true }
});
return result.content;
}
/**
* GÉNÉRATION AVANCÉE AVEC CONTRÔLE GRANULAIRE
* Permet de choisir exactement quelles étapes exécuter
*/
async function generateAdvanced(hierarchy, csvData, stageConfig = {}) {
const {
initial = true,
technical = true,
transitions = true,
style = true,
patternBreaking = false, // ✨ NOUVEAU: Niveau 2
patternBreakingConfig = {} // ✨ NOUVEAU: Config Pattern Breaking
} = stageConfig;
const options = {
skipTechnical: !technical,
skipTransitions: !transitions,
skipStyle: !style,
patternBreaking, // ✨ NOUVEAU
patternBreakingConfig // ✨ NOUVEAU
};
const activeStages = [
initial && 'Initial',
technical && 'Technical',
transitions && 'Transitions',
style && 'Style',
patternBreaking && 'PatternBreaking' // ✨ NOUVEAU
].filter(Boolean);
logSh(`🎛️ GÉNÉRATION AVANCÉE: ${activeStages.join(' + ')}`, 'INFO');
return await generateWithContext(hierarchy, csvData, options);
}
/**
* GÉNÉRATION NIVEAU 2 (AVEC PATTERN BREAKING)
* Shortcut pour activer Pattern Breaking facilement
*/
async function generateWithPatternBreaking(hierarchy, csvData, patternConfig = {}) {
logSh(`🎯 GÉNÉRATION NIVEAU 2: Pattern Breaking activé`, 'INFO');
const options = {
patternBreaking: true,
patternBreakingConfig: {
intensity: 0.6,
sentenceVariation: true,
fingerprintRemoval: true,
transitionHumanization: true,
...patternConfig
}
};
return await generateWithContext(hierarchy, csvData, options);
}
/**
* DIAGNOSTIC PIPELINE
* Exécute chaque étape avec mesures détaillées
*/
async function diagnosticPipeline(hierarchy, csvData) {
logSh(`🔬 MODE DIAGNOSTIC: Analyse détaillée pipeline`, 'INFO');
const diagnostics = {
stages: [],
errors: [],
performance: {},
content: {}
};
let currentContent = {};
try {
// Test étape 1
const step1Start = Date.now();
const step1Result = await generateInitialContent({ hierarchy, csvData });
diagnostics.stages.push({
stage: 1,
name: 'InitialGeneration',
success: true,
duration: Date.now() - step1Start,
elementsGenerated: Object.keys(step1Result.content).length,
stats: step1Result.stats
});
currentContent = step1Result.content;
} catch (error) {
diagnostics.errors.push({ stage: 1, error: error.message });
diagnostics.stages.push({ stage: 1, name: 'InitialGeneration', success: false });
return diagnostics;
}
// Test étapes 2-4 individuellement
const stages = [
{ stage: 2, name: 'TechnicalEnhancement', func: enhanceTechnicalTerms },
{ stage: 3, name: 'TransitionEnhancement', func: enhanceTransitions },
{ stage: 4, name: 'StyleEnhancement', func: applyPersonalityStyle }
];
for (const stageInfo of stages) {
try {
const stageStart = Date.now();
const stageResult = await stageInfo.func({ content: currentContent, csvData });
diagnostics.stages.push({
...stageInfo,
success: true,
duration: Date.now() - stageStart,
stats: stageResult.stats
});
currentContent = stageResult.content;
} catch (error) {
diagnostics.errors.push({ stage: stageInfo.stage, error: error.message });
diagnostics.stages.push({ ...stageInfo, success: false });
}
}
diagnostics.content = currentContent;
diagnostics.performance.totalDuration = diagnostics.stages.reduce((sum, stage) => sum + (stage.duration || 0), 0);
logSh(`🔬 DIAGNOSTIC TERMINÉ: ${diagnostics.stages.filter(s => s.success).length}/4 étapes réussies`, 'INFO');
return diagnostics;
}
module.exports = {
generateWithContext, // ← MAIN ENTRY POINT (compatible ancien code)
generateSimple, // ← Génération rapide
generateAdvanced, // ← Contrôle granulaire
generateWithPatternBreaking, // ← NOUVEAU: Niveau 2 shortcut
diagnosticPipeline // ← Tests et debug
};