HFL (HuggingFace Local) es una herramienta CLI y servidor API que permite descargar, gestionar y ejecutar modelos de inteligencia artificial de HuggingFace Hub directamente en la máquina local del usuario. Su diseño aspira a ser un drop-in replacement de Ollama, pero conectado al ecosistema de HuggingFace con más de 500.000 modelos.
Los usuarios necesitan ejecutar LLMs localmente sin depender de APIs en la nube. Ollama resuelve esto pero con un catálogo limitado. HFL conecta directamente con HuggingFace Hub, ofreciendo acceso al catálogo más grande de modelos open-weight del mundo, con descarga, conversión automática a GGUF y ejecución local.
Modularidad extrema con lazy imports, compatibilidad API dual (OpenAI + Ollama), cumplimiento legal riguroso (licencias, privacidad, EU AI Act), y soporte multi-backend (llama.cpp, Transformers, vLLM) con selección automática según el formato del modelo y el hardware disponible.
Python ≥3.10 | Lenguaje base |
typer ≥0.12 | Framework CLI |
rich ≥13.0 | Output con estilo |
pydantic ≥2.10 | Validación de datos API |
pyyaml ≥6.0 | Parsing de configuración |
fastapi ≥0.115 | Framework web async |
uvicorn ≥0.32 | Servidor ASGI |
sse-starlette ≥2.0 | Server-Sent Events |
httpx ≥0.28 | HTTP client async |
huggingface-hub ≥0.27 | API de HF Hub |
llama-cpp-python | Backend GGUF |
transformers+torch | Backend GPU nativo |
vllm | Backend producción |
gguf | Conversión de formato |
hfl/ ├── pyproject.toml — Definición del paquete, deps, scripts, herramientas ├── hfl.spec — PyInstaller spec para ejecutable standalone ├── LICENSE — HRUL v1.0 (licencia propia) ├── LICENSE-DEPENDENCIES.md — Licencias de todas las dependencias ├── PRIVACY.md — Política de privacidad ├── NOTICE-EU-AI-ACT.md — Cumplimiento EU AI Act ├── DISCLAIMER.md — Exención de responsabilidad ├── README.md — Documentación principal ├── README.es.md — Documentación principal (español) ├── LICENSE-FAQ.md — Preguntas frecuentes sobre la licencia HRUL ├── CONTRIBUTING.md — Guía de contribución ├── CODE_OF_CONDUCT.md — Código de conducta (Contributor Covenant 2.1) ├── CHANGELOG.md — Historial de cambios (Keep a Changelog) ├── SECURITY.md — Política de seguridad │ ├── src/hfl/ — CÓDIGO FUENTE PRINCIPAL │ ├── __init__.py — Versión del paquete (0.1.0) │ ├── config.py — HFLConfig dataclass — configuración global │ ├── exceptions.py — Jerarquía completa de excepciones │ ├── events.py — EventBus para pub/sub interno │ ├── metrics.py — Métricas de rendimiento │ ├── plugins.py — Sistema de plugins con entry_points │ ├── security.py — Sanitización y validación de seguridad │ ├── validators.py — Validadores de datos comunes │ ├── logging_config.py — Configuración centralizada de logging │ │ │ ├── core/ — NÚCLEO DEL SISTEMA │ │ ├── __init__.py │ │ ├── container.py — Contenedor DI con Singleton thread-safe │ │ ├── observability_setup.py — Setup de listeners de observabilidad │ │ └── tracing.py — Request tracing con IDs │ │ │ ├── cli/ — INTERFAZ DE LÍNEA DE COMANDOS │ │ ├── __init__.py │ │ ├── main.py — 12 comandos: pull, run, serve, list, search, rm, inspect, alias, login, logout, version, compliance-report │ │ └── commands/ — Comandos modularizados │ │ └── _utils.py — Utilidades compartidas CLI │ │ │ ├── api/ — SERVIDOR REST API │ │ ├── __init__.py │ │ ├── server.py — FastAPI app, CORS, disclaimer, lifespan │ │ ├── state.py — ServerState con async locks para LLM/TTS │ │ ├── streaming.py — SSE streaming helpers │ │ ├── model_loader.py — Carga dinámica de modelos │ │ ├── helpers.py — ensure_llm_loaded, ensure_tts_loaded │ │ ├── errors.py — Manejo centralizado de errores HTTP │ │ ├── middleware.py — Logging privacy-safe │ │ ├── rate_limit.py — Rate limiting por IP/token │ │ ├── routes_openai.py — /v1/chat/completions, /v1/completions, /v1/models │ │ ├── routes_native.py — /api/generate, /api/chat, /api/tags (Ollama-compatible) │ │ ├── routes_tts.py — /v1/audio/speech, /api/tts (TTS endpoints) │ │ ├── routes_health.py — /health, /ready, /live (healthchecks) │ │ ├── routes_metrics.py — /metrics (Prometheus + JSON) │ │ ├── exception_handlers.py — Manejo global de excepciones HFLError │ │ └── timeout.py — Decorator @with_timeout + timeout configurable │ │ │ ├── engine/ — MOTORES DE INFERENCIA │ │ ├── __init__.py │ │ ├── base.py — InferenceEngine + AudioEngine ABCs + dataclasses │ │ ├── selector.py — Selección automática de backend LLM/TTS │ │ ├── llama_cpp.py — LlamaCppEngine (GGUF, CPU/GPU) │ │ ├── transformers_engine.py — TransformersEngine (safetensors, GPU) │ │ ├── vllm_engine.py — VLLMEngine (producción GPU) │ │ ├── bark_engine.py — BarkEngine (TTS via transformers) │ │ ├── coqui_engine.py — CoquiEngine (TTS XTTS-v2) │ │ ├── async_wrapper.py — Wrapper sync→async para engines │ │ ├── model_pool.py — Pool de modelos con LRU eviction │ │ ├── dependency_check.py — Verificación de dependencias opcionales │ │ ├── failover.py — FailoverEngine (multi-engine retry con sticky routing) │ │ ├── memory.py — Tracking de memoria RAM/GPU en tiempo real │ │ ├── observability.py — Métricas de rendimiento del engine │ │ └── prompt_builder.py — Formatos de prompt + escape de delimitadores │ │ │ ├── hub/ — INTEGRACIÓN HUGGINGFACE │ │ ├── __init__.py │ │ ├── auth.py — Autenticación y tokens │ │ ├── client.py — Cliente HTTP para HF Hub │ │ ├── downloader.py — Descarga con resume y rate limiting │ │ ├── license_checker.py — Clasificación de licencias (5 niveles) │ │ └── resolver.py — Resolución inteligente de modelos │ │ │ ├── models/ — MODELOS DE DATOS │ │ ├── __init__.py │ │ ├── manifest.py — ModelManifest — metadata completa │ │ ├── provenance.py — ConversionRecord + ProvenanceLog │ │ ├── registry.py — ModelRegistry — inventario local JSON │ │ └── backends/ — Backends de almacenamiento (File + SQLite) │ │ │ ├── converter/ — CONVERSIÓN DE FORMATOS │ │ ├── __init__.py │ │ ├── formats.py — ModelFormat + ModelType enums │ │ └── gguf_converter.py — GGUFConverter — conversión + cuantización │ │ │ ├── utils/ — UTILIDADES TRANSVERSALES │ │ ├── __init__.py │ │ ├── circuit_breaker.py — Circuit breaker para resiliencia │ │ └── retry.py — Retry con exponential backoff │ │ │ └── i18n/ — INTERNACIONALIZACIÓN │ ├── __init__.py — t(), get_language(), set_language() │ └── locales/ — Archivos de traducción │ ├── en.json — Traducciones en inglés │ └── es.json — Traducciones en español │ ├── docs/ — DOCUMENTACIÓN │ ├── adr/ — Architecture Decision Records │ │ ├── 0001-singleton-pattern.md │ │ ├── 0002-async-api-sync-engines.md │ │ ├── 0003-gguf-default-format.md │ │ ├── 0004-ollama-compatibility.md │ │ ├── 0005-license-classification.md │ │ └── 0006-rate-limiting-strategy.md │ └── *.html — Documentación arquitectura │ └── tests/ — SUITE DE TESTS (80+ archivos, 90%+ cobertura) ├── conftest.py — Fixtures compartidas ├── test_api*.py — Tests API (5 archivos) ├── test_cli*.py — Tests CLI (4 archivos) ├── test_engine*.py — Tests engines (8 archivos) ├── test_hub*.py — Tests HF Hub (5 archivos) ├── test_i18n*.py — Tests i18n (4 archivos) └── test_*.py — Tests unitarios y de integración │ ├── .github/ — INFRAESTRUCTURA GITHUB │ ├── workflows/ │ │ ├── ci.yml — CI: lint + test + type-check (Python 3.10/3.11/3.12) │ │ ├── pages.yml — Deploy docs a GitHub Pages │ │ ├── build-executables.yml — Build ejecutables multiplataforma │ │ ├── lint.yml — Linting con ruff │ │ ├── test.yml — Tests con pytest │ │ ├── security.yml — Auditoría de seguridad │ │ └── license-check.yml — Verificación de licencias │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml — Template para reportes de bugs │ │ └── feature_request.yml — Template para solicitudes de features │ └── PULL_REQUEST_TEMPLATE.md — Template de PR con checklist de compliance
Archivo: src/hfl/config.py
Contiene la clase HFLConfig (dataclass) que define toda la configuración global de la aplicación. Se instancia una vez como singleton (config = HFLConfig()) al importar el módulo, y se llama a ensure_dirs() para crear la estructura de directorios.
| Propiedad | Tipo | Default | Descripción |
|---|---|---|---|
home_dir | Path | ~/.hfl | Directorio raíz. Override con HFL_HOME env var |
models_dir | Path (prop) | ~/.hfl/models | Almacenamiento de modelos descargados |
cache_dir | Path (prop) | ~/.hfl/cache | Caché temporal de HuggingFace |
registry_path | Path (prop) | ~/.hfl/models.json | Registro de modelos (inventario local) |
llama_cpp_dir | Path (prop) | ~/.hfl/tools/llama.cpp | Herramientas de conversión compiladas |
host | str | 127.0.0.1 | Dirección del servidor API. Override con HFL_HOST |
port | int | 11434 | Puerto (igual que Ollama para compatibilidad). Override con HFL_PORT |
default_ctx_size | int | 4096 | Tokens de contexto por defecto |
default_n_gpu_layers | int | -1 | Capas GPU (-1 = todas) |
hf_token | str|None | env HF_TOKEN | Token de autenticación (solo memoria, nunca persiste) |
rate_limit_enabled | bool | true | Habilitar rate limiting. Override con HFL_RATE_LIMIT_ENABLED |
rate_limit_requests | int | 60 | Peticiones permitidas por ventana. Override con HFL_RATE_LIMIT_REQUESTS |
rate_limit_window | int | 60 | Ventana de tiempo en segundos. Override con HFL_RATE_LIMIT_WINDOW |
Adicionalmente, SLOConfig define los Service Level Objectives del servidor: availability target, latencia P50/P95/P99.
hf_token se lee SOLO de la variable de entorno. Nunca se persiste a disco, nunca se guarda en models.json ni en ningún archivo de configuración. Existe solo en memoria durante la ejecución del proceso.
Archivo: src/hfl/cli/main.py (~870 líneas)
Framework: Typer + Rich. Entry point registrado en pyproject.toml como hfl = "hfl.cli.main:app"
| Comando | Descripción | Opciones Clave |
|---|---|---|
hfl pull <modelo> | Descarga modelo desde HF Hub | --quantize Q4_K_M, --format auto|gguf|safetensors, --alias, --skip-license |
hfl run <modelo> | Chat interactivo en terminal | --backend auto|llama-cpp|transformers|vllm, --ctx, --system, --verbose |
hfl serve | Servidor API REST | --host, --port, --model (pre-carga), --api-key (autenticación) |
hfl list | Lista modelos locales con tabla Rich | Muestra nombre, alias, formato, cuantización, licencia (coloreada por riesgo), tamaño |
hfl search <query> | Búsqueda paginada interactiva en HF Hub | --gguf, --max-params, --min-params, --sort, --page-size |
hfl rm <modelo> | Elimina modelo con confirmación | Borra archivos + entrada del registro |
hfl inspect <modelo> | Detalle completo (panel Rich) | Muestra metadata, licencia, restricciones, timestamps |
hfl alias <modelo> <alias> | Asigna alias corto | Permite referir modelos por nombres simples |
hfl login | Configura token HF | --token o interactivo. Verifica con whoami() |
hfl logout | Elimina token guardado | Usa huggingface_hub.logout() |
hfl version | Muestra versión + licencia | — |
hfl compliance-report | Informe de cumplimiento legal (JSON/Markdown) | — |
run maneja Ctrl+C durante el streaming de tokens de forma limpia, preservando la respuesta parcial.
_format_size() convierte bytes a formato legible. _get_key() lee una tecla sin Enter (raw terminal). _extract_params_from_name() extrae parámetros del nombre (regex: "70b", "7b", "1.5b"). _estimate_model_size() estima tamaño en disco según parámetros y cuantización. _display_model_row() renderiza una fila de resultado de búsqueda. _get_params_value() extrae el valor numérico para filtrado.
Clase ResolvedModel (dataclass) con: repo_id, revision, filename, format, quantization.
La función resolve() soporta tres formatos de entrada:
1. org/modelo → repo directo en HF
2. org/modelo:Q4_K_M → repo con cuantización estilo Ollama
3. nombre-modelo → búsqueda por nombre (top 5 por descargas)
Tras resolver, detecta si el repo tiene archivos GGUF (prefiere _select_gguf() con prioridad Q4_K_M > Q5_K_M > Q4_K_S), safetensors, o pytorch.
Función principal pull_model(resolved). Para GGUF descarga archivo individual con hf_hub_download(). Para safetensors descarga snapshot completo con snapshot_download() filtrando: *.safetensors, config.json, tokenizer*.json, tokenizer.model.
Implementa rate limiting (0.5s entre llamadas API) y User-Agent identificativo (hfl/0.1.0) para cumplir con ToS de HuggingFace.
get_hf_token() obtiene token con prioridad: 1) env var HF_TOKEN, 2) token guardado por huggingface_hub.
ensure_auth(repo_id) verifica acceso al repo. Si falla y no hay token, solicita interactivamente. Respeta el sistema de gating de HF: NO bypasea la aceptación de licencias de modelos gated.
Enum LicenseRisk: PERMISSIVE, CONDITIONAL, NON_COMMERCIAL, RESTRICTED, UNKNOWN.
Diccionario LICENSE_CLASSIFICATION con ~20 licencias conocidas. Diccionario LICENSE_RESTRICTIONS con restricciones específicas por familia (Llama: 700M MAU, attribution, etc.).
check_model_license() consulta la API de HF, clasifica el riesgo, y devuelve LicenseInfo. require_user_acceptance() presenta un panel Rich con la licencia y requiere confirmación explícita para licencias no permisivas.
Dataclass que almacena la metadata completa de cada modelo descargado. Es la unidad fundamental de información en el sistema.
| Campo | Tipo | Propósito |
|---|---|---|
name, repo_id | str | Identificación (nombre corto + repo HF) |
alias | str|None | Nombre personalizado por el usuario |
local_path, format | str | Ubicación y tipo (gguf/safetensors/pytorch) |
size_bytes, quantization | int, str | Tamaño en disco + nivel Q |
architecture, parameters, context_length | str, str, int | Características del modelo |
license, license_name, license_url | str | Información legal (R1) |
license_restrictions, gated, license_accepted_at | list, bool, str | Restricciones y aceptación |
gpai_classification, training_flops | str | EU AI Act (R4) |
created_at, last_used | str | Timestamps |
Gestiona el inventario local. Persiste a ~/.hfl/models.json como array JSON. Operaciones: add() (evita duplicados), get() (busca por name, alias, o repo_id), set_alias(), list_all() (ordenado por fecha), remove().
Log inmutable de conversiones en ~/.hfl/provenance.json. Cada ConversionRecord documenta: origen (repo, formato, revisión), destino (formato, path, cuantización), herramienta usada (llama.cpp + versión), licencia original, y timestamps. Sirve para trazabilidad legal y auditoría de cumplimiento (R3).
Backend principal. Usa llama-cpp-python. Parámetros: n_ctx, n_gpu_layers (-1=todas), n_threads (0=auto), flash_attn, chat_format (auto-detect). Incluye supresión de stderr para silenciar logs de Metal/CUDA cuando verbose=False. Genera resultados con métricas: tokens/s, prompt tokens, stop reason.
Usa modelos en formato nativo con GPU. Soporte cuantización dinámica: 4bit (NF4 double quant via BitsAndBytes) y 8bit. Streaming via TextIteratorStreamer en thread separado. Usa apply_chat_template() del tokenizer o fallback Llama-style.
EXPERIMENTAL Backend para producción GPU. Wrappea vllm.LLM con SamplingParams. Streaming real con AsyncLLMEngine, con fallback sincrono para compatibilidad. Requiere GPU NVIDIA con CUDA.
Motor con multiple backends y sticky routing — reintenta automaticamente con el siguiente engine disponible si uno falla.
Pool de modelos con espera no recursiva (bounded polling), evitando stack overflow con multiples cargas concurrentes. Tracking de memoria RAM y GPU en tiempo real mediante psutil y GPUtil.
Lógica de decisión en select_engine(model_path, backend):
Todos los imports son lazy (_get_llama_cpp_engine(), etc.) para no requerir todas las dependencias instaladas.
Enum ModelFormat: GGUF, SAFETENSORS, PYTORCH, UNKNOWN. La función detect_format(path) inspecciona extensiones (.gguf, .safetensors, .pt/.pth/.bin) tanto en archivos individuales como en directorios (rglob). find_model_file() localiza el archivo principal del modelo.
Clase GGUFConverter con pipeline de dos pasos:
ensure_tools() auto-instala llama.cpp si no existe: git clone → cmake build → pip install requirements. check_model_convertibility() valida que el modelo sea convertible (rechaza LoRA adapters, modelos de imagen, modelos sin config.json).
| Cuantización | Bits/peso | Calidad | Caso de Uso |
|---|---|---|---|
| Q2_K | ~2.5 | ~80% | Extrema compresión |
| Q3_K_M | ~3.5 | ~87% | Poca RAM |
| Q4_K_M | ~4.5 | ~92% | DEFAULT — mejor balance |
| Q5_K_M | ~5.0 | ~96% | Alta calidad |
| Q6_K | ~6.5 | ~97% | Premium |
| Q8_0 | ~8.0 | ~98%+ | Máxima calidad cuantizada |
| F16 | 16.0 | 100% | Sin cuantización |
Clase con engine (InferenceEngine activo), current_model (ModelManifest cargado) y api_key (str|None para autenticación). Instanciada como singleton global. El lifespan del servidor hace cleanup al cerrar.
Orden de ejecución Starlette (outer → inner): RequestLogger → APIKey → RateLimit (condicional) → Disclaimer → CORS. El orden de add_middleware() es inverso: CORS, Disclaimer, RateLimit, APIKey, RequestLogger. APIKey se ejecuta ANTES de RateLimit, de modo que las peticiones no autenticadas son rechazadas sin consumir tokens del rate limiter.
CORSMiddleware — Permite todas las orígenes, métodos y headers (desarrollo local).
DisclaimerMiddleware — Añade header X-AI-Disclaimer a respuestas de endpoints de generación AI (R9).
RateLimitMiddleware — Limitación de tasa por IP, configurable vía env vars. Soporta rate limiting por modelo.
APIKeyMiddleware — Autenticación opcional via --api-key. Soporta Authorization: Bearer <key> y X-API-Key: <key>. Endpoints públicos (/health, /) exentos.
RequestLogger — Logging privacy-safe. NUNCA registra: bodies (prompts/outputs), headers auth, User-Agent. Solo: método, path, status, duración.
Manejo centralizado de excepciones via register_exception_handlers(app). Mapea toda la jerarquia HFLError a respuestas HTTP con codigos apropiados (400 para validacion, 429 para rate limit, 500 para errores internos).
Todos los endpoints incluyen tags, summary y responses en sus decoradores para documentacion OpenAPI auto-generada. Tags: OpenAI, Ollama, TTS, Health, Metrics.
Todo el logging usa format strings %-style (logger.info('Model loaded: %s', name)) en lugar de f-strings, evitando evaluacion innecesaria cuando el nivel de log esta deshabilitado.
/health/deep?probe=true — Ejecuta un test de inferencia minimo para verificar que el modelo funciona correctamente. Si falla, reporta estado "degraded".
/health/sli — Service Level Indicators con metricas de disponibilidad y latencia.
/metrics — Metricas en formato Prometheus.
/metrics/json — Metricas en formato JSON.
Archivo: src/hfl/i18n/__init__.py
Sistema de internacionalización completo que permite al CLI mostrar todos sus mensajes en múltiples idiomas. Utiliza archivos JSON de traducción con claves anidadas y acceso mediante notación de puntos.
| Función | Descripción |
|---|---|
t(key, **kwargs) | Traduce una clave (ej: t("commands.pull.downloading")). Soporta interpolación con .format(**kwargs). Cache con lru_cache para rendimiento. |
get_language() | Retorna el idioma actual. Lee de HFL_LANG env var, default "en" |
set_language(lang) | Cambia el idioma en runtime y limpia la caché de traducciones |
_load_translations(lang) | Carga el archivo JSON del idioma desde locales/ |
_get_nested_value(data, key) | Navega diccionarios anidados con notación de puntos |
Cada idioma tiene un archivo JSON (~193 líneas) en src/hfl/i18n/locales/:
en.json (inglés) y es.json (español). Las claves siguen la estructura module.action.message, por ejemplo: commands.pull.downloading, commands.search.no_results, errors.model_not_found.
export HFL_LANG=es. Todos los comandos CLI utilizan t() para sus mensajes, permitiendo cambiar el idioma sin modificar código.
Todas heredan de HFLError(message, details) con __str__ que combina ambos. Cada excepción incluye datos contextuales específicos (model_name, repo_id, required_gb, etc.) para diagnóstico detallado.
HFL implementa un sistema exhaustivo de cumplimiento legal documentado con referencias a auditoría (R1-R9):
Verificación obligatoria antes de descarga. Clasificación en 5 niveles de riesgo. Presentación al usuario con panel visual. Aceptación explícita requerida para licencias no permisivas. Metadata de licencia persistida en ModelManifest.
ProvenanceLog inmutable registra cada conversión: origen, destino, herramienta, versión, licencia, timestamp. Advertencia legal en pantalla durante conversión: "la licencia original sigue vigente".
Campos en ModelManifest para clasificación GPAI: gpai_classification ("gpai", "gpai-systemic", "exempt") y training_flops. Archivo NOTICE-EU-AI-ACT.md.
Token HF solo en memoria. Logging privacy-safe: NUNCA se registran prompts, outputs AI, tokens, User-Agent. Solo metadata (método, path, status, duración). Archivo PRIVACY.md.
Rate limiting (0.5s entre llamadas API). User-Agent identificativo (hfl/0.1.0). Respeto al sistema de gating: NO se bypasea la aceptación de licencias. El usuario debe aceptar en huggingface.co primero.
Middleware DisclaimerMiddleware añade header X-AI-Disclaimer a todas las respuestas de endpoints AI. Disclaimer en chat CLI: "Los modelos AI pueden generar información incorrecta..."
LICENSE | HRUL v1.0 — Licencia propia del proyecto |
LICENSE-FAQ.md | Preguntas frecuentes sobre la licencia HRUL v1.0 |
LICENSE-DEPENDENCIES.md | Licencias de todas las dependencias de terceros |
PRIVACY.md | Política de privacidad (no se recopilan datos) |
NOTICE-EU-AI-ACT.md | Cumplimiento con EU AI Act |
DISCLAIMER.md | Exención de responsabilidad general |
La interfaz InferenceEngine (ABC) define el contrato. Tres implementaciones concretas (LlamaCpp, Transformers, vLLM) son intercambiables. El selector.py actúa como factory que elige la estrategia correcta según contexto.
Todas las dependencias pesadas (torch, transformers, vllm, llama-cpp-python) se importan solo cuando se necesitan. Los imports dentro de funciones evitan que una instalación mínima falle por dependencias opcionales ausentes.
core/container.py implementa un contenedor DI con singletons thread-safe (double-checked locking). Provee get_config(), get_registry(), get_state(), get_event_bus(), get_metrics(). Facilita testing con reset_container().
ChatMessage, GenerationConfig, GenerationResult, ModelManifest, ResolvedModel, ConversionRecord, LicenseInfo — todos son dataclasses inmutables o semi-inmutables que encapsulan datos.
El comando pull implementa un pipeline secuencial de 7 pasos: resolver → verificar licencia → descargar → detectar formato → convertir (condicional) → crear manifest → registrar. Cada paso es un componente desacoplado.
Los mismos motores de inferencia se exponen a través de dos interfaces API diferentes (OpenAI y Ollama) mediante routers separados que adaptan los formatos de request/response al formato esperado por cada ecosistema.
utils/circuit_breaker.py implementa circuit breaker para llamadas externas. utils/retry.py provee retry con exponential backoff configurable. Ambos mejoran la resiliencia ante fallos de red o servicios externos.
events.py implementa un EventBus interno para comunicación desacoplada entre componentes. Permite suscribirse a eventos como model_loaded, inference_complete, download_progress sin crear dependencias directas.
Las decisiones arquitectónicas importantes están documentadas en docs/adr/ siguiendo el formato estándar ADR:
| ADR | Título | Estado |
|---|---|---|
0001 | Singleton Pattern para Config y Registry | Accepted |
0002 | API Async con Engines Sync (sync_to_thread) | Accepted |
0003 | GGUF como Formato Default | Accepted |
0004 | Compatibilidad API Ollama | Accepted |
0005 | Clasificación de Licencias (5 niveles) | Accepted |
0006 | Estrategia de Rate Limiting | Accepted |
pytest + pytest-asyncio (asyncio_mode = "auto"). 80+ archivos de test con fixtures compartidas en conftest.py.
test_config.py | HFLConfig, paths, env vars |
test_container.py | DI container, Singleton pattern |
test_exceptions.py | Jerarquía de excepciones |
test_events.py | EventBus, pub/sub |
test_metrics.py | Métricas de rendimiento |
test_validators.py | Validación de datos |
test_security.py | Seguridad y sanitización |
test_api*.py (5) | Endpoints, auth, contracts |
test_routes_*.py (4) | OpenAI, Native, TTS |
test_state.py | ServerState, async locks |
test_streaming.py | SSE streaming |
test_model_loader.py | Carga dinámica de modelos |
test_helpers.py | ensure_llm/tts_loaded |
test_middleware.py | Privacy logger, disclaimer |
test_engine*.py (2) | Base, LlamaCpp |
test_selector*.py (3) | Auto-selección backend |
test_transformers*.py (2) | TransformersEngine |
test_vllm_engine.py | vLLM con mocks |
test_tts_*.py (2) | Bark, Coqui TTS |
test_async_wrapper.py | Sync→Async wrapper |
test_model_pool.py | Pool de modelos |
test_hub*.py (2) | Integración HF Hub |
test_downloader*.py (2) | Descarga con resume |
test_resolver*.py (2) | Resolución de modelos |
test_license_checker.py | Clasificación licencias |
test_auth.py | Autenticación HF |
test_cli*.py (4) | Comandos Typer |
test_converter*.py (3) | GGUF conversion |
test_i18n*.py (4) | Internacionalización |
test_circuit_breaker.py | Circuit breaker |
test_retry.py | Retry con backoff |
test_integration.py | Flujos end-to-end completos (pull→run→serve) |
test_concurrency.py | Concurrencia, thread safety, race conditions |
test_network_errors.py | Timeouts, disconnects, retry logic |
test_edge_cases.py | Inputs malformados, estados límite |
test_server_lifecycle.py | Startup, shutdown, cleanup |
| Signal handling CLI | Ctrl+C durante streaming preserva respuesta parcial |
| Middleware order | Verifica orden correcto de ejecucion del middleware stack |
| Exception handlers | Mapeo HFLError → HTTP status codes |
| Health probe | Deep health check con inferencia minima |
| Config env vars | Override de configuracion via variables de entorno |
| Model pool wait | Espera no recursiva (bounded polling) |
| Stress tests | Concurrent streaming, model pool stress |
| Timeout | Decorator @with_timeout y timeout configurable |
| Failover | Multi-engine retry con sticky routing |
| Per-model rate limit | Rate limiting diferenciado por modelo |
| Conversion caching | Cache de conversiones GGUF |
Fixtures principales (conftest.py): tmp_hfl_home (directorio temporal), mock_hf_api (mock HfApi), sample_manifest (modelo ejemplo), populated_registry (registro pre-populado), mock_engine (engine de inferencia mockeado), test_client (FastAPI TestClient).
# Ejecutar tests con cobertura
pytest --cov=hfl --cov-report=html --cov-fail-under=80
# Tests por categoría
pytest tests/test_api*.py -v # Solo API
pytest tests/test_engine*.py -v # Solo engines
pytest -k "not slow" -v # Excluir tests lentos
Configurado en pyproject.toml. Packages source en src/hfl. Entry point: hfl = "hfl.cli.main:app". Instalación con pip install . o pip install .[all] para todas las dependencias opcionales.
Spec para generar ejecutable standalone. Permite distribuir HFL como binario único sin requerir Python instalado. Se genera con pyinstaller hfl.spec y el resultado queda en dist/hfl.
~/.hfl/
├── models/ # Archivos de modelos descargados
│ └── org--model/ # Directorio por modelo (org--model format)
├── cache/ # Caché de HuggingFace Hub
├── tools/
│ └── llama.cpp/ # Herramientas compiladas para conversión
│ └── build/bin/ # Binarios: llama-quantize, etc.
├── models.json # Registro de modelos (array de ModelManifest)
└── provenance.json # Log inmutable de conversiones
| Workflow | Trigger | Descripción |
|---|---|---|
ci.yml | Push/PR a main | Pipeline completo: lint (ruff) + tests (pytest matrix Python 3.10/3.11/3.12) + type-check |
pages.yml | Push a main | Deploy automático de docs HTML a GitHub Pages |
build-executables.yml | Release/manual | Build ejecutables multiplataforma con PyInstaller |
lint.yml | Push/PR | Linting con ruff (E, F, W, I) |
test.yml | Push/PR | Tests con pytest + coverage |
security.yml | Programado/manual | Auditoría de seguridad de dependencias |
license-check.yml | Push/PR | Verificación de licencias de dependencias |
Issue Templates: bug_report.yml (formulario estructurado con info de sistema, pasos para reproducir, logs) y feature_request.yml (problema, solución propuesta, componente afectado).
PR Template: Checklist que incluye verificación de los 5 Compliance Modules (licencia, provenance, disclaimer, privacidad, gating) según requisitos de la HRUL.
Archivos de comunidad: CONTRIBUTING.md (guía de desarrollo, estilo de código, proceso de PR), CODE_OF_CONDUCT.md (Contributor Covenant v2.1), SECURITY.md (política de reporte de vulnerabilidades).
HFL v0.1.0 — Documentación de Arquitectura Exhaustiva
Actualizado el 7 de marzo de 2026 — Todos los diagramas son SVG interactivos
Licencia: HRUL v1.0 (source-available) — Copyright © 2026 Gabriel Galán Pelayo