543 lines
21 KiB
JavaScript
543 lines
21 KiB
JavaScript
// ========================================
|
|
// FICHIER: BrainConfig.js - Version Node.js
|
|
// Description: Configuration cerveau + sélection personnalité IA
|
|
// ========================================
|
|
|
|
require('dotenv').config();
|
|
const axios = require('axios');
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
|
|
// Import de la fonction logSh (assumant qu'elle existe dans votre projet Node.js)
|
|
const { logSh } = require('./ErrorReporting');
|
|
|
|
// Configuration
|
|
const CONFIG = {
|
|
openai: {
|
|
apiKey: process.env.OPENAI_API_KEY || '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',
|
|
endpoint: 'https://api.openai.com/v1/chat/completions'
|
|
},
|
|
dataSource: {
|
|
type: process.env.DATA_SOURCE_TYPE || 'json', // 'json', 'csv', 'database'
|
|
instructionsPath: './data/instructions.json',
|
|
personalitiesPath: './data/personalities.json'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* FONCTION PRINCIPALE - Équivalent getBrainConfig()
|
|
* @param {number|object} data - Numéro de ligne ou données directes
|
|
* @returns {object} Configuration avec données CSV + personnalité
|
|
*/
|
|
async function getBrainConfig(data) {
|
|
try {
|
|
logSh("🧠 Début getBrainConfig Node.js", "INFO");
|
|
|
|
// 1. RÉCUPÉRER LES DONNÉES CSV
|
|
let csvData;
|
|
if (typeof data === 'number') {
|
|
// Numéro de ligne fourni - lire depuis fichier
|
|
csvData = await readInstructionsData(data);
|
|
} else if (typeof data === 'object' && data.rowNumber) {
|
|
csvData = await readInstructionsData(data.rowNumber);
|
|
} else {
|
|
// Données déjà fournies
|
|
csvData = data;
|
|
}
|
|
|
|
logSh(`✅ CSV récupéré: ${csvData.mc0}`, "INFO");
|
|
|
|
// 2. RÉCUPÉRER LES PERSONNALITÉS
|
|
const personalities = await getPersonalities();
|
|
logSh(`✅ ${personalities.length} personnalités chargées`, "INFO");
|
|
|
|
// 3. SÉLECTIONNER LA MEILLEURE PERSONNALITÉ VIA IA
|
|
const selectedPersonality = await selectPersonalityWithAI(
|
|
csvData.mc0,
|
|
csvData.t0,
|
|
personalities
|
|
);
|
|
|
|
logSh(`✅ Personnalité sélectionnée: ${selectedPersonality.nom}`, "INFO");
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
...csvData,
|
|
personality: selectedPersonality,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
};
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur getBrainConfig: ${error.message}`, "ERROR");
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* LIRE DONNÉES INSTRUCTIONS depuis Google Sheets DIRECTEMENT
|
|
* @param {number} rowNumber - Numéro de ligne (2 = première ligne de données)
|
|
* @returns {object} Données CSV parsées
|
|
*/
|
|
async function readInstructionsData(rowNumber = 2) {
|
|
try {
|
|
logSh(`📊 Lecture Google Sheet ligne ${rowNumber}...`, 'INFO');
|
|
|
|
// NOUVEAU : Lecture directe depuis Google Sheets
|
|
const { google } = require('googleapis');
|
|
|
|
// Configuration auth Google Sheets - FORCE utilisation fichier JSON pour éviter problème TLS
|
|
const keyFilePath = path.join(__dirname, '..', 'seo-generator-470715-85d4a971c1af.json');
|
|
const auth = new google.auth.GoogleAuth({
|
|
keyFile: keyFilePath,
|
|
scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly']
|
|
});
|
|
logSh('🔑 Utilisation fichier JSON pour contourner problème TLS OAuth', 'INFO');
|
|
|
|
const sheets = google.sheets({ version: 'v4', auth });
|
|
const SHEET_ID = process.env.GOOGLE_SHEETS_ID || '1iA2GvWeUxX-vpnAMfVm3ZMG9LhaC070SdGssEcXAh2c';
|
|
|
|
// Récupérer la ligne spécifique (A à I au minimum)
|
|
const response = await sheets.spreadsheets.values.get({
|
|
spreadsheetId: SHEET_ID,
|
|
range: `Instructions!A${rowNumber}:I${rowNumber}` // Ligne spécifique A-I
|
|
});
|
|
|
|
if (!response.data.values || response.data.values.length === 0) {
|
|
throw new Error(`Ligne ${rowNumber} non trouvée dans Google Sheet`);
|
|
}
|
|
|
|
const row = response.data.values[0];
|
|
logSh(`✅ Ligne ${rowNumber} récupérée: ${row.length} colonnes`, 'INFO');
|
|
|
|
const xmlTemplateValue = row[8] || '';
|
|
let xmlTemplate = xmlTemplateValue;
|
|
let xmlFileName = null;
|
|
|
|
// Si c'est un nom de fichier, garder le nom ET utiliser un template par défaut
|
|
if (xmlTemplateValue && xmlTemplateValue.endsWith('.xml') && xmlTemplateValue.length < 100) {
|
|
logSh(`🔧 XML filename detected (${xmlTemplateValue}), keeping filename for Digital Ocean`, 'INFO');
|
|
xmlFileName = xmlTemplateValue; // Garder le nom du fichier pour Digital Ocean
|
|
xmlTemplate = createDefaultXMLTemplate(); // Template par défaut pour le processing
|
|
}
|
|
|
|
return {
|
|
rowNumber: rowNumber,
|
|
slug: row[0] || '', // Colonne A
|
|
t0: row[1] || '', // Colonne B
|
|
mc0: row[2] || '', // Colonne C
|
|
tMinus1: row[3] || '', // Colonne D
|
|
lMinus1: row[4] || '', // Colonne E
|
|
mcPlus1: row[5] || '', // Colonne F
|
|
tPlus1: row[6] || '', // Colonne G
|
|
lPlus1: row[7] || '', // Colonne H
|
|
xmlTemplate: xmlTemplate, // XML template pour processing
|
|
xmlFileName: xmlFileName // Nom fichier pour Digital Ocean (si applicable)
|
|
};
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur lecture Google Sheet: ${error.message}`, "ERROR");
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* RÉCUPÉRER PERSONNALITÉS depuis l'onglet "Personnalites" du Google Sheet
|
|
* @returns {Array} Liste des personnalités disponibles
|
|
*/
|
|
async function getPersonalities() {
|
|
try {
|
|
logSh('📊 Lecture personnalités depuis Google Sheet (onglet Personnalites)...', 'INFO');
|
|
|
|
// Configuration auth Google Sheets - FORCE utilisation fichier JSON pour éviter problème TLS
|
|
const { google } = require('googleapis');
|
|
const keyFilePath = path.join(__dirname, '..', 'seo-generator-470715-85d4a971c1af.json');
|
|
const auth = new google.auth.GoogleAuth({
|
|
keyFile: keyFilePath,
|
|
scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly']
|
|
});
|
|
logSh('🔑 Utilisation fichier JSON pour contourner problème TLS OAuth (personnalités)', 'INFO');
|
|
|
|
const sheets = google.sheets({ version: 'v4', auth });
|
|
const SHEET_ID = process.env.GOOGLE_SHEETS_ID || '1iA2GvWeUxX-vpnAMfVm3ZMG9LhaC070SdGssEcXAh2c';
|
|
|
|
// Récupérer toutes les personnalités (après la ligne d'en-tête)
|
|
const response = await sheets.spreadsheets.values.get({
|
|
spreadsheetId: SHEET_ID,
|
|
range: 'Personnalites!A2:O' // Colonnes A à O pour inclure les nouvelles colonnes IA
|
|
});
|
|
|
|
if (!response.data.values || response.data.values.length === 0) {
|
|
throw new Error('Aucune personnalité trouvée dans l\'onglet Personnalites');
|
|
}
|
|
|
|
const personalities = [];
|
|
|
|
// Traiter chaque ligne de personnalité
|
|
response.data.values.forEach((row, index) => {
|
|
if (row[0] && row[0].toString().trim() !== '') { // Si nom existe (colonne A)
|
|
const personality = {
|
|
nom: row[0]?.toString().trim() || '',
|
|
description: row[1]?.toString().trim() || 'Expert généraliste',
|
|
style: row[2]?.toString().trim() || 'professionnel',
|
|
|
|
// Configuration avancée depuis colonnes Google Sheet
|
|
motsClesSecteurs: parseCSVField(row[3]),
|
|
vocabulairePref: parseCSVField(row[4]),
|
|
connecteursPref: parseCSVField(row[5]),
|
|
erreursTypiques: parseCSVField(row[6]),
|
|
longueurPhrases: row[7]?.toString().trim() || 'moyennes',
|
|
niveauTechnique: row[8]?.toString().trim() || 'moyen',
|
|
ctaStyle: parseCSVField(row[9]),
|
|
defautsSimules: parseCSVField(row[10]),
|
|
|
|
// NOUVEAU: Configuration IA par étape depuis Google Sheets (colonnes L-O)
|
|
aiEtape1Base: row[11]?.toString().trim().toLowerCase() || '',
|
|
aiEtape2Technique: row[12]?.toString().trim().toLowerCase() || '',
|
|
aiEtape3Transitions: row[13]?.toString().trim().toLowerCase() || '',
|
|
aiEtape4Style: row[14]?.toString().trim().toLowerCase() || '',
|
|
|
|
// Backward compatibility
|
|
motsCles: parseCSVField(row[3] || '') // Utilise motsClesSecteurs
|
|
};
|
|
|
|
personalities.push(personality);
|
|
logSh(`✓ Personnalité chargée: ${personality.nom} (${personality.style})`, 'DEBUG');
|
|
}
|
|
});
|
|
|
|
logSh(`📊 ${personalities.length} personnalités chargées depuis Google Sheet`, "INFO");
|
|
|
|
return personalities;
|
|
|
|
} catch (error) {
|
|
logSh(`❌ ÉCHEC: Impossible de récupérer les personnalités Google Sheets - ${error.message}`, "ERROR");
|
|
throw new Error(`FATAL: Personnalités Google Sheets inaccessibles - arrêt du workflow: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PARSER CHAMP CSV - Helper function
|
|
* @param {string} field - Champ à parser
|
|
* @returns {Array} Liste des éléments parsés
|
|
*/
|
|
function parseCSVField(field) {
|
|
if (!field || field.toString().trim() === '') return [];
|
|
|
|
return field.toString()
|
|
.split(',')
|
|
.map(item => item.trim())
|
|
.filter(item => item.length > 0);
|
|
}
|
|
|
|
/**
|
|
* Sélectionner un sous-ensemble aléatoire de personnalités
|
|
* @param {Array} allPersonalities - Liste complète des personnalités
|
|
* @param {number} percentage - Pourcentage à garder (0.6 = 60%)
|
|
* @returns {Array} Sous-ensemble aléatoire
|
|
*/
|
|
function selectRandomPersonalities(allPersonalities, percentage = 0.6) {
|
|
const count = Math.ceil(allPersonalities.length * percentage);
|
|
|
|
// Mélanger avec Fisher-Yates shuffle (meilleur que sort())
|
|
const shuffled = [...allPersonalities];
|
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
}
|
|
|
|
return shuffled.slice(0, count);
|
|
}
|
|
|
|
/**
|
|
* NOUVELLE FONCTION: Sélection de 4 personnalités complémentaires pour le pipeline multi-AI
|
|
* @param {string} mc0 - Mot-clé principal
|
|
* @param {string} t0 - Titre principal
|
|
* @param {Array} personalities - Liste des personnalités
|
|
* @returns {Array} 4 personnalités sélectionnées pour chaque étape
|
|
*/
|
|
async function selectMultiplePersonalitiesWithAI(mc0, t0, personalities) {
|
|
try {
|
|
logSh(`🎭 Sélection MULTI-personnalités IA pour: ${mc0}`, "INFO");
|
|
|
|
// Sélection aléatoire de 80% des personnalités (plus large pour 4 choix)
|
|
const randomPersonalities = selectRandomPersonalities(personalities, 0.8);
|
|
const totalCount = personalities.length;
|
|
const selectedCount = randomPersonalities.length;
|
|
|
|
logSh(`🎲 Pool aléatoire: ${selectedCount}/${totalCount} personnalités disponibles`, "DEBUG");
|
|
logSh(`📋 Personnalités dans le pool: ${randomPersonalities.map(p => p.nom).join(', ')}`, "DEBUG");
|
|
|
|
const prompt = `Choisis 4 personnalités COMPLÉMENTAIRES pour générer du contenu sur "${mc0}":
|
|
|
|
OBJECTIF: Créer une équipe de 4 rédacteurs avec styles différents mais cohérents
|
|
|
|
PERSONNALITÉS DISPONIBLES:
|
|
${randomPersonalities.map(p => `- ${p.nom}: ${p.description} (Style: ${p.style})`).join('\n')}
|
|
|
|
RÔLES À ATTRIBUER:
|
|
1. GÉNÉRATEUR BASE: Personnalité technique/experte pour la génération initiale
|
|
2. ENHANCER TECHNIQUE: Personnalité commerciale/précise pour améliorer les termes techniques
|
|
3. FLUIDITÉ: Personnalité créative/littéraire pour améliorer les transitions
|
|
4. STYLE FINAL: Personnalité terrain/accessible pour le style final
|
|
|
|
CRITÈRES:
|
|
- 4 personnalités aux styles DIFFÉRENTS mais complémentaires
|
|
- Adapté au secteur: ${mc0}
|
|
- Variabilité maximale pour anti-détection
|
|
- Éviter les doublons de style
|
|
|
|
FORMAT DE RÉPONSE (EXACTEMENT 4 noms séparés par des virgules):
|
|
Nom1, Nom2, Nom3, Nom4`;
|
|
|
|
const requestData = {
|
|
model: "gpt-4o-mini",
|
|
messages: [{"role": "user", "content": prompt}],
|
|
max_tokens: 100,
|
|
temperature: 1.0
|
|
};
|
|
|
|
const response = await axios.post(CONFIG.openai.endpoint, requestData, {
|
|
headers: {
|
|
'Authorization': `Bearer ${CONFIG.openai.apiKey}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
timeout: 300000
|
|
});
|
|
|
|
const selectedNames = response.data.choices[0].message.content.trim()
|
|
.split(',')
|
|
.map(name => name.trim());
|
|
|
|
logSh(`🔍 Noms retournés par IA: ${selectedNames.join(', ')}`, "DEBUG");
|
|
|
|
// Mapper aux vraies personnalités
|
|
const selectedPersonalities = [];
|
|
selectedNames.forEach(name => {
|
|
const personality = randomPersonalities.find(p => p.nom === name);
|
|
if (personality) {
|
|
selectedPersonalities.push(personality);
|
|
}
|
|
});
|
|
|
|
// Compléter si pas assez de personnalités trouvées (sécurité)
|
|
while (selectedPersonalities.length < 4 && randomPersonalities.length > selectedPersonalities.length) {
|
|
const remaining = randomPersonalities.filter(p =>
|
|
!selectedPersonalities.some(selected => selected.nom === p.nom)
|
|
);
|
|
if (remaining.length > 0) {
|
|
const randomIndex = Math.floor(Math.random() * remaining.length);
|
|
selectedPersonalities.push(remaining[randomIndex]);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Garantir exactement 4 personnalités
|
|
const final4Personalities = selectedPersonalities.slice(0, 4);
|
|
|
|
logSh(`✅ Équipe de 4 personnalités sélectionnée:`, "INFO");
|
|
final4Personalities.forEach((p, index) => {
|
|
const roles = ['BASE', 'TECHNIQUE', 'FLUIDITÉ', 'STYLE'];
|
|
logSh(` ${index + 1}. ${roles[index]}: ${p.nom} (${p.style})`, "INFO");
|
|
});
|
|
|
|
return final4Personalities;
|
|
|
|
} catch (error) {
|
|
logSh(`❌ FATAL: Sélection multi-personnalités échouée: ${error.message}`, "ERROR");
|
|
throw new Error(`FATAL: Sélection multi-personnalités IA impossible - arrêt du workflow: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* FONCTION LEGACY: Sélection personnalité unique (maintenue pour compatibilité)
|
|
* @param {string} mc0 - Mot-clé principal
|
|
* @param {string} t0 - Titre principal
|
|
* @param {Array} personalities - Liste des personnalités
|
|
* @returns {object} Personnalité sélectionnée
|
|
*/
|
|
async function selectPersonalityWithAI(mc0, t0, personalities) {
|
|
try {
|
|
logSh(`🤖 Sélection personnalité IA UNIQUE pour: ${mc0}`, "DEBUG");
|
|
|
|
// Appeler la fonction multi et prendre seulement la première
|
|
const multiPersonalities = await selectMultiplePersonalitiesWithAI(mc0, t0, personalities);
|
|
const selectedPersonality = multiPersonalities[0];
|
|
|
|
logSh(`✅ Personnalité IA sélectionnée (mode legacy): ${selectedPersonality.nom}`, "INFO");
|
|
|
|
return selectedPersonality;
|
|
|
|
} catch (error) {
|
|
logSh(`❌ FATAL: Sélection personnalité par IA échouée: ${error.message}`, "ERROR");
|
|
throw new Error(`FATAL: Sélection personnalité IA inaccessible - arrêt du workflow: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CRÉER TEMPLATE XML PAR DÉFAUT quand colonne I contient un nom de fichier
|
|
* Utilise les données CSV disponibles pour créer un template robuste
|
|
*/
|
|
function createDefaultXMLTemplate() {
|
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
<article>
|
|
<header>
|
|
<h1>|Titre_Principal{{T0}}{Rédige un titre H1 accrocheur de maximum 10 mots pour {{MC0}}. Style {{personality.style}}}|</h1>
|
|
<intro>|Introduction{{MC0}}{Rédige une introduction engageante de 2-3 phrases sur {{MC0}}. Ton {{personality.style}}, utilise {{personality.vocabulairePref}}}|</intro>
|
|
</header>
|
|
|
|
<main>
|
|
<section class="primary">
|
|
<h2>|Titre_H2_1{{MC+1_1}}{Crée un titre H2 informatif sur {{MC+1_1}}. Style {{personality.style}}}|</h2>
|
|
<p>|Paragraphe_1{{MC+1_1}}{Rédige un paragraphe détaillé de 4-5 phrases sur {{MC+1_1}}. Explique les avantages et caractéristiques. Ton {{personality.style}}}|</p>
|
|
</section>
|
|
|
|
<section class="secondary">
|
|
<h2>|Titre_H2_2{{MC+1_2}}{Titre H2 pour {{MC+1_2}}. Mets en valeur les points forts. Ton {{personality.style}}}|</h2>
|
|
<p>|Paragraphe_2{{MC+1_2}}{Paragraphe de 4-5 phrases sur {{MC+1_2}}. Détaille pourquoi c'est important pour {{MC0}}. Ton {{personality.style}}}|</p>
|
|
</section>
|
|
|
|
<section class="benefits">
|
|
<h2>|Titre_H2_3{{MC+1_3}}{Titre H2 sur les bénéfices de {{MC+1_3}}. Accrocheur et informatif}|</h2>
|
|
<p>|Paragraphe_3{{MC+1_3}}{Explique en 4-5 phrases les avantages de {{MC+1_3}} pour {{MC0}}. Ton {{personality.style}}}|</p>
|
|
</section>
|
|
</main>
|
|
|
|
<aside class="faq">
|
|
<h2>|FAQ_Titre{Titre de section FAQ accrocheur sur {{MC0}}}|</h2>
|
|
|
|
<div class="faq-item">
|
|
<h3>|Faq_q_1{{MC+1_1}}{Question fréquente sur {{MC+1_1}} et {{MC0}}}|</h3>
|
|
<p>|Faq_a_1{{MC+1_1}}{Réponse claire et précise. 2-3 phrases. Ton {{personality.style}}}|</p>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<h3>|Faq_q_2{{MC+1_2}}{Question pratique sur {{MC+1_2}} en lien avec {{MC0}}}|</h3>
|
|
<p>|Faq_a_2{{MC+1_2}}{Réponse détaillée et utile. 2-3 phrases explicatives. Ton {{personality.style}}}|</p>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<h3>|Faq_q_3{{MC+1_3}}{Question sur {{MC+1_3}} que se posent les clients}|</h3>
|
|
<p>|Faq_a_3{{MC+1_3}}{Réponse complète qui rassure et informe. 2-3 phrases. Ton {{personality.style}}}|</p>
|
|
</div>
|
|
</aside>
|
|
|
|
<footer>
|
|
<p>|Conclusion{{MC0}}{Conclusion engageante de 2 phrases sur {{MC0}}. Appel à l'action subtil. Ton {{personality.style}}}|</p>
|
|
</footer>
|
|
</article>`;
|
|
}
|
|
|
|
/**
|
|
* CRÉER FICHIERS DE DONNÉES D'EXEMPLE
|
|
* Fonction utilitaire pour initialiser les fichiers JSON
|
|
*/
|
|
async function createSampleDataFiles() {
|
|
try {
|
|
// Créer répertoire data s'il n'existe pas
|
|
await fs.mkdir('./data', { recursive: true });
|
|
|
|
// Exemple instructions.json
|
|
const sampleInstructions = [
|
|
{
|
|
slug: "plaque-test",
|
|
t0: "Plaque test signalétique",
|
|
mc0: "plaque signalétique",
|
|
"t-1": "Signalétique",
|
|
"l-1": "/signaletique/",
|
|
"mc+1": "plaque dibond, plaque aluminium, plaque PVC",
|
|
"t+1": "Plaque dibond, Plaque alu, Plaque PVC",
|
|
"l+1": "/plaque-dibond/, /plaque-aluminium/, /plaque-pvc/",
|
|
xmlFileName: "template-plaque.xml"
|
|
}
|
|
];
|
|
|
|
// Exemple personalities.json
|
|
const samplePersonalities = [
|
|
{
|
|
nom: "Marc",
|
|
description: "Expert technique en signalétique",
|
|
style: "professionnel et précis",
|
|
motsClesSecteurs: "technique,dibond,aluminium,impression",
|
|
vocabulairePref: "précision,qualité,expertise,performance",
|
|
connecteursPref: "par ailleurs,en effet,notamment,cependant",
|
|
erreursTypiques: "accord_proximite,repetition_legere",
|
|
longueurPhrases: "moyennes",
|
|
niveauTechnique: "élevé",
|
|
ctaStyle: "découvrir,choisir,commander",
|
|
defautsSimules: "fatigue_cognitive,hesitation_technique"
|
|
},
|
|
{
|
|
nom: "Sophie",
|
|
description: "Passionnée de décoration et design",
|
|
style: "familier et chaleureux",
|
|
motsClesSecteurs: "décoration,design,esthétique,tendances",
|
|
vocabulairePref: "joli,magnifique,tendance,style",
|
|
connecteursPref: "du coup,en fait,sinon,au fait",
|
|
erreursTypiques: "familiarite_excessive,expression_populaire",
|
|
longueurPhrases: "courtes",
|
|
niveauTechnique: "moyen",
|
|
ctaStyle: "craquer,adopter,foncer",
|
|
defautsSimules: "enthousiasme_variable,anecdote_personnelle"
|
|
}
|
|
];
|
|
|
|
// Écrire les fichiers
|
|
await fs.writeFile('./data/instructions.json', JSON.stringify(sampleInstructions, null, 2));
|
|
await fs.writeFile('./data/personalities.json', JSON.stringify(samplePersonalities, null, 2));
|
|
|
|
logSh('✅ Fichiers de données d\'exemple créés dans ./data/', "INFO");
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur création fichiers exemple: ${error.message}`, "ERROR");
|
|
}
|
|
}
|
|
|
|
// ============= EXPORTS NODE.JS =============
|
|
|
|
module.exports = {
|
|
getBrainConfig,
|
|
getPersonalities,
|
|
selectPersonalityWithAI,
|
|
selectMultiplePersonalitiesWithAI, // NOUVEAU: Export de la fonction multi-personnalités
|
|
selectRandomPersonalities,
|
|
parseCSVField,
|
|
readInstructionsData,
|
|
createSampleDataFiles,
|
|
createDefaultXMLTemplate,
|
|
CONFIG
|
|
};
|
|
|
|
// ============= TEST RAPIDE SI LANCÉ DIRECTEMENT =============
|
|
|
|
if (require.main === module) {
|
|
(async () => {
|
|
try {
|
|
logSh('🧪 Test BrainConfig Node.js...', "INFO");
|
|
|
|
// Créer fichiers exemple si nécessaire
|
|
try {
|
|
await fs.access('./data/instructions.json');
|
|
} catch {
|
|
await createSampleDataFiles();
|
|
}
|
|
|
|
// Test de la fonction principale
|
|
const result = await getBrainConfig(2);
|
|
|
|
if (result.success) {
|
|
logSh(`✅ Test réussi: ${result.data.personality.nom} pour ${result.data.mc0}`, "INFO");
|
|
} else {
|
|
logSh(`❌ Test échoué: ${result.error}`, "ERROR");
|
|
}
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur test: ${error.message}`, "ERROR");
|
|
}
|
|
})();
|
|
} |