Servicio de calculo predictivo con modelos matematicos intercambiables. No requiere login. No almacena datos. Solo calcula.
REST • JSON • Sin autenticacionUn servicio que recibe datos historicos (fecha, talla, biomasa, densidad) y devuelve una serie de valores futuros calculados mediante modelos matematicos, Machine Learning o Deep Learning.
Endpoint principal. Recibe datos y devuelve predicciones.
Verifica que la API esta funcionando. Retorna {"status": "ok"}.
Esta pagina de documentacion.
Muestra la configuracion actual (modelo por defecto, filtros activos, rangos).
Lista todos los modelos disponibles.
Lista todos los filtros de validacion disponibles.
Necesitas un array de mediciones historicas. Cada medicion requiere fecha
y talla. Los campos biomasa y densidad son opcionales.
{
"datos": [
{"fecha": "2024-01-01", "talla": 15.5, "biomasa": 120.0, "densidad": 0.8},
{"fecha": "2024-01-15", "talla": 18.2, "biomasa": 150.0, "densidad": 0.85},
{"fecha": "2024-02-01", "talla": 22.1, "biomasa": 200.0},
{"fecha": "2024-02-15", "talla": 25.8},
{"fecha": "2024-03-01", "talla": 28.3, "biomasa": 310.0, "densidad": 0.9}
],
"modelo": "logistic_growth", // opcional, si no se envia usa el modelo por defecto
"config": { // opcional
"horizon": 30, // dias a proyectar (default: 30)
"talla_objetivo": 45.0 // talla objetivo en mm (opcional)
}
}
{
"success": true,
"modelo_usado": "logistic_growth",
"predicciones": [
{"fecha": "2024-03-02", "talla": 29.1},
{"fecha": "2024-03-03", "talla": 29.8},
// ... 30 dias de predicciones
],
"parametros_modelo": {"L": 48.2, "k": 0.05, "x0": 42.1},
"metricas": {"r_squared": 0.97, "rmse": 1.23},
"incertidumbre": null,
"warnings": []
}
| Campo | Tipo | Requerido | Descripcion |
|---|---|---|---|
fecha | string (YYYY-MM-DD) | Si | Fecha de la medicion |
talla | number | Si | Talla en mm |
biomasa | number | null | No | Biomasa |
densidad | number | null | No | Densidad |
Slug del modelo a utilizar. Si no se indica, se usa el modelo por defecto configurado en el servidor.
| Campo | Tipo | Default | Descripcion |
|---|---|---|---|
horizon | integer | 30 | Dias futuros a proyectar |
talla_objetivo | number | null | null | Talla objetivo para calcular dias estimados |
parametros | object | {} | Parametros especificos del modelo |
| Campo | Tipo | Descripcion |
|---|---|---|
success | boolean | Si la prediccion fue exitosa |
modelo_usado | string | Slug del modelo que proceso los datos |
predicciones | array | Serie de puntos futuros proyectados |
parametros_modelo | object | Parametros ajustados del modelo |
metricas | object | Metricas de calidad (R², RMSE, etc.) |
incertidumbre | object | null | Bandas de incertidumbre si el modelo las genera |
warnings | array | Advertencias (pocos datos, ajuste pobre, etc.) |
Puedes elegir cualquiera enviando su slug en el campo modelo del request.
Si no envias el campo, se usa el modelo por defecto configurado en el servidor.
"modelo": "slug" en el request → se usa ese modelo.modelo → se usa el modelo por defecto (ACTIVE_MODEL en .env).modelo.
Todo modelo implementa la clase BasePredictionModel con:
| Atributo | Tipo | Descripcion |
|---|---|---|
slug | string | Identificador unico (e.g. "logistic_growth") |
name | string | Nombre legible (e.g. "Crecimiento Logistico") |
description | string | Descripcion breve del modelo |
Y un unico metodo obligatorio:
Recibe un objeto PredictionInput con:
| Campo | Tipo | Descripcion |
|---|---|---|
datos | list[Measurement] | Array de mediciones historicas (fecha, talla, biomasa, densidad) |
config.horizon | int | Dias futuros a proyectar |
config.talla_objetivo | float | null | Talla objetivo (opcional) |
config.parametros | dict | Parametros adicionales libres para el modelo |
Debe retornar un objeto PredictionOutput con:
| Campo | Tipo | Requerido | Descripcion |
|---|---|---|---|
success | bool | Si | Si la prediccion fue exitosa |
modelo_usado | string | Si | El slug del modelo (self.slug) |
predicciones | list[PredictionPoint] | Si | Puntos futuros: cada uno con fecha y opcionalmente talla, biomasa, densidad |
parametros_modelo | dict | No | Parametros ajustados internos (e.g. coeficientes de la curva) |
metricas | dict | No | Metricas de calidad del ajuste (R², RMSE, etc.) |
incertidumbre | dict | null | No | Bandas de incertidumbre si el modelo las genera |
warnings | list[string] | No | Advertencias (pocos datos, ajuste pobre, etc.) |
# app/models/mi_modelo.py from app.models.base import BasePredictionModel from app.schemas.prediction import PredictionInput, PredictionOutput, PredictionPoint from datetime import timedelta class MiModelo(BasePredictionModel): slug = "mi_modelo" name = "Mi Modelo Personalizado" description = "Descripcion de que hace este modelo." def predict(self, payload: PredictionInput) -> PredictionOutput: datos = payload.datos horizon = payload.config.horizon # ... tu logica de prediccion aqui ... predicciones = [] ultima_fecha = datos[-1].fecha for i in range(1, horizon + 1): predicciones.append(PredictionPoint( fecha=ultima_fecha + timedelta(days=i), talla=42.0, # tu calculo )) return PredictionOutput( success=True, modelo_usado=self.slug, predicciones=predicciones, parametros_modelo={"mi_param": 1.5}, metricas={"r_squared": 0.95}, )
Luego registrarlo en app/models/registry.py:
from app.models.mi_modelo import MiModelo MODEL_REGISTRY = { # ... modelos existentes ... "mi_modelo": MiModelo(), }
Listo. Ya se puede usar con "modelo": "mi_modelo" en el request.
Antes de ejecutar el modelo, los datos pasan por los filtros activos en orden.
Si algun filtro detecta errores, el request es rechazado con un 422 y detalles de que corregir.
Los filtros activos se configuran en el panel HTML (/) o en .env.
Todo filtro implementa la clase BaseFilter con:
| Atributo | Tipo | Descripcion |
|---|---|---|
slug | string | Identificador unico (e.g. "non_negative") |
name | string | Nombre legible (e.g. "No Negativos") |
description | string | Descripcion breve |
Y un unico metodo obligatorio:
Recibe el mismo PredictionInput que el modelo (antes de que el modelo lo procese):
| Campo | Tipo | Lo que puedes validar |
|---|---|---|
payload.datos | list[Measurement] | Iterar cada fila: fecha, talla, biomasa, densidad |
payload.config | PredictionConfig | Horizon, talla_objetivo, parametros |
payload.modelo | string | null | Modelo solicitado (si aplica) |
Debe retornar un objeto FilterResult con:
| Campo | Tipo | Descripcion |
|---|---|---|
valid | bool | True si pasa la validacion, False si hay errores |
errors | list[string] | Lista de errores. Si hay errores, el request se rechaza con 422. |
warnings | list[string] | Advertencias no bloqueantes. Se agregan al campo warnings de la respuesta. |
valid = False, el request se detiene y se devuelve un error 422
con todos los mensajes de errors. Si valid = True pero hay warnings,
el request continua y las advertencias se incluyen en la respuesta.
# app/filters/mi_filtro.py from app.filters.base import BaseFilter, FilterResult from app.schemas.prediction import PredictionInput class MiFiltro(BaseFilter): slug = "mi_filtro" name = "Mi Filtro Personalizado" description = "Valida que los datos cumplan mi regla." def validate(self, payload: PredictionInput) -> FilterResult: errors = [] warnings = [] for i, row in enumerate(payload.datos, start=1): # Ejemplo: validar que haya al menos biomasa o densidad if row.biomasa is None and row.densidad is None: warnings.append( f"Fila {i}: sin biomasa ni densidad, prediccion menos precisa." ) # Ejemplo: validar minimo de datos if len(payload.datos) < 3: errors.append("Se requieren al menos 3 mediciones.") return FilterResult( valid=len(errors) == 0, errors=errors, warnings=warnings, )
Luego registrarlo en app/filters/registry.py:
from app.filters.mi_filtro import MiFiltro FILTER_REGISTRY = { # ... filtros existentes ... "mi_filtro": MiFiltro(), }
Luego activarlo en .env o desde el panel HTML (/):
# .env ACTIVE_FILTERS=required_fields,non_negative,range_validation,mi_filtro
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{
"datos": [
{"fecha": "2024-01-01", "talla": 15.5, "biomasa": 120.0},
{"fecha": "2024-01-15", "talla": 18.2, "biomasa": 150.0},
{"fecha": "2024-02-01", "talla": 22.1},
{"fecha": "2024-02-15", "talla": 25.8},
{"fecha": "2024-03-01", "talla": 28.3}
],
"config": {"horizon": 15}
}'
const response = await fetch("http://localhost:8000/predict", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ datos: [ { fecha: "2024-01-01", talla: 15.5 }, { fecha: "2024-01-15", talla: 18.2 }, { fecha: "2024-02-01", talla: 22.1 }, { fecha: "2024-02-15", talla: 25.8 }, { fecha: "2024-03-01", talla: 28.3 } ], modelo: "von_bertalanffy", config: { horizon: 30, talla_objetivo: 45.0 } }) }); const data = await response.json(); console.log(data.predicciones);
import requests resp = requests.post("http://localhost:8000/predict", json={ "datos": [ {"fecha": "2024-01-01", "talla": 15.5}, {"fecha": "2024-01-15", "talla": 18.2}, {"fecha": "2024-02-01", "talla": 22.1}, {"fecha": "2024-02-15", "talla": 25.8}, {"fecha": "2024-03-01", "talla": 28.3}, ], "modelo": "gompertz", }) data = resp.json() for p in data["predicciones"]: print(p["fecha"], p["talla"])