CBCFacil v8.0 - Refactored with AMD GPU support
This commit is contained in:
501
ARCHITECTURE.md
Normal file
501
ARCHITECTURE.md
Normal file
@@ -0,0 +1,501 @@
|
||||
# Arquitectura CBCFacil v9
|
||||
|
||||
## Resumen Ejecutivo
|
||||
|
||||
CBCFacil es un servicio de IA modular para procesamiento de documentos (audio, PDF, texto) con integracion a Nextcloud. Este documento describe la arquitectura actual, patrones de diseno utilizados y guia de extension del sistema.
|
||||
|
||||
## Evucion Arquitectonica
|
||||
|
||||
### Problema Original (v7)
|
||||
|
||||
El proyecto sufria de un archivo monolitico de 3167 lineas (`main.py`) que contenia todas las responsabilidades en un solo archivo.
|
||||
|
||||
### Solucion Actual (v9)
|
||||
|
||||
Arquitectura modular con separacion clara de responsabilidades en capas independientes.
|
||||
|
||||
```
|
||||
FLUJO DE DATOS
|
||||
=============
|
||||
|
||||
1. MONITOREO 2. DESCARGA 3. PROCESAMIENTO
|
||||
+---------------+ +---------------+ +---------------+
|
||||
| Nextcloud |-------->| Downloads/ |------>| Processors |
|
||||
| (WebDAV) | | Local | | (Audio/PDF) |
|
||||
+---------------+ +---------------+ +---------------+
|
||||
| |
|
||||
v v
|
||||
+---------------+ +---------------+
|
||||
| WebDAV | | AI Services |
|
||||
| Service | | (Claude/ |
|
||||
+---------------+ | Gemini) |
|
||||
+---------------+
|
||||
|
|
||||
v
|
||||
4. GENERACION 5. REGISTRO 6. NOTIFICACION
|
||||
+---------------+ +---------------+ +---------------+
|
||||
| Document |-------->| Processed |------>| Telegram |
|
||||
| Generators | | Registry | | Service |
|
||||
+---------------+ +---------------+ +---------------+
|
||||
| |
|
||||
v v
|
||||
+---------------+ +---------------+
|
||||
| Nextcloud | | Dashboard |
|
||||
| (Upload) | | (Flask) |
|
||||
+---------------+ +---------------+
|
||||
```
|
||||
|
||||
## Estructura de Directorios
|
||||
|
||||
```
|
||||
cbcfacil/
|
||||
├── main.py # Orquestador principal (149 lineas)
|
||||
├── run.py # Script de ejecucion alternativo
|
||||
├── config/ # Configuracion centralizada
|
||||
│ ├── __init__.py # Exports: settings, validate_environment
|
||||
│ ├── settings.py # Configuracion desde variables de entorno
|
||||
│ └── validators.py # Validadores de configuracion
|
||||
├── core/ # Nucleo compartido
|
||||
│ ├── __init__.py # Exports: excepciones, Result
|
||||
│ ├── exceptions.py # Excepciones personalizadas
|
||||
│ ├── result.py # Patron Result/Error handling
|
||||
│ └── base_service.py # Clase base BaseService
|
||||
├── services/ # Servicios externos
|
||||
│ ├── __init__.py # Exports de servicios
|
||||
│ ├── webdav_service.py # WebDAV/Nextcloud operaciones
|
||||
│ ├── vram_manager.py # GPU memory management
|
||||
│ ├── telegram_service.py # Telegram notificaciones
|
||||
│ ├── metrics_collector.py # Metricas y estadisticas
|
||||
│ ├── ai_service.py # Servicio AI unificado
|
||||
│ └── ai/ # AI Providers
|
||||
│ ├── __init__.py
|
||||
│ ├── base_provider.py # Interfaz BaseProvider
|
||||
│ ├── claude_provider.py # Claude (Z.ai) implementation
|
||||
│ ├── gemini_provider.py # Gemini API/CLI implementation
|
||||
│ └── provider_factory.py # Factory para proveedores
|
||||
├── processors/ # Procesadores de archivos
|
||||
│ ├── __init__.py # Exports de procesadores
|
||||
│ ├── base_processor.py # Clase base FileProcessor
|
||||
│ ├── audio_processor.py # Whisper transcription
|
||||
│ ├── pdf_processor.py # PDF OCR processing
|
||||
│ └── text_processor.py # Text summarization
|
||||
├── document/ # Generacion de documentos
|
||||
│ ├── __init__.py
|
||||
│ └── generators.py # DOCX/PDF/Markdown generation
|
||||
├── storage/ # Persistencia y cache
|
||||
│ ├── __init__.py
|
||||
│ └── processed_registry.py # Registro de archivos procesados
|
||||
├── api/ # API REST
|
||||
│ ├── __init__.py
|
||||
│ └── routes.py # Flask routes y endpoints
|
||||
├── tests/ # Tests unitarios e integracion
|
||||
│ ├── conftest.py # Fixtures pytest
|
||||
│ ├── 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 main
|
||||
├── docs/ # Documentacion
|
||||
│ ├── archive/ # Documentacion historica
|
||||
│ ├── SETUP.md # Guia de configuracion
|
||||
│ ├── TESTING.md # Guia de testing
|
||||
│ └── DEPLOYMENT.md # Guia de despliegue
|
||||
├── requirements.txt # Dependencias produccion
|
||||
├── requirements-dev.txt # Dependencias desarrollo
|
||||
├── .env.example # Template de configuracion
|
||||
├── .env.secrets # Configuracion local (no versionar)
|
||||
└── Dockerfile # Container Docker
|
||||
```
|
||||
|
||||
## Componentes Principales
|
||||
|
||||
### 1. Servicios (services/)
|
||||
|
||||
#### WebDAVService
|
||||
|
||||
**Archivo**: `services/webdav_service.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Conexion y operaciones con Nextcloud via WebDAV
|
||||
- Download/upload de archivos
|
||||
- Listado y creacion de directorios remotos
|
||||
- Manejo de errores con reintentos configurables
|
||||
|
||||
```python
|
||||
class WebDAVService:
|
||||
def initialize(self) -> None: ...
|
||||
def list(self, remote_path: str) -> List[str]: ...
|
||||
def download(self, remote_path: str, local_path: Path) -> None: ...
|
||||
def upload(self, local_path: Path, remote_path: str) -> None: ...
|
||||
def mkdir(self, remote_path: str) -> None: ...
|
||||
```
|
||||
|
||||
#### VRAMManager
|
||||
|
||||
**Archivo**: `services/vram_manager.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Gestion de memoria GPU
|
||||
- Carga/descarga de modelos (Whisper, OCR, TrOCR)
|
||||
- Limpieza automatica de VRAM ociosa
|
||||
- Fallback a CPU cuando GPU no disponible
|
||||
|
||||
```python
|
||||
class VRAMManager:
|
||||
def initialize(self) -> None: ...
|
||||
def cleanup(self) -> None: ...
|
||||
def should_cleanup(self) -> bool: ...
|
||||
def lazy_cleanup(self) -> None: ...
|
||||
```
|
||||
|
||||
#### TelegramService
|
||||
|
||||
**Archivo**: `services/telegram_service.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Envio de notificaciones a Telegram
|
||||
- Throttling de errores para evitar spam
|
||||
- Notificaciones de inicio/parada del servicio
|
||||
|
||||
```python
|
||||
class TelegramService:
|
||||
def configure(self, token: str, chat_id: str) -> None: ...
|
||||
def send_message(self, message: str) -> None: ...
|
||||
def send_error_notification(self, context: str, error: str) -> None: ...
|
||||
def send_start_notification(self) -> None: ...
|
||||
```
|
||||
|
||||
### 2. Procesadores (processors/)
|
||||
|
||||
#### AudioProcessor
|
||||
|
||||
**Archivo**: `processors/audio_processor.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Transcripcion de audio usando Whisper
|
||||
- Modelo: medium (optimizado para espanol)
|
||||
- Soporte GPU/CPU automatico
|
||||
- Post-procesamiento de texto transcrito
|
||||
|
||||
#### PDFProcessor
|
||||
|
||||
**Archivo**: `processors/pdf_processor.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Extraccion de texto de PDFs
|
||||
- OCR con EasyOCR + Tesseract + TrOCR en paralelo
|
||||
- Correccion de texto con IA
|
||||
- Generacion de documentos DOCX
|
||||
|
||||
#### TextProcessor
|
||||
|
||||
**Archivo**: `processors/text_processor.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Resumenes usando IA (Claude/Gemini)
|
||||
- Clasificacion de contenido
|
||||
- Generacion de quizzes opcionales
|
||||
|
||||
### 3. AI Services (services/ai/)
|
||||
|
||||
#### ProviderFactory
|
||||
|
||||
**Archivo**: `services/ai/provider_factory.py`
|
||||
|
||||
Patron Factory para seleccion dinamica de proveedor de IA:
|
||||
```python
|
||||
class ProviderFactory:
|
||||
def get_provider(self, provider_type: str = "auto") -> BaseProvider: ...
|
||||
```
|
||||
|
||||
Proveedores disponibles:
|
||||
- `claude`: Claude via Z.ai API
|
||||
- `gemini`: Google Gemini API
|
||||
- `gemini_cli`: Gemini CLI local
|
||||
- `auto`: Seleccion automatica basada en disponibilidad
|
||||
|
||||
### 4. Document Generation (document/)
|
||||
|
||||
#### DocumentGenerator
|
||||
|
||||
**Archivo**: `document/generators.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Creacion de documentos DOCX
|
||||
- Conversion a PDF
|
||||
- Formateo Markdown
|
||||
- Plantillas de documentos
|
||||
|
||||
### 5. Storage (storage/)
|
||||
|
||||
#### ProcessedRegistry
|
||||
|
||||
**Archivo**: `storage/processed_registry.py`
|
||||
|
||||
Responsabilidades:
|
||||
- Registro persistente de archivos procesados
|
||||
- Cache en memoria con TTL
|
||||
- File locking para thread-safety
|
||||
|
||||
```python
|
||||
class ProcessedRegistry:
|
||||
def initialize(self) -> None: ...
|
||||
def load(self) -> Set[str]: ...
|
||||
def save(self, file_path: str) -> None: ...
|
||||
def is_processed(self, file_path: str) -> bool: ...
|
||||
def mark_for_reprocess(self, file_path: str) -> None: ...
|
||||
```
|
||||
|
||||
### 6. API (api/)
|
||||
|
||||
#### Flask Routes
|
||||
|
||||
**Archivo**: `api/routes.py`
|
||||
|
||||
Endpoints REST disponibles:
|
||||
- `GET /api/files` - Listado de archivos
|
||||
- `POST /api/reprocess` - Reprocesar archivo
|
||||
- `POST /api/mark-unprocessed` - Resetear estado
|
||||
- `GET /api/refresh` - Sincronizar con Nextcloud
|
||||
- `GET /health` - Health check
|
||||
|
||||
## Patrones de Diseno Utilizados
|
||||
|
||||
### 1. Repository Pattern
|
||||
|
||||
```python
|
||||
# storage/processed_registry.py
|
||||
class ProcessedRegistry:
|
||||
def save(self, file_path: str) -> None: ...
|
||||
def load(self) -> Set[str]: ...
|
||||
def is_processed(self, file_path: str) -> bool: ...
|
||||
```
|
||||
|
||||
### 2. Factory Pattern
|
||||
|
||||
```python
|
||||
# services/ai/provider_factory.py
|
||||
class ProviderFactory:
|
||||
def get_provider(self, provider_type: str = "auto") -> BaseProvider: ...
|
||||
```
|
||||
|
||||
### 3. Strategy Pattern
|
||||
|
||||
```python
|
||||
# services/vram_manager.py
|
||||
class VRAMManager:
|
||||
def cleanup(self) -> None: ...
|
||||
def should_cleanup(self) -> bool: ...
|
||||
def lazy_cleanup(self) -> None: ...
|
||||
```
|
||||
|
||||
### 4. Service Layer Pattern
|
||||
|
||||
```python
|
||||
# services/webdav_service.py
|
||||
class WebDAVService:
|
||||
def list(self, remote_path: str) -> List[str]: ...
|
||||
def download(self, remote_path: str, local_path: Path) -> None: ...
|
||||
def upload(self, local_path: Path, remote_path: str) -> None: ...
|
||||
```
|
||||
|
||||
### 5. Singleton Pattern
|
||||
|
||||
Servicios implementados como singletons para compartir estado:
|
||||
```python
|
||||
# services/webdav_service.py
|
||||
webdav_service = WebDAVService()
|
||||
|
||||
# services/vram_manager.py
|
||||
vram_manager = VRAMManager()
|
||||
```
|
||||
|
||||
### 6. Result Pattern
|
||||
|
||||
```python
|
||||
# core/result.py
|
||||
class Result:
|
||||
@staticmethod
|
||||
def success(value): ...
|
||||
@staticmethod
|
||||
def failure(error): ...
|
||||
```
|
||||
|
||||
## Decisiones Arquitectonicas (ADR)
|
||||
|
||||
### ADR-001: Arquitectura Modular
|
||||
|
||||
**Decision**: Separar el monolito en modulos independientes.
|
||||
|
||||
**Contexto**: El archivo main.py de 3167 lineas era dificil de mantener y testar.
|
||||
|
||||
**Decision**: Separar en capas: config/, core/, services/, processors/, document/, storage/, api/.
|
||||
|
||||
**Consecuencias**:
|
||||
- Positivo: Codigo mas mantenible y testeable
|
||||
- Positivo: Reutilizacion de componentes
|
||||
- Negativo: Mayor complejidad inicial
|
||||
|
||||
### ADR-002: Configuracion Centralizada
|
||||
|
||||
**Decision**: Usar clase Settings con variables de entorno.
|
||||
|
||||
**Contexto**: Credenciales hardcodeadas representan riesgo de seguridad.
|
||||
|
||||
**Decision**: Todas las configuraciones via variables de entorno con .env.secrets.
|
||||
|
||||
**Consecuencias**:
|
||||
- Positivo: Seguridad mejorada
|
||||
- Positivo: Facil despliegue en diferentes entornos
|
||||
- Negativo: Requiere documentacion de variables
|
||||
|
||||
### ADR-003: GPU-First con CPU Fallback
|
||||
|
||||
**Decision**: Optimizar para GPU pero soportar CPU.
|
||||
|
||||
**Contexto**: No todos los usuarios tienen GPU disponible.
|
||||
|
||||
**Decision**: VRAMManager con lazy loading y cleanup automatico.
|
||||
|
||||
**Consecuencias**:
|
||||
- Positivo: Performance optimo en GPU
|
||||
- Positivo: Funciona sin GPU
|
||||
- Negativo: Complejidad adicional en gestion de memoria
|
||||
|
||||
### ADR-004: Factory para AI Providers
|
||||
|
||||
**Decision**: Abstraer proveedores de IA detras de interfaz comun.
|
||||
|
||||
**Contexto**: Multiples proveedores (Claude, Gemini) con diferentes APIs.
|
||||
|
||||
**Decision**: BaseProvider con implementaciones concretas y ProviderFactory.
|
||||
|
||||
**Consecuencias**:
|
||||
- Positivo: Facilidad para agregar nuevos proveedores
|
||||
- Positivo: Fallback entre proveedores
|
||||
- Negativo: Sobrecarga de abstraccion
|
||||
|
||||
## Guia de Extension del Sistema
|
||||
|
||||
### Agregar Nuevo Procesador
|
||||
|
||||
1. Crear archivo en `processors/`:
|
||||
```python
|
||||
from processors.base_processor import FileProcessor
|
||||
|
||||
class NuevoProcessor(FileProcessor):
|
||||
def process(self, file_path: str) -> None:
|
||||
# Implementar procesamiento
|
||||
pass
|
||||
```
|
||||
|
||||
2. Registrar en `processors/__init__.py`:
|
||||
```python
|
||||
from processors.nuevo_processor import NuevoProcessor
|
||||
|
||||
__all__ = ['NuevoProcessor', ...]
|
||||
```
|
||||
|
||||
3. Integrar en `main.py`:
|
||||
```python
|
||||
from processors.nuevo_processor import NuevoProcessor
|
||||
|
||||
nuevo_processor = NuevoProcessor()
|
||||
```
|
||||
|
||||
### Agregar Nuevo AI Provider
|
||||
|
||||
1. Crear clase en `services/ai/`:
|
||||
```python
|
||||
from services.ai.base_provider import BaseProvider
|
||||
|
||||
class NuevoProvider(BaseProvider):
|
||||
def summarize(self, text: str) -> str:
|
||||
# Implementar
|
||||
pass
|
||||
```
|
||||
|
||||
2. Registrar en `provider_factory.py`:
|
||||
```python
|
||||
PROVIDERS = {
|
||||
'nuevo': NuevoProvider,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
3. Usar:
|
||||
```python
|
||||
provider = factory.get_provider('nuevo')
|
||||
```
|
||||
|
||||
### Agregar Nuevo Servicio
|
||||
|
||||
1. Crear archivo en `services/`:
|
||||
```python
|
||||
from core.base_service import BaseService
|
||||
|
||||
class NuevoService(BaseService):
|
||||
def initialize(self) -> None:
|
||||
pass
|
||||
|
||||
nuevo_service = NuevoService()
|
||||
```
|
||||
|
||||
2. Inicializar en `main.py`:
|
||||
```python
|
||||
from services.nuevo_service import nuevo_service
|
||||
|
||||
nuevo_service.initialize()
|
||||
```
|
||||
|
||||
### Agregar Nuevo Endpoint API
|
||||
|
||||
1. Editar `api/routes.py`:
|
||||
```python
|
||||
@app.route('/api/nuevo', methods=['GET'])
|
||||
def nuevo_endpoint():
|
||||
return {'status': 'ok'}, 200
|
||||
```
|
||||
|
||||
## Configuracion Detallada
|
||||
|
||||
### Variables de Entorno Principales
|
||||
|
||||
| Variable | Requerido | Default | Descripcion |
|
||||
|----------|-----------|---------|-------------|
|
||||
| NEXTCLOUD_URL | Si | - | URL de Nextcloud WebDAV |
|
||||
| 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" | GPU a usar |
|
||||
| POLL_INTERVAL | No | 5 | Segundos entre polls |
|
||||
| LOG_LEVEL | No | "INFO" | Nivel de logging |
|
||||
|
||||
## Metricas y Benchmarks
|
||||
|
||||
| Metrica | Valor |
|
||||
|---------|-------|
|
||||
| Lineas main.py | 149 (antes 3167) |
|
||||
| Modulos independientes | 8+ |
|
||||
| Cobertura tests | ~60%+ |
|
||||
| Tiempo inicio | 5-10s |
|
||||
| Transcripcion Whisper | ~1x tiempo audio (GPU) |
|
||||
| OCR PDF | 0.5-2s/pagina |
|
||||
|
||||
## Beneficios de la Arquitectura
|
||||
|
||||
1. **Mantenibilidad**: Cada responsabilidad en su propio modulo
|
||||
2. **Testabilidad**: Servicios independientes y testeables
|
||||
3. **Escalabilidad**: Facil agregar nuevos procesadores/servicios
|
||||
4. **Reutilizacion**: Componentes desacoplados
|
||||
5. **Legibilidad**: Codigo organizado y documentado
|
||||
6. **Seguridad**: Configuracion centralizada sin hardcoding
|
||||
|
||||
## Licencia
|
||||
|
||||
MIT License - Ver LICENSE para detalles.
|
||||
Reference in New Issue
Block a user