diff --git a/vololite.sh b/vololite.sh new file mode 100644 index 0000000..d8f90fc --- /dev/null +++ b/vololite.sh @@ -0,0 +1,510 @@ +#!/bin/bash + +################################################################################ +# Script LITE de instalación de VOLO con Docker +# Para sistemas con Docker, Ollama, Kiwix y ZIM ya instalados +# Autor: Script generado para instalación simplificada +# Fecha: Noviembre 2025 +################################################################################ + +set -e + +# Colores para output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Puertos Volo (solo verificamos estos) +VOLO_UI_PORT=3002 +VOLO_API_PORT=1255 + +# Puertos de servicios existentes +OPENWEBUI_PORT=8380 +OLLAMA_PORT=11434 +KIWIX_PORT=8888 + +# Directorios +INSTALL_DIR="/opt/volo" +ZIM_FILE_PATH="" + +echo -e "${BLUE}╔═══════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ Instalador LITE de VOLO con Docker ║${NC}" +echo -e "${BLUE}║ Usa infraestructura existente ║${NC}" +echo -e "${BLUE}╚═══════════════════════════════════════╝${NC}" +echo "" + +################################################################################ +# Función: Verificar si el script se ejecuta como root +################################################################################ +check_root() { + if [ "$EUID" -ne 0 ]; then + echo -e "${RED}[ERROR]${NC} Este script debe ejecutarse como root o con sudo" + exit 1 + fi +} + +################################################################################ +# Función: Verificar disponibilidad de puerto +################################################################################ +check_port() { + local port=$1 + local service=$2 + + if ss -tuln | grep -q ":$port "; then + echo -e "${RED}[✗]${NC} Puerto $port ($service) está en uso" + ss -tulnp | grep ":$port " + return 1 + else + echo -e "${GREEN}[✓]${NC} Puerto $port ($service) disponible" + return 0 + fi +} + +################################################################################ +# Función: Verificar puertos de Volo +################################################################################ +check_volo_ports() { + echo -e "\n${BLUE}[INFO]${NC} Verificando puertos de Volo...\n" + + local all_available=true + + check_port $VOLO_UI_PORT "Volo UI" || all_available=false + check_port $VOLO_API_PORT "Volo API" || all_available=false + + if [ "$all_available" = false ]; then + echo -e "\n${RED}[ERROR]${NC} Puertos de Volo en uso. ¿Continuar de todos modos? (s/N)" + read -r response + if [[ ! "$response" =~ ^[Ss]$ ]]; then + exit 1 + fi + else + echo -e "\n${GREEN}[✓]${NC} Puertos de Volo disponibles\n" + fi +} + +################################################################################ +# Función: Verificar servicios existentes +################################################################################ +check_existing_services() { + echo -e "${BLUE}[INFO]${NC} Verificando servicios existentes...\n" + + # Verificar Docker + if ! command -v docker &> /dev/null; then + echo -e "${RED}[ERROR]${NC} Docker no está instalado" + exit 1 + fi + echo -e "${GREEN}[✓]${NC} Docker instalado: $(docker --version)" + + # Verificar Docker Compose + if ! docker compose version &> /dev/null; then + echo -e "${RED}[ERROR]${NC} Docker Compose no está disponible" + exit 1 + fi + echo -e "${GREEN}[✓]${NC} Docker Compose instalado: $(docker compose version)" + + # Verificar Ollama + if ss -tuln | grep -q ":$OLLAMA_PORT "; then + echo -e "${GREEN}[✓]${NC} Ollama detectado en puerto $OLLAMA_PORT" + else + echo -e "${YELLOW}[WARN]${NC} Ollama no detectado en puerto $OLLAMA_PORT" + echo -e "${YELLOW} ${NC}Volo necesita Ollama para funcionar" + fi + + # Verificar Kiwix + if ss -tuln | grep -q ":$KIWIX_PORT "; then + echo -e "${GREEN}[✓]${NC} Kiwix detectado en puerto $KIWIX_PORT" + else + echo -e "${YELLOW}[WARN]${NC} Kiwix no detectado en puerto $KIWIX_PORT" + echo -e "${YELLOW} ${NC}Volo puede funcionar con Kiwix integrado o externo" + fi + + # Verificar OpenWebUI + if ss -tuln | grep -q ":$OPENWEBUI_PORT "; then + echo -e "${GREEN}[✓]${NC} OpenWebUI detectado en puerto $OPENWEBUI_PORT" + fi + + echo "" +} + +################################################################################ +# Función: Solicitar ruta de archivos ZIM +################################################################################ +ask_zim_path() { + echo -e "${BLUE}[INFO]${NC} Especifica la ruta COMPLETA al archivo ZIM de Wikipedia" + echo -e "${YELLOW}Ejemplo:${NC} /mnt/data/zim/wikipedia_en_all_nopic_2024-06.zim" + read -e -p "Ruta al archivo .zim: " ZIM_FILE_PATH + + if [ -z "$ZIM_FILE_PATH" ]; then + echo -e "${RED}[ERROR]${NC} Debes especificar la ruta al archivo ZIM" + exit 1 + fi + + if [ ! -f "$ZIM_FILE_PATH" ]; then + echo -e "${YELLOW}[WARN]${NC} El archivo no existe: $ZIM_FILE_PATH" + echo -e "${YELLOW} ${NC}¿Continuar de todos modos? (s/N)" + read -r response + if [[ ! "$response" =~ ^[Ss]$ ]]; then + exit 1 + fi + else + echo -e "${GREEN}[✓]${NC} Archivo ZIM encontrado: $ZIM_FILE_PATH" + fi +} + +################################################################################ +# Función: Preguntar modo de Kiwix +################################################################################ +ask_kiwix_mode() { + echo -e "\n${BLUE}[INFO]${NC} ¿Cómo deseas usar Kiwix?" + echo -e " 1) Usar Kiwix externo existente (puerto $KIWIX_PORT)" + echo -e " 2) Integrar Kiwix dentro del contenedor de Volo" + read -p "Opción [1-2]: " kiwix_choice + + if [ "$kiwix_choice" = "1" ]; then + USE_EXTERNAL_KIWIX=true + echo -e "${GREEN}[✓]${NC} Usando Kiwix externo en http://localhost:$KIWIX_PORT" + else + USE_EXTERNAL_KIWIX=false + echo -e "${GREEN}[✓]${NC} Kiwix se integrará en el contenedor de Volo" + fi +} + +################################################################################ +# Función: Crear estructura de directorios +################################################################################ +create_directories() { + echo -e "\n${BLUE}[INFO]${NC} Creando directorios..." + + mkdir -p "$INSTALL_DIR" + + echo -e "${GREEN}[✓]${NC} Directorios creados" +} + +################################################################################ +# Función: Crear Dockerfile optimizado +################################################################################ +create_dockerfile() { + echo -e "${BLUE}[INFO]${NC} Creando Dockerfile..." + + if [ "$USE_EXTERNAL_KIWIX" = true ]; then + # Dockerfile sin Kiwix (usa el externo) + cat > "$INSTALL_DIR/Dockerfile" << 'EOF' +FROM python:3.11-slim-bookworm + +# Instalar dependencias mínimas +RUN apt-get update && apt-get install -y \ + git \ + curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Clonar Volo +RUN git clone https://github.com/AdyTech99/volo.git . + +# Instalar dependencias Python +RUN pip install --no-cache-dir -r requirements.txt + +# Crear directorios +RUN mkdir -p /app/data + +# Exponer puertos +EXPOSE 3002 1255 + +COPY entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh + +ENTRYPOINT ["/app/entrypoint.sh"] +EOF + else + # Dockerfile con Kiwix integrado + cat > "$INSTALL_DIR/Dockerfile" << 'EOF' +FROM python:3.11-slim-bookworm + +# Instalar dependencias +RUN apt-get update && apt-get install -y \ + git \ + wget \ + curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Clonar Volo +RUN git clone https://github.com/AdyTech99/volo.git . + +# Instalar dependencias Python +RUN pip install --no-cache-dir -r requirements.txt + +# Descargar Kiwix tools +RUN mkdir -p /app/kiwix_tools && \ + wget https://download.kiwix.org/release/kiwix-tools/kiwix-tools_linux-x86_64-3.7.0.tar.gz && \ + tar -xzf kiwix-tools_linux-x86_64-3.7.0.tar.gz -C /app/kiwix_tools --strip-components=1 && \ + rm kiwix-tools_linux-x86_64-3.7.0.tar.gz && \ + chmod +x /app/kiwix_tools/* + +# Crear directorios +RUN mkdir -p /app/data + +# Exponer puertos +EXPOSE 3002 1255 821 + +COPY entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh + +ENTRYPOINT ["/app/entrypoint.sh"] +EOF + fi + + echo -e "${GREEN}[✓]${NC} Dockerfile creado" +} + +################################################################################ +# Función: Crear entrypoint +################################################################################ +create_entrypoint() { + echo -e "${BLUE}[INFO]${NC} Creando script de entrada..." + + cat > "$INSTALL_DIR/entrypoint.sh" << 'EOF' +#!/bin/bash +set -e + +echo "Iniciando Volo..." + +# Generar config.ini si no existe +if [ ! -f /app/config.ini ]; then + echo "Generando config.ini..." + timeout 10 python3 /app/start.py || true +fi + +# Aplicar configuración +if [ -f /app/config.ini ]; then + # Configurar paths según el config.ini montado + echo "Configuración cargada desde /app/config.ini" +fi + +echo "Volo iniciado correctamente" +exec python3 /app/start.py +EOF + + chmod +x "$INSTALL_DIR/entrypoint.sh" + + echo -e "${GREEN}[✓]${NC} Script de entrada creado" +} + +################################################################################ +# Función: Crear config.ini +################################################################################ +create_config() { + echo -e "${BLUE}[INFO]${NC} Creando configuración..." + + if [ "$USE_EXTERNAL_KIWIX" = true ]; then + # Configuración con Kiwix externo + cat > "$INSTALL_DIR/config.ini" << EOF +[PATHS] +zim_file_path = $ZIM_FILE_PATH + +[SERVER] +port = 1255 +ui_port = 3002 +kiwix_serve_url = http://host.docker.internal:$KIWIX_PORT +heading_count = 64 +ai_model = qwen2.5:3b +ollama_api_url = http://host.docker.internal:$OLLAMA_PORT/api/chat + +[RAG] +max_results = 5 +context_window = 4096 +EOF + else + # Configuración con Kiwix integrado + cat > "$INSTALL_DIR/config.ini" << EOF +[PATHS] +kiwix_search_path = /app/kiwix_tools/kiwix-search +kiwix_serve_path = /app/kiwix_tools/kiwix-serve +zim_file_path = $ZIM_FILE_PATH + +[SERVER] +port = 1255 +ui_port = 3002 +kiwix_serve_url = http://localhost:821 +heading_count = 64 +ai_model = qwen2.5:3b +ollama_api_url = http://host.docker.internal:$OLLAMA_PORT/api/chat + +[RAG] +max_results = 5 +context_window = 4096 +EOF + fi + + echo -e "${GREEN}[✓]${NC} Configuración creada" +} + +################################################################################ +# Función: Crear docker-compose.yml +################################################################################ +create_docker_compose() { + echo -e "${BLUE}[INFO]${NC} Creando docker-compose.yml..." + + local expose_kiwix="" + if [ "$USE_EXTERNAL_KIWIX" = false ]; then + expose_kiwix=" - \"821:821\" # Kiwix Server interno" + fi + + # Obtener directorio del archivo ZIM + local zim_dir=$(dirname "$ZIM_FILE_PATH") + local zim_file=$(basename "$ZIM_FILE_PATH") + + cat > "$INSTALL_DIR/docker-compose.yml" << EOF +version: '3.8' + +services: + volo: + build: . + container_name: volo + restart: unless-stopped + ports: + - "${VOLO_UI_PORT}:3002" # UI Web + - "${VOLO_API_PORT}:1255" # API REST +$expose_kiwix + volumes: + - ${INSTALL_DIR}/config.ini:/app/config.ini:rw + - ${INSTALL_DIR}/data:/app/data + - ${zim_dir}:/zim:ro + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - TZ=Europe/Madrid + - PYTHONUNBUFFERED=1 + networks: + - volo-network + +networks: + volo-network: + driver: bridge +EOF + + # Actualizar config.ini con la ruta correcta dentro del contenedor + sed -i "s|zim_file_path = .*|zim_file_path = /zim/$zim_file|g" "$INSTALL_DIR/config.ini" + + echo -e "${GREEN}[✓]${NC} docker-compose.yml creado" +} + +################################################################################ +# Función: Crear archivo systemd service (opcional) +################################################################################ +create_systemd_service() { + echo -e "\n${BLUE}[INFO]${NC} ¿Deseas crear un servicio systemd para iniciar Volo automáticamente? (S/n)" + read -r response + + if [[ ! "$response" =~ ^[Nn]$ ]]; then + cat > /etc/systemd/system/volo.service << EOF +[Unit] +Description=Volo RAG Wikipedia Service +Requires=docker.service +After=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=$INSTALL_DIR +ExecStart=/usr/bin/docker compose up -d +ExecStop=/usr/bin/docker compose down +User=root + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload + systemctl enable volo.service + + echo -e "${GREEN}[✓]${NC} Servicio systemd creado y habilitado" + echo -e "${YELLOW}Comandos:${NC}" + echo -e " sudo systemctl start volo" + echo -e " sudo systemctl stop volo" + echo -e " sudo systemctl status volo" + fi +} + +################################################################################ +# Función: Construir e iniciar +################################################################################ +build_and_start() { + echo -e "\n${BLUE}[INFO]${NC} Construyendo imagen de Volo..." + + cd "$INSTALL_DIR" + docker compose build + + echo -e "${GREEN}[✓]${NC} Imagen construida" + + echo -e "\n${BLUE}[INFO]${NC} ¿Deseas iniciar Volo ahora? (S/n)" + read -r response + + if [[ ! "$response" =~ ^[Nn]$ ]]; then + docker compose up -d + sleep 3 + echo -e "\n${GREEN}[✓]${NC} Volo iniciado" + echo -e "\n${BLUE}[INFO]${NC} Verificando estado..." + docker compose ps + echo -e "\n${YELLOW}Ver logs:${NC} cd $INSTALL_DIR && docker compose logs -f" + fi +} + +################################################################################ +# Función: Mostrar información final +################################################################################ +show_final_info() { + echo -e "\n${GREEN}╔════════════════════════════════════════════════════════╗${NC}" + echo -e "${GREEN}║ Instalación LITE completada exitosamente ║${NC}" + echo -e "${GREEN}╚════════════════════════════════════════════════════════╝${NC}" + echo -e "\n${BLUE}🎯 Acceso a Volo:${NC}\n" + echo -e " 🌐 UI Web: ${YELLOW}http://localhost:$VOLO_UI_PORT${NC}" + echo -e " 🔌 API REST: ${YELLOW}http://localhost:$VOLO_API_PORT/v1${NC}" + echo -e "\n${BLUE}🔗 Integración con OpenWebUI (puerto $OPENWEBUI_PORT):${NC}\n" + echo -e " 1. Accede a OpenWebUI: ${YELLOW}http://localhost:$OPENWEBUI_PORT${NC}" + echo -e " 2. Ve a Admin Panel > Settings > Connections" + echo -e " 3. Añade esta URL: ${YELLOW}http://host.docker.internal:$VOLO_API_PORT/v1${NC}" + echo -e " 4. ${RED}IMPORTANTE:${NC} Desactiva streaming en OpenWebUI" + echo -e " 5. Selecciona el modelo ${YELLOW}volo-workflow${NC}" + echo -e "\n${BLUE}📁 Directorios:${NC}\n" + echo -e " Instalación: ${YELLOW}$INSTALL_DIR${NC}" + echo -e " Configuración: ${YELLOW}$INSTALL_DIR/config.ini${NC}" + echo -e " Archivo ZIM: ${YELLOW}$ZIM_FILE_PATH${NC}" + echo -e "\n${BLUE}🛠️ Comandos útiles:${NC}\n" + echo -e " Iniciar: ${YELLOW}cd $INSTALL_DIR && docker compose up -d${NC}" + echo -e " Detener: ${YELLOW}cd $INSTALL_DIR && docker compose down${NC}" + echo -e " Logs: ${YELLOW}cd $INSTALL_DIR && docker compose logs -f${NC}" + echo -e " Reiniciar: ${YELLOW}cd $INSTALL_DIR && docker compose restart${NC}" + echo -e " Estado: ${YELLOW}cd $INSTALL_DIR && docker compose ps${NC}" + echo -e "\n${BLUE}🧪 Prueba rápida:${NC}\n" + echo -e " ${YELLOW}curl -X POST http://localhost:$VOLO_API_PORT/v1/chat/completions \\${NC}" + echo -e " ${YELLOW} -H \"Content-Type: application/json\" \\${NC}" + echo -e " ${YELLOW} -d '{\"model\": \"volo-workflow\", \"messages\": [{\"role\": \"user\", \"content\": \"What is Linux?\"}]}'${NC}" + echo -e "" +} + +################################################################################ +# MAIN - Ejecución principal +################################################################################ +main() { + check_root + check_existing_services + check_volo_ports + ask_zim_path + ask_kiwix_mode + create_directories + create_dockerfile + create_entrypoint + create_config + create_docker_compose + create_systemd_service + build_and_start + show_final_info +} + +# Ejecutar script principal +main