502 lines
15 KiB
Markdown
502 lines
15 KiB
Markdown
# 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.
|