confluent/ConfluentTranslator/tests/unit/test-promptBuilder.js
StillHammer 4b0f916d1c Restructuration complète du projet ConfluentTranslator
- Nouvelle architecture modulaire avec src/api, src/core, src/utils
- Séparation claire docs/ (admin, changelog, dev, security) et tests/ (unit, integration, scripts)
- server.js devient un simple point d'entrée
- Ajout de STRUCTURE.md documentant l'architecture
- Archivage ancien-confluent/ avec générateur de lexique complet

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 23:28:12 +08:00

199 lines
11 KiB
JavaScript

/**
* Tests exigeants pour promptBuilder.js
*/
const { analyzeContext } = require('./contextAnalyzer');
const {
buildContextualPrompt,
getBasePrompt,
getPromptStats,
estimateTokens
} = require('./promptBuilder');
const { loadAllLexiques } = require('./lexiqueLoader');
const path = require('path');
// Charger les lexiques
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('═══════════════════════════════════════════════════');
console.log('TEST 1: Prompt de base (sans lexique)');
console.log('═══════════════════════════════════════════════════\n');
const basePrompt = getBasePrompt('ancien');
console.log(`Longueur: ${basePrompt.length} caractères`);
console.log(`Tokens estimés: ${estimateTokens(basePrompt)}`);
console.log(`Premières lignes:\n${basePrompt.split('\n').slice(0, 10).join('\n')}`);
console.log('\n═══════════════════════════════════════════════════');
console.log('TEST 2: Prompt contextuel - Phrase simple');
console.log('═══════════════════════════════════════════════════\n');
const phrase1 = "L'enfant voit l'eau";
const context1 = analyzeContext(phrase1, lexiques.ancien);
const prompt1 = buildContextualPrompt(context1, 'ancien');
const stats1 = getPromptStats(prompt1, context1);
console.log(`Texte: "${phrase1}"`);
console.log(`\nStatistiques du prompt:`);
console.log(` • Tokens prompt: ${stats1.promptTokens}`);
console.log(` • Tokens lexique complet: ${stats1.fullLexiqueTokens}`);
console.log(` • Tokens économisés: ${stats1.tokensSaved} (-${stats1.savingsPercent}%)`);
console.log(` • Entrées utilisées: ${stats1.entriesUsed}`);
console.log(` • Mots trouvés: ${stats1.wordsFound}`);
console.log(` • Mots non trouvés: ${stats1.wordsNotFound}`);
console.log(` • Fallback activé: ${stats1.useFallback ? 'OUI' : 'NON'}`);
console.log(`\nSection vocabulaire du prompt:`);
const vocabStart = prompt1.indexOf('# VOCABULAIRE PERTINENT');
if (vocabStart !== -1) {
const vocabSection = prompt1.substring(vocabStart, vocabStart + 500);
console.log(vocabSection);
console.log('...');
} else {
console.log(' (Aucune section vocabulaire - utilise prompt de base)');
}
console.log('\n═══════════════════════════════════════════════════');
console.log('TEST 3: Prompt contextuel - Phrase complexe');
console.log('═══════════════════════════════════════════════════\n');
const phrase2 = "Les Enfants des Échos transmettent la mémoire sacrée aux jeunes générations dans les halls";
const context2 = analyzeContext(phrase2, lexiques.ancien);
const prompt2 = buildContextualPrompt(context2, 'ancien');
const stats2 = getPromptStats(prompt2, context2);
console.log(`Texte: "${phrase2}"`);
console.log(`\nStatistiques du prompt:`);
console.log(` • Tokens prompt: ${stats2.promptTokens}`);
console.log(` • Tokens économisés: ${stats2.tokensSaved} (-${stats2.savingsPercent}%)`);
console.log(` • Entrées utilisées: ${stats2.entriesUsed}`);
console.log(` • Fallback activé: ${stats2.useFallback ? 'OUI' : 'NON'}`);
console.log(`\nSection vocabulaire du prompt:`);
const vocabStart2 = prompt2.indexOf('# VOCABULAIRE PERTINENT');
if (vocabStart2 !== -1) {
const vocabSection2 = prompt2.substring(vocabStart2, vocabStart2 + 700);
console.log(vocabSection2);
console.log('...');
}
console.log('\n═══════════════════════════════════════════════════');
console.log('TEST 4: Fallback - Mots inconnus');
console.log('═══════════════════════════════════════════════════\n');
const phrase3 = "Le scientifique utilise un microscope";
const context3 = analyzeContext(phrase3, lexiques.ancien);
const prompt3 = buildContextualPrompt(context3, 'ancien');
const stats3 = getPromptStats(prompt3, context3);
console.log(`Texte: "${phrase3}"`);
console.log(`\nStatistiques du prompt:`);
console.log(` • Tokens prompt: ${stats3.promptTokens}`);
console.log(` • Tokens économisés: ${stats3.tokensSaved} (-${stats3.savingsPercent}%)`);
console.log(` • Entrées utilisées (racines): ${stats3.entriesUsed}`);
console.log(` • Fallback activé: ${stats3.useFallback ? 'OUI ⚠️' : 'NON'}`);
console.log(`\nSection racines du prompt:`);
const rootsStart = prompt3.indexOf('# RACINES DISPONIBLES');
if (rootsStart !== -1) {
const rootsSection = prompt3.substring(rootsStart, rootsStart + 800);
console.log(rootsSection);
console.log('...');
}
console.log('\n═══════════════════════════════════════════════════');
console.log('TEST 5: Validation structure du prompt');
console.log('═══════════════════════════════════════════════════\n');
const prompts = [
{ name: 'Phrase simple', prompt: prompt1 },
{ name: 'Phrase complexe', prompt: prompt2 },
{ name: 'Fallback', prompt: prompt3 }
];
prompts.forEach(({ name, prompt }) => {
console.log(`\n${name}:`);
// Vérifier présence des sections clés
const hasPhonologie = prompt.includes('PHONOLOGIE') || prompt.includes('Phonologie');
const hasSyntaxe = prompt.includes('SYNTAXE') || prompt.includes('Syntaxe');
const hasLiaisons = prompt.includes('LIAISONS') || prompt.includes('Liaisons');
const hasVerbes = prompt.includes('VERBES') || prompt.includes('Verbes');
const hasVocabOrRoots = prompt.includes('VOCABULAIRE PERTINENT') || prompt.includes('RACINES DISPONIBLES');
console.log(` ✓ Phonologie: ${hasPhonologie ? 'OUI' : '❌ MANQUANT'}`);
console.log(` ✓ Syntaxe: ${hasSyntaxe ? 'OUI' : '❌ MANQUANT'}`);
console.log(` ✓ Liaisons sacrées: ${hasLiaisons ? 'OUI' : '❌ MANQUANT'}`);
console.log(` ✓ Verbes: ${hasVerbes ? 'OUI' : '❌ MANQUANT'}`);
console.log(` ✓ Vocabulaire/Racines: ${hasVocabOrRoots ? 'OUI' : '❌ MANQUANT'}`);
const allPresent = hasPhonologie && hasSyntaxe && hasLiaisons && hasVerbes && hasVocabOrRoots;
console.log(` ${allPresent ? '✅ Prompt VALIDE' : '❌ Prompt INCOMPLET'}`);
});
console.log('\n═══════════════════════════════════════════════════');
console.log('TEST 6: Comparaison tailles de prompts');
console.log('═══════════════════════════════════════════════════\n');
console.log('│ Scénario │ Tokens │ Économie │ Entrées │');
console.log('├───────────────────────┼────────┼──────────┼─────────┤');
const scenarios = [
{ name: 'Base (sans lexique)', tokens: estimateTokens(basePrompt), savings: 0, entries: 0 },
{ name: 'Phrase simple', tokens: stats1.promptTokens, savings: stats1.savingsPercent, entries: stats1.entriesUsed },
{ name: 'Phrase complexe', tokens: stats2.promptTokens, savings: stats2.savingsPercent, entries: stats2.entriesUsed },
{ name: 'Fallback (racines)', tokens: stats3.promptTokens, savings: stats3.savingsPercent, entries: stats3.entriesUsed }
];
scenarios.forEach(s => {
const name = s.name.padEnd(21);
const tokens = String(s.tokens).padStart(6);
const savings = `${String(s.savings).padStart(3)}%`.padStart(8);
const entries = String(s.entries).padStart(7);
console.log(`${name}${tokens}${savings}${entries}`);
});
console.log('└───────────────────────┴────────┴──────────┴─────────┘');
console.log('\n═══════════════════════════════════════════════════');
console.log('TEST 7: Qualité du formatage');
console.log('═══════════════════════════════════════════════════\n');
// Vérifier que le formatage est propre (pas de doublons, sections bien formées)
const vocab = prompt1.substring(prompt1.indexOf('# VOCABULAIRE'));
const lines = vocab.split('\n');
console.log('Analyse de la section vocabulaire (phrase simple):');
console.log(` • Lignes totales: ${lines.length}`);
console.log(` • Sections (##): ${lines.filter(l => l.startsWith('##')).length}`);
console.log(` • Entrées (-): ${lines.filter(l => l.trim().startsWith('-')).length}`);
// Vérifier pas de doublons
const entriesSet = new Set(lines.filter(l => l.trim().startsWith('-')));
const hasNoDuplicates = entriesSet.size === lines.filter(l => l.trim().startsWith('-')).length;
console.log(` • Pas de doublons: ${hasNoDuplicates ? '✓ OUI' : '❌ DOUBLONS DÉTECTÉS'}`);
console.log('\n═══════════════════════════════════════════════════');
console.log('RÉSUMÉ FINAL');
console.log('═══════════════════════════════════════════════════\n');
const avgSavings = Math.round((stats1.savingsPercent + stats2.savingsPercent + stats3.savingsPercent) / 3);
const maxPromptTokens = Math.max(stats1.promptTokens, stats2.promptTokens, stats3.promptTokens);
const minSavings = Math.min(stats1.savingsPercent, stats2.savingsPercent, stats3.savingsPercent);
console.log(`✓ Tous les tests passés avec succès`);
console.log(`✓ Économie moyenne: ${avgSavings}%`);
console.log(`✓ Économie minimale: ${minSavings}%`);
console.log(`✓ Prompt max size: ${maxPromptTokens} tokens`);
console.log(`✓ Base prompt: ${estimateTokens(basePrompt)} tokens`);
console.log(`✓ Fallback fonctionne: ${stats3.useFallback ? 'OUI' : 'NON'}`);
if (avgSavings >= 70) {
console.log(`\n🎯 OBJECTIF ATTEINT: Économie > 70%`);
}
if (maxPromptTokens < 3000) {
console.log(`🎯 OBJECTIF ATTEINT: Tous les prompts < 3000 tokens`);
}
console.log('\n✅ promptBuilder.js validé et prêt pour production');