// ======================================== // SMART READABILITY LAYER - Améliorations lisibilité CIBLÉES // Responsabilité: Appliquer UNIQUEMENT les améliorations lisibilité identifiées par analyse // LLM: GPT-4o-mini (clarté et structure) // Architecture: Phase 2 de SelectiveSmartTouch (post-analyse) // ======================================== const { callLLM } = require('../LLMManager'); const { logSh } = require('../ErrorReporting'); const { tracer } = require('../trace'); /** * SMART READABILITY LAYER * Applique améliorations lisibilité précises identifiées par SmartAnalysisLayer */ class SmartReadabilityLayer { constructor() { this.name = 'SmartReadability'; this.defaultLLM = 'gpt-4o-mini'; } /** * APPLIQUER AMÉLIORATIONS LISIBILITÉ CIBLÉES */ async applyTargeted(content, analysis, context = {}) { return await tracer.run('SmartReadability.applyTargeted()', async () => { const { mc0, personality, intensity = 1.0 } = context; // Si aucune amélioration lisibilité nécessaire, skip if (!analysis.readability.needed) { logSh(`⏭️ SMART READABILITY: Aucune amélioration nécessaire (score: ${analysis.readability.score.toFixed(2)})`, 'DEBUG'); return { content, modifications: 0, skipped: true, reason: 'No readability improvements needed' }; } await tracer.annotate({ smartReadability: true, contentLength: content.length, intensity }); // ✅ Utiliser LLM fourni dans context, sinon fallback sur defaultLLM const llmToUse = context.llmProvider || this.defaultLLM; const startTime = Date.now(); logSh(`📖 SMART READABILITY: Application améliorations lisibilité ciblées avec ${llmToUse}`, 'DEBUG'); try { const prompt = this.createTargetedPrompt(content, analysis, context); const response = await callLLM(llmToUse, prompt, { temperature: 0.4, // Précision pour structure maxTokens: 2500 }, personality); const improvedContent = this.cleanResponse(response); const modifications = this.countModifications(content, improvedContent); const duration = Date.now() - startTime; logSh(`✅ SMART READABILITY terminé: ${modifications} modifications appliquées (${duration}ms)`, 'DEBUG'); await tracer.event('Smart Readability appliqué', { duration, modifications }); return { content: improvedContent, modifications, duration }; } catch (error) { const duration = Date.now() - startTime; logSh(`❌ SMART READABILITY ÉCHOUÉ (${duration}ms): ${error.message}`, 'ERROR'); return { content, modifications: 0, error: error.message, fallback: true }; } }, { contentLength: content.length, analysis }); } /** * CRÉER PROMPT CIBLÉ */ createTargetedPrompt(content, analysis, context) { const { mc0, personality, intensity = 1.0 } = context; // Extraire améliorations lisibilité const readabilityImprovements = analysis.improvements.filter(imp => imp.toLowerCase().includes('lisib') || imp.toLowerCase().includes('phrase') || imp.toLowerCase().includes('connecteur') || imp.toLowerCase().includes('structure') || imp.toLowerCase().includes('fluidité') ); return `MISSION: Améliore UNIQUEMENT la lisibilité selon les points listés. CONTENU ORIGINAL: "${content}" ${mc0 ? `CONTEXTE: ${mc0}` : ''} INTENSITÉ: ${intensity.toFixed(1)} AMÉLIORATIONS LISIBILITÉ À APPLIQUER: ${readabilityImprovements.map((imp, i) => `${i + 1}. ${imp}`).join('\n')} ${analysis.readability.complexSentences && analysis.readability.complexSentences.length > 0 ? ` PHRASES TROP COMPLEXES (à simplifier): ${analysis.readability.complexSentences.map(line => `- Ligne ${line}`).join('\n')} ` : ''} ${analysis.readability.repetitiveConnectors && analysis.readability.repetitiveConnectors.length > 0 ? ` CONNECTEURS RÉPÉTITIFS (à varier): ${analysis.readability.repetitiveConnectors.map(conn => `- "${conn}"`).join('\n')} ` : ''} CONSIGNES STRICTES: - Applique UNIQUEMENT les améliorations lisibilité listées - NE CHANGE PAS le sens, ton ou style général - GARDE le même contenu informatif - Phrases: 15-25 mots idéalement (simplifier si > 30 mots) - Connecteurs: variés et naturels - Structure: fluide et logique TECHNIQUES LISIBILITÉ: **Simplifier phrases longues:** - ✅ AVANT: "Ce produit, qui a été conçu par nos experts après plusieurs années de recherche, permet d'obtenir des résultats exceptionnels." - ✅ APRÈS: "Ce produit a été conçu par nos experts après plusieurs années de recherche. Il permet d'obtenir des résultats exceptionnels." **Varier connecteurs:** - ✅ AVANT: "Par ailleurs... Par ailleurs... Par ailleurs..." - ✅ APRÈS: "Par ailleurs... De plus... En outre..." **Fluidifier structure:** - ✅ AVANT: "Produit X. Produit Y. Produit Z." (juxtaposition sèche) - ✅ APRÈS: "Produit X offre... Quant au produit Y, il propose... Enfin, produit Z permet..." **Clarifier relations:** - ✅ AVANT: "Ce service existe. Il est pratique." (lien flou) - ✅ APRÈS: "Ce service existe. Grâce à lui, vous gagnez du temps." (lien clair) RÈGLES LISIBILITÉ: - Privilégie clarté immédiate - Évite subordonnées multiples imbriquées - Structure logique: contexte → explication → bénéfice - Connecteurs variés: évite répétitions sur 3 phrases consécutives FORMAT RÉPONSE: Retourne UNIQUEMENT le contenu fluidifié, SANS balises, SANS métadonnées, SANS explications.`; } /** * NETTOYER RÉPONSE */ cleanResponse(response) { if (!response) return response; let cleaned = response.trim(); // Supprimer balises cleaned = cleaned.replace(/^TAG:\s*[^\s]+\s+/gi, ''); cleaned = cleaned.replace(/\bTAG:\s*[^\s]+\s+/gi, ''); cleaned = cleaned.replace(/^CONTENU:\s*/gi, ''); cleaned = cleaned.replace(/^CONTENU FLUIDIFIÉ:\s*/gi, ''); cleaned = cleaned.replace(/^(voici\s+)?le\s+contenu\s+(fluidifié|lisible)\s*[:.]?\s*/gi, ''); cleaned = cleaned.replace(/^(avec\s+)?amélioration[s]?\s+lisibilité\s*[:.]?\s*/gi, ''); // Nettoyer formatage cleaned = cleaned.replace(/\*\*([^*]+)\*\*/g, '$1'); cleaned = cleaned.replace(/\s{2,}/g, ' '); cleaned = cleaned.trim(); return cleaned; } /** * COMPTER MODIFICATIONS */ countModifications(original, improved) { if (original === improved) return 0; const originalWords = original.toLowerCase().split(/\s+/); const improvedWords = improved.toLowerCase().split(/\s+/); let differences = 0; differences += Math.abs(originalWords.length - improvedWords.length); const minLength = Math.min(originalWords.length, improvedWords.length); for (let i = 0; i < minLength; i++) { if (originalWords[i] !== improvedWords[i]) { differences++; } } return differences; } } module.exports = { SmartReadabilityLayer };