## SelectiveSmartTouch (NEW) - Architecture révolutionnaire: Analyse intelligente → Améliorations ciblées précises - 5 modules: SmartAnalysisLayer, SmartTechnicalLayer, SmartStyleLayer, SmartReadabilityLayer, SmartTouchCore - Système 10% segments: amélioration uniquement des segments les plus faibles (intensity-based) - Détection contexte globale pour prompts adaptatifs multi-secteurs - Intégration complète dans PipelineExecutor et PipelineDefinition ## Pipeline Validator Spec (NEW) - Spécification complète système validation qualité par LLM - 5 critères universels: Qualité, Verbosité, SEO, Répétitions, Naturalité - Échantillonnage intelligent par filtrage balises (pas XML) - Évaluation multi-versions avec justifications détaillées - Coût estimé: ~$1/validation (260 appels LLM) ## Optimizations - Réduction intensités fullEnhancement (technical 1.0→0.7, style 0.8→0.5) - Ajout gardes-fous anti-familiarité excessive dans StyleLayer - Sauvegarde étapes intermédiaires activée par défaut (pipeline-runner) ## Fixes - Fix typo critique SmartTouchCore.js:110 (determineLayers ToApply → determineLayersToApply) - Prompts généralisés multi-secteurs (e-commerce, SaaS, services, informatif) 🚀 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
380 lines
14 KiB
JavaScript
380 lines
14 KiB
JavaScript
// ========================================
|
|
// SMART TOUCH CORE - Orchestrateur SelectiveSmartTouch
|
|
// Responsabilité: Orchestration complète Analyse → Améliorations ciblées
|
|
// Architecture: Analyse intelligente PUIS améliorations précises (contrôle total)
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
const { tracer } = require('../trace');
|
|
const { SmartAnalysisLayer } = require('./SmartAnalysisLayer');
|
|
const { SmartTechnicalLayer } = require('./SmartTechnicalLayer');
|
|
const { SmartStyleLayer } = require('./SmartStyleLayer');
|
|
const { SmartReadabilityLayer } = require('./SmartReadabilityLayer');
|
|
|
|
/**
|
|
* SMART TOUCH CORE
|
|
* Orchestrateur principal: Analyse → Technical → Style → Readability (ciblé)
|
|
*/
|
|
class SmartTouchCore {
|
|
constructor() {
|
|
this.name = 'SelectiveSmartTouch';
|
|
|
|
// Instancier les layers
|
|
this.analysisLayer = new SmartAnalysisLayer();
|
|
this.technicalLayer = new SmartTechnicalLayer();
|
|
this.styleLayer = new SmartStyleLayer();
|
|
this.readabilityLayer = new SmartReadabilityLayer();
|
|
}
|
|
|
|
/**
|
|
* APPLIQUER SMART TOUCH COMPLET
|
|
* @param {object} content - Map {tag: texte}
|
|
* @param {object} config - Configuration
|
|
* @returns {object} - Résultat avec stats détaillées
|
|
*/
|
|
async apply(content, config = {}) {
|
|
return await tracer.run('SmartTouchCore.apply()', async () => {
|
|
const {
|
|
mode = 'full', // 'analysis_only', 'technical_only', 'style_only', 'readability_only', 'full'
|
|
intensity = 1.0,
|
|
csvData = null,
|
|
llmProvider = 'gpt-4o-mini', // ✅ LLM à utiliser (extrait du pipeline config)
|
|
skipAnalysis = false, // Si true, applique sans analyser (mode legacy)
|
|
layersOrder = ['technical', 'style', 'readability'] // Ordre d'application personnalisable
|
|
} = config;
|
|
|
|
await tracer.annotate({
|
|
selectiveSmartTouch: true,
|
|
mode,
|
|
intensity,
|
|
elementsCount: Object.keys(content).length,
|
|
personality: csvData?.personality?.nom
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
logSh(`🧠 SELECTIVE SMART TOUCH START: ${Object.keys(content).length} éléments | Mode: ${mode}`, 'INFO');
|
|
|
|
try {
|
|
let currentContent = { ...content };
|
|
const stats = {
|
|
mode,
|
|
analysisResults: {},
|
|
layersApplied: [],
|
|
totalModifications: 0,
|
|
elementsProcessed: Object.keys(content).length,
|
|
elementsImproved: 0,
|
|
duration: 0
|
|
};
|
|
|
|
// ========================================
|
|
// PHASE 1: ANALYSE INTELLIGENTE
|
|
// ========================================
|
|
if (!skipAnalysis) {
|
|
logSh(`\n📊 === PHASE 1: ANALYSE INTELLIGENTE ===`, 'INFO');
|
|
|
|
const analysisResults = await this.analysisLayer.analyzeBatch(currentContent, {
|
|
mc0: csvData?.mc0,
|
|
personality: csvData?.personality,
|
|
llmProvider // ✅ Passer LLM à l'analyse batch
|
|
});
|
|
|
|
stats.analysisResults = analysisResults;
|
|
|
|
// Résumer analyse
|
|
const summary = this.analysisLayer.summarizeBatchAnalysis(analysisResults);
|
|
logSh(` 📋 Résumé analyse: ${summary.needsImprovement}/${summary.totalElements} éléments nécessitent amélioration`, 'INFO');
|
|
logSh(` 📊 Score moyen: ${summary.averageScore.toFixed(2)} | Améliorations totales: ${summary.totalImprovements}`, 'INFO');
|
|
logSh(` 🎯 Besoins: Technical=${summary.commonIssues.technical} | Style=${summary.commonIssues.style} | Readability=${summary.commonIssues.readability}`, 'INFO');
|
|
|
|
// Si mode analysis_only, retourner ici
|
|
if (mode === 'analysis_only') {
|
|
const duration = Date.now() - startTime;
|
|
logSh(`✅ SELECTIVE SMART TOUCH (ANALYSIS ONLY) terminé: ${duration}ms`, 'INFO');
|
|
|
|
return {
|
|
content: currentContent,
|
|
stats: {
|
|
...stats,
|
|
duration,
|
|
analysisOnly: true
|
|
},
|
|
modifications: 0,
|
|
analysisResults
|
|
};
|
|
}
|
|
|
|
// ========================================
|
|
// PHASE 2: AMÉLIORATIONS CIBLÉES
|
|
// ========================================
|
|
logSh(`\n🔧 === PHASE 2: AMÉLIORATIONS CIBLÉES ===`, 'INFO');
|
|
|
|
// Déterminer quelles couches appliquer
|
|
const layersToApply = this.determineLayersToApply(mode, layersOrder);
|
|
|
|
// === DÉTECTION CONTEXTE GLOBALE (1 seule fois) ===
|
|
const contentContext = this.analysisLayer.detectContentContext(
|
|
Object.values(currentContent).join(' '),
|
|
csvData?.personality
|
|
);
|
|
|
|
for (const layerName of layersToApply) {
|
|
const layerStartTime = Date.now();
|
|
logSh(`\n 🎯 Couche: ${layerName}`, 'INFO');
|
|
|
|
let layerModifications = 0;
|
|
const layerResults = {};
|
|
|
|
// Appliquer la couche sur chaque élément
|
|
for (const [tag, text] of Object.entries(currentContent)) {
|
|
const analysis = analysisResults[tag];
|
|
if (!analysis) continue;
|
|
|
|
try {
|
|
// === SYSTÈME 10% SEGMENTS ===
|
|
// Calculer pourcentage de texte à améliorer selon intensity
|
|
// intensity 1.0 = 10%, 0.5 = 5%, 1.5 = 15%
|
|
const percentageToImprove = intensity * 0.1;
|
|
|
|
// Analyser par segments pour identifier les plus faibles
|
|
const segments = this.analysisLayer.analyzeBySegments(text, {
|
|
mc0: csvData?.mc0,
|
|
personality: csvData?.personality
|
|
});
|
|
|
|
// Sélectionner les X% segments les plus faibles
|
|
const weakestSegments = this.analysisLayer.selectWeakestSegments(
|
|
segments,
|
|
percentageToImprove
|
|
);
|
|
|
|
logSh(` 📊 [${tag}] ${segments.length} segments, ${weakestSegments.length} sélectionnés (${(percentageToImprove * 100).toFixed(0)}%)`, 'DEBUG');
|
|
|
|
// Appliquer amélioration UNIQUEMENT sur segments sélectionnés
|
|
const result = await this.applyLayerToSegments(
|
|
layerName,
|
|
segments,
|
|
weakestSegments,
|
|
analysis,
|
|
{
|
|
mc0: csvData?.mc0,
|
|
personality: csvData?.personality,
|
|
intensity,
|
|
contentContext, // Passer contexte aux layers
|
|
llmProvider // ✅ Passer LLM choisi dans pipeline
|
|
}
|
|
);
|
|
|
|
if (!result.skipped && result.content !== text) {
|
|
currentContent[tag] = result.content;
|
|
layerModifications += result.modifications || 0;
|
|
stats.elementsImproved++;
|
|
}
|
|
|
|
layerResults[tag] = result;
|
|
|
|
} catch (error) {
|
|
logSh(` ❌ [${tag}] Échec ${layerName}: ${error.message}`, 'ERROR');
|
|
}
|
|
}
|
|
|
|
const layerDuration = Date.now() - layerStartTime;
|
|
|
|
stats.layersApplied.push({
|
|
name: layerName,
|
|
modifications: layerModifications,
|
|
duration: layerDuration
|
|
});
|
|
|
|
stats.totalModifications += layerModifications;
|
|
|
|
logSh(` ✅ ${layerName} terminé: ${layerModifications} modifications (${layerDuration}ms)`, 'INFO');
|
|
}
|
|
|
|
} else {
|
|
// Mode skipAnalysis: appliquer sans analyse (legacy fallback)
|
|
logSh(`⚠️ Mode skipAnalysis activé: application directe sans analyse préalable`, 'WARNING');
|
|
|
|
// TODO: Implémenter mode legacy si nécessaire
|
|
logSh(`❌ Mode skipAnalysis non implémenté pour SmartTouch (requiert analyse)`, 'ERROR');
|
|
}
|
|
|
|
// ========================================
|
|
// RÉSULTATS FINAUX
|
|
// ========================================
|
|
const duration = Date.now() - startTime;
|
|
stats.duration = duration;
|
|
|
|
logSh(`\n✅ === SELECTIVE SMART TOUCH TERMINÉ ===`, 'INFO');
|
|
logSh(` 📊 ${stats.elementsImproved}/${stats.elementsProcessed} éléments améliorés`, 'INFO');
|
|
logSh(` 🔄 ${stats.totalModifications} modifications totales`, 'INFO');
|
|
logSh(` ⏱️ Durée: ${duration}ms`, 'INFO');
|
|
logSh(` 🎯 Couches appliquées: ${stats.layersApplied.map(l => l.name).join(' → ')}`, 'INFO');
|
|
|
|
await tracer.event('SelectiveSmartTouch terminé', stats);
|
|
|
|
return {
|
|
content: currentContent,
|
|
stats,
|
|
modifications: stats.totalModifications,
|
|
analysisResults: stats.analysisResults
|
|
};
|
|
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
logSh(`❌ SELECTIVE SMART TOUCH ÉCHOUÉ après ${duration}ms: ${error.message}`, 'ERROR');
|
|
|
|
return {
|
|
content, // Fallback: contenu original
|
|
stats: {
|
|
error: error.message,
|
|
duration,
|
|
fallback: true
|
|
},
|
|
modifications: 0,
|
|
fallback: true
|
|
};
|
|
}
|
|
}, { content: Object.keys(content), config });
|
|
}
|
|
|
|
/**
|
|
* DÉTERMINER COUCHES À APPLIQUER
|
|
*/
|
|
determineLayersToApply(mode, layersOrder) {
|
|
switch (mode) {
|
|
case 'technical_only':
|
|
return ['technical'];
|
|
case 'style_only':
|
|
return ['style'];
|
|
case 'readability_only':
|
|
return ['readability'];
|
|
case 'full':
|
|
default:
|
|
return layersOrder; // Ordre personnalisable
|
|
}
|
|
}
|
|
|
|
/**
|
|
* APPLIQUER UNE COUCHE SPÉCIFIQUE
|
|
*/
|
|
async applyLayer(layerName, content, analysis, context) {
|
|
switch (layerName) {
|
|
case 'technical':
|
|
return await this.technicalLayer.applyTargeted(content, analysis, context);
|
|
case 'style':
|
|
return await this.styleLayer.applyTargeted(content, analysis, context);
|
|
case 'readability':
|
|
return await this.readabilityLayer.applyTargeted(content, analysis, context);
|
|
default:
|
|
throw new Error(`Couche inconnue: ${layerName}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* APPLIQUER COUCHE SUR SEGMENTS SÉLECTIONNÉS UNIQUEMENT (10% système)
|
|
* @param {string} layerName - Nom de la couche
|
|
* @param {array} allSegments - Tous les segments du texte
|
|
* @param {array} weakestSegments - Segments sélectionnés à améliorer
|
|
* @param {object} analysis - Analyse globale
|
|
* @param {object} context - Contexte
|
|
* @returns {object} - { content: texte réassemblé, modifications, ... }
|
|
*/
|
|
async applyLayerToSegments(layerName, allSegments, weakestSegments, analysis, context) {
|
|
// Si aucun segment à améliorer, retourner texte original
|
|
if (weakestSegments.length === 0) {
|
|
const originalContent = allSegments.map(s => s.content).join(' ');
|
|
return {
|
|
content: originalContent,
|
|
modifications: 0,
|
|
skipped: true,
|
|
reason: 'No weak segments identified'
|
|
};
|
|
}
|
|
|
|
// Créer Map des indices des segments à améliorer pour lookup rapide
|
|
const weakIndices = new Set(weakestSegments.map(s => s.index));
|
|
|
|
// === AMÉLIORER UNIQUEMENT LES SEGMENTS FAIBLES ===
|
|
const improvedSegments = [];
|
|
let totalModifications = 0;
|
|
|
|
for (const segment of allSegments) {
|
|
if (weakIndices.has(segment.index)) {
|
|
// AMÉLIORER ce segment
|
|
try {
|
|
const result = await this.applyLayer(layerName, segment.content, analysis, context);
|
|
|
|
improvedSegments.push({
|
|
...segment,
|
|
content: result.skipped ? segment.content : result.content,
|
|
improved: !result.skipped
|
|
});
|
|
|
|
totalModifications += result.modifications || 0;
|
|
|
|
} catch (error) {
|
|
logSh(` ⚠️ Échec amélioration segment ${segment.index}: ${error.message}`, 'WARN');
|
|
// Fallback: garder segment original
|
|
improvedSegments.push({ ...segment, improved: false });
|
|
}
|
|
} else {
|
|
// GARDER segment intact
|
|
improvedSegments.push({ ...segment, improved: false });
|
|
}
|
|
}
|
|
|
|
// === RÉASSEMBLER TEXTE COMPLET ===
|
|
const reassembledContent = improvedSegments.map(s => s.content).join(' ');
|
|
|
|
// Nettoyer espaces multiples
|
|
const cleanedContent = reassembledContent.replace(/\s{2,}/g, ' ').trim();
|
|
|
|
const improvedCount = improvedSegments.filter(s => s.improved).length;
|
|
|
|
logSh(` ✅ ${improvedCount}/${allSegments.length} segments améliorés (${totalModifications} modifs)`, 'DEBUG');
|
|
|
|
return {
|
|
content: cleanedContent,
|
|
modifications: totalModifications,
|
|
segmentsImproved: improvedCount,
|
|
segmentsTotal: allSegments.length,
|
|
skipped: false
|
|
};
|
|
}
|
|
|
|
/**
|
|
* MODES DISPONIBLES
|
|
*/
|
|
static getAvailableModes() {
|
|
return [
|
|
{
|
|
name: 'analysis_only',
|
|
description: 'Analyse uniquement (sans amélioration)',
|
|
layers: []
|
|
},
|
|
{
|
|
name: 'technical_only',
|
|
description: 'Améliorations techniques ciblées uniquement',
|
|
layers: ['technical']
|
|
},
|
|
{
|
|
name: 'style_only',
|
|
description: 'Améliorations style ciblées uniquement',
|
|
layers: ['style']
|
|
},
|
|
{
|
|
name: 'readability_only',
|
|
description: 'Améliorations lisibilité ciblées uniquement',
|
|
layers: ['readability']
|
|
},
|
|
{
|
|
name: 'full',
|
|
description: 'Analyse + toutes améliorations ciblées (recommandé)',
|
|
layers: ['technical', 'style', 'readability']
|
|
}
|
|
];
|
|
}
|
|
}
|
|
|
|
module.exports = { SmartTouchCore };
|