CBCFacil v8.0 - Refactored with AMD GPU support
This commit is contained in:
418
docs/DEPLOYMENT.md
Normal file
418
docs/DEPLOYMENT.md
Normal file
@@ -0,0 +1,418 @@
|
||||
# Guia de Despliegue - CBCFacil
|
||||
|
||||
Esta guia describe las opciones y procedimientos para desplegar CBCFacil en diferentes entornos.
|
||||
|
||||
## Opciones de Despliegue
|
||||
|
||||
| Metodo | Complejidad | Recomendado para |
|
||||
|--------|-------------|------------------|
|
||||
| Docker Compose | Baja | Desarrollo, Produccion ligera |
|
||||
| Docker Standalone | Media | Produccion con orquestacion |
|
||||
| Virtual Environment | Baja | Desarrollo local |
|
||||
| Kubernetes | Alta | Produccion a escala |
|
||||
|
||||
## Despliegue con Docker Compose
|
||||
|
||||
### Prerrequisitos
|
||||
|
||||
- Docker 24.0+
|
||||
- Docker Compose 2.20+
|
||||
- NVIDIA Container Toolkit (para GPU)
|
||||
|
||||
### Configuracion
|
||||
|
||||
1. Crear archivo de configuracion:
|
||||
```bash
|
||||
cp .env.example .env.production
|
||||
nano .env.production
|
||||
```
|
||||
|
||||
2. Configurar variables sensibles:
|
||||
```bash
|
||||
# .env.production
|
||||
NEXTCLOUD_URL=https://nextcloud.example.com/remote.php/webdav
|
||||
NEXTCLOUD_USER=tu_usuario
|
||||
NEXTCLOUD_PASSWORD=tu_contrasena_segura
|
||||
|
||||
ANTHROPIC_AUTH_TOKEN=sk-ant-...
|
||||
GEMINI_API_KEY=AIza...
|
||||
|
||||
TELEGRAM_TOKEN=bot_token
|
||||
TELEGRAM_CHAT_ID=chat_id
|
||||
|
||||
CUDA_VISIBLE_DEVICES=all
|
||||
LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
3. Verificar docker-compose.yml:
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
cbcfacil:
|
||||
build: .
|
||||
container_name: cbcfacil
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5000:5000"
|
||||
volumes:
|
||||
- ./downloads:/app/downloads
|
||||
- ./resumenes_docx:/app/resumenes_docx
|
||||
- ./logs:/app/logs
|
||||
- ./data:/app/data
|
||||
environment:
|
||||
- NEXTCLOUD_URL=${NEXTCLOUD_URL}
|
||||
- NEXTCLOUD_USER=${NEXTCLOUD_USER}
|
||||
- NEXTCLOUD_PASSWORD=${NEXTCLOUD_PASSWORD}
|
||||
- ANTHROPIC_AUTH_TOKEN=${ANTHROPIC_AUTH_TOKEN}
|
||||
- GEMINI_API_KEY=${GEMINI_API_KEY}
|
||||
- TELEGRAM_TOKEN=${TELEGRAM_TOKEN}
|
||||
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
|
||||
- CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES}
|
||||
- LOG_LEVEL=${LOG_LEVEL}
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### Despliegue
|
||||
|
||||
```bash
|
||||
# Construir y levantar
|
||||
docker compose up -d --build
|
||||
|
||||
# Ver logs
|
||||
docker compose logs -f cbcfacil
|
||||
|
||||
# Ver estado
|
||||
docker compose ps
|
||||
|
||||
# Reiniciar
|
||||
docker compose restart cbcfacil
|
||||
|
||||
# Detener
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Actualizacion
|
||||
|
||||
```bash
|
||||
# Hacer backup de datos
|
||||
docker cp cbcfacil:/app/data ./backup/data
|
||||
|
||||
# Actualizar imagen
|
||||
docker compose pull
|
||||
docker compose up -d --build
|
||||
|
||||
# Verificar
|
||||
docker compose logs -f cbcfacil
|
||||
```
|
||||
|
||||
## Despliegue con Docker Standalone
|
||||
|
||||
### Construir Imagen
|
||||
|
||||
```bash
|
||||
# Construir imagen
|
||||
docker build -t cbcfacil:latest .
|
||||
|
||||
# Verificar imagen
|
||||
docker images cbcfacil
|
||||
```
|
||||
|
||||
### Ejecutar Contenedor
|
||||
|
||||
```bash
|
||||
# Con GPU
|
||||
docker run -d \
|
||||
--name cbcfacil \
|
||||
--gpus all \
|
||||
-p 5000:5000 \
|
||||
-v $(pwd)/downloads:/app/downloads \
|
||||
-v $(pwd)/resumenes_docx:/app/resumenes_docx \
|
||||
-v $(pwd)/logs:/app/logs \
|
||||
-e NEXTCLOUD_URL=${NEXTCLOUD_URL} \
|
||||
-e NEXTCLOUD_USER=${NEXTCLOUD_USER} \
|
||||
-e NEXTCLOUD_PASSWORD=${NEXTCLOUD_PASSWORD} \
|
||||
-e ANTHROPIC_AUTH_TOKEN=${ANTHROPIC_AUTH_TOKEN} \
|
||||
-e GEMINI_API_KEY=${GEMINI_API_KEY} \
|
||||
cbcfacil:latest
|
||||
|
||||
# Ver logs
|
||||
docker logs -f cbcfacil
|
||||
|
||||
# Detener
|
||||
docker stop cbcfacil && docker rm cbcfacil
|
||||
```
|
||||
|
||||
## Despliegue Local (Virtual Environment)
|
||||
|
||||
### Prerrequisitos
|
||||
|
||||
- Python 3.10+
|
||||
- NVIDIA drivers (opcional)
|
||||
|
||||
### Instalacion
|
||||
|
||||
```bash
|
||||
# Clonar y entrar al directorio
|
||||
git clone <repo_url>
|
||||
cd cbcfacil
|
||||
|
||||
# Crear entorno virtual
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
|
||||
# Instalar dependencias
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Configurar variables de entorno
|
||||
cp .env.example .env.production
|
||||
nano .env.production
|
||||
|
||||
# Crear directorios
|
||||
mkdir -p downloads resumenes_docx logs data
|
||||
|
||||
# Ejecutar
|
||||
python main.py
|
||||
```
|
||||
|
||||
### Como Servicio Systemd
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/cbcfacil.service
|
||||
[Unit]
|
||||
Description=CBCFacil AI Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=cbcfacil
|
||||
WorkingDirectory=/opt/cbcfacil
|
||||
Environment="PATH=/opt/cbcfacil/.venv/bin"
|
||||
EnvironmentFile=/opt/cbcfacil/.env.production
|
||||
ExecStart=/opt/cbcfacil/.venv/bin/python main.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=cbcfacil
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
# Instalar servicio
|
||||
sudo cp cbcfacil.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable cbcfacil
|
||||
sudo systemctl start cbcfacil
|
||||
|
||||
# Verificar estado
|
||||
sudo systemctl status cbcfacil
|
||||
|
||||
# Ver logs
|
||||
journalctl -u cbcfacil -f
|
||||
```
|
||||
|
||||
## Configuracion de Produccion
|
||||
|
||||
### Variables de Entorno Criticas
|
||||
|
||||
```bash
|
||||
# Obligatorias
|
||||
NEXTCLOUD_URL=...
|
||||
NEXTCLOUD_USER=...
|
||||
NEXTCLOUD_PASSWORD=...
|
||||
|
||||
# Recomendadas para produccion
|
||||
DEBUG=false
|
||||
LOG_LEVEL=WARNING
|
||||
POLL_INTERVAL=10
|
||||
|
||||
# GPU
|
||||
CUDA_VISIBLE_DEVICES=all
|
||||
```
|
||||
|
||||
### Optimizaciones
|
||||
|
||||
```bash
|
||||
# En .env.production
|
||||
# Reducir polling para menor carga
|
||||
POLL_INTERVAL=10
|
||||
|
||||
# Optimizar memoria GPU
|
||||
PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512
|
||||
|
||||
# Limitar threads
|
||||
OMP_NUM_THREADS=4
|
||||
MKL_NUM_THREADS=4
|
||||
```
|
||||
|
||||
### Seguridad
|
||||
|
||||
```bash
|
||||
# Crear usuario dedicado
|
||||
sudo useradd -r -s /bin/false cbcfacil
|
||||
|
||||
# Asignar permisos
|
||||
sudo chown -R cbcfacil:cbcfacil /opt/cbcfacil
|
||||
|
||||
# Proteger archivo de variables
|
||||
sudo chmod 600 /opt/cbcfacil/.env.production
|
||||
```
|
||||
|
||||
## Monitoreo
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
# Endpoint de salud
|
||||
curl http://localhost:5000/health
|
||||
|
||||
# Respuesta esperada
|
||||
{"status": "healthy"}
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
```bash
|
||||
# Ver logs en tiempo real
|
||||
docker logs -f cbcfacil
|
||||
|
||||
# O con journalctl
|
||||
journalctl -u cbcfacil -f
|
||||
|
||||
# Logs estructurados en JSON (produccion)
|
||||
LOG_LEVEL=WARNING
|
||||
```
|
||||
|
||||
### Metricas
|
||||
|
||||
El sistema expone metricas via API:
|
||||
```bash
|
||||
# Estado del servicio
|
||||
curl http://localhost:5000/api/status
|
||||
```
|
||||
|
||||
## Respaldo y Recuperacion
|
||||
|
||||
### Respaldo de Datos
|
||||
|
||||
```bash
|
||||
# Directorios a respaldar
|
||||
# - downloads/ (archivos procesados)
|
||||
# - resumenes_docx/ (documentos generados)
|
||||
# - data/ (registros y estados)
|
||||
# - logs/ (logs del sistema)
|
||||
|
||||
# Script de backup
|
||||
#!/bin/bash
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_DIR=/backup/cbcfacil
|
||||
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
tar -czf $BACKUP_DIR/cbcfacil_$DATE.tar.gz \
|
||||
/opt/cbcfacil/downloads \
|
||||
/opt/cbcfacil/resumenes_docx \
|
||||
/opt/cbcfacil/data
|
||||
|
||||
# Limpiar backups antiguos (mantener ultimos 7 dias)
|
||||
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
|
||||
```
|
||||
|
||||
### Recuperacion
|
||||
|
||||
```bash
|
||||
# Detener servicio
|
||||
docker compose down
|
||||
|
||||
# Restaurar datos
|
||||
tar -xzf backup_*.tar.gz -C /opt/cbcfacil/
|
||||
|
||||
# Verificar permisos
|
||||
chown -R cbcfacil:cbcfacil /opt/cbcfacil
|
||||
|
||||
# Reiniciar servicio
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting de Produccion
|
||||
|
||||
### Contenedor no Inicia
|
||||
|
||||
```bash
|
||||
# Verificar logs
|
||||
docker logs cbcfacil
|
||||
|
||||
# Verificar configuracion
|
||||
docker exec -it cbcfacil python -c "from config import settings; print(settings.has_webdav_config)"
|
||||
```
|
||||
|
||||
### Error de Memoria GPU
|
||||
|
||||
```bash
|
||||
# Verificar GPUs disponibles
|
||||
nvidia-smi
|
||||
|
||||
# Liberar memoria
|
||||
sudo nvidia-smi --gpu-reset
|
||||
|
||||
# O limitar GPU
|
||||
CUDA_VISIBLE_DEVICES=0
|
||||
```
|
||||
|
||||
### WebDAV Connection Failed
|
||||
|
||||
```bash
|
||||
# Verificar conectividad
|
||||
curl -u $NEXTCLOUD_USER:$NEXTCLOUD_PASSWORD $NEXTCLOUD_URL
|
||||
|
||||
# Verificar credenciales
|
||||
echo "URL: $NEXTCLOUD_URL"
|
||||
echo "User: $NEXTCLOUD_USER"
|
||||
```
|
||||
|
||||
### High CPU Usage
|
||||
|
||||
```bash
|
||||
# Reducir threads
|
||||
OMP_NUM_THREADS=2
|
||||
MKL_NUM_THREADS=2
|
||||
|
||||
# Aumentar intervalo de polling
|
||||
POLL_INTERVAL=15
|
||||
```
|
||||
|
||||
## Checklist de Produccion
|
||||
|
||||
- [ ] Variables de entorno configuradas
|
||||
- [ ] Credenciales seguras (.env.production)
|
||||
- [ ] Usuario dedicado creado
|
||||
- [ ] Permisos correctos asignados
|
||||
- [ ] Logs configurados
|
||||
- [ ] Health check funcionando
|
||||
- [ ] Backup automatizado configurado
|
||||
- [ ] Monitoreo activo
|
||||
- [ ] SSL/TLS configurado (si aplica)
|
||||
- [ ] Firewall configurado
|
||||
|
||||
## Recursos Adicionales
|
||||
|
||||
- `docs/SETUP.md` - Guia de configuracion inicial
|
||||
- `docs/TESTING.md` - Guia de testing
|
||||
- `ARCHITECTURE.md` - Documentacion arquitectonica
|
||||
337
docs/SETUP.md
Normal file
337
docs/SETUP.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# Guia de Configuracion - CBCFacil
|
||||
|
||||
Esta guia describe los pasos para configurar el entorno de desarrollo de CBCFacil.
|
||||
|
||||
## Requisitos Previos
|
||||
|
||||
### Software Requerido
|
||||
|
||||
| Componente | Version Minima | Recomendada |
|
||||
|------------|----------------|-------------|
|
||||
| Python | 3.10 | 3.11+ |
|
||||
| Git | 2.0 | Latest |
|
||||
| NVIDIA Driver | 535+ | 550+ (para CUDA 12.1) |
|
||||
| Docker | 24.0 | 25.0+ (opcional) |
|
||||
| Docker Compose | 2.20 | Latest (opcional) |
|
||||
|
||||
### Hardware Recomendado
|
||||
|
||||
- **CPU**: 4+ nucleos
|
||||
- **RAM**: 8GB minimum (16GB+ recomendado)
|
||||
- **GPU**: NVIDIA con 4GB+ VRAM (opcional, soporta CPU)
|
||||
- **Almacenamiento**: 10GB+ libres
|
||||
|
||||
## Instalacion Paso a Paso
|
||||
|
||||
### 1. Clonar el Repositorio
|
||||
|
||||
```bash
|
||||
git clone <repo_url>
|
||||
cd cbcfacil
|
||||
```
|
||||
|
||||
### 2. Crear Entorno Virtual
|
||||
|
||||
```bash
|
||||
# Crear venv
|
||||
python3 -m venv .venv
|
||||
|
||||
# Activar (Linux/macOS)
|
||||
source .venv/bin/activate
|
||||
|
||||
# Activar (Windows)
|
||||
.venv\Scripts\activate
|
||||
```
|
||||
|
||||
### 3. Instalar Dependencias
|
||||
|
||||
```bash
|
||||
# Actualizar pip
|
||||
pip install --upgrade pip
|
||||
|
||||
# Instalar dependencias de produccion
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Instalar dependencias de desarrollo (opcional)
|
||||
pip install -r requirements-dev.txt
|
||||
```
|
||||
|
||||
### 4. Configurar Variables de Entorno
|
||||
|
||||
```bash
|
||||
# Copiar template de configuracion
|
||||
cp .env.example .env.secrets
|
||||
|
||||
# Editar con tus credenciales
|
||||
nano .env.secrets
|
||||
```
|
||||
|
||||
### 5. Estructura de Archivos Necesaria
|
||||
|
||||
```bash
|
||||
# Crear directorios requeridos
|
||||
mkdir -p downloads resumenes_docx logs
|
||||
|
||||
# Verificar estructura
|
||||
ls -la
|
||||
```
|
||||
|
||||
## Configuracion de Credenciales
|
||||
|
||||
### Nextcloud/WebDAV
|
||||
|
||||
```bash
|
||||
# Obligatorio para sincronizacion de archivos
|
||||
NEXTCLOUD_URL=https://tu-nextcloud.com/remote.php/webdav
|
||||
NEXTCLOUD_USER=tu_usuario
|
||||
NEXTCLOUD_PASSWORD=tu_contrasena
|
||||
```
|
||||
|
||||
### AI Providers (Opcional)
|
||||
|
||||
```bash
|
||||
# Claude via Z.ai
|
||||
ANTHROPIC_AUTH_TOKEN=sk-ant-...
|
||||
|
||||
# Google Gemini
|
||||
GEMINI_API_KEY=AIza...
|
||||
|
||||
# Gemini CLI (opcional)
|
||||
GEMINI_CLI_PATH=/usr/local/bin/gemini
|
||||
```
|
||||
|
||||
### Telegram (Opcional)
|
||||
|
||||
```bash
|
||||
TELEGRAM_TOKEN=bot_token
|
||||
TELEGRAM_CHAT_ID=chat_id
|
||||
```
|
||||
|
||||
### GPU Configuration (Opcional)
|
||||
|
||||
```bash
|
||||
# Usar GPU especifica
|
||||
CUDA_VISIBLE_DEVICES=0
|
||||
|
||||
# Usar todas las GPUs
|
||||
CUDA_VISIBLE_DEVICES=all
|
||||
|
||||
# Forzar CPU
|
||||
CUDA_VISIBLE_DEVICES=
|
||||
```
|
||||
|
||||
## Verificacion de Instalacion
|
||||
|
||||
### 1. Verificar Python
|
||||
|
||||
```bash
|
||||
python --version
|
||||
# Debe mostrar Python 3.10+
|
||||
```
|
||||
|
||||
### 2. Verificar Dependencias
|
||||
|
||||
```bash
|
||||
python -c "import torch; print(f'PyTorch: {torch.__version__}')"
|
||||
python -c "import flask; print(f'Flask: {flask.__version__}')"
|
||||
python -c "import whisper; print('Whisper instalado correctamente')"
|
||||
```
|
||||
|
||||
### 3. Verificar Configuracion
|
||||
|
||||
```bash
|
||||
python -c "from config import settings; print(f'WebDAV configurado: {settings.has_webdav_config}')"
|
||||
python -c "from config import settings; print(f'AI configurado: {settings.has_ai_config}')"
|
||||
```
|
||||
|
||||
### 4. Test Rapido
|
||||
|
||||
```bash
|
||||
# Ejecutar tests basicos
|
||||
pytest tests/test_config.py -v
|
||||
```
|
||||
|
||||
## Configuracion de Desarrollo
|
||||
|
||||
### IDE Recomendado
|
||||
|
||||
#### VS Code
|
||||
|
||||
```json
|
||||
// .vscode/settings.json
|
||||
{
|
||||
"python.defaultInterpreterPath": ".venv/bin/python",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"editor.formatOnSave": true,
|
||||
"python.formatting.provider": "black"
|
||||
}
|
||||
```
|
||||
|
||||
#### PyCharm
|
||||
|
||||
1. Open Settings > Project > Python Interpreter
|
||||
2. Add Interpreter > Existing Environment
|
||||
3. Select `.venv/bin/python`
|
||||
|
||||
### Git Hooks (Opcional)
|
||||
|
||||
```bash
|
||||
# Instalar pre-commit
|
||||
pip install pre-commit
|
||||
pre-commit install
|
||||
|
||||
# Verificar hooks
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
### Formateo de Codigo
|
||||
|
||||
```bash
|
||||
# Instalar formateadores
|
||||
pip install black isort
|
||||
|
||||
# Formatear codigo
|
||||
black .
|
||||
isort .
|
||||
|
||||
# Verificar estilo
|
||||
black --check .
|
||||
isort --check-only .
|
||||
```
|
||||
|
||||
## Ejecucion del Servicio
|
||||
|
||||
### Modo Desarrollo
|
||||
|
||||
```bash
|
||||
# Activar entorno virtual
|
||||
source .venv/bin/activate
|
||||
|
||||
# Ejecutar servicio completo
|
||||
python main.py
|
||||
|
||||
# Con logging verbose
|
||||
LOG_LEVEL=DEBUG python main.py
|
||||
```
|
||||
|
||||
### Comandos CLI Disponibles
|
||||
|
||||
```bash
|
||||
# Transcribir audio
|
||||
python main.py whisper <archivo_audio> <directorio_output>
|
||||
|
||||
# Procesar PDF
|
||||
python main.py pdf <archivo_pdf> <directorio_output>
|
||||
```
|
||||
|
||||
### Dashboard
|
||||
|
||||
El dashboard estara disponible en:
|
||||
- URL: http://localhost:5000
|
||||
- API: http://localhost:5000/api/
|
||||
|
||||
## Solucion de Problemas Comunes
|
||||
|
||||
### Error: "Module not found"
|
||||
|
||||
```bash
|
||||
# Verificar que el venv esta activado
|
||||
which python
|
||||
# Debe mostrar path hacia .venv/bin/python
|
||||
|
||||
# Reinstalar dependencias
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Error: "CUDA out of memory"
|
||||
|
||||
```bash
|
||||
# Reducir uso de GPU
|
||||
export CUDA_VISIBLE_DEVICES=
|
||||
|
||||
# O usar solo una GPU
|
||||
export CUDA_VISIBLE_DEVICES=0
|
||||
```
|
||||
|
||||
### Error: "WebDAV connection failed"
|
||||
|
||||
```bash
|
||||
# Verificar credenciales
|
||||
echo $NEXTCLOUD_URL
|
||||
echo $NEXTCLOUD_USER
|
||||
|
||||
# Probar conexion manualmente
|
||||
curl -u $NEXTCLOUD_USER:$NEXTCLOUD_PASSWORD $NEXTCLOUD_URL
|
||||
```
|
||||
|
||||
### Error: "Telegram token invalid"
|
||||
|
||||
```bash
|
||||
# Verificar token con BotFather
|
||||
# https://t.me/BotFather
|
||||
|
||||
# Verificar variables de entorno
|
||||
echo $TELEGRAM_TOKEN
|
||||
echo $TELEGRAM_CHAT_ID
|
||||
```
|
||||
|
||||
### Error: "Whisper model not found"
|
||||
|
||||
```bash
|
||||
# El modelo se descarga automaticamente la primera vez
|
||||
# Para forzar recarga:
|
||||
rm -rf ~/.cache/whisper
|
||||
```
|
||||
|
||||
## Configuracion de GPU (Opcional)
|
||||
|
||||
### Verificar Instalacion de CUDA
|
||||
|
||||
```bash
|
||||
# Verificar drivers NVIDIA
|
||||
nvidia-smi
|
||||
|
||||
# Verificar CUDA Toolkit
|
||||
nvcc --version
|
||||
|
||||
# Verificar PyTorch CUDA
|
||||
python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')"
|
||||
```
|
||||
|
||||
### Configurar Memoria GPU
|
||||
|
||||
```bash
|
||||
# En .env.secrets
|
||||
PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512
|
||||
```
|
||||
|
||||
## Variables de Entorno Completa
|
||||
|
||||
| Variable | Requerido | Default | Descripcion |
|
||||
|----------|-----------|---------|-------------|
|
||||
| NEXTCLOUD_URL | Si | - | URL WebDAV de Nextcloud |
|
||||
| NEXTCLOUD_USER | Si | - | Usuario Nextcloud |
|
||||
| NEXTCLOUD_PASSWORD | Si | - | Contrasena Nextcloud |
|
||||
| ANTHROPIC_AUTH_TOKEN | No | - | Token Claude/Z.ai |
|
||||
| GEMINI_API_KEY | No | - | API Key Gemini |
|
||||
| TELEGRAM_TOKEN | No | - | Token Bot Telegram |
|
||||
| TELEGRAM_CHAT_ID | No | - | Chat ID Telegram |
|
||||
| CUDA_VISIBLE_DEVICES | No | "all" | GPUs a usar |
|
||||
| POLL_INTERVAL | No | 5 | Segundos entre polls |
|
||||
| LOG_LEVEL | No | "INFO" | Nivel de logging |
|
||||
| DEBUG | No | false | Modo debug |
|
||||
| DASHBOARD_PORT | No | 5000 | Puerto del dashboard |
|
||||
| DASHBOARD_HOST | No | "0.0.0.0" | Host del dashboard |
|
||||
|
||||
## Siguientes Pasos
|
||||
|
||||
1. Verificar instalacion con tests
|
||||
2. Configurar integracion con Nextcloud
|
||||
3. Probar procesamiento de archivos
|
||||
4. Configurar notificaciones Telegram (opcional)
|
||||
|
||||
Ver juga:
|
||||
- `docs/TESTING.md` - Guia de testing
|
||||
- `docs/DEPLOYMENT.md` - Guia de despliegue
|
||||
- `ARCHITECTURE.md` - Documentacion arquitectonica
|
||||
482
docs/TESTING.md
Normal file
482
docs/TESTING.md
Normal file
@@ -0,0 +1,482 @@
|
||||
# Guia de Testing - CBCFacil
|
||||
|
||||
Esta guia describe como ejecutar y escribir tests para CBCFacil.
|
||||
|
||||
## Estructura de Tests
|
||||
|
||||
```
|
||||
tests/
|
||||
├── conftest.py # Fixtures compartidos
|
||||
├── test_config.py # Tests de configuracion
|
||||
├── test_storage.py # Tests de almacenamiento
|
||||
├── test_webdav.py # Tests de WebDAV
|
||||
├── test_processors.py # Tests de procesadores
|
||||
├── test_ai_providers.py # Tests de AI providers
|
||||
├── test_vram_manager.py # Tests de VRAM manager
|
||||
└── test_main_integration.py # Tests de integracion
|
||||
```
|
||||
|
||||
## Instalacion de Dependencias de Test
|
||||
|
||||
```bash
|
||||
# Activar entorno virtual
|
||||
source .venv/bin/activate
|
||||
|
||||
# Instalar dependencias de desarrollo
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
# Verificar instalacion
|
||||
pytest --version
|
||||
```
|
||||
|
||||
## Ejecutar Tests
|
||||
|
||||
### Todos los Tests
|
||||
|
||||
```bash
|
||||
# Ejecutar todos los tests
|
||||
pytest tests/
|
||||
|
||||
# Con output detallado
|
||||
pytest tests/ -v
|
||||
```
|
||||
|
||||
### Tests Especificos
|
||||
|
||||
```bash
|
||||
# Tests de configuracion
|
||||
pytest tests/test_config.py -v
|
||||
|
||||
# Tests de almacenamiento
|
||||
pytest tests/test_storage.py -v
|
||||
|
||||
# Tests de WebDAV
|
||||
pytest tests/test_webdav.py -v
|
||||
|
||||
# Tests de procesadores
|
||||
pytest tests/test_processors.py -v
|
||||
|
||||
# Tests de AI providers
|
||||
pytest tests/test_ai_providers.py -v
|
||||
|
||||
# Tests de VRAM manager
|
||||
pytest tests/test_vram_manager.py -v
|
||||
|
||||
# Tests de integracion
|
||||
pytest tests/test_main_integration.py -v
|
||||
```
|
||||
|
||||
### Tests con Coverage
|
||||
|
||||
```bash
|
||||
# Coverage basico
|
||||
pytest tests/ --cov=cbcfacil
|
||||
|
||||
# Coverage con reporte HTML
|
||||
pytest tests/ --cov=cbcfacil --cov-report=html
|
||||
|
||||
# Coverage con reporte term-missing
|
||||
pytest tests/ --cov=cbcfacil --cov-report=term-missing
|
||||
|
||||
# Coverage por modulo
|
||||
pytest tests/ --cov=cbcfacil --cov-report=term-missing --cov-report=annotate
|
||||
```
|
||||
|
||||
### Tests en Modo Watch
|
||||
|
||||
```bash
|
||||
# Recargar automaticamente al detectar cambios
|
||||
pytest-watch tests/
|
||||
```
|
||||
|
||||
### Tests Parallelos
|
||||
|
||||
```bash
|
||||
# Ejecutar tests en paralelo
|
||||
pytest tests/ -n auto
|
||||
|
||||
# Con numero fijo de workers
|
||||
pytest tests/ -n 4
|
||||
```
|
||||
|
||||
## Escribir Nuevos Tests
|
||||
|
||||
### Estructura Basica
|
||||
|
||||
```python
|
||||
# tests/test_ejemplo.py
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
class TestEjemplo:
|
||||
"""Clase de tests para un modulo"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup antes de cada test"""
|
||||
pass
|
||||
|
||||
def teardown_method(self):
|
||||
"""Cleanup despues de cada test"""
|
||||
pass
|
||||
|
||||
def test_funcion_basica(self):
|
||||
"""Test de una funcion basica"""
|
||||
# Arrange
|
||||
input_value = "test"
|
||||
|
||||
# Act
|
||||
result = mi_funcion(input_value)
|
||||
|
||||
# Assert
|
||||
assert result is not None
|
||||
assert result == "expected"
|
||||
```
|
||||
|
||||
### Usar Fixtures
|
||||
|
||||
```python
|
||||
# tests/conftest.py
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
@pytest.fixture
|
||||
def temp_directory(tmp_path):
|
||||
"""Fixture para directorio temporal"""
|
||||
dir_path = tmp_path / "test_files"
|
||||
dir_path.mkdir()
|
||||
return dir_path
|
||||
|
||||
@pytest.fixture
|
||||
def mock_settings():
|
||||
"""Fixture con settings de prueba"""
|
||||
class MockSettings:
|
||||
NEXTCLOUD_URL = "https://test.example.com"
|
||||
NEXTCLOUD_USER = "test_user"
|
||||
NEXTCLOUD_PASSWORD = "test_pass"
|
||||
return MockSettings()
|
||||
|
||||
# En tu test
|
||||
def test_con_fixture(temp_directory, mock_settings):
|
||||
"""Test usando fixtures"""
|
||||
assert temp_directory.exists()
|
||||
assert mock_settings.NEXTCLOUD_URL == "https://test.example.com"
|
||||
```
|
||||
|
||||
### Tests de Configuracion
|
||||
|
||||
```python
|
||||
# tests/test_config.py
|
||||
import pytest
|
||||
from config import settings
|
||||
|
||||
class TestSettings:
|
||||
"""Tests para configuracion"""
|
||||
|
||||
def test_has_webdav_config_true(self):
|
||||
"""Test con WebDAV configurado"""
|
||||
# Verificar que las properties funcionan
|
||||
assert hasattr(settings, 'has_webdav_config')
|
||||
assert hasattr(settings, 'has_ai_config')
|
||||
|
||||
def test_processed_files_path(self):
|
||||
"""Test del path de archivos procesados"""
|
||||
path = settings.processed_files_path
|
||||
assert isinstance(path, Path)
|
||||
assert path.suffix == ".txt"
|
||||
```
|
||||
|
||||
### Tests de WebDAV
|
||||
|
||||
```python
|
||||
# tests/test_webdav.py
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
class TestWebDAVService:
|
||||
"""Tests para WebDAV Service"""
|
||||
|
||||
@pytest.fixture
|
||||
def webdav_service(self):
|
||||
"""Crear instancia del servicio"""
|
||||
from services.webdav_service import webdav_service
|
||||
return webdav_service
|
||||
|
||||
def test_list_remote_path(self, webdav_service):
|
||||
"""Test de listado de archivos remotos"""
|
||||
# Mock del cliente WebDAV
|
||||
with patch('services.webdav_service.WebDAVClient') as mock_client:
|
||||
mock_instance = Mock()
|
||||
mock_instance.list.return_value = ['file1.pdf', 'file2.mp3']
|
||||
mock_client.return_value = mock_instance
|
||||
|
||||
# Inicializar servicio
|
||||
webdav_service.initialize()
|
||||
|
||||
# Test
|
||||
files = webdav_service.list("TestFolder")
|
||||
assert len(files) == 2
|
||||
assert "file1.pdf" in files
|
||||
```
|
||||
|
||||
### Tests de Procesadores
|
||||
|
||||
```python
|
||||
# tests/test_processors.py
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
class TestAudioProcessor:
|
||||
"""Tests para Audio Processor"""
|
||||
|
||||
@pytest.fixture
|
||||
def processor(self):
|
||||
"""Crear procesador"""
|
||||
from processors.audio_processor import AudioProcessor
|
||||
return AudioProcessor()
|
||||
|
||||
def test_process_audio_file(self, processor, tmp_path):
|
||||
"""Test de procesamiento de audio"""
|
||||
# Crear archivo de prueba
|
||||
audio_file = tmp_path / "test.mp3"
|
||||
audio_file.write_bytes(b"fake audio content")
|
||||
|
||||
# Mock de Whisper
|
||||
with patch('processors.audio_processor.whisper') as mock_whisper:
|
||||
mock_whisper.load_model.return_value.transcribe.return_value = {
|
||||
"text": "Texto transcrito de prueba"
|
||||
}
|
||||
|
||||
# Ejecutar
|
||||
result = processor.process(str(audio_file))
|
||||
|
||||
# Verificar
|
||||
assert result is not None
|
||||
```
|
||||
|
||||
### Tests de AI Providers
|
||||
|
||||
```python
|
||||
# tests/test_ai_providers.py
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
class TestClaudeProvider:
|
||||
"""Tests para Claude Provider"""
|
||||
|
||||
def test_summarize_text(self):
|
||||
"""Test de resumen con Claude"""
|
||||
from services.ai.claude_provider import ClaudeProvider
|
||||
|
||||
provider = ClaudeProvider()
|
||||
test_text = "Texto largo para resumir..."
|
||||
|
||||
# Mock de la llamada API
|
||||
with patch.object(provider, '_call_api') as mock_call:
|
||||
mock_call.return_value = "Texto resumido"
|
||||
|
||||
result = provider.summarize(test_text)
|
||||
|
||||
assert result == "Texto resumido"
|
||||
mock_call.assert_called_once()
|
||||
```
|
||||
|
||||
### Tests de Integracion
|
||||
|
||||
```python
|
||||
# tests/test_main_integration.py
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
|
||||
class TestMainIntegration:
|
||||
"""Tests de integracion del main"""
|
||||
|
||||
def test_main_loop_no_files(self):
|
||||
"""Test del loop principal sin archivos nuevos"""
|
||||
with patch('main.webdav_service') as mock_webdav:
|
||||
with patch('main.processed_registry') as mock_registry:
|
||||
mock_webdav.list.return_value = []
|
||||
mock_registry.is_processed.return_value = True
|
||||
|
||||
# El loop no debe procesar nada
|
||||
# Verificar que no se llama a procesadores
|
||||
```
|
||||
|
||||
## Configuracion de pytest
|
||||
|
||||
```ini
|
||||
# pytest.ini o pyproject.toml
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
python_classes = ["Test*"]
|
||||
python_functions = ["test_*"]
|
||||
addopts = [
|
||||
"-v",
|
||||
"--tb=short",
|
||||
"--strict-markers",
|
||||
]
|
||||
filterwarnings = [
|
||||
"ignore::DeprecationWarning",
|
||||
]
|
||||
```
|
||||
|
||||
## Mejores Practicas
|
||||
|
||||
### 1. Nombrado de Tests
|
||||
|
||||
```python
|
||||
# BIEN
|
||||
def test_webdav_service_list_returns_files():
|
||||
...
|
||||
|
||||
def test_processed_registry_is_processed_true_for_processed_file():
|
||||
...
|
||||
|
||||
# MAL
|
||||
def test_list():
|
||||
...
|
||||
|
||||
def test_check():
|
||||
...
|
||||
```
|
||||
|
||||
### 2. Estructura AAA
|
||||
|
||||
```python
|
||||
def test_ejemplo_aaa():
|
||||
# Arrange
|
||||
input_data = {"key": "value"}
|
||||
expected = "result"
|
||||
|
||||
# Act
|
||||
actual = function_under_test(input_data)
|
||||
|
||||
# Assert
|
||||
assert actual == expected
|
||||
```
|
||||
|
||||
### 3. Tests Aislados
|
||||
|
||||
```python
|
||||
# Cada test debe ser independiente
|
||||
def test_independent():
|
||||
# No depender de estado de otros tests
|
||||
# Usar fixtures para setup/cleanup
|
||||
pass
|
||||
```
|
||||
|
||||
### 4. Evitar TestLego
|
||||
|
||||
```python
|
||||
# BIEN - Test del comportamiento, no la implementacion
|
||||
def test_registry_returns_true_for_processed_file():
|
||||
registry = ProcessedRegistry()
|
||||
registry.save("file.txt")
|
||||
assert registry.is_processed("file.txt") is True
|
||||
|
||||
# MAL - Test de implementacion
|
||||
def test_registry_uses_set_internally():
|
||||
# No testar detalles de implementacion
|
||||
registry = ProcessedRegistry()
|
||||
assert hasattr(registry, '_processed_files')
|
||||
```
|
||||
|
||||
### 5. Mocks Appropriados
|
||||
|
||||
```python
|
||||
# Usar mocks para dependencias externas
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
|
||||
def test_with_mocked_api():
|
||||
with patch('requests.get') as mock_get:
|
||||
mock_response = Mock()
|
||||
mock_response.json.return_value = {"key": "value"}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
result = my_api_function()
|
||||
|
||||
assert result == {"key": "value"}
|
||||
```
|
||||
|
||||
## Coverage Objetivo
|
||||
|
||||
| Componente | Coverage Minimo |
|
||||
|------------|-----------------|
|
||||
| config/ | 90% |
|
||||
| core/ | 90% |
|
||||
| services/ | 70% |
|
||||
| processors/ | 60% |
|
||||
| storage/ | 90% |
|
||||
| api/ | 80% |
|
||||
|
||||
## Integracion con CI/CD
|
||||
|
||||
```yaml
|
||||
# .github/workflows/tests.yml
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
pytest tests/ --cov=cbcfacil --cov-report=xml
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Tests Fallan por Imports
|
||||
|
||||
```bash
|
||||
# Verificar que el venv esta activado
|
||||
source .venv/bin/activate
|
||||
|
||||
# Reinstalar el paquete en modo desarrollo
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### Tests Muy Lentos
|
||||
|
||||
```bash
|
||||
# Ejecutar en paralelo
|
||||
pytest tests/ -n auto
|
||||
|
||||
# O ejecutar solo tests rapidos
|
||||
pytest tests/ -m "not slow"
|
||||
```
|
||||
|
||||
### Memory Errors
|
||||
|
||||
```bash
|
||||
# Reducir workers
|
||||
pytest tests/ -n 2
|
||||
|
||||
# O ejecutar secuencial
|
||||
pytest tests/ -n 0
|
||||
```
|
||||
|
||||
## Recursos Adicionales
|
||||
|
||||
- [Documentacion de pytest](https://docs.pytest.org/)
|
||||
- [Documentacion de unittest.mock](https://docs.python.org/3/library/unittest.mock.html)
|
||||
- [pytest-cov](https://pytest-cov.readthedocs.io/)
|
||||
Reference in New Issue
Block a user