Intelligent merge: Align Main.js/StepExecutor with API setup base

Combines two parallel developments:
1. Remote: API setup base (326a6a5) with new /api/generate-simple endpoint
2. Local: Main.js/StepExecutor alignment for production consistency

Key technical improvements:
- Main.js: Migrated from generateSimple() to StepExecutor.executeInitialGeneration()
- StepExecutor: Fixed to respect HTML options (selectiveStack, adversarialMode, etc.)
  * executeSelective() now uses applyPredefinedStack() with specific stack
  * executeAdversarial() uses proper mode mapping (light→lightDefense, etc.)
  * executeHumanSimulation() uses applyPredefinedSimulation() with mode
  * executePatternBreaking() uses applyPatternBreakingStack() with mode
- Updated process_real.js and DigitalOceanWorkflow.js to use handleModularWorkflow()
- Removed legacy ManualTrigger.js (Apps Script compatibility)

This ensures 100% consistency between step-by-step debugging interface and
automated Main.js workflow, while preserving the new API functionality.
Both development paths now use identical, tested and validated functions.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
StillHammer 2025-09-15 15:43:21 +08:00
parent 326a6a5284
commit b60bb48e9e
5 changed files with 47 additions and 143 deletions

View File

@ -10,7 +10,7 @@ const { JWT } = require('google-auth-library');
// Import des autres modules du projet (à adapter selon votre structure)
const { logSh } = require('./ErrorReporting');
const { handleFullWorkflow } = require('./Main');
const { handleModularWorkflow } = require('./Main');
const { getPersonalities, selectPersonalityWithAI } = require('./BrainConfig');
// ============= CONFIGURATION DIGITAL OCEAN =============
@ -104,7 +104,7 @@ async function runAutonomousWorkflowFromTrigger(rowNumber) {
source: 'digital_ocean_autonomous'
};
const result = await handleFullWorkflow(workflowData);
const result = await handleModularWorkflow(workflowData);
const duration = Date.now() - startTime;
logSh(`🏆 ORCHESTRATION TERMINÉE en ${Math.round(duration/1000)}s`, 'INFO');

View File

@ -11,8 +11,8 @@ const { tracer } = require('./trace');
const { readInstructionsData, selectPersonalityWithAI, getPersonalities } = require('./BrainConfig');
const { extractElements, buildSmartHierarchy } = require('./ElementExtraction');
const { generateMissingKeywords } = require('./MissingKeywords');
// ContentGeneration.js supprimé - Utiliser generateSimple depuis selective-enhancement
const { generateSimple } = require('./selective-enhancement/SelectiveUtils');
// Migration vers StepExecutor pour garantir la cohérence avec step-by-step
const { StepExecutor } = require('./StepExecutor');
const { injectGeneratedContent } = require('./ContentAssembly');
const { saveGeneratedArticleOrganic } = require('./ArticleStorage');
@ -107,7 +107,8 @@ async function handleModularWorkflowWithData(data, config = {}) {
// ========================================
// Continuer avec le workflow modulaire normal
const generationResult = await generateSimple(hierarchy, csvData);
const executor = new StepExecutor();
const generationResult = await executor.executeInitialGeneration(csvData, { hierarchy });
const generatedContent = generationResult.content || generationResult;
logSh(`${Object.keys(generatedContent).length} éléments générés`, 'DEBUG');
@ -315,7 +316,9 @@ async function handleModularWorkflow(config = {}) {
// ========================================
logSh(`💫 PHASE 5: Génération contenu de base`, 'INFO');
const generatedContent = await generateSimple(hierarchy, csvData);
const executor = new StepExecutor();
const generationResult = await executor.executeInitialGeneration(csvData, { hierarchy });
const generatedContent = generationResult.content;
logSh(`${Object.keys(generatedContent).length} éléments générés`, 'DEBUG');

View File

@ -1,120 +0,0 @@
const { logSh } = require('./ErrorReporting'); // Using unified logSh from ErrorReporting
/**
* 🚀 TRIGGER MANUEL - Lit ligne 2 et lance le workflow
* Exécute cette fonction depuis l'éditeur Apps Script
*/
function runWorkflowLigne(numeroLigne = 2) {
cleanLogSheet(); // Nettoie les logs pour ce test
try {
logSh('🎬 >>> DÉMARRAGE WORKFLOW MANUEL <<<', 'INFO');
// 1. LIRE AUTOMATIQUEMENT LA LIGNE INDIQUÉ
const csvData = readCSVDataFromRow(numeroLigne);
logSh(`✅ Données lues - MC0: ${csvData.mc0}`, 'INFO');
logSh(`✅ Titre: ${csvData.t0}`, 'INFO');
logSh(`✅ Personnalité: ${csvData.personality.nom}`, 'INFO');
// 2. XML TEMPLATE SIMPLE POUR TEST (ou lit depuis Digital Ocean si configuré)
const xmlTemplate = getXMLTemplateForTest(csvData);
logSh(`✅ XML template: ${xmlTemplate.length} caractères`, 'INFO');
// 3. 🎯 LANCER LE WORKFLOW PRINCIPAL
const workflowData = {
csvData: csvData,
xmlTemplate: Utilities.base64Encode(xmlTemplate),
source: 'manuel_ligne2'
};
const result = handleFullWorkflow(workflowData);
logSh('🏆 === WORKFLOW MANUEL TERMINÉ ===', 'INFO');
// ← EXTRAIRE LES VRAIES DONNÉES
let actualData;
if (result && result.getContentText) {
// C'est un ContentService, extraire le JSON
actualData = JSON.parse(result.getContentText());
} else {
actualData = result;
}
logSh(`Type result: ${typeof result}`, 'DEBUG');
logSh(`Result keys: ${Object.keys(result || {})}`, 'DEBUG');
logSh(`ActualData keys: ${Object.keys(actualData || {})}`, 'DEBUG');
logSh(`ActualData: ${JSON.stringify(actualData)}`, 'DEBUG');
if (actualData && actualData.stats) {
logSh(`📊 Éléments générés: ${actualData.stats.contentPieces}`, 'INFO');
logSh(`📝 Nombre de mots: ${actualData.stats.wordCount}`, 'INFO');
} else {
logSh('⚠️ Format résultat inattendu', 'WARNING');
logSh('ActualData: ' + JSON.stringify(actualData, null, 2), 'DEBUG'); // Using logSh instead of console.log
}
return actualData;
} catch (error) {
logSh(`❌ ERREUR WORKFLOW MANUEL: ${error.toString()}`, 'ERROR');
logSh(`Stack: ${error.stack}`, 'ERROR');
throw error;
}
}
/**
* HELPER - Lire CSV depuis une ligne spécifique
*/
function readCSVDataFromRow(rowNumber) {
const sheetId = '1iA2GvWeUxX-vpnAMfVm3ZMG9LhaC070SdGssEcXAh2c';
const spreadsheet = SpreadsheetApp.openById(sheetId);
const articlesSheet = spreadsheet.getSheetByName('instructions');
// Lire la ligne complète (colonnes A à H)
const range = articlesSheet.getRange(rowNumber, 1, 1, 9);
const [slug, t0, mc0, tMinus1, lMinus1, mcPlus1, tPlus1, lPlus1, xmlFileName] = range.getValues()[0];
logSh(`📖 Lecture ligne ${rowNumber}: ${slug}`, 'DEBUG');
// Récupérer personnalités et sélectionner automatiquement
const personalitiesSheet = spreadsheet.getSheetByName('Personnalites');
const personalities = getPersonalities(personalitiesSheet);
const selectedPersonality = selectPersonalityWithAI(mc0, t0, personalities);
return {
rowNumber: rowNumber,
slug: slug || 'test-slug',
t0: t0 || 'Titre par défaut',
mc0: mc0 || 'mot-clé test',
tMinus1: tMinus1 || 'parent',
lMinus1: lMinus1 || '/parent',
mcPlus1: mcPlus1 || 'mot1,mot2,mot3,mot4',
tPlus1: tPlus1 || 'Titre1,Titre2,Titre3,Titre4',
lPlus1: lPlus1 || '/lien1,/lien2,/lien3,/lien4',
personality: selectedPersonality,
xmlFileName: xmlFileName ? xmlFileName.toString().trim() : null
};
}
/**
* HELPER - XML Template simple pour test (ou depuis Digital Ocean)
*/
function getXMLTemplateForTest(csvData) {
logSh("csvData.xmlFileName: " + csvData.xmlFileName, 'DEBUG'); // Using logSh instead of console.log
if (csvData.xmlFileName) {
logSh("Tentative Digital Ocean...", 'INFO'); // Using logSh instead of console.log
try {
return fetchXMLFromDigitalOceanSimple(csvData.xmlFileName);
} catch (error) {
// ← ENLÈVE LE CATCH SILENCIEUX
logSh("Erreur DO: " + error.toString(), 'WARNING'); // Using logSh instead of console.log
logSh(`❌ ERREUR DO DÉTAILLÉE: ${error.toString()}`, 'ERROR');
// Continue sans Digital Ocean
}
}
logSh("❌ FATAL: Aucun template XML disponible", 'ERROR');
throw new Error("FATAL: Template XML indisponible (Digital Ocean inaccessible + pas de fallback) - arrêt du workflow");
}

View File

@ -149,7 +149,7 @@ class StepExecutor {
async executeSelective(inputData, options = {}) {
try {
// Import dynamique pour éviter les dépendances circulaires
const { applyAllSelectiveLayers } = require('./selective-enhancement/SelectiveCore');
const { applyPredefinedStack } = require('./selective-enhancement/SelectiveLayers');
logSh('🎯 Démarrage Selective Enhancement seulement', 'DEBUG');
@ -178,11 +178,10 @@ class StepExecutor {
const beforeContent = JSON.parse(JSON.stringify(contentToEnhance)); // Deep copy
// ÉTAPE ENHANCEMENT - Améliorer le contenu fourni
logSh('🎯 Enhancement sélectif du contenu fourni', 'DEBUG');
const result = await applyAllSelectiveLayers(contentToEnhance, {
...inputData,
...config,
// ÉTAPE ENHANCEMENT - Améliorer le contenu fourni avec la stack spécifiée
logSh(`🎯 Enhancement sélectif du contenu avec stack: ${config.selectiveStack}`, 'DEBUG');
const result = await applyPredefinedStack(contentToEnhance, config.selectiveStack, {
csvData: inputData,
analysisMode: false
});
@ -216,7 +215,7 @@ class StepExecutor {
*/
async executeAdversarial(inputData, options = {}) {
try {
const { applyAdversarialLayer } = require('./adversarial-generation/AdversarialCore');
const { applyPredefinedStack: applyAdversarialStack } = require('./adversarial-generation/AdversarialLayers');
logSh('🎯 Démarrage Adversarial Generation', 'DEBUG');
@ -244,12 +243,22 @@ class StepExecutor {
const beforeContent = JSON.parse(JSON.stringify(contentToTransform)); // Deep copy
const result = await applyAdversarialLayer(contentToTransform, {
...config,
// Mapping des modes vers les stacks prédéfinies
const modeToStack = {
'light': 'lightDefense',
'standard': 'standardDefense',
'heavy': 'heavyDefense',
'none': 'none',
'adaptive': 'adaptive'
};
const stackName = modeToStack[config.adversarialMode] || 'standardDefense';
logSh(`🎯 Adversarial avec stack: ${stackName} (mode: ${config.adversarialMode})`, 'DEBUG');
const result = await applyAdversarialStack(contentToTransform, stackName, {
csvData: inputData,
detectorTarget: config.detectorTarget || 'general',
intensity: config.intensity || 1.0,
method: config.method || 'regeneration'
intensity: config.intensity || 1.0
});
return {
@ -280,7 +289,7 @@ class StepExecutor {
*/
async executeHumanSimulation(inputData, options = {}) {
try {
const { applyHumanSimulationLayer } = require('./human-simulation/HumanSimulationCore');
const { applyPredefinedSimulation } = require('./human-simulation/HumanSimulationLayers');
logSh('🎯 Démarrage Human Simulation', 'DEBUG');
@ -308,7 +317,13 @@ class StepExecutor {
const beforeContent = JSON.parse(JSON.stringify(contentToHumanize)); // Deep copy
const result = await applyHumanSimulationLayer(contentToHumanize, inputData, config);
const simulationMode = config.humanSimulationMode || 'standardSimulation';
logSh(`🎯 Human Simulation avec mode: ${simulationMode}`, 'DEBUG');
const result = await applyPredefinedSimulation(contentToHumanize, simulationMode, {
csvData: inputData,
...config
});
return {
content: result.content || result,
@ -338,7 +353,7 @@ class StepExecutor {
*/
async executePatternBreaking(inputData, options = {}) {
try {
const { applyPatternBreakingLayer } = require('./pattern-breaking/PatternBreakingCore');
const { applyPatternBreakingStack } = require('./pattern-breaking/PatternBreakingLayers');
logSh('🎯 Démarrage Pattern Breaking', 'DEBUG');
@ -366,7 +381,13 @@ class StepExecutor {
const beforeContent = JSON.parse(JSON.stringify(contentToTransform)); // Deep copy
const result = await applyPatternBreakingLayer(contentToTransform, inputData, config);
const patternMode = config.patternBreakingMode || 'standardPatternBreaking';
logSh(`🎯 Pattern Breaking avec mode: ${patternMode}`, 'DEBUG');
const result = await applyPatternBreakingStack(contentToTransform, patternMode, {
csvData: inputData,
...config
});
return {
content: result.content || result,

View File

@ -4,7 +4,7 @@
// ========================================
const { readCSVDataWithXMLFileName, fetchXMLFromDigitalOceanSimple } = require('./lib/DigitalOceanWorkflow');
const { handleFullWorkflow } = require('./lib/Main');
const { handleModularWorkflow } = require('./lib/Main');
/**
* Fonction principale qui fait VRAIMENT tout le processus
@ -53,7 +53,7 @@ async function processRealData(rowNumber) {
console.log('4⃣ Lancement workflow (6 LLMs)...');
const startTime = Date.now();
const result = await handleFullWorkflow(workflowData);
const result = await handleModularWorkflow(workflowData);
const duration = Date.now() - startTime;