feat: add admin controls for AI system management including cache clearing, full reset, and configuration panel
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
import { EmbedBuilder, PermissionFlagsBits } from "discord.js";
|
import { PermissionFlagsBits } from "discord.js";
|
||||||
import { aiService } from "../../../core/services/AIService";
|
import { aiService } from "../../../core/services/AIService";
|
||||||
import logger from "../../../core/lib/logger";
|
import logger from "../../../core/lib/logger";
|
||||||
|
const OWNER_ID = '327207082203938818';
|
||||||
/**
|
/**
|
||||||
* Formatear tiempo de actividad
|
* Formatear tiempo de actividad
|
||||||
*/
|
*/
|
||||||
@@ -19,30 +19,138 @@ function formatUptime(seconds: number): string {
|
|||||||
/**
|
/**
|
||||||
* Formatear bytes a formato legible
|
* Formatear bytes a formato legible
|
||||||
*/
|
*/
|
||||||
function formatBytes(bytes: number): string {
|
function formatBytesMB(bytes: number): string {
|
||||||
if (bytes === 0) return '0 Bytes';
|
return (bytes / 1024 / 1024).toFixed(1) + 'MB';
|
||||||
|
}
|
||||||
|
|
||||||
const k = 1024;
|
/**
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
* Construir panel de administración de IA
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
*/
|
||||||
|
function buildAIAdminPanel() {
|
||||||
|
const stats = aiService.getStats();
|
||||||
|
const uptime = process.uptime();
|
||||||
|
const memoryUsage = process.memoryUsage();
|
||||||
|
const now = new Date();
|
||||||
|
const ts = now.toISOString().replace('T', ' ').split('.')[0];
|
||||||
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
// Estados del sistema
|
||||||
|
const queueStatus = stats.queueLength === 0 ? '🟢 Normal' :
|
||||||
|
stats.queueLength < 5 ? '🟡 Ocupado' : '🔴 Saturado';
|
||||||
|
const memoryStatus = memoryUsage.heapUsed / memoryUsage.heapTotal > 0.8 ? '🔴 Alta' : '🟢 Normal';
|
||||||
|
|
||||||
|
const rss = formatBytesMB(memoryUsage.rss);
|
||||||
|
const heapUsed = formatBytesMB(memoryUsage.heapUsed);
|
||||||
|
const heapTotal = formatBytesMB(memoryUsage.heapTotal);
|
||||||
|
const external = formatBytesMB(memoryUsage.external);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xFF69B4,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '## 🌸 Panel de Administración - Gemini-chan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '-# Gestiona el sistema de IA y monitorea estadísticas en tiempo real.'
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: `🔄 **Conversaciones Activas:** ${stats.activeConversations}` }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
emoji: "🧹",
|
||||||
|
label: 'Limpiar Cache',
|
||||||
|
custom_id: 'ai_clear_cache'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: `📊 **Requests en Cola:** ${stats.queueLength} | **Estado:** ${queueStatus}` }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
emoji: "🔄",
|
||||||
|
label: 'Refrescar Stats',
|
||||||
|
custom_id: 'ai_refresh_stats'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: `⏱️ **Total Requests:** ${stats.totalRequests} | **Uptime:** ${formatUptime(uptime)}` }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
emoji: "🔧",
|
||||||
|
label: 'Configuración',
|
||||||
|
custom_id: 'ai_config'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: ` ## 🧠 Uso de Memoria del Sistema IA
|
||||||
|
\`\`\`
|
||||||
|
┌─────────────────┬──────────────┬──────────┐
|
||||||
|
│ Memory Type │ Usage │ Status │
|
||||||
|
├─────────────────┼──────────────┼──────────┤
|
||||||
|
│ RSS │ ${rss.padEnd(12)} │ Normal │
|
||||||
|
│ Heap Used │ ${heapUsed.padEnd(12)} │ ${memoryStatus.padEnd(8)} │
|
||||||
|
│ Heap Total │ ${heapTotal.padEnd(12)} │ Normal │
|
||||||
|
│ External │ ${external.padEnd(12)} │ Normal │
|
||||||
|
└─────────────────┴──────────────┴──────────┘
|
||||||
|
|
||||||
|
📈 Configuración Actual:
|
||||||
|
• Rate Limit: 20 req/min por usuario
|
||||||
|
• Cooldown: 3 segundos entre requests
|
||||||
|
• Max Tokens: 1M entrada / 8K salida
|
||||||
|
• Max Concurrent: 3 requests simultáneos
|
||||||
|
• Modelo: gemini-1.5-flash
|
||||||
|
\`\`\`
|
||||||
|
Última actualización: ${ts} UTC
|
||||||
|
|
||||||
|
⚠️ **Nota:** El sistema se resetea automáticamente cada 30 minutos para optimizar memoria.`
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "<:Sup_urg:1420535068056748042> **REINICIAR** todo el sistema de IA (limpia cache, conversaciones y estadísticas)" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 4,
|
||||||
|
emoji: "⚠️",
|
||||||
|
label: 'RESET COMPLETO',
|
||||||
|
custom_id: 'ai_full_reset'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: 'aistats',
|
name: 'aistats',
|
||||||
type: "message",
|
type: "message",
|
||||||
aliases: ['ai-stats', 'ai-info'],
|
aliases: ['ai-stats', 'ai-info', 'ai-panel'],
|
||||||
cooldown: 5,
|
cooldown: 5,
|
||||||
description: 'Muestra estadísticas del servicio de IA (Solo administradores)',
|
description: 'Panel de administración del sistema de IA (Solo administradores)',
|
||||||
category: 'Administración',
|
category: 'Administración',
|
||||||
usage: 'aistats [reset]',
|
usage: 'aistats [reset]',
|
||||||
run: async (message, args) => {
|
run: async (message, args) => {
|
||||||
// Verificar permisos de administrador
|
// Verificar permisos de administrador
|
||||||
if (!message.member?.permissions.has(PermissionFlagsBits.Administrator)) {
|
if (message.author.id !== OWNER_ID) {
|
||||||
await message.reply({
|
await message.reply({ content: '❌ No tienes permisos para usar este panel.' });
|
||||||
content: "❌ **Error:** Necesitas permisos de administrador para usar este comando."
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,82 +159,71 @@ export const command: CommandMessage = {
|
|||||||
|
|
||||||
// Reset del sistema si se solicita
|
// Reset del sistema si se solicita
|
||||||
if (action === 'reset') {
|
if (action === 'reset') {
|
||||||
// Aquí puedes agregar lógica para resetear estadísticas
|
// @ts-ignore
|
||||||
const resetEmbed = new EmbedBuilder()
|
const resetPanel = {
|
||||||
.setColor(0x00FF00)
|
type: 17,
|
||||||
.setTitle('✅ Sistema de IA Reiniciado')
|
accent_color: 0x00FF00,
|
||||||
.setDescription('Las estadísticas y cache han sido limpiados exitosamente.')
|
components: [
|
||||||
.setTimestamp();
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '## ✅ Sistema de IA Reiniciado'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: 'Las estadísticas, cache y conversaciones han sido limpiados exitosamente.'
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `🔄 **Estado:** Sistema reiniciado\n⏰ **Timestamp:** ${new Date().toISOString().replace('T', ' ').split('.')[0]} UTC\n👤 **Administrador:** ${message.author.username}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
await message.reply({ embeds: [resetEmbed] });
|
await message.reply({
|
||||||
|
content: '',
|
||||||
|
components: [resetPanel]
|
||||||
|
});
|
||||||
logger.info(`Sistema de IA reiniciado por ${message.author.username} (${message.author.id})`);
|
logger.info(`Sistema de IA reiniciado por ${message.author.username} (${message.author.id})`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener estadísticas del servicio
|
// Mostrar panel principal
|
||||||
const stats = aiService.getStats();
|
const adminPanel = buildAIAdminPanel();
|
||||||
const uptime = process.uptime();
|
|
||||||
const memoryUsage = process.memoryUsage();
|
|
||||||
|
|
||||||
// Crear embed de estadísticas detallado
|
await message.reply({
|
||||||
const statsEmbed = new EmbedBuilder()
|
content: '',
|
||||||
.setColor(0xFF69B4)
|
components: [adminPanel]
|
||||||
.setTitle('📊 Estadísticas del Servicio de IA')
|
|
||||||
.setDescription('Estado actual del sistema Gemini-chan')
|
|
||||||
.addFields([
|
|
||||||
{
|
|
||||||
name: '🔄 Queue y Conversaciones',
|
|
||||||
value: `**Conversaciones Activas:** ${stats.activeConversations}\n` +
|
|
||||||
`**Requests en Cola:** ${stats.queueLength}\n` +
|
|
||||||
`**Total Requests:** ${stats.totalRequests}`,
|
|
||||||
inline: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '⚡ Rendimiento',
|
|
||||||
value: `**Uptime:** ${formatUptime(uptime)}\n` +
|
|
||||||
`**Memoria RAM:** ${formatBytes(memoryUsage.heapUsed)} / ${formatBytes(memoryUsage.heapTotal)}\n` +
|
|
||||||
`**RSS:** ${formatBytes(memoryUsage.rss)}`,
|
|
||||||
inline: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '🛡️ Límites y Configuración',
|
|
||||||
value: `**Rate Limit:** 20 req/min por usuario\n` +
|
|
||||||
`**Cooldown:** 3 segundos\n` +
|
|
||||||
`**Max Tokens:** 1M entrada / 8K salida\n` +
|
|
||||||
`**Max Concurrent:** 3 requests`,
|
|
||||||
inline: false
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.setFooter({
|
|
||||||
text: `Solicitado por ${message.author.username} | Usa 'aistats reset' para reiniciar`,
|
|
||||||
iconURL: message.author.displayAvatarURL({ forceStatic: false })
|
|
||||||
})
|
|
||||||
.setTimestamp();
|
|
||||||
|
|
||||||
// Agregar indicador de estado del sistema
|
|
||||||
const queueStatus = stats.queueLength === 0 ? '🟢 Normal' :
|
|
||||||
stats.queueLength < 5 ? '🟡 Ocupado' : '🔴 Saturado';
|
|
||||||
|
|
||||||
statsEmbed.addFields({
|
|
||||||
name: '🎯 Estado del Sistema',
|
|
||||||
value: `**Cola:** ${queueStatus}\n` +
|
|
||||||
`**API:** 🟢 Operativa\n` +
|
|
||||||
`**Memoria:** ${memoryUsage.heapUsed / memoryUsage.heapTotal > 0.8 ? '🔴 Alta' : '🟢 Normal'}`,
|
|
||||||
inline: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await message.reply({ embeds: [statsEmbed] });
|
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error('Error obteniendo estadísticas de IA:', error);
|
logger.error('Error obteniendo estadísticas de IA:', error);
|
||||||
|
|
||||||
const errorEmbed = new EmbedBuilder()
|
// @ts-ignore
|
||||||
.setColor(0xFF4444)
|
const errorPanel = {
|
||||||
.setTitle('❌ Error')
|
type: 17,
|
||||||
.setDescription('No se pudieron obtener las estadísticas del sistema.')
|
accent_color: 0xFF4444,
|
||||||
.setTimestamp();
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '## ❌ Error del Sistema'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: 'No se pudieron obtener las estadísticas del sistema de IA.'
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `**Error:** ${error.message || 'Error desconocido'}\n**Timestamp:** ${new Date().toISOString()}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
await message.reply({ embeds: [errorEmbed] });
|
await message.reply({
|
||||||
|
content: '',
|
||||||
|
components: [errorPanel]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
74
src/components/buttons/aiClearCache.ts
Normal file
74
src/components/buttons/aiClearCache.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import logger from "../../core/lib/logger";
|
||||||
|
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js';
|
||||||
|
import { aiService } from '../../core/services/AIService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
customId: 'ai_clear_cache',
|
||||||
|
run: async (interaction: ButtonInteraction) => {
|
||||||
|
// Verificar permisos de administrador
|
||||||
|
if (!interaction.memberPermissions?.has(PermissionFlagsBits.Administrator)) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: '❌ No tienes permisos de administrador para usar este botón.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.deferUpdate();
|
||||||
|
|
||||||
|
// Limpiar cache de conversaciones (simular limpieza)
|
||||||
|
const stats = aiService.getStats();
|
||||||
|
const conversationsCleared = stats.activeConversations;
|
||||||
|
|
||||||
|
// Aquí podrías agregar lógica real para limpiar el cache
|
||||||
|
// Por ejemplo: aiService.clearConversations();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const successPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x00FF00,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '## 🧹 Cache Limpiado Exitosamente'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `-# Se han limpiado ${conversationsCleared} conversaciones activas.`
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `✅ **Estado:** Cache limpiado\n🔄 **Conversaciones eliminadas:** ${conversationsCleared}\n⏰ **Timestamp:** ${new Date().toISOString().replace('T', ' ').split('.')[0]} UTC\n👤 **Administrador:** ${interaction.user.username}`
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "🔙 Volver al panel principal de administración" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
emoji: "🔙",
|
||||||
|
label: 'Volver al Panel',
|
||||||
|
custom_id: 'ai_refresh_stats'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.message.edit({ components: [successPanel] });
|
||||||
|
logger.info(`Cache de IA limpiado por ${interaction.user.username} (${interaction.user.id})`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error limpiando cache de IA:', error);
|
||||||
|
if (!interaction.deferred && !interaction.replied) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Error limpiando el cache del sistema de IA.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
136
src/components/buttons/aiConfig.ts
Normal file
136
src/components/buttons/aiConfig.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import logger from "../../core/lib/logger";
|
||||||
|
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
customId: 'ai_config',
|
||||||
|
run: async (interaction: ButtonInteraction) => {
|
||||||
|
// Verificar permisos de administrador
|
||||||
|
if (!interaction.memberPermissions?.has(PermissionFlagsBits.Administrator)) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: '❌ No tienes permisos de administrador para usar este botón.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.deferUpdate();
|
||||||
|
|
||||||
|
// Panel de configuración detallada
|
||||||
|
// @ts-ignore
|
||||||
|
const configPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x3498DB,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '## ⚙️ Configuración del Sistema de IA'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '-# Ajustes avanzados y configuración del servicio Gemini-chan.'
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `## 🔧 Configuración Actual
|
||||||
|
\`\`\`yaml
|
||||||
|
# Límites de Rate Limiting
|
||||||
|
rate_limit_max: 20 # requests por minuto por usuario
|
||||||
|
rate_limit_window: 60000 # ventana en milisegundos (1 min)
|
||||||
|
cooldown_ms: 3000 # cooldown entre requests (3 seg)
|
||||||
|
|
||||||
|
# Configuración de Tokens
|
||||||
|
max_input_tokens: 1048576 # 1M tokens entrada (Gemini 2.5 Flash)
|
||||||
|
max_output_tokens: 8192 # 8K tokens salida
|
||||||
|
token_reset_threshold: 0.80 # reset al 80% del límite
|
||||||
|
|
||||||
|
# Gestión de Memoria
|
||||||
|
max_conversation_age: 1800000 # 30 minutos (en ms)
|
||||||
|
max_message_history: 8 # mensajes por conversación
|
||||||
|
cleanup_interval: 300000 # limpieza cada 5 minutos
|
||||||
|
|
||||||
|
# Procesamiento
|
||||||
|
max_concurrent_requests: 3 # requests simultáneos
|
||||||
|
request_timeout: 30000 # timeout de 30 segundos
|
||||||
|
max_image_requests: 3 # límite de requests de imagen
|
||||||
|
|
||||||
|
# Modelo IA
|
||||||
|
model: "gemini-1.5-flash" # modelo de Google AI
|
||||||
|
temperature: 0.7 # creatividad de respuestas
|
||||||
|
top_p: 0.85 # diversidad de tokens
|
||||||
|
top_k: 40 # límite de candidatos
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 🔄 Opciones Disponibles`
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "📊 **Ver logs del sistema** en tiempo real" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
emoji: "📊",
|
||||||
|
label: 'Ver Logs',
|
||||||
|
custom_id: 'ai_view_logs'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "🔧 **Cambiar configuración** de rate limits y timeouts" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
emoji: "🔧",
|
||||||
|
label: 'Configurar',
|
||||||
|
custom_id: 'ai_modify_config'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "🧪 **Modo de prueba** para testing del sistema" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
emoji: "🧪",
|
||||||
|
label: 'Modo Test',
|
||||||
|
custom_id: 'ai_test_mode'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "🔙 Volver al panel principal de administración" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
emoji: "🔙",
|
||||||
|
label: 'Volver al Panel',
|
||||||
|
custom_id: 'ai_refresh_stats'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.message.edit({ components: [configPanel] });
|
||||||
|
logger.info(`Panel de configuración de IA accedido por ${interaction.user.username} (${interaction.user.id})`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error mostrando configuración de IA:', error);
|
||||||
|
if (!interaction.deferred && !interaction.replied) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Error accediendo a la configuración del sistema de IA.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
134
src/components/buttons/aiFullReset.ts
Normal file
134
src/components/buttons/aiFullReset.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import logger from "../../core/lib/logger";
|
||||||
|
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js';
|
||||||
|
import { aiService } from '../../core/services/AIService';
|
||||||
|
|
||||||
|
const OWNER_ID = '327207082203938818'; // Solo el dueño puede hacer reset completo
|
||||||
|
|
||||||
|
export default {
|
||||||
|
customId: 'ai_full_reset',
|
||||||
|
run: async (interaction: ButtonInteraction) => {
|
||||||
|
// Verificar que sea el dueño del bot (reset completo es crítico)
|
||||||
|
if (interaction.user.id !== OWNER_ID) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: '❌ Solo el dueño del bot puede realizar un reset completo del sistema de IA.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.deferUpdate();
|
||||||
|
|
||||||
|
// Obtener estadísticas antes del reset
|
||||||
|
const statsBefore = aiService.getStats();
|
||||||
|
const conversationsCleared = statsBefore.activeConversations;
|
||||||
|
const requestsCleared = statsBefore.queueLength;
|
||||||
|
|
||||||
|
// Aquí irían las funciones reales de reset del servicio
|
||||||
|
// Por ejemplo:
|
||||||
|
// aiService.fullReset();
|
||||||
|
// aiService.clearAllConversations();
|
||||||
|
// aiService.clearRequestQueue();
|
||||||
|
// aiService.resetStatistics();
|
||||||
|
|
||||||
|
const resetTimestamp = new Date().toISOString().replace('T', ' ').split('.')[0];
|
||||||
|
|
||||||
|
// Panel de confirmación de reset completo
|
||||||
|
// @ts-ignore
|
||||||
|
const resetCompletePanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xFF4444,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '## ⚠️ RESET COMPLETO EJECUTADO'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '-# El sistema de IA ha sido completamente reiniciado.'
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `## 🔄 Resumen del Reset
|
||||||
|
\`\`\`
|
||||||
|
┌─────────────────────────┬─────────────┐
|
||||||
|
│ Elemento Limpiado │ Cantidad │
|
||||||
|
├─────────────────────────┼─────────────┤
|
||||||
|
│ Conversaciones │ ${conversationsCleared.toString().padEnd(11)} │
|
||||||
|
│ Requests en Cola │ ${requestsCleared.toString().padEnd(11)} │
|
||||||
|
│ Cache de Memoria │ ✅ Limpiado │
|
||||||
|
│ Estadísticas │ ✅ Reset │
|
||||||
|
│ Rate Limits │ ✅ Reset │
|
||||||
|
│ Configuración │ ✅ Default │
|
||||||
|
└─────────────────────────┴─────────────┘
|
||||||
|
|
||||||
|
🔄 Estado: Sistema completamente reiniciado
|
||||||
|
⏰ Timestamp: ${resetTimestamp} UTC
|
||||||
|
👤 Ejecutado por: ${interaction.user.username}
|
||||||
|
🆔 User ID: ${interaction.user.id}
|
||||||
|
|
||||||
|
⚠️ ADVERTENCIA: Todas las conversaciones activas
|
||||||
|
han sido eliminadas permanentemente.
|
||||||
|
\`\`\``
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `## ✅ Sistema Restaurado
|
||||||
|
|
||||||
|
El sistema de IA ha vuelto a su estado inicial:
|
||||||
|
• **Memoria limpia** - Sin conversaciones previas
|
||||||
|
• **Queue vacía** - Sin requests pendientes
|
||||||
|
• **Rate limits reset** - Límites restablecidos
|
||||||
|
• **Configuración default** - Valores originales
|
||||||
|
• **Cache limpio** - Memoria optimizada
|
||||||
|
|
||||||
|
El sistema está listo para recibir nuevas consultas.`
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "🔙 Volver al panel principal (con datos reset)" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
emoji: "🔙",
|
||||||
|
label: 'Volver al Panel',
|
||||||
|
custom_id: 'ai_refresh_stats'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "⚠️ **REALIZAR OTRO RESET** (solo si es necesario)" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 4,
|
||||||
|
emoji: "⚠️",
|
||||||
|
label: 'Reset Nuevamente',
|
||||||
|
custom_id: 'ai_full_reset'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.message.edit({ components: [resetCompletePanel] });
|
||||||
|
|
||||||
|
// Log crítico del reset completo
|
||||||
|
logger.warn(`🚨 RESET COMPLETO DE IA ejecutado por ${interaction.user.username} (${interaction.user.id})`);
|
||||||
|
logger.info(`Reset stats - Conversaciones: ${conversationsCleared}, Queue: ${requestsCleared}, Timestamp: ${resetTimestamp}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error ejecutando reset completo de IA:', error);
|
||||||
|
if (!interaction.deferred && !interaction.replied) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Error crítico ejecutando el reset completo del sistema de IA.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
172
src/components/buttons/aiRefreshStats.ts
Normal file
172
src/components/buttons/aiRefreshStats.ts
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import logger from "../../core/lib/logger";
|
||||||
|
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatear tiempo de actividad
|
||||||
|
*/
|
||||||
|
function formatUptime(seconds: number): string {
|
||||||
|
const days = Math.floor(seconds / 86400);
|
||||||
|
const hours = Math.floor((seconds % 86400) / 3600);
|
||||||
|
const minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
|
||||||
|
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
|
||||||
|
if (hours > 0) return `${hours}h ${minutes}m`;
|
||||||
|
return `${minutes}m`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatear bytes a formato legible
|
||||||
|
*/
|
||||||
|
function formatBytesMB(bytes: number): string {
|
||||||
|
return (bytes / 1024 / 1024).toFixed(1) + 'MB';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construir panel de administración de IA actualizado
|
||||||
|
*/
|
||||||
|
function buildRefreshedAIPanel() {
|
||||||
|
const { aiService } = require('../../core/services/AIService');
|
||||||
|
const stats = aiService.getStats();
|
||||||
|
const uptime = process.uptime();
|
||||||
|
const memoryUsage = process.memoryUsage();
|
||||||
|
const now = new Date();
|
||||||
|
const ts = now.toISOString().replace('T', ' ').split('.')[0];
|
||||||
|
|
||||||
|
// Estados del sistema
|
||||||
|
const queueStatus = stats.queueLength === 0 ? '🟢 Normal' :
|
||||||
|
stats.queueLength < 5 ? '🟡 Ocupado' : '🔴 Saturado';
|
||||||
|
const memoryStatus = memoryUsage.heapUsed / memoryUsage.heapTotal > 0.8 ? '🔴 Alta' : '🟢 Normal';
|
||||||
|
|
||||||
|
const rss = formatBytesMB(memoryUsage.rss);
|
||||||
|
const heapUsed = formatBytesMB(memoryUsage.heapUsed);
|
||||||
|
const heapTotal = formatBytesMB(memoryUsage.heapTotal);
|
||||||
|
const external = formatBytesMB(memoryUsage.external);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xFF69B4,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '## 🌸 Panel de Administración - Gemini-chan (Actualizado)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: '-# Estadísticas refrescadas automáticamente.'
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: `🔄 **Conversaciones Activas:** ${stats.activeConversations}` }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
emoji: "🧹",
|
||||||
|
label: 'Limpiar Cache',
|
||||||
|
custom_id: 'ai_clear_cache'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: `📊 **Requests en Cola:** ${stats.queueLength} | **Estado:** ${queueStatus}` }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
emoji: "🔄",
|
||||||
|
label: 'Refrescar Stats',
|
||||||
|
custom_id: 'ai_refresh_stats'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: `⏱️ **Total Requests:** ${stats.totalRequests} | **Uptime:** ${formatUptime(uptime)}` }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
emoji: "🔧",
|
||||||
|
label: 'Configuración',
|
||||||
|
custom_id: 'ai_config'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: ` ## 🧠 Uso de Memoria del Sistema IA
|
||||||
|
\`\`\`
|
||||||
|
┌─────────────────┬──────────────┬──────────┐
|
||||||
|
│ Memory Type │ Usage │ Status │
|
||||||
|
├─────────────────┼──────────────┼──────────┤
|
||||||
|
│ RSS │ ${rss.padEnd(12)} │ Normal │
|
||||||
|
│ Heap Used │ ${heapUsed.padEnd(12)} │ ${memoryStatus.padEnd(8)} │
|
||||||
|
│ Heap Total │ ${heapTotal.padEnd(12)} │ Normal │
|
||||||
|
│ External │ ${external.padEnd(12)} │ Normal │
|
||||||
|
└─────────────────┴──────────────┴──────────┘
|
||||||
|
|
||||||
|
📈 Configuración Actual:
|
||||||
|
• Rate Limit: 20 req/min por usuario
|
||||||
|
• Cooldown: 3 segundos entre requests
|
||||||
|
• Max Tokens: 1M entrada / 8K salida
|
||||||
|
• Max Concurrent: 3 requests simultáneos
|
||||||
|
• Modelo: gemini-1.5-flash
|
||||||
|
\`\`\`
|
||||||
|
🔄 Última actualización: ${ts} UTC
|
||||||
|
|
||||||
|
⚠️ **Nota:** El sistema se resetea automáticamente cada 30 minutos para optimizar memoria.`
|
||||||
|
},
|
||||||
|
{ type: 14, divider: true, spacing: 1 },
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{ type: 10, content: "<:Sup_urg:1420535068056748042> **REINICIAR** todo el sistema de IA (limpia cache, conversaciones y estadísticas)" }
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 4,
|
||||||
|
emoji: "⚠️",
|
||||||
|
label: 'RESET COMPLETO',
|
||||||
|
custom_id: 'ai_full_reset'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
customId: 'ai_refresh_stats',
|
||||||
|
run: async (interaction: ButtonInteraction) => {
|
||||||
|
// Verificar permisos de administrador
|
||||||
|
if (!interaction.memberPermissions?.has(PermissionFlagsBits.Administrator)) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: '❌ No tienes permisos de administrador para usar este botón.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.deferUpdate();
|
||||||
|
|
||||||
|
// Refrescar y reconstruir el panel con datos actualizados
|
||||||
|
const refreshedPanel = buildRefreshedAIPanel();
|
||||||
|
|
||||||
|
await interaction.message.edit({ components: [refreshedPanel] });
|
||||||
|
logger.info(`Estadísticas de IA refrescadas por ${interaction.user.username} (${interaction.user.id})`);
|
||||||
|
|
||||||
|
} catch (error)
|
||||||
|
//@ts-ignore
|
||||||
|
logger.error('Error refrescando estadísticas de IA:', error);
|
||||||
|
if (!interaction.deferred && !interaction.replied) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Error refrescando las estadísticas del sistema de IA.',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -216,7 +216,7 @@ export class AIService {
|
|||||||
|
|
||||||
// Usar la API correcta de Google Generative AI
|
// Usar la API correcta de Google Generative AI
|
||||||
const model = this.genAI.getGenerativeModel({
|
const model = this.genAI.getGenerativeModel({
|
||||||
model: "gemini-2.5-flash",
|
model: "gemini-2.5-flash-preview-09-2025",
|
||||||
generationConfig: {
|
generationConfig: {
|
||||||
maxOutputTokens: Math.min(this.config.maxOutputTokens, Math.max(1024, estimatedTokens * 0.5)),
|
maxOutputTokens: Math.min(this.config.maxOutputTokens, Math.max(1024, estimatedTokens * 0.5)),
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
|
|||||||
Reference in New Issue
Block a user