feat: новая функция

This commit is contained in:
2025-10-09 16:48:20 +03:00
parent dd2c9988a5
commit 13fb51e447
60 changed files with 7694 additions and 1157 deletions

View File

@@ -4,12 +4,16 @@
const crypto = require('crypto');
const logger = require('../utils/logger');
const ollamaConfig = require('./ollamaConfig');
class AICache {
constructor() {
const timeouts = ollamaConfig.getTimeouts();
this.cache = new Map();
this.maxSize = 1000; // Максимальное количество кэшированных ответов
this.ttl = 24 * 60 * 60 * 1000; // 24 часа в миллисекундах
this.maxSize = timeouts.cacheMax; // Из централизованных настроек
this.ttl = timeouts.cacheLLM; // 24 часа (для LLM)
this.ragTtl = timeouts.cacheRAG; // 5 минут (для RAG результатов)
}
// Генерация ключа кэша на основе запроса
@@ -22,6 +26,12 @@ class AICache {
return crypto.createHash('md5').update(content).digest('hex');
}
// ✨ НОВОЕ: Генерация ключа для RAG результатов
generateKeyForRAG(tableId, userQuestion, product = null) {
const content = JSON.stringify({ tableId, userQuestion, product });
return crypto.createHash('md5').update(content).digest('hex');
}
// Получение ответа из кэша
get(key) {
const cached = this.cache.get(key);
@@ -37,6 +47,24 @@ class AICache {
return cached.response;
}
// ✨ НОВОЕ: Получение с учетом типа кэша (RAG или LLM)
getWithTTL(key, type = 'llm') {
const cached = this.cache.get(key);
if (!cached) return null;
// Выбираем TTL в зависимости от типа
const ttl = type === 'rag' ? this.ragTtl : this.ttl;
// Проверяем TTL
if (Date.now() - cached.timestamp > ttl) {
this.cache.delete(key);
return null;
}
logger.info(`[AICache] Cache hit (${type}) for key: ${key.substring(0, 8)}...`);
return cached.response;
}
// Сохранение ответа в кэш
set(key, response) {
// Очищаем старые записи если кэш переполнен
@@ -53,6 +81,23 @@ class AICache {
logger.info(`[AICache] Cached response for key: ${key.substring(0, 8)}...`);
}
// ✨ НОВОЕ: Сохранение с указанием типа (rag или llm)
setWithType(key, response, type = 'llm') {
// Очищаем старые записи если кэш переполнен
if (this.cache.size >= this.maxSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, {
response,
timestamp: Date.now(),
type: type // Сохраняем тип для статистики
});
logger.info(`[AICache] Cached ${type} response for key: ${key.substring(0, 8)}...`);
}
// Очистка кэша
clear() {
this.cache.clear();
@@ -90,6 +135,31 @@ class AICache {
if (this.maxSize === 0) return 0;
return this.cache.size / this.maxSize;
}
// ✨ НОВОЕ: Статистика по типу кэша
getStatsByType() {
const stats = { rag: 0, llm: 0, other: 0 };
for (const [key, value] of this.cache.entries()) {
const type = value.type || 'other';
stats[type] = (stats[type] || 0) + 1;
}
return stats;
}
// ✨ НОВОЕ: Инвалидация по префиксу (для очистки RAG кэша при обновлении таблиц)
invalidateByPrefix(prefix) {
let deletedCount = 0;
for (const [key, value] of this.cache.entries()) {
if (key.startsWith(prefix)) {
this.cache.delete(key);
deletedCount++;
}
}
if (deletedCount > 0) {
logger.info(`[AICache] Инвалидировано ${deletedCount} записей с префиксом: ${prefix}`);
}
return deletedCount;
}
}
module.exports = new AICache();