Spaces:
Sleeping
title: Oraculo Adult Income API
emoji: 🧠
colorFrom: green
colorTo: blue
sdk: docker
app_port: 7860
base_path: /docs
pinned: false
🧠 Oráculo Adult Income API
API REST para inferencia del problema Adult Census Income, con autenticación, persistencia, trazabilidad, validaciones estrictas, middlewares de seguridad y una capa de compatibilidad entre el notebook de entrenamiento y el backend de producción.
✨ Qué es este proyecto
oraculo_api es el backend de inferencia estructurada del ecosistema Oráculo.
Su responsabilidad no es solamente “cargar un .pkl y responder una predicción”, sino construir una frontera sólida entre:
- el trabajo analítico hecho en notebook,
- el artefacto serializado del modelo,
- el contrato HTTP consumido por otras capas,
- la autenticación de usuarios,
- y la trazabilidad persistente de cada inferencia.
En términos prácticos, este servicio permite:
- registrar usuarios;
- autenticar con JWT;
- validar payloads del dataset Adult Income;
- ejecutar predicciones sobre el pipeline cargado;
- guardar historial por usuario;
- consultar predicciones anteriores;
- verificar salud de la API, del modelo y de la base de datos;
- desplegar localmente, en Docker, Render o Hugging Face Spaces.
⚠️ Además, el backend incluye una capa de compatibilidad para artefactos exportados desde notebook. Si el
.pklno serializa todas las recetas de feature engineering, la clasePipelineProduccionMLOpspuede reconstruir parte de esas reglas a partir de artefactos y del dataset de referenciaadult.csv.
🎯 Objetivo del backend
Esta API está diseñada para resolver cuatro necesidades del proyecto:
- Exponer inferencia de modelo como servicio HTTP estable y autenticado.
- Blindar el salto notebook → backend, evitando que el modelo quede atrapado en un entorno puramente exploratorio.
- Persistir evidencia operativa de cada solicitud de predicción: payload, versión del modelo, request id, latencia y usuario.
- Servir como capa fuente de verdad para otras aplicaciones del ecosistema, como el agente IA y la interfaz web.
🧱 Stack tecnológico
Backend y servidor
- FastAPI: framework principal de la API.
- Uvicorn: servidor ASGI.
- Starlette middlewares: capa de compresión, hosts confiables y middleware base.
- python-multipart: soporte de cuerpos multipart cuando sea necesario.
Configuración y seguridad
- Pydantic v2: validaciones, contratos de entrada y salida.
- pydantic-settings: configuración por variables de entorno.
- python-dotenv: soporte para
.envlocal. - bcrypt: hashing de contraseñas.
- PyJWT: creación y validación de tokens JWT.
Persistencia y migraciones
- SQLAlchemy 2.0: ORM y acceso a base de datos.
- Alembic: migraciones versionadas.
- SQLite por defecto, con soporte para cambiar a PostgreSQL vía
DATABASE_URL.
ML y datos
- joblib: carga del artefacto serializado.
- LightGBM: modelo principal del pipeline.
- numpy, pandas, scikit-learn, scipy: base del pipeline tabular y de las transformaciones.
Testing
- pytest: framework de pruebas.
- httpx / TestClient: validación de endpoints y contrato HTTP.
- pytest-asyncio: soporte adicional para contextos asíncronos de prueba.
📦 Dependencias exactas (requirements.txt)
API / Web
fastapi==0.135.3uvicorn==0.44.0httptools==0.7.1watchfiles==1.1.1websockets==16.0python-multipart==0.0.26
Configuración / Seguridad
bcrypt==5.0.0PyJWT==2.12.1pydantic==2.12.5pydantic-settings==2.13.1python-dotenv==1.2.2
Base de datos / ORM / Migraciones
SQLAlchemy==2.0.49alembic==1.18.4
Machine Learning / Data
joblib==1.5.3lightgbm==4.6.0numpy==2.4.4pandas==3.0.2scikit-learn==1.8.0scipy==1.17.1
Testing
httpx==0.28.1pytest==9.0.3pytest-asyncio==1.3.0
🗂️ Estructura real del proyecto
oraculo_api/
├── .dockerignore
├── .env.example
├── Dockerfile
├── README.md
├── requirements.txt
├── alembic.ini
├── adult.csv
├── compas-scores-raw.csv
├── EDA_For_All_Tree_clean.ipynb
├── oraculo.db
├── alembic/
│ └── env.py
├── app/
│ ├── main.py
│ ├── api/
│ │ ├── dependencies.py
│ │ ├── router.py
│ │ └── v1/
│ │ ├── router.py
│ │ └── endpoints/
│ │ ├── auth.py
│ │ ├── health.py
│ │ └── predictions.py
│ ├── core/
│ │ ├── config.py
│ │ ├── error_handlers.py
│ │ ├── exceptions.py
│ │ ├── logging.py
│ │ ├── middleware.py
│ │ └── security.py
│ ├── db/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── session.py
│ │ ├── seeds.py
│ │ ├── models/
│ │ │ ├── __init__.py
│ │ │ ├── user.py
│ │ │ └── prediction_log.py
│ │ └── repositories/
│ │ ├── __init__.py
│ │ ├── users.py
│ │ └── predictions.py
│ ├── ml/
│ │ ├── custom_transformers.py
│ │ ├── model_manager.py
│ │ └── pipeline_produccion.pkl
│ ├── schemas/
│ │ ├── auth.py
│ │ ├── common.py
│ │ ├── health.py
│ │ └── prediction.py
│ └── services/
│ ├── __init__.py
│ ├── auth.py
│ ├── health.py
│ └── prediction.py
└── tests/
├── __init__.py
├── conftest.py
└── api/
├── test_auth_api.py
├── test_health_api.py
└── test_prediction_api.py
🧭 Arquitectura por capas
flowchart TD
A[Cliente / Swagger / Web / Agente] --> B[FastAPI Routers]
B --> C[Dependencies]
C --> D[Services]
D --> E1[Repositories / SQLAlchemy]
D --> E2[ModelManager / PipelineProduccionMLOps]
E1 --> F[(SQLite / PostgreSQL)]
E2 --> G[pipeline_produccion.pkl]
E2 --> H[model_manifest.json]
E2 --> I[adult.csv]
Flujo interno de una predicción
- El cliente envía un
POST /api/v1/predictions. - FastAPI valida el payload con
PredictionInput. get_current_userexige un JWT válido.PredictionServicetransforma el payload en dos versiones:- input payload con aliases del contrato HTTP;
- normalized payload con nombres internos Python-friendly.
ModelManager.predict_one()ejecuta el pipeline cargado.- Se calcula la latencia.
PredictionRepository.create()persiste el evento completo enprediction_logs.- La respuesta vuelve con
id,prediction,probability,request_id,model_versiony payloads trazables.
🧠 Qué hace cada capa
app/main.py
Es el punto de entrada de la aplicación.
Responsabilidades:
- crear la instancia FastAPI;
- configurar logging;
- inicializar
engineysession_factory; - crear tablas automáticamente si está habilitado;
- seedear un administrador inicial;
- cargar el modelo al arrancar;
- instalar middlewares;
- registrar handlers globales de error;
- exponer el endpoint raíz
/.
app/api/
Contiene la superficie HTTP del sistema.
router.py: compone el router principal.v1/router.py: agrupa los endpoints bajo/api/v1.v1/endpoints/auth.py: registro, login yme.v1/endpoints/health.py: saludliveyready.v1/endpoints/predictions.py: creación, listado y consulta de predicciones.
app/api/dependencies.py
Resuelve dependencias reutilizables de FastAPI:
- settings actuales;
- model manager cargado en
app.state; - sesiones de base de datos;
- repositorios;
- servicios;
- usuario autenticado;
- usuario administrador.
app/core/
Es la capa transversal del backend.
config.py: defineSettings, lee variables de entorno y normaliza listas comoALLOWED_HOSTSyCORS_ALLOW_ORIGINS.security.py: hashing, verificación de passwords, creación y decodificación de JWT, extracción del bearer token.middleware.py: request id, client ip, headers de seguridad, límite de tamaño y rate limiting.exceptions.py: errores tipados de dominio y operación.error_handlers.py: respuestas JSON consistentes para errores esperados e inesperados.logging.py: configura logging estructurado a nivel global.
app/db/
Contiene persistencia y modelo relacional.
base.py:DeclarativeBase, naming convention yTimestampMixin.session.py: engine, factory de sesiones, chequeo de conexión y manejo transaccional.models/user.py: entidadusers.models/prediction_log.py: entidadprediction_logs.repositories/users.py: consultas y creación de usuarios.repositories/predictions.py: creación, listado filtrado y consulta de predicciones por usuario.seeds.py: creación opcional del admin bootstrap.
app/services/
Aquí está la lógica de negocio, separada del transporte HTTP.
AuthService: registro, login y recuperación de usuario.HealthService: estado de vida y readiness real.PredictionService: inferencia, hashing de payload, persistencia y respuestas paginadas.
app/ml/
Es la capa de integración con el artefacto del modelo.
model_manager.py: carga el.pkl, leemodel_manifest.json, verifica estado y exponepredict,predict_probaypredict_one.custom_transformers.py: definePipelineProduccionMLOps, que actúa como puente de compatibilidad entre el notebook exportado y el backend.
app/schemas/
Modelos Pydantic del contrato público.
auth.py: DTOs de registro, login, token y usuario.health.py: respuestas de health.prediction.py: payload de entrada, detalle de predicción y lista paginada.common.py: base schema y metadatos de paginación.
tests/
La suite prueba el contrato HTTP desacoplándolo del modelo real cuando conviene.
conftest.pycreaFakeModelManager, app de prueba, token, headers y payload válido.test_auth_api.pyvalida registro, conflicto, login yme.test_health_api.pyvalida raíz,liveyready.test_prediction_api.pyvalida autenticación, creación, filtros, aislamiento por usuario, payload grande, errores y recuperación por id.
🔐 Seguridad implementada
Autenticación
- Login con email y password.
- Emisión de JWT firmado con
HS256por defecto. - Expiración configurable (
ORACULO_ACCESS_TOKEN_EXPIRE_MINUTES). - Endpoint
/api/v1/auth/mepara resolver el usuario autenticado.
Contraseñas
- Hash con bcrypt.
- Si la contraseña supera el límite interno de bcrypt (72 bytes), el código hace pre-hashing SHA-256 antes de
bcrypt.hashpw, evitando truncamientos silenciosos.
Validación de entrada
extra="forbid"en esquemas sensibles para bloquear campos sorpresa.- Validaciones de longitud, tipo y rango.
- Passwords fuertes obligatorias en registro:
- mayúscula,
- minúscula,
- número,
- carácter especial,
- longitud mínima de 12.
Middlewares defensivos
- TrustedHostMiddleware para hosts permitidos.
- GZipMiddleware para compresión de respuestas.
- SecurityHeadersMiddleware con:
X-Content-Type-Options: nosniffReferrer-Policy: no-referrerPermissions-PolicyCache-Control: no-storePragma: no-cacheContent-Security-Policy
- MaxRequestSizeMiddleware para rechazar payloads demasiado grandes.
- RateLimitMiddleware in-memory por IP.
- RequestContextMiddleware para generar
X-Request-IDyX-Process-Time-MS.
Errores controlados
Todos los errores retornan JSON consistente bajo la forma:
{
"error": {
"code": "validation_error",
"message": "Request validation failed.",
"detail": {},
"request_id": "..."
}
}
🗃️ Modelo de datos
Tabla users
Campos principales:
idemailfull_namepassword_hashroleis_activecreated_atupdated_at
Tabla prediction_logs
Campos principales:
iduser_idrequest_idip_addresslabelprobabilitylatency_msmodel_versionpayload_hashinput_payloadnormalized_payloadnotescreated_atupdated_at
Esto convierte cada inferencia en un evento auditable y recuperable.
🤖 Integración con el modelo
ModelManager
El ModelManager es el punto central de carga e inferencia. Se encarga de:
- cargar el artefacto
pipeline_produccion.pkl; - registrar una clase puente en
__main__para quejoblibpueda deserializar objetos definidos originalmente en notebook; - intentar leer
model_manifest.jsonsi existe; - exponer
predict,predict_probaypredict_one; - encapsular fallas como
ServiceUnavailableErroroModelInferenceError.
PipelineProduccionMLOps
Esta clase existe para hacer el artefacto más robusto en producción.
Responsabilidades principales:
- normalizar nombres de columnas (
snake_case, puntos, caracteres raros); - limpiar texto categórico;
- reconstruir recetas faltantes si el notebook no serializó todo correctamente;
- aplicar rare labeling;
- aplicar target encoding y mapeos binarios;
- reconstruir fórmulas derivadas;
- generar ratios matemáticos;
- aplicar winsorización;
- reusar escaladores si existen;
- eliminar columnas de fuga o basura si están declaradas;
- realinear el DataFrame final con las features esperadas por el modelo entrenado.
Artefactos esperados
El backend espera, como mínimo:
app/ml/pipeline_produccion.pkl
Y opcionalmente:
app/ml/model_manifest.json
Adicionalmente, puede usar:
adult.csvcomo dataset de referencia para recomponer recetas faltantes de ingeniería de features.
🌱 Variables de entorno
Crea un archivo .env local a partir de .env.example.
Variables principales
| Variable | Descripción |
|---|---|
ORACULO_APP_NAME |
Nombre público del servicio. |
ORACULO_APP_VERSION |
Versión de la API. |
ORACULO_ENVIRONMENT |
Entorno (local, development, test, staging, production). |
ORACULO_DEBUG |
Activa modo debug. |
ORACULO_DATABASE_URL |
URL de conexión a BD. Por defecto SQLite local. |
ORACULO_DATABASE_ECHO |
Log SQL de SQLAlchemy. |
ORACULO_AUTO_CREATE_TABLES |
Crea tablas automáticamente al arrancar. |
ORACULO_AUTO_SEED_ADMIN |
Activa siembra automática de admin. |
ORACULO_SEED_ADMIN_EMAIL |
Email del admin bootstrap. |
ORACULO_SEED_ADMIN_PASSWORD |
Password del admin bootstrap. |
ORACULO_SEED_ADMIN_NAME |
Nombre visible del admin bootstrap. |
ORACULO_MODEL_PATH |
Ruta al .pkl de producción. |
ORACULO_JWT_SECRET_KEY |
Clave secreta JWT. |
ORACULO_JWT_ALGORITHM |
Algoritmo JWT. |
ORACULO_ACCESS_TOKEN_EXPIRE_MINUTES |
Duración del token. |
ORACULO_ALLOWED_HOSTS |
Hosts permitidos. |
ORACULO_CORS_ALLOW_ORIGINS |
Orígenes CORS permitidos. |
ORACULO_MAX_REQUEST_SIZE_BYTES |
Tamaño máximo del body. |
ORACULO_RATE_LIMIT_ENABLED |
Activa rate limit. |
ORACULO_RATE_LIMIT_REQUESTS |
Número máximo de requests por ventana. |
ORACULO_RATE_LIMIT_WINDOW_SECONDS |
Duración de la ventana. |
ORACULO_DOCS_ENABLED |
Activa/desactiva /docs, /redoc y /openapi.json. |
Ejemplo .env
ORACULO_APP_NAME=Oraculo Adult Income API
ORACULO_APP_VERSION=2.0.0
ORACULO_ENVIRONMENT=development
ORACULO_DEBUG=false
ORACULO_DATABASE_URL=sqlite:///./oraculo.db
ORACULO_DATABASE_ECHO=false
ORACULO_AUTO_CREATE_TABLES=true
ORACULO_AUTO_SEED_ADMIN=true
ORACULO_SEED_ADMIN_EMAIL=admin@example.com
ORACULO_SEED_ADMIN_PASSWORD=ChangeMe!12345
ORACULO_SEED_ADMIN_NAME=Administrator
ORACULO_MODEL_PATH=app/ml/pipeline_produccion.pkl
ORACULO_JWT_SECRET_KEY=replace-this-with-a-long-random-secret-at-least-32-chars
ORACULO_JWT_ALGORITHM=HS256
ORACULO_ACCESS_TOKEN_EXPIRE_MINUTES=60
ORACULO_ALLOWED_HOSTS=localhost,127.0.0.1,*.hf.space,*.huggingface.co
ORACULO_CORS_ALLOW_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
ORACULO_MAX_REQUEST_SIZE_BYTES=32768
ORACULO_RATE_LIMIT_ENABLED=true
ORACULO_RATE_LIMIT_REQUESTS=60
ORACULO_RATE_LIMIT_WINDOW_SECONDS=60
ORACULO_DOCS_ENABLED=true
🚀 Instalación local paso a paso
1) Clonar el proyecto
git clone https://github.com/DiiegoA/Proyecto_modelo_IA.git
cd Proyecto_modelo_IA/oraculo_api
2) Crear entorno virtual
Linux / macOS
python -m venv .venv
source .venv/bin/activate
Windows (PowerShell)
python -m venv .venv
.\.venv\Scripts\Activate.ps1
3) Instalar dependencias
pip install --upgrade pip
pip install -r requirements.txt
4) Configurar variables de entorno
cp .env.example .env
En Windows, copia el archivo manualmente o usa el explorador.
5) Ejecutar migraciones
alembic upgrade head
6) Iniciar el servidor
uvicorn app.main:app --reload
7) Abrir documentación interactiva
- Swagger UI:
http://127.0.0.1:8000/docs - ReDoc:
http://127.0.0.1:8000/redoc
🧪 Cómo probar la API
Health checks
curl http://127.0.0.1:8000/
curl http://127.0.0.1:8000/api/v1/health/live
curl http://127.0.0.1:8000/api/v1/health/ready
Registrar usuario
curl -X POST http://127.0.0.1:8000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"full_name": "Test User",
"password": "StrongPass!123"
}'
Login
curl -X POST http://127.0.0.1:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "StrongPass!123"
}'
Crear predicción
curl -X POST http://127.0.0.1:8000/api/v1/predictions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_TOKEN" \
-d '{
"age": 45,
"workclass": "Private",
"fnlwgt": 250000,
"education": "Masters",
"education.num": 14,
"marital.status": "Married-civ-spouse",
"occupation": "Exec-managerial",
"relationship": "Husband",
"race": "White",
"sex": "Male",
"capital.gain": 15000,
"capital.loss": 0,
"hours.per.week": 50,
"native.country": "United-States"
}'
Listar historial
curl -H "Authorization: Bearer TU_TOKEN" \
"http://127.0.0.1:8000/api/v1/predictions?skip=0&limit=20"
Filtrar historial
curl -H "Authorization: Bearer TU_TOKEN" \
"http://127.0.0.1:8000/api/v1/predictions?label=%3E50K&min_probability=0.8"
Consultar una predicción por id
curl -H "Authorization: Bearer TU_TOKEN" \
http://127.0.0.1:8000/api/v1/predictions/PREDICTION_ID
📡 Endpoints disponibles
Salud
| Método | Ruta | Descripción |
|---|---|---|
GET |
/ |
Metadatos básicos del servicio. |
GET |
/api/v1/health/live |
Verifica que la API está viva. |
GET |
/api/v1/health/ready |
Verifica base de datos + modelo cargado. |
Autenticación
| Método | Ruta | Descripción |
|---|---|---|
POST |
/api/v1/auth/register |
Registro de usuario. |
POST |
/api/v1/auth/login |
Login y emisión de token. |
GET |
/api/v1/auth/me |
Usuario autenticado actual. |
Predicciones
| Método | Ruta | Descripción |
|---|---|---|
POST |
/api/v1/predictions |
Ejecuta una predicción y la persiste. |
GET |
/api/v1/predictions |
Lista historial paginado del usuario. |
GET |
/api/v1/predictions/{prediction_id} |
Recupera una predicción puntual del usuario. |
🧾 Contrato de entrada para predicción
El payload que espera la API está alineado con el dataset Adult Income y admite aliases con puntos:
| Campo HTTP | Campo normalizado | Tipo |
|---|---|---|
age |
age |
int |
workclass |
workclass |
str |
fnlwgt |
fnlwgt |
int |
education |
education |
str |
education.num |
education_num |
int |
marital.status |
marital_status |
str |
occupation |
occupation |
str |
relationship |
relationship |
str |
race |
race |
str |
sex |
sex |
Male | Female |
capital.gain |
capital_gain |
int |
capital.loss |
capital_loss |
int |
hours.per.week |
hours_per_week |
int |
native.country |
native_country |
str |
Reglas de validación destacadas
age: entre17y100fnlwgt: entre1y2_000_000education.num: entre1y16capital.gain: entre0y100_000capital.loss: entre0y10_000hours.per.week: entre1y99- categorías no vacías, sin caracteres de control y con longitud máxima razonable
📚 Respuesta de predicción
Una predicción devuelve información útil tanto para negocio como para auditoría:
{
"id": "uuid",
"prediction": ">50K",
"probability": 0.91,
"is_counterfactual_applied": false,
"execution_time_ms": 12.34,
"model_version": "1.0.0",
"request_id": "uuid",
"created_at": "2026-01-01T00:00:00Z",
"input_payload": {},
"normalized_payload": {}
}
Diferencia entre input_payload y normalized_payload
input_payload: conserva la forma del contrato HTTP, incluyendo aliases comoeducation.num.normalized_payload: usa nombres internos Python (education_num,hours_per_week, etc.).
Esto es útil para depuración, auditoría y trazabilidad del contrato público frente al contrato interno.
🧪 Tests
La suite existente valida el comportamiento público del backend con una estrategia pragmática:
Qué se prueba
- registro exitoso;
- rechazo por usuario duplicado;
- login correcto;
- login con credenciales inválidas;
- endpoint
/meprotegido; - raíz
/; health/liveyhealth/ready;- creación de predicción autenticada;
- rechazo de payload inválido;
- rechazo de payload excesivo;
- filtros por
labelymin_probability; - consulta por
prediction_id; - aislamiento de historial entre usuarios;
- mapeo controlado de errores del modelo.
Cómo se ejecutan
pytest -q
Estrategia de pruebas
La suite usa un FakeModelManager en tests/conftest.py para:
- desacoplar el contrato HTTP del artefacto real;
- acelerar las pruebas;
- validar reglas de negocio y seguridad sin depender siempre del
.pkl.
🛠️ Migraciones con Alembic
Aplicar migraciones
alembic upgrade head
Crear una nueva migración
alembic revision --autogenerate -m "descripcion"
Revertir una migración
alembic downgrade -1
Qué hace alembic/env.py
- carga
Settingsreales del entorno; - inyecta
settings.database_urlen la configuración de Alembic; - usa
Base.metadatacomo fuente de verdad del esquema; - permite migraciones offline y online.
👤 Seed automático de administrador
Si activas estas variables:
ORACULO_AUTO_SEED_ADMIN=true
ORACULO_SEED_ADMIN_EMAIL=admin@example.com
ORACULO_SEED_ADMIN_PASSWORD=ChangeMe!12345
ORACULO_SEED_ADMIN_NAME=Administrator
al arrancar la aplicación se crea un administrador si no existe uno previo con ese email.
Esto es útil para bootstrap local, demos o primeros despliegues.
🐳 Docker
Qué hace el Dockerfile
- usa
python:3.11-slimcomo base; - instala
libgomp1para dependencias del stack de ML; - crea un usuario no root;
- instala dependencias desde
requirements.txt; - copia el proyecto completo al contenedor;
- expone el puerto
7860; - ejecuta
alembic upgrade headantes de levantar Uvicorn.
Construcción
docker build -t oraculo-api .
Ejecución
docker run --rm -p 7860:7860 --env-file .env oraculo-api
Comando final del contenedor
alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 7860
🤗 Despliegue en Hugging Face Spaces
Este repositorio ya está preparado para un Docker Space.
Puntos importantes
- El front matter al inicio del README define
sdk: docker,app_port: 7860ybase_path: /docs. - El contenedor escucha en
7860. - Swagger queda accesible en
/docs. - Los headers de seguridad contemplan el caso especial de embebido en dominios
*.hf.spacey*.huggingface.co.
Variables mínimas recomendadas en Spaces
ORACULO_JWT_SECRET_KEYORACULO_ALLOWED_HOSTSORACULO_DOCS_ENABLEDORACULO_DATABASE_URLORACULO_SEED_ADMIN_EMAILORACULO_SEED_ADMIN_PASSWORD
Persistencia en Spaces
Si quieres que SQLite sobreviva reinicios, usa almacenamiento persistente y configura:
ORACULO_DATABASE_URL=sqlite:////data/oraculo.db
☁️ Despliegue en Render
Start Command sugerido
alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port $PORT
Variables mínimas recomendadas
ORACULO_ENVIRONMENT=production
ORACULO_DATABASE_URL=<postgres-url>
ORACULO_JWT_SECRET_KEY=<secret-largo>
ORACULO_ALLOWED_HOSTS=<tu-dominio>
ORACULO_DOCS_ENABLED=false
🧯 Troubleshooting
1. El modelo no carga
Revisa:
- que
ORACULO_MODEL_PATHapunte realmente aapp/ml/pipeline_produccion.pkl; - que el archivo exista dentro del contenedor o del entorno local;
- que
joblibpueda deserializar el artefacto; - que, si usas un manifiesto,
model_manifest.jsonesté bien formado.
2. ready devuelve degradado o error
Revisa:
- conexión a la base de datos;
- permisos del archivo SQLite;
- existencia del
.pkl; - carga correcta del modelo al arrancar.
3. El App tab de Hugging Face no muestra la app aunque /docs responde
Esto suele estar relacionado con headers de embebido. El proyecto ya contempla el caso *.hf.space, pero vale la pena revisar:
ORACULO_ALLOWED_HOSTS;- si el Space tiene la última versión desplegada;
- si hiciste
Factory reboottras cambios de seguridad; - si
ORACULO_DOCS_ENABLED=true.
4. SQLite se reinicia
Si el proveedor no tiene almacenamiento persistente, la BD local es efímera. En producción real, usa Postgres o un volumen persistente.
📈 Mejoras futuras naturales
Si quieres endurecer todavía más esta API, las siguientes mejoras son coherentes con la arquitectura actual:
- rate limiting distribuido con Redis;
- refresh tokens;
- observabilidad con Prometheus u OpenTelemetry;
- roles más finos (
admin,analyst,service); - pipeline CI con lint, type-check y cobertura;
- separación formal entre API pública e interna;
- Postgres como default de desarrollo colaborativo;
- versionado explícito del contrato de inferencia y del manifiesto del modelo.
✅ Resumen ejecutivo
oraculo_api ya no es un backend improvisado alrededor de un notebook.
Hoy es una base con:
- arquitectura por capas;
- contrato HTTP claro;
- autenticación JWT;
- hashing robusto de contraseñas;
- middlewares de seguridad;
- validación estricta de payloads;
- persistencia auditada de predicciones;
- health checks reales;
- migraciones con Alembic;
- compatibilidad con artefactos exportados desde notebook;
- pruebas automatizadas sobre el contrato principal.
En otras palabras: esta carpeta es la capa de inferencia seria y trazable del ecosistema Oráculo.
📌 Recomendación final de uso
Si alguien entra por primera vez a oraculo_api, el orden ideal para entenderlo es:
README.mdapp/main.pyapp/api/v1/endpoints/app/services/app/db/app/ml/model_manager.pyapp/ml/custom_transformers.pytests/
Ese recorrido permite entender primero la interfaz pública, luego la lógica de negocio, después la persistencia y, por último, la capa de compatibilidad del modelo.