library(vetiver)
library(pins)
board <- board_folder("model-store") # or board_s3(), board_connect(), ...
vetiver_pin_write(board, v)
vetiver_write_plumber(board, "gaussian_glm")The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
Once a model is pinned to a board (see vignette("vetiver")), the same /predict API can run in a container. This vignette shows a Dockerfile that works for inlabru fits, and why it differs from what vetiver_prepare_docker() would generate. The commands work with Docker and Podman alike.
vetiver_prepare_docker()?For most models vetiver_prepare_docker() writes a Dockerfile, a plumber.R and an renv lockfile in one go. It does not work for inlabru fits:
bru fits live in inlaops, so the container has to run library(inlaops) before building the API.So write plumber.R with vetiver and the Dockerfile by hand.
plumber.RPin the model to a board the container can reach, then let vetiver write the API file:
library(vetiver)
library(pins)
board <- board_folder("model-store") # or board_s3(), board_connect(), ...
vetiver_pin_write(board, v)
vetiver_write_plumber(board, "gaussian_glm")The generated plumber.R reads the model from the board when the API starts, so deploying a new version means pinning it and restarting the container — no rebuild.
Dockerfile# inlabru imports sf, so every image needs GDAL/GEOS/PROJ:
# rocker/geospatial ships them prebuilt.
FROM docker.io/rocker/geospatial:4.6.1
# INLA is not on CRAN.
RUN Rscript -e "install.packages('INLA', repos = c(getOption('repos'), \
INLA = 'https://inla.r-inla-download.org/R/stable'))"
# sn: INLA's posterior sampler needs it but only lists it in Suggests.
RUN Rscript -e "install.packages(c('inlabru', 'sn', 'vetiver', 'pins', \
'plumber', 'rapidoc', 'remotes'))"
RUN Rscript -e "remotes::install_github('novica/inlaops', upgrade = 'never')"
WORKDIR /opt/ml
# Only for a local board_folder(); remote boards are read at startup.
COPY model-store model-store
COPY plumber.R plumber.R
EXPOSE 8000
# Load inlaops first: it provides the handlers that serve bru fits.
ENTRYPOINT ["R", "-e", "library(inlaops); pr <- plumber::plumb('/opt/ml/plumber.R'); pr$run(host = '0.0.0.0', port = 8000)"]Notes:
COPY model-store line and pass credentials as environment variables at docker run time (see vignette("versioning")).docker build -t gaussian_glm .
docker run --rm -p 8000:8000 gaussian_glmThe API answers once INLA has loaded, after a few seconds:
curl http://localhost:8000/ping
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '[{"x1": 0.5, "x2": 1.0}, {"x1": -1.2, "x2": 0.3}]'
#> [{".pred":1.9943,".pred_low":1.893,".pred_high":2.1142,".pred_sd":0.057},
#> {".pred":0.9361,".pred_low":0.8352,".pred_high":1.0505,".pred_sd":0.0543}]Values differ slightly between calls because they come from posterior sampling.
Point-referenced fits (sf / sp points, SPDE fields, LGCP intensity surfaces) use the same image. The request carries coordinates, so the pin metadata needs coord_names and crs:
v <- vetiver_model(
fit_sf, "sf_points",
metadata = list(
pred_formula = "~ Intercept + x1",
coord_names = c("X", "Y"), # sf fits: sf::st_coordinates() names
crs = 4326L # EPSG integer or WKT string
)
)curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '[{"X": 10.3, "Y": 59.3, "x1": 0.8}]'See vignette("spatial") for the full request contract.
Polygon / areal fits (sf polygons, SpatialPolygonsDataFrame) are rejected at pin time. Until areal serving lands, use the custom-plumber pattern in vignette("lgcp") (section 3); its plumber.R goes into the same image.
The image is about 5 GB: rocker/geospatial plus INLA, which bundles binaries for several platforms. The first build takes several minutes; later builds reuse the cached layers until the package lines change.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.