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
JavaScript
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;
}
}