Subir archivos a "/"

This commit is contained in:
MrPokeYT 2026-02-02 16:38:56 +00:00
commit 3d7564b822
5 changed files with 385 additions and 0 deletions

8
.env Normal file
View File

@ -0,0 +1,8 @@
# URL del servidor Ollama (por defecto localhost)
OLLAMA_URL=http://localhost:11434/api/generate
# Modelo a utilizar (debe estar instalado en Ollama)
MODEL=openchat
# Timeout en milisegundos (por defecto 120000 = 2 minutos)
TIMEOUT=120000

87
ai_client.js Normal file
View File

@ -0,0 +1,87 @@
// ai_client.js
const OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434/api/generate";
const MODEL = process.env.MODEL || "openchat";
const TIMEOUT = parseInt(process.env.TIMEOUT || "120000"); // 2 minutos por defecto
// Rate limiting
let lastCallTime = 0;
const MIN_CALL_INTERVAL = 2000; // 2 segundos
// Caché básica
const cache = new Map();
async function callLLM(prompt, maxTokens = 1000) {
// Rate limiting
const now = Date.now();
const timeSinceLastCall = now - lastCallTime;
if (timeSinceLastCall < MIN_CALL_INTERVAL) {
const waitTime = MIN_CALL_INTERVAL - timeSinceLastCall;
console.log(`⏳ Esperando ${waitTime}ms por rate limit...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
// Verificar caché
const cacheKey = `${prompt}_${maxTokens}`;
if (cache.has(cacheKey)) {
console.log("✅ Respuesta obtenida de caché");
return cache.get(cacheKey);
}
try {
// Crear un AbortController manual para timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);
const response = await fetch(OLLAMA_URL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
model: MODEL,
prompt: prompt,
stream: false,
options: {
num_predict: maxTokens,
temperature: 0.7
}
}),
signal: controller.signal
});
clearTimeout(timeoutId);
lastCallTime = Date.now();
if (response.status === 429) {
console.error("⚠️ Error 429: Demasiadas peticiones. Esperando 5 segundos...");
await new Promise(resolve => setTimeout(resolve, 5000));
return callLLM(prompt, maxTokens); // Reintentar
}
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error Ollama: ${response.status} - ${errorText}`);
}
const data = await response.json();
if (!data.response) {
throw new Error("La respuesta de Ollama no contiene el campo 'response'");
}
// Guardar en caché
cache.set(cacheKey, data.response);
return data.response;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error(`⏱️ Timeout: La petición tardó más de ${TIMEOUT/1000} segundos. Intenta con un prompt más corto o aumenta TIMEOUT en .env`);
}
if (error.code === 'ECONNREFUSED') {
throw new Error("🔌 No se puede conectar a Ollama. Asegúrate de que está ejecutándose con 'ollama serve'");
}
throw error;
}
}
module.exports = { callLLM };

22
logger.js Normal file
View File

@ -0,0 +1,22 @@
// logger.js
const fs = require('fs');
const path = require('path');
const LOG_FILE = path.join(__dirname, 'logs.jsonl');
function log(entry) {
const logEntry = {
timestamp: new Date().toISOString(),
...entry
};
const logLine = JSON.stringify(logEntry) + '\n';
try {
fs.appendFileSync(LOG_FILE, logLine, 'utf8');
} catch (error) {
console.error("Error escribiendo en logs:", error.message);
}
}
module.exports = { log };

6
logs.jsonl Normal file
View File

@ -0,0 +1,6 @@
{"timestamp":"2026-01-26T18:24:51.723Z","opcion":"respuesta_guiada","error":"⏱️ Timeout: La petición tardó demasiado","solicitud":"Dime que sabes sobre javascript"}
{"timestamp":"2026-01-26T18:28:08.737Z","opcion":"respuesta_guiada","proveedor":"ollama","modelo":"openchat","solicitud":"Dime que es javascript","parametros":{"max_tokens":1000},"respuesta_length":2034,"duracion_ms":61085}
{"timestamp":"2026-01-26T18:31:54.756Z","opcion":"checklist_tecnico","proveedor":"ollama","modelo":"openchat","tema":"Hardening SSH Debian","prompt_length":390,"parametros":{"max_tokens":1500},"respuesta_length":3428,"duracion_ms":96414}
{"timestamp":"2026-01-26T18:34:25.287Z","opcion":"transformacion_itil","proveedor":"ollama","modelo":"openchat","texto_original_length":27,"parametros":{"max_tokens":1200},"respuesta_length":1356,"duracion_ms":38090}
{"timestamp":"2026-01-26T18:42:05.237Z","opcion":"checklist_tecnico","proveedor":"ollama","modelo":"openchat","tema":"Que es el Kernel?","prompt_length":384,"parametros":{"max_tokens":1500},"respuesta_length":3499,"duracion_ms":104562}
{"timestamp":"2026-01-26T18:46:59.464Z","opcion":"respuesta_guiada","proveedor":"ollama","modelo":"openchat","solicitud":"Que necesita una VPN Para funcionar?","parametros":{"max_tokens":1000},"respuesta_length":1340,"duracion_ms":43898}

262
main.js Normal file
View File

@ -0,0 +1,262 @@
// main.js
const readline = require('readline');
const { callLLM } = require('./ai_client');
const { log } = require('./logger');
require('dotenv').config();
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function question(prompt) {
return new Promise(resolve => {
rl.question(prompt, resolve);
});
}
function mostrarMenu() {
console.log("\n" + "=".repeat(60));
console.log("🤖 ASISTENTE IA - GESTIÓN TÉCNICA IT");
console.log("=".repeat(60));
console.log("1. Checklist técnico (salida estructurada)");
console.log("2. Transformación a plantilla ITIL");
console.log("3. Respuesta guiada (preguntas + respuesta final)");
console.log("4. Salir");
console.log("=".repeat(60));
}
async function checklistTecnico() {
console.log("\n📋 CHECKLIST TÉCNICO");
console.log("-".repeat(60));
const tema = await question("Introduce el tema técnico (ej: hardening SSH Debian): ");
const prompt = `Genera un checklist técnico detallado sobre "${tema}" en formato Markdown.
Estructura requerida:
# Checklist: ${tema}
## Items
Para cada item incluye:
- [ ] **Nombre del item**: Breve descripción
- **Justificación**: Por qué es importante
- **Riesgo si no se aplica**: Consecuencias de omitirlo
Genera entre 5 y 8 items relevantes. específico y práctico.`;
console.log("\n⏳ Generando checklist...\n");
const startTime = Date.now();
try {
const respuesta = await callLLM(prompt, 1500);
const duration = Date.now() - startTime;
console.log(respuesta);
// Logging
log({
opcion: "checklist_tecnico",
proveedor: "ollama",
modelo: process.env.MODEL || "openchat",
tema: tema,
prompt_length: prompt.length,
parametros: { max_tokens: 1500 },
respuesta_length: respuesta.length,
duracion_ms: duration
});
} catch (error) {
console.error("\n❌ Error:", error.message);
log({
opcion: "checklist_tecnico",
error: error.message,
tema: tema
});
}
}
async function transformacionITIL() {
console.log("\n📄 TRANSFORMACIÓN A PLANTILLA ITIL");
console.log("-".repeat(60));
console.log("Introduce el texto de la incidencia (escribe 'FIN' en una línea aparte para terminar):");
let textoIncidencia = "";
let linea;
while ((linea = await question("")) !== "FIN") {
textoIncidencia += linea + "\n";
}
const prompt = `Transforma el siguiente texto de incidencia en una plantilla ITIL estructurada:
TEXTO ORIGINAL:
${textoIncidencia}
PLANTILLA ITIL REQUERIDA:
## Resumen
[Descripción breve y clara del problema]
## Impacto
[Alto/Medio/Bajo - Afectación al negocio]
## Urgencia
[Alta/Media/Baja - Tiempo crítico de resolución]
## Acciones Realizadas
- [Lista de acciones ya tomadas]
## Evidencias
- [Logs, capturas, datos técnicos relevantes]
## Diagnóstico Preliminar
[Análisis de la causa probable]
## Próximos Pasos
1. [Primer paso]
2. [Segundo paso]
3. [Tercer paso]
Completa la plantilla de forma profesional y técnica.`;
console.log("\n⏳ Transformando a formato ITIL...\n");
const startTime = Date.now();
try {
const respuesta = await callLLM(prompt, 1200);
const duration = Date.now() - startTime;
console.log(respuesta);
// Logging
log({
opcion: "transformacion_itil",
proveedor: "ollama",
modelo: process.env.MODEL || "openchat",
texto_original_length: textoIncidencia.length,
parametros: { max_tokens: 1200 },
respuesta_length: respuesta.length,
duracion_ms: duration
});
} catch (error) {
console.error("\n❌ Error:", error.message);
log({
opcion: "transformacion_itil",
error: error.message
});
}
}
async function respuestaGuiada() {
console.log("\n❓ RESPUESTA GUIADA");
console.log("-".repeat(60));
const solicitud = await question("Describe el problema (ej: no funciona la VPN): ");
const prompt = `Un usuario reporta: "${solicitud}"
Como técnico IT experto, necesitas:
1. **PREGUNTAS DE ACLARACIÓN** (3-5 preguntas específicas):
- Pregunta 1: [pregunta técnica específica]
- Pregunta 2: [pregunta sobre contexto]
- Pregunta 3: [pregunta sobre síntomas]
- [2 preguntas más si es necesario]
2. **HIPÓTESIS INICIAL**:
Basándote en la información limitada, cuál es la causa más probable.
3. **PLAN DE DIAGNÓSTICO** (pasos numerados):
1. [Primer paso de verificación]
2. [Segundo paso]
3. [Tercer paso]
4. [Cuarto paso]
5. [Quinto paso si es necesario]
Proporciona una respuesta estructurada y profesional.`;
console.log("\n⏳ Analizando y generando preguntas...\n");
const startTime = Date.now();
try {
const respuesta = await callLLM(prompt, 1000);
const duration = Date.now() - startTime;
console.log(respuesta);
// Logging
log({
opcion: "respuesta_guiada",
proveedor: "ollama",
modelo: process.env.MODEL || "openchat",
solicitud: solicitud,
parametros: { max_tokens: 1000 },
respuesta_length: respuesta.length,
duracion_ms: duration
});
} catch (error) {
console.error("\n❌ Error:", error.message);
log({
opcion: "respuesta_guiada",
error: error.message,
solicitud: solicitud
});
}
}
async function main() {
// Verificar variables de entorno
if (!process.env.OLLAMA_URL) {
console.log("⚠️ Usando URL por defecto de Ollama: http://localhost:11434/api/generate");
}
let continuar = true;
while (continuar) {
mostrarMenu();
const opcion = await question("\nSelecciona una opción (1-4): ");
switch (opcion.trim()) {
case "1":
await checklistTecnico();
break;
case "2":
await transformacionITIL();
break;
case "3":
await respuestaGuiada();
break;
case "4":
console.log("\n👋 ¡Hasta pronto!");
continuar = false;
break;
default:
console.log("\n❌ Opción no válida. Por favor, selecciona 1-4.");
}
if (continuar) {
await question("\nPresiona ENTER para continuar...");
}
}
rl.close();
}
// Manejo de errores global
process.on('unhandledRejection', (error) => {
console.error('\n❌ Error no manejado:', error.message);
log({
tipo: "error_no_manejado",
error: error.message,
stack: error.stack
});
});
// Ejecutar
main().catch(error => {
console.error("❌ Error fatal:", error.message);
process.exit(1);
});