smart-thinking-mcp
Version:
Un serveur MCP avancé pour le raisonnement multi-dimensionnel, adaptatif et collaboratif
416 lines • 18.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryManager = void 0;
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
/**
* Classe qui gère la mémoire persistante des sessions précédentes
*/
class MemoryManager {
memories = new Map();
knowledgeBase = new Map();
embeddingService;
// Chemins pour les fichiers de persistance
dataDir = path_1.default.join(process.cwd(), 'data');
memoriesDir = path_1.default.join(this.dataDir, 'memories');
knowledgeFilePath = path_1.default.join(this.dataDir, 'knowledge.json');
constructor(embeddingService) {
this.embeddingService = embeddingService;
// Charger les mémoires et la base de connaissances depuis le stockage persistant
this.loadFromStorage();
}
/**
* Génère un identifiant unique
*/
generateUniqueId() {
return Date.now().toString(36) + Math.random().toString(36).substring(2);
}
/**
* Assure que les répertoires de stockage nécessaires existent
*/
async ensureDirectoriesExist() {
try {
// Créer les répertoires data et memories s'ils n'existent pas
await fs_1.promises.mkdir(this.memoriesDir, { recursive: true });
console.error('Smart-Thinking: Répertoires de données créés ou confirmés');
}
catch (error) {
console.error('Smart-Thinking: Erreur lors de la création des répertoires:', error);
// Ne pas interrompre l'application si la création échoue
}
}
/**
* Charge les mémoires et la base de connaissances depuis le stockage persistant
*/
async loadFromStorage() {
try {
// S'assurer que les répertoires existent
await this.ensureDirectoriesExist();
// Essayer de charger les mémoires depuis le répertoire memories
const memoriesLoaded = await this.loadMemoriesFromFiles();
// Si aucune mémoire n'a été chargée, utiliser les exemples prédéfinis
if (!memoriesLoaded) {
const savedMemories = this.getSavedMemories();
if (savedMemories.length > 0) {
for (const memory of savedMemories) {
this.memories.set(memory.id, {
...memory,
timestamp: new Date(memory.timestamp)
});
}
}
}
// Essayer de charger la base de connaissances depuis le fichier knowledge.json
const knowledgeLoaded = await this.loadKnowledgeFromFile();
// Si la base de connaissances n'a pas été chargée, utiliser les exemples prédéfinis
if (!knowledgeLoaded) {
const savedKnowledge = this.getSavedKnowledge();
if (Object.keys(savedKnowledge).length > 0) {
for (const [key, value] of Object.entries(savedKnowledge)) {
this.knowledgeBase.set(key, value);
}
}
}
console.error('Smart-Thinking: Chargement des données terminé');
}
catch (error) {
console.error('Erreur lors du chargement de la mémoire:', error);
// En cas d'erreur, utiliser les données prédéfinies
const savedMemories = this.getSavedMemories();
if (savedMemories.length > 0) {
for (const memory of savedMemories) {
this.memories.set(memory.id, {
...memory,
timestamp: new Date(memory.timestamp)
});
}
}
const savedKnowledge = this.getSavedKnowledge();
if (Object.keys(savedKnowledge).length > 0) {
for (const [key, value] of Object.entries(savedKnowledge)) {
this.knowledgeBase.set(key, value);
}
}
}
}
/**
* Charge les mémoires depuis les fichiers dans le répertoire memories
* @returns true si au moins une mémoire a été chargée, false sinon
*/
async loadMemoriesFromFiles() {
try {
// Lire la liste des fichiers dans le répertoire memories
const files = await fs_1.promises.readdir(this.memoriesDir);
const jsonFiles = files.filter(file => file.endsWith('.json'));
if (jsonFiles.length === 0) {
return false;
}
let memoryLoaded = false;
// Charger chaque fichier JSON
for (const file of jsonFiles) {
try {
const filePath = path_1.default.join(this.memoriesDir, file);
const content = await fs_1.promises.readFile(filePath, 'utf8');
const memoryItems = JSON.parse(content);
// Traiter chaque élément de mémoire dans le fichier
for (const item of memoryItems) {
// Convertir la chaîne de date en objet Date
this.memories.set(item.id, {
...item,
timestamp: new Date(item.timestamp)
});
memoryLoaded = true;
}
}
catch (fileError) {
console.error(`Erreur lors du chargement du fichier ${file}:`, fileError);
// Continuer avec le fichier suivant
}
}
return memoryLoaded;
}
catch (error) {
console.error('Erreur lors du chargement des mémoires depuis les fichiers:', error);
return false;
}
}
/**
* Charge la base de connaissances depuis le fichier knowledge.json
* @returns true si la base de connaissances a été chargée, false sinon
*/
async loadKnowledgeFromFile() {
try {
// Vérifier si le fichier knowledge.json existe
const exists = await fs_1.promises.stat(this.knowledgeFilePath)
.then(() => true)
.catch(() => false);
if (!exists) {
return false;
}
// Lire le fichier knowledge.json
const content = await fs_1.promises.readFile(this.knowledgeFilePath, 'utf8');
const knowledge = JSON.parse(content);
// Traiter chaque entrée de la base de connaissances
for (const [key, value] of Object.entries(knowledge)) {
this.knowledgeBase.set(key, value);
}
return true;
}
catch (error) {
console.error('Erreur lors du chargement de la base de connaissances:', error);
return false;
}
}
/**
* Simule la récupération de mémoires sauvegardées
* Utilisé comme fallback si aucun fichier n'est disponible
*/
getSavedMemories() {
// Exemple de mémoires préconfigurées pour la démonstration
return [
{
id: 'mem1',
content: 'La structure en graphe est plus flexible que la structure linéaire pour représenter des pensées complexes.',
tags: ['structure', 'graphe', 'raisonnement'],
timestamp: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // 7 jours avant
},
{
id: 'mem2',
content: 'L\'auto-évaluation régulière du processus de raisonnement améliore sa qualité globale.',
tags: ['méta-cognition', 'évaluation', 'qualité'],
timestamp: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000) // 3 jours avant
},
{
id: 'mem3',
content: 'Intégrer des outils de recherche dans le processus de raisonnement permet une vérification factuelle en temps réel.',
tags: ['outils', 'recherche', 'vérification', 'faits'],
timestamp: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000) // 1 jour avant
}
];
}
/**
* Simule la récupération de connaissances sauvegardées
* Utilisé comme fallback si aucun fichier n'est disponible
*/
getSavedKnowledge() {
// Exemple de connaissances préconfigurées pour la démonstration
return {
'raisonnement-efficace': {
patterns: ['décomposition', 'analyse', 'synthèse', 'méta-cognition'],
critères: ['clarté', 'cohérence', 'précision', 'profondeur']
},
'biais-cognitifs': {
communs: ['confirmation', 'ancrage', 'disponibilité', 'halo'],
mitigations: ['diversité de perspectives', 'devil\'s advocate', 'données contraires']
}
};
}
/**
* Sauvegarde l'état actuel dans le stockage persistant
*/
async saveToStorage() {
try {
// S'assurer que les répertoires existent
await this.ensureDirectoriesExist();
// Préparer les mémoires par session pour la sauvegarde
const memoriesBySession = new Map();
// Regrouper les mémoires par session
for (const memory of this.memories.values()) {
// Déterminer l'ID de session (utiliser 'default' si non spécifié)
const sessionId = memory.metadata?.sessionId || 'default';
if (!memoriesBySession.has(sessionId)) {
memoriesBySession.set(sessionId, []);
}
memoriesBySession.get(sessionId).push(memory);
}
// Sauvegarder chaque groupe de mémoires dans un fichier par session
for (const [sessionId, memories] of memoriesBySession.entries()) {
const filePath = path_1.default.join(this.memoriesDir, `${sessionId}.json`);
await fs_1.promises.writeFile(filePath, JSON.stringify(memories, null, 2), 'utf8');
}
// Sauvegarder la base de connaissances
await fs_1.promises.writeFile(this.knowledgeFilePath, JSON.stringify(Object.fromEntries(this.knowledgeBase), null, 2), 'utf8');
console.error('Smart-Thinking: Données sauvegardées avec succès');
}
catch (error) {
console.error('Erreur lors de la sauvegarde de la mémoire:', error);
// En cas d'erreur, afficher les données qui auraient dû être sauvegardées (pour débogage)
console.error('Mémoires qui auraient dû être sauvegardées:', Array.from(this.memories.values()));
console.error('Base de connaissances qui aurait dû être sauvegardée:', Object.fromEntries(this.knowledgeBase));
}
}
/**
* Ajoute un élément à la mémoire
*
* @param content Le contenu de l'élément de mémoire
* @param tags Les tags associés
* @returns L'identifiant de l'élément ajouté
*/
addMemory(content, tags = []) {
const id = this.generateUniqueId();
const memory = {
id,
content,
tags,
timestamp: new Date()
};
this.memories.set(id, memory);
this.saveToStorage();
return id;
}
/**
* Récupère un élément de mémoire par son identifiant
*
* @param id L'identifiant de l'élément
* @returns L'élément de mémoire ou undefined si non trouvé
*/
getMemory(id) {
return this.memories.get(id);
}
/**
* Récupère les éléments de mémoire les plus récents
*
* @param limit Le nombre maximum d'éléments à récupérer
* @returns Un tableau des éléments les plus récents
*/
getRecentMemories(limit = 5) {
return Array.from(this.memories.values())
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
.slice(0, limit);
}
/**
* Récupère les éléments de mémoire les plus pertinents pour un contexte donné
* AMÉLIORÉ: Fiabilité améliorée et meilleure gestion des erreurs
*
* @param context Le contexte pour lequel chercher des éléments pertinents
* @param limit Le nombre maximum d'éléments à récupérer
* @returns Un tableau des éléments les plus pertinents
*/
async getRelevantMemories(context, limit = 3) {
const allMemories = Array.from(this.memories.values());
// Vérifier si nous avons des mémoires à traiter
if (allMemories.length === 0) {
console.error('Smart-Thinking: Aucune mémoire disponible pour la recherche de pertinence');
return [];
}
// Si nous n'avons pas de service d'embeddings, utiliser l'algorithme de base
if (!this.embeddingService) {
console.error('Smart-Thinking: Service d\'embeddings non disponible, utilisation de l\'algorithme de mots-clés');
return this.getRelevantMemoriesWithKeywords(context, limit);
}
try {
// Utiliser le service d'embeddings pour trouver les mémoires similaires
const memoryTexts = allMemories.map(memory => memory.content);
console.error(`Smart-Thinking: Recherche de mémoires pertinentes parmi ${memoryTexts.length} éléments avec embeddings`);
// Utiliser un seuil de similarité plus bas pour augmenter les chances de trouver des correspondances
const threshold = 0.3; // Seuil de similarité plus bas
const similarResults = await this.embeddingService.findSimilarTexts(context, memoryTexts, limit, threshold);
console.error(`Smart-Thinking: ${similarResults.length} résultats similaires trouvés avec embeddings`);
if (similarResults.length === 0) {
// Aucun résultat avec embeddings, essayer avec mots-clés
console.error('Smart-Thinking: Aucun résultat avec embeddings, utilisation des mots-clés');
return this.getRelevantMemoriesWithKeywords(context, limit);
}
// Convertir les résultats en mémoires
const memoryResults = [];
for (const result of similarResults) {
const matchingMemory = allMemories.find(memory => memory.content === result.text);
if (matchingMemory) {
// Créer une copie avec le score de pertinence
memoryResults.push({
...matchingMemory,
relevanceScore: result.score
});
console.error(`Smart-Thinking: Mémoire correspondante trouvée avec score ${result.score.toFixed(2)}`);
}
}
return memoryResults;
}
catch (error) {
console.error('Smart-Thinking: Erreur lors de la recherche de mémoires pertinentes avec embeddings:', error);
// En cas d'erreur, revenir à l'algorithme basé sur les mots-clés
return this.getRelevantMemoriesWithKeywords(context, limit);
}
}
/**
* Implémentation de secours basée sur les mots-clés
*
* @param context Le contexte pour lequel chercher des éléments pertinents
* @param limit Le nombre maximum d'éléments à récupérer
* @returns Un tableau des éléments les plus pertinents
*/
getRelevantMemoriesWithKeywords(context, limit = 3) {
// Une implémentation simple basée sur la correspondance de mots-clés
const contextWords = context.toLowerCase().split(/\W+/).filter(word => word.length > 3);
return Array.from(this.memories.values())
.map(memory => {
const memoryWords = memory.content.toLowerCase().split(/\W+/).filter(word => word.length > 3);
const memoryTagWords = memory.tags.join(' ').toLowerCase().split(/\W+/).filter(word => word.length > 3);
// Calculer un score basé sur le contenu et les tags
const contentMatchingWords = contextWords.filter(word => memoryWords.includes(word));
const tagMatchingWords = contextWords.filter(word => memoryTagWords.includes(word));
const contentScore = contentMatchingWords.length / Math.max(contextWords.length, 1);
const tagScore = tagMatchingWords.length / Math.max(contextWords.length, 1);
// Les tags ont un poids plus élevé
const score = contentScore * 0.7 + tagScore * 1.3;
return {
memory,
score,
relevanceScore: score
};
})
.sort((a, b) => b.score - a.score)
.slice(0, limit)
.map(item => {
return {
...item.memory,
relevanceScore: item.relevanceScore
};
});
}
/**
* Récupère les éléments de mémoire par tag
*
* @param tag Le tag à rechercher
* @param limit Le nombre maximum d'éléments à récupérer
* @returns Un tableau des éléments correspondant au tag
*/
getMemoriesByTag(tag, limit = 10) {
return Array.from(this.memories.values())
.filter(memory => memory.tags.includes(tag))
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
.slice(0, limit);
}
/**
* Ajoute ou met à jour un élément dans la base de connaissances
*
* @param key La clé de l'élément
* @param value La valeur de l'élément
*/
setKnowledge(key, value) {
this.knowledgeBase.set(key, value);
this.saveToStorage();
}
/**
* Récupère un élément de la base de connaissances
*
* @param key La clé de l'élément
* @returns La valeur de l'élément ou undefined si non trouvé
*/
getKnowledge(key) {
return this.knowledgeBase.get(key);
}
/**
* Efface toutes les mémoires et connaissances
*/
clear() {
this.memories.clear();
this.knowledgeBase.clear();
this.saveToStorage();
}
}
exports.MemoryManager = MemoryManager;
//# sourceMappingURL=memory-manager.js.map