// ======================================== // ÉTAPE 2: ENHANCEMENT TECHNIQUE // Responsabilité: Améliorer la précision technique avec GPT-4 // LLM: GPT-4o-mini (température 0.4) // ======================================== const { callLLM } = require('../LLMManager'); const { logSh } = require('../ErrorReporting'); const { tracer } = require('../trace'); /** * MAIN ENTRY POINT - ENHANCEMENT TECHNIQUE * Input: { content: {}, csvData: {}, context: {} } * Output: { content: {}, stats: {}, debug: {} } */ async function enhanceTechnicalTerms(input) { return await tracer.run('TechnicalEnhancement.enhanceTechnicalTerms()', async () => { const { content, csvData, context = {} } = input; await tracer.annotate({ step: '2/4', llmProvider: 'gpt4', elementsCount: Object.keys(content).length, mc0: csvData.mc0 }); const startTime = Date.now(); logSh(`🔧 ÉTAPE 2/4: Enhancement technique (GPT-4)`, 'INFO'); logSh(` 📊 ${Object.keys(content).length} éléments à analyser`, 'INFO'); try { // 1. Analyser tous les éléments pour détecter termes techniques const technicalAnalysis = await analyzeTechnicalTerms(content, csvData); // 2. Filter les éléments qui ont besoin d'enhancement const elementsNeedingEnhancement = technicalAnalysis.filter(item => item.needsEnhancement); logSh(` 📋 Analyse: ${elementsNeedingEnhancement.length}/${Object.keys(content).length} éléments nécessitent enhancement`, 'INFO'); if (elementsNeedingEnhancement.length === 0) { logSh(`✅ ÉTAPE 2/4: Aucun enhancement nécessaire`, 'INFO'); return { content, stats: { processed: Object.keys(content).length, enhanced: 0, duration: Date.now() - startTime }, debug: { llmProvider: 'gpt4', step: 2, enhancementsApplied: [] } }; } // 3. Améliorer les éléments sélectionnés const enhancedResults = await enhanceSelectedElements(elementsNeedingEnhancement, csvData); // 4. Merger avec contenu original const finalContent = { ...content }; let actuallyEnhanced = 0; Object.keys(enhancedResults).forEach(tag => { if (enhancedResults[tag] !== content[tag]) { finalContent[tag] = enhancedResults[tag]; actuallyEnhanced++; } }); const duration = Date.now() - startTime; const stats = { processed: Object.keys(content).length, enhanced: actuallyEnhanced, candidate: elementsNeedingEnhancement.length, duration }; logSh(`✅ ÉTAPE 2/4 TERMINÉE: ${stats.enhanced} éléments améliorés (${duration}ms)`, 'INFO'); await tracer.event(`Enhancement technique terminé`, stats); return { content: finalContent, stats, debug: { llmProvider: 'gpt4', step: 2, enhancementsApplied: Object.keys(enhancedResults), technicalTermsFound: elementsNeedingEnhancement.map(e => e.technicalTerms) } }; } catch (error) { const duration = Date.now() - startTime; logSh(`❌ ÉTAPE 2/4 ÉCHOUÉE après ${duration}ms: ${error.message}`, 'ERROR'); throw new Error(`TechnicalEnhancement failed: ${error.message}`); } }, input); } /** * Analyser tous les éléments pour détecter termes techniques */ async function analyzeTechnicalTerms(content, csvData) { logSh(`🔍 Analyse termes techniques batch`, 'DEBUG'); const contentEntries = Object.keys(content); const analysisPrompt = `MISSION: Analyser ces ${contentEntries.length} contenus et identifier leurs termes techniques. CONTEXTE: ${csvData.mc0} - Secteur: signalétique/impression CONTENUS À ANALYSER: ${contentEntries.map((tag, i) => `[${i + 1}] TAG: ${tag} CONTENU: "${content[tag]}"`).join('\n\n')} CONSIGNES: - Identifie UNIQUEMENT les vrais termes techniques métier/industrie - Évite mots génériques (qualité, service, pratique, personnalisé) - Focus: matériaux, procédés, normes, dimensions, technologies - Si aucun terme technique → "AUCUN" EXEMPLES VALIDES: dibond, impression UV, fraisage CNC, épaisseur 3mm EXEMPLES INVALIDES: durable, pratique, personnalisé, moderne FORMAT RÉPONSE: [1] dibond, impression UV OU AUCUN [2] AUCUN [3] aluminium, fraisage CNC OU AUCUN etc...`; try { const analysisResponse = await callLLM('gpt4', analysisPrompt, { temperature: 0.3, maxTokens: 2000 }, csvData.personality); return parseAnalysisResponse(analysisResponse, content, contentEntries); } catch (error) { logSh(`❌ Analyse termes techniques échouée: ${error.message}`, 'ERROR'); throw error; } } /** * Améliorer les éléments sélectionnés */ async function enhanceSelectedElements(elementsNeedingEnhancement, csvData) { logSh(`🛠️ Enhancement ${elementsNeedingEnhancement.length} éléments`, 'DEBUG'); const enhancementPrompt = `MISSION: Améliore UNIQUEMENT la précision technique de ces contenus. CONTEXTE: ${csvData.mc0} - Secteur signalétique/impression PERSONNALITÉ: ${csvData.personality?.nom} (${csvData.personality?.style}) CONTENUS À AMÉLIORER: ${elementsNeedingEnhancement.map((item, i) => `[${i + 1}] TAG: ${item.tag} CONTENU: "${item.content}" TERMES TECHNIQUES: ${item.technicalTerms.join(', ')}`).join('\n\n')} CONSIGNES: - GARDE même longueur, structure et ton ${csvData.personality?.style} - Intègre naturellement les termes techniques listés - NE CHANGE PAS le fond du message - Vocabulaire expert mais accessible - Termes secteur: dibond, aluminium, impression UV, fraisage, PMMA FORMAT RÉPONSE: [1] Contenu avec amélioration technique [2] Contenu avec amélioration technique etc...`; try { const enhancedResponse = await callLLM('gpt4', enhancementPrompt, { temperature: 0.4, maxTokens: 5000 }, csvData.personality); return parseEnhancementResponse(enhancedResponse, elementsNeedingEnhancement); } catch (error) { logSh(`❌ Enhancement éléments échoué: ${error.message}`, 'ERROR'); throw error; } } /** * Parser réponse analyse */ function parseAnalysisResponse(response, content, contentEntries) { const results = []; const regex = /\[(\d+)\]\s*([^[]*?)(?=\[\d+\]|$)/gs; let match; const parsedItems = {}; while ((match = regex.exec(response)) !== null) { const index = parseInt(match[1]) - 1; const termsText = match[2].trim(); parsedItems[index] = termsText; } contentEntries.forEach((tag, index) => { const termsText = parsedItems[index] || 'AUCUN'; const hasTerms = !termsText.toUpperCase().includes('AUCUN'); const technicalTerms = hasTerms ? termsText.split(',').map(t => t.trim()).filter(t => t.length > 0) : []; results.push({ tag, content: content[tag], technicalTerms, needsEnhancement: hasTerms && technicalTerms.length > 0 }); logSh(`🔍 [${tag}]: ${hasTerms ? technicalTerms.join(', ') : 'aucun terme technique'}`, 'DEBUG'); }); return results; } /** * Parser réponse enhancement */ function parseEnhancementResponse(response, elementsNeedingEnhancement) { const results = {}; const regex = /\[(\d+)\]\s*([^[]*?)(?=\[\d+\]|$)/gs; let match; let index = 0; while ((match = regex.exec(response)) && index < elementsNeedingEnhancement.length) { let enhancedContent = match[2].trim(); const element = elementsNeedingEnhancement[index]; // Nettoyer le contenu généré enhancedContent = cleanEnhancedContent(enhancedContent); if (enhancedContent && enhancedContent.length > 10) { results[element.tag] = enhancedContent; logSh(`✅ Enhanced [${element.tag}]: "${enhancedContent.substring(0, 100)}..."`, 'DEBUG'); } else { results[element.tag] = element.content; logSh(`⚠️ Fallback [${element.tag}]: contenu invalide`, 'WARNING'); } index++; } // Compléter les manquants while (index < elementsNeedingEnhancement.length) { const element = elementsNeedingEnhancement[index]; results[element.tag] = element.content; index++; } return results; } /** * Nettoyer contenu amélioré */ function cleanEnhancedContent(content) { if (!content) return content; // Supprimer préfixes indésirables content = content.replace(/^(Bon,?\s*)?(alors,?\s*)?pour\s+/gi, ''); content = content.replace(/\*\*[^*]+\*\*/g, ''); content = content.replace(/\s{2,}/g, ' '); content = content.trim(); return content; } module.exports = { enhanceTechnicalTerms, // ← MAIN ENTRY POINT analyzeTechnicalTerms, enhanceSelectedElements, parseAnalysisResponse, parseEnhancementResponse };