148 lines
4.1 KiB
JavaScript
148 lines
4.1 KiB
JavaScript
/**
|
||
* Очередь для AI запросов с приоритизацией
|
||
*/
|
||
|
||
const EventEmitter = require('events');
|
||
const logger = require('../utils/logger');
|
||
|
||
class AIQueue extends EventEmitter {
|
||
constructor() {
|
||
super();
|
||
this.queue = [];
|
||
this.processing = false;
|
||
this.maxConcurrent = 2; // Максимум 2 запроса одновременно
|
||
this.activeRequests = 0;
|
||
this.stats = {
|
||
total: 0,
|
||
completed: 0,
|
||
failed: 0,
|
||
avgResponseTime: 0
|
||
};
|
||
}
|
||
|
||
// Добавление запроса в очередь
|
||
async addRequest(request, priority = 0) {
|
||
const queueItem = {
|
||
id: Date.now() + Math.random(),
|
||
request,
|
||
priority,
|
||
timestamp: Date.now(),
|
||
status: 'pending'
|
||
};
|
||
|
||
this.queue.push(queueItem);
|
||
this.queue.sort((a, b) => b.priority - a.priority); // Сортировка по приоритету
|
||
|
||
this.stats.total++;
|
||
logger.info(`[AIQueue] Added request ${queueItem.id} with priority ${priority}`);
|
||
|
||
// Запускаем обработку если не запущена
|
||
if (!this.processing) {
|
||
this.processQueue();
|
||
}
|
||
|
||
return queueItem.id;
|
||
}
|
||
|
||
// Обработка очереди
|
||
async processQueue() {
|
||
if (this.processing || this.activeRequests >= this.maxConcurrent) {
|
||
return;
|
||
}
|
||
|
||
this.processing = true;
|
||
|
||
while (this.queue.length > 0 && this.activeRequests < this.maxConcurrent) {
|
||
const item = this.queue.shift();
|
||
if (!item) continue;
|
||
|
||
this.activeRequests++;
|
||
item.status = 'processing';
|
||
|
||
try {
|
||
const startTime = Date.now();
|
||
const result = await this.processRequest(item.request);
|
||
const responseTime = Date.now() - startTime;
|
||
|
||
item.status = 'completed';
|
||
item.result = result;
|
||
item.responseTime = responseTime;
|
||
|
||
this.stats.completed++;
|
||
this.updateAvgResponseTime(responseTime);
|
||
|
||
logger.info(`[AIQueue] Request ${item.id} completed in ${responseTime}ms`);
|
||
|
||
// Эмитим событие о завершении
|
||
this.emit('completed', item);
|
||
|
||
} catch (error) {
|
||
item.status = 'failed';
|
||
item.error = error.message;
|
||
|
||
this.stats.failed++;
|
||
logger.error(`[AIQueue] Request ${item.id} failed:`, error.message);
|
||
|
||
// Эмитим событие об ошибке
|
||
this.emit('failed', item);
|
||
} finally {
|
||
this.activeRequests--;
|
||
}
|
||
}
|
||
|
||
this.processing = false;
|
||
|
||
// Если в очереди еще есть запросы, продолжаем обработку
|
||
if (this.queue.length > 0) {
|
||
setTimeout(() => this.processQueue(), 100);
|
||
}
|
||
}
|
||
|
||
// Обработка одного запроса
|
||
async processRequest(request) {
|
||
// Прямой вызов AI без очереди
|
||
const aiAssistant = require('./ai-assistant');
|
||
|
||
// Используем прямой метод без очереди
|
||
const messages = [];
|
||
if (request.systemPrompt) {
|
||
messages.push({ role: 'system', content: request.systemPrompt });
|
||
}
|
||
if (request.history && Array.isArray(request.history)) {
|
||
for (const msg of request.history) {
|
||
if (msg.role && msg.content) {
|
||
messages.push({ role: msg.role, content: msg.content });
|
||
}
|
||
}
|
||
}
|
||
messages.push({ role: 'user', content: request.message });
|
||
|
||
// Прямой вызов API без очереди
|
||
return await aiAssistant.fallbackRequestOpenAI(messages, request.language, request.systemPrompt);
|
||
}
|
||
|
||
// Обновление средней скорости ответа
|
||
updateAvgResponseTime(responseTime) {
|
||
const total = this.stats.completed;
|
||
this.stats.avgResponseTime =
|
||
(this.stats.avgResponseTime * (total - 1) + responseTime) / total;
|
||
}
|
||
|
||
// Получение статистики
|
||
getStats() {
|
||
return {
|
||
...this.stats,
|
||
queueLength: this.queue.length,
|
||
activeRequests: this.activeRequests,
|
||
processing: this.processing
|
||
};
|
||
}
|
||
|
||
// Очистка очереди
|
||
clear() {
|
||
this.queue = [];
|
||
logger.info('[AIQueue] Queue cleared');
|
||
}
|
||
}
|
||
|
||
module.exports = new AIQueue();
|