Preparar repositorio para despliegue: código fuente limpio

This commit is contained in:
jlimolina 2026-01-23 02:00:40 +01:00
parent 866f5c432d
commit 3eca832c1a
76 changed files with 5434 additions and 3496 deletions

93
scripts/download_llm_model.sh Executable file
View file

@ -0,0 +1,93 @@
#!/bin/bash
# Script para descargar modelo LLM compatible con RTX 3060 12GB
set -e
MODEL_DIR="/home/x/rss2/models/llm"
export PATH="$HOME/.local/bin:$PATH"
echo "=== Descarga de Modelo LLM para Categorización de Noticias ==="
echo ""
echo "Para RTX 3060 12GB, se recomienda un modelo 7B cuantizado."
echo ""
echo "Opciones disponibles:"
echo ""
echo "1) Mistral-7B-Instruct-v0.2 (GPTQ 4-bit) - RECOMENDADO"
echo " - Tamaño: ~4.5GB"
echo " - Calidad: Excelente para clasificación"
echo " - VRAM: ~6-7GB"
echo ""
echo "2) Mistral-7B-Instruct-v0.2 (EXL2 4.0bpw)"
echo " - Tamaño: ~4.2GB"
echo " - Calidad: Excelente (optimizado para ExLlamaV2)"
echo " - VRAM: ~5-6GB"
echo ""
echo "3) OpenHermes-2.5-Mistral-7B (GPTQ 4-bit)"
echo " - Tamaño: ~4.5GB"
echo " - Calidad: Muy buena para tareas generales"
echo " - VRAM: ~6-7GB"
echo ""
echo "4) Neural-Chat-7B-v3-1 (GPTQ 4-bit)"
echo " - Tamaño: ~4.5GB"
echo " - Calidad: Buena para español"
echo " - VRAM: ~6-7GB"
echo ""
read -p "Selecciona una opción (1-4) [1]: " CHOICE
CHOICE=${CHOICE:-1}
case $CHOICE in
1)
MODEL_REPO="TheBloke/Mistral-7B-Instruct-v0.2-GPTQ"
MODEL_FILE="model.safetensors"
;;
2)
MODEL_REPO="turboderp/Mistral-7B-instruct-exl2"
MODEL_FILE="4.0bpw"
;;
3)
MODEL_REPO="TheBloke/OpenHermes-2.5-Mistral-7B-GPTQ"
MODEL_FILE="model.safetensors"
;;
4)
MODEL_REPO="TheBloke/neural-chat-7B-v3-1-GPTQ"
MODEL_FILE="model.safetensors"
;;
*)
echo "Opción inválida"
exit 1
;;
esac
echo ""
echo "Descargando: $MODEL_REPO"
echo "Destino: $MODEL_DIR"
echo ""
# Crear directorio si no existe
mkdir -p "$MODEL_DIR"
# Verificar si huggingface-cli está instalado
# Verificar si huggingface-cli está instalado o si el modulo existe
# Forzamos actualización a una versión reciente para asegurar soporte de CLI
echo "Actualizando huggingface-hub..."
pip3 install -U "huggingface_hub[cli]>=0.23.0" --break-system-packages
# Descargar modelo usando script de python directo para evitar problemas de CLI
echo "Iniciando descarga..."
python3 -c "
from huggingface_hub import snapshot_download
print(f'Descargando { \"$MODEL_REPO\" } a { \"$MODEL_DIR\" }...')
snapshot_download(repo_id='$MODEL_REPO', local_dir='$MODEL_DIR', local_dir_use_symlinks=False)
"
echo ""
echo "✓ Modelo descargado exitosamente en: $MODEL_DIR"
echo ""
echo "Información del modelo:"
echo "----------------------"
ls -lh "$MODEL_DIR"
echo ""
echo "Para usar este modelo, actualiza docker-compose.yml con:"
echo " LLM_MODEL_PATH=/app/models/llm"
echo ""

140
scripts/test_llm_categorizer.py Executable file
View file

@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""
Script de prueba para el LLM Categorizer
Prueba la categorización con datos de ejemplo sin necesidad del contenedor
"""
import os
import sys
# Datos de prueba
TEST_NEWS = [
{
'id': 'test_1',
'titulo': 'El gobierno anuncia nuevas medidas económicas para combatir la inflación',
'resumen': 'El presidente del gobierno ha presentado un paquete de medidas económicas destinadas a reducir la inflación y proteger el poder adquisitivo de las familias.'
},
{
'id': 'test_2',
'titulo': 'Nueva vacuna contra el cáncer muestra resultados prometedores',
'resumen': 'Investigadores de la Universidad de Stanford han desarrollado una vacuna experimental que ha mostrado una eficacia del 85% en ensayos clínicos con pacientes con melanoma.'
},
{
'id': 'test_3',
'titulo': 'El Real Madrid gana la Champions League por decimoquinta vez',
'resumen': 'El equipo blanco se impuso por 2-1 en la final celebrada en Wembley, consolidándose como el club más laureado de la competición europea.'
},
{
'id': 'test_4',
'titulo': 'OpenAI lanza GPT-5 con capacidades multimodales mejoradas',
'resumen': 'La nueva versión del modelo de lenguaje incorpora mejor comprensión de imágenes, video y audio, además de un razonamiento más avanzado.'
},
{
'id': 'test_5',
'titulo': 'Crisis diplomática entre Estados Unidos y China por aranceles',
'resumen': 'Las tensiones comerciales se intensifican después de que Washington impusiera nuevos aranceles del 25% a productos tecnológicos chinos.'
}
]
def test_without_llm():
"""Prueba básica sin LLM (categorización basada en keywords)"""
print("=== Prueba de Categorización Básica (sin LLM) ===\n")
# Categorías con palabras clave simples
CATEGORIES_KEYWORDS = {
'Política': ['gobierno', 'presidente', 'político', 'parlamento', 'elecciones'],
'Economía': ['económic', 'inflación', 'aranceles', 'bolsa', 'financiero'],
'Salud': ['vacuna', 'hospital', 'médico', 'tratamiento', 'enfermedad'],
'Deportes': ['fútbol', 'champions', 'equipo', 'partido', 'gana'],
'Tecnología': ['tecnológic', 'digital', 'software', 'ai', 'gpt', 'openai'],
'Internacional': ['estados unidos', 'china', 'rusia', 'diplomática', 'crisis'],
}
for news in TEST_NEWS:
text = (news['titulo'] + ' ' + news['resumen']).lower()
best_category = 'Otros'
max_score = 0
for category, keywords in CATEGORIES_KEYWORDS.items():
score = sum(1 for kw in keywords if kw in text)
if score > max_score:
max_score = score
best_category = category
print(f"ID: {news['id']}")
print(f"Título: {news['titulo']}")
print(f"Categoría: {best_category} (score: {max_score})")
print()
def test_with_llm():
"""Prueba con el LLM real (requiere modelo descargado)"""
print("\n=== Prueba de Categorización con LLM ===\n")
# Configurar path del modelo
MODEL_PATH = os.environ.get("LLM_MODEL_PATH", "/home/x/rss2/models/llm")
if not os.path.exists(MODEL_PATH):
print(f"❌ Error: No se encuentra el modelo en {MODEL_PATH}")
print(f"Por favor ejecuta primero: ./scripts/download_llm_model.sh")
return
# Verificar si exllamav2 está instalado
try:
import exllamav2
print(f"✓ ExLlamaV2 instalado: {exllamav2.__version__}")
except ImportError:
print("❌ Error: ExLlamaV2 no está instalado")
print("Instalar con: pip install exllamav2")
return
# Importar el categorizer
sys.path.insert(0, '/home/x/rss2')
from workers.llm_categorizer_worker import ExLlamaV2Categorizer
print(f"Cargando modelo desde: {MODEL_PATH}")
print("(Esto puede tardar unos minutos...)\n")
try:
categorizer = ExLlamaV2Categorizer(MODEL_PATH)
print("✓ Modelo cargado exitosamente\n")
results = categorizer.categorize_news(TEST_NEWS)
print("\n=== Resultados ===\n")
for i, news in enumerate(TEST_NEWS):
result = results[i]
print(f"ID: {news['id']}")
print(f"Título: {news['titulo']}")
print(f"Categoría: {result['categoria']}")
print(f"Confianza: {result['confianza']:.2f}")
print()
except Exception as e:
print(f"❌ Error: {e}")
import traceback
traceback.print_exc()
def main():
print("=" * 60)
print("Script de Prueba del LLM Categorizer")
print("=" * 60)
print()
# Prueba básica siempre funciona
test_without_llm()
# Preguntar si probar con LLM
print("\n¿Deseas probar con el LLM real? (requiere modelo descargado)")
print("Esto cargará el modelo en GPU y puede tardar varios minutos.")
response = input("Continuar? [s/N]: ").strip().lower()
if response in ['s', 'si', 'y', 'yes']:
test_with_llm()
else:
print("\nPrueba finalizada. Para probar con el LLM:")
print("1. Descarga el modelo: ./scripts/download_llm_model.sh")
print("2. Ejecuta este script de nuevo y acepta probar con LLM")
if __name__ == "__main__":
main()