UNPKG

ollama-code-qwen

Version:

Un assistant IA en ligne de commande utilisant Ollama et le modèle qwen2.5-coder pour aider au développement, avec des capacités MCP améliorées et détection d'intentions en français et anglais

243 lines (208 loc) 6.65 kB
import fs from 'fs/promises'; import path from 'path'; import os from 'os'; import crypto from 'crypto'; /** * Service de cache pour améliorer les performances */ export class CacheService { /** * Initialise le service de cache * @param {Object} options - Options du cache */ constructor(options = {}) { this.cacheDir = options.cacheDir || path.join(os.homedir(), '.cache', 'ollama-code'); this.ttl = options.ttl || 3600000; // Durée de vie par défaut: 1 heure this.enabled = options.enabled !== undefined ? options.enabled : true; // Mémoire cache pour les accès fréquents this.memoryCache = new Map(); } /** * Initialise le cache */ async init() { try { await fs.mkdir(this.cacheDir, { recursive: true }); } catch (error) { console.error(`Failed to create cache directory: ${error.message}`); this.enabled = false; } } /** * Génère une clé de cache basée sur les paramètres * @param {string} category - Catégorie du cache * @param {Object} params - Paramètres à hacher * @returns {string} - Clé de cache */ _generateKey(category, params) { const hash = crypto .createHash('md5') .update(JSON.stringify(params)) .digest('hex'); return `${category}_${hash}`; } /** * Chemin complet vers un fichier cache * @param {string} key - Clé de cache * @returns {string} - Chemin complet */ _getCachePath(key) { return path.join(this.cacheDir, `${key}.json`); } /** * Vérifie si un élément est dans le cache et valide * @param {string} category - Catégorie du cache * @param {Object} params - Paramètres qui identifient l'élément * @returns {Promise<Object|null>} - Élément mis en cache ou null */ async get(category, params) { if (!this.enabled) return null; const key = this._generateKey(category, params); // Vérifier d'abord dans le cache mémoire if (this.memoryCache.has(key)) { const entry = this.memoryCache.get(key); if (entry.expires > Date.now()) { return entry.data; } // Expiré, supprimer de la mémoire this.memoryCache.delete(key); } // Vérifier dans le cache disque try { const cachePath = this._getCachePath(key); const content = await fs.readFile(cachePath, 'utf-8'); const entry = JSON.parse(content); if (entry.expires > Date.now()) { // Ajouter également à la mémoire cache this.memoryCache.set(key, entry); return entry.data; } // Expiré, supprimer du disque await fs.unlink(cachePath); return null; } catch (error) { // Fichier non trouvé ou autre erreur return null; } } /** * Met un élément dans le cache * @param {string} category - Catégorie du cache * @param {Object} params - Paramètres qui identifient l'élément * @param {*} data - Données à mettre en cache * @param {number} ttl - Durée de vie en millisecondes * @returns {Promise<boolean>} - Succès ou échec */ async set(category, params, data, ttl = null) { if (!this.enabled) return false; const key = this._generateKey(category, params); const now = Date.now(); const expires = now + (ttl || this.ttl); const entry = { data, created: now, expires, category, params }; // Mettre à jour la mémoire cache this.memoryCache.set(key, entry); // Écrire sur le disque try { const cachePath = this._getCachePath(key); await fs.writeFile(cachePath, JSON.stringify(entry), 'utf-8'); return true; } catch (error) { console.error(`Failed to write to cache: ${error.message}`); return false; } } /** * Supprime un élément du cache * @param {string} category - Catégorie du cache * @param {Object} params - Paramètres qui identifient l'élément * @returns {Promise<boolean>} - Succès ou échec */ async invalidate(category, params) { if (!this.enabled) return false; const key = this._generateKey(category, params); // Supprimer de la mémoire cache this.memoryCache.delete(key); // Supprimer du disque try { const cachePath = this._getCachePath(key); await fs.unlink(cachePath); return true; } catch (error) { // Le fichier n'existe peut-être pas, ce n'est pas grave return false; } } /** * Supprime tous les éléments d'une catégorie * @param {string} category - Catégorie à vider * @returns {Promise<number>} - Nombre d'éléments supprimés */ async invalidateCategory(category) { if (!this.enabled) return 0; let count = 0; // Supprimer de la mémoire cache for (const [key, entry] of this.memoryCache.entries()) { if (entry.category === category) { this.memoryCache.delete(key); count++; } } // Supprimer du disque try { const files = await fs.readdir(this.cacheDir); for (const file of files) { if (file.startsWith(`${category}_`) && file.endsWith('.json')) { await fs.unlink(path.join(this.cacheDir, file)); count++; } } } catch (error) { console.error(`Failed to invalidate category: ${error.message}`); } return count; } /** * Nettoie le cache en supprimant les entrées expirées * @returns {Promise<number>} - Nombre d'éléments supprimés */ async cleanup() { if (!this.enabled) return 0; let count = 0; const now = Date.now(); // Nettoyer la mémoire cache for (const [key, entry] of this.memoryCache.entries()) { if (entry.expires <= now) { this.memoryCache.delete(key); count++; } } // Nettoyer le cache disque try { const files = await fs.readdir(this.cacheDir); for (const file of files) { if (file.endsWith('.json')) { const cachePath = path.join(this.cacheDir, file); try { const content = await fs.readFile(cachePath, 'utf-8'); const entry = JSON.parse(content); if (entry.expires <= now) { await fs.unlink(cachePath); count++; } } catch (error) { // Ignorer les erreurs par fichier } } } } catch (error) { console.error(`Failed to cleanup cache: ${error.message}`); } return count; } }