From 604ee49390cc1701f3da9de0311beed9151737d7 Mon Sep 17 00:00:00 2001 From: Mantra Date: Tue, 26 May 2026 10:43:56 +0100 Subject: [PATCH 1/5] wip --- DESCRIPTION | 2 +- R/api.R | 13 +++++++++++ R/odin.R | 14 ++++++++++++ R/porcelain.R | 9 ++++++++ docker/Dockerfile | 5 +++++ inst/schema/compile2_request.json | 11 ++++++++++ inst/schema/compile2_response.json | 35 ++++++++++++++++++++++++++++++ 7 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 inst/schema/compile2_request.json create mode 100644 inst/schema/compile2_response.json diff --git a/DESCRIPTION b/DESCRIPTION index d2e763b..46d344b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,7 +9,7 @@ Description: API server for odin models, using porcelain/plumber. License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE, roclets = c("rd", "namespace", "porcelain::porcelain_roclet")) -RoxygenNote: 7.2.1 +RoxygenNote: 7.3.3 URL: https://github.com/mrc-ide/odin.api BugReports: https://github.com/mrc-ide/odin.api/issues Imports: diff --git a/R/api.R b/R/api.R index 2f76fe5..3202d71 100644 --- a/R/api.R +++ b/R/api.R @@ -52,6 +52,19 @@ model_compile <- function(data, pretty = FALSE) { } +##' @porcelain POST /compile2 => json(compile2_response) +##' body data :: json(compile2_request) +model_compile2 <- function(data) { + data <- jsonlite::fromJSON(data, simplifyDataFrame = FALSE) + result <- odin2_js_validate(data$model) + if (result$valid) { + code <- odin2::odin_show_js(data$model, "text") + result$model <- scalar(paste(code, collapse = "\n")) + } + result +} + + ##' @porcelain GET /support/runner-ode => json support_runner_ode <- function() { code <- read_string(system_file("js/odin.js", "odin")) diff --git a/R/odin.R b/R/odin.R index d1c2e05..37c6752 100644 --- a/R/odin.R +++ b/R/odin.R @@ -6,6 +6,20 @@ odin_js_model <- function(code) { } +odin2_js_validate <- function(code) { + result <- odin2::odin_validate(code, "text") + + if (!result$success) { + err <- result$error + list(valid = scalar(FALSE), + error = odin_error_detail(err$message, err$src)) + } else { + list(valid = scalar(TRUE), + metadata = result$result) + } +} + + odin_js_validate <- function(code, requirements) { options <- odin::odin_options(target = "js") result <- odin::odin_validate(code, "text", options) diff --git a/R/porcelain.R b/R/porcelain.R index f2797ae..befd9fd 100644 --- a/R/porcelain.R +++ b/R/porcelain.R @@ -28,6 +28,15 @@ returning = porcelain::porcelain_returning_json("compile_response"), validate = validate) }, + "POST /compile2" = function(state, validate) { + porcelain::porcelain_endpoint$new( + "POST", + "/compile2", + model_compile2, + porcelain::porcelain_input_body_json("data", "compile2_request"), + returning = porcelain::porcelain_returning_json("compile2_response"), + validate = validate) + }, "GET /support/runner-ode" = function(state, validate) { porcelain::porcelain_endpoint$new( "GET", diff --git a/docker/Dockerfile b/docker/Dockerfile index 3390ad0..2426671 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -22,6 +22,11 @@ RUN install2.r --error \ pkgbuild \ porcelain +RUN install2.r --error \ + remotes + +RUN installGithub.r mrc-ide/odin2@odin2-js-wip + COPY DESCRIPTION /tmp/DESCRIPTION # Then get the full list via remotes diff --git a/inst/schema/compile2_request.json b/inst/schema/compile2_request.json new file mode 100644 index 0000000..1070b9d --- /dev/null +++ b/inst/schema/compile2_request.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "model": { + "$ref": "model.json" + } + }, + "additionalProperties": false, + "required": ["model"] +} diff --git a/inst/schema/compile2_response.json b/inst/schema/compile2_response.json new file mode 100644 index 0000000..eec7e29 --- /dev/null +++ b/inst/schema/compile2_response.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "oneOf": [ + { + "type": "object", + "properties": { + "valid": { + "enum": [ true ] + }, + "metadata": { + "type": "object" + }, + "model": { + "type": "string" + } + }, + "additionalProperties": false, + "required": ["valid", "metadata", "model"] + }, + { + "type": "object", + "properties": { + "valid": { + "enum": [ false ] + }, + "error": { + "$ref": "diagnostic.json" + } + }, + "additionalProperties": false, + "required": ["valid", "error"] + } + ] +} From 529e69f3918bab45110412eac913bb0fa11b6bc3 Mon Sep 17 00:00:00 2001 From: Mantra Date: Wed, 3 Jun 2026 17:29:03 +0100 Subject: [PATCH 2/5] odin2 done --- R/api.R | 1 + R/odin.R | 2 +- docker/Dockerfile | 50 ++++++++++++++++++++++++++++++++++++++++---- docker/Rprofile.site | 2 +- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/R/api.R b/R/api.R index 3202d71..74d620d 100644 --- a/R/api.R +++ b/R/api.R @@ -59,6 +59,7 @@ model_compile2 <- function(data) { result <- odin2_js_validate(data$model) if (result$valid) { code <- odin2::odin_show_js(data$model, "text") + code <- c(code, "odin_system;") result$model <- scalar(paste(code, collapse = "\n")) } result diff --git a/R/odin.R b/R/odin.R index 37c6752..13b7632 100644 --- a/R/odin.R +++ b/R/odin.R @@ -12,7 +12,7 @@ odin2_js_validate <- function(code) { if (!result$success) { err <- result$error list(valid = scalar(FALSE), - error = odin_error_detail(err$message, err$src)) + error = odin_error_detail(err$message, err$src$start)) } else { list(valid = scalar(TRUE), metadata = result$result) diff --git a/docker/Dockerfile b/docker/Dockerfile index 2426671..5b466ac 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM rocker/r-ver:4.1.2 +FROM rocker/r-ver:4.4.3 RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl4-openssl-dev \ @@ -14,18 +14,60 @@ COPY docker/Rprofile.site /usr/local/lib/R/etc/Rprofile.site # More cache-friendly installation of some dependencies: RUN install2.r --error \ --repos=https://mrc-ide.github.io/drat \ - --repos=https://packagemanager.rstudio.com/all/__linux__/focal/latest \ + --repos=https://packagemanager.rstudio.com/all/__linux__/noble/latest \ docopt \ jsonlite \ logr \ + later \ + fastmap \ + otel \ + ps \ + Rcpp \ + curl \ + crayon \ + httpuv \ + lifecycle \ + magrittr \ + mime \ + promises \ + rlang \ + sodium + + +RUN install2.r --error \ + --repos=https://mrc-ide.github.io/drat \ + --repos=https://packagemanager.rstudio.com/all/__linux__/noble/latest \ + stringi \ + swagger \ + webutils \ + common \ + withr \ + R6 \ + cinterpolate \ + deSolve \ + digest \ + glue \ + ring \ + callr \ + cli \ + desc \ + processx + +RUN install2.r --error \ + --repos=https://mrc-ide.github.io/drat \ + --repos=https://packagemanager.rstudio.com/all/__linux__/noble/latest \ odin \ - pkgbuild \ + pkgbuild + +RUN install2.r --error \ + --repos=https://mrc-ide.github.io/drat \ + --repos=https://packagemanager.rstudio.com/all/__linux__/noble/latest \ porcelain RUN install2.r --error \ remotes -RUN installGithub.r mrc-ide/odin2@odin2-js-wip +RUN installGithub.r mrc-ide/odin2@dc3c925 COPY DESCRIPTION /tmp/DESCRIPTION diff --git a/docker/Rprofile.site b/docker/Rprofile.site index 22b4c2f..b9cb401 100644 --- a/docker/Rprofile.site +++ b/docker/Rprofile.site @@ -1,4 +1,4 @@ -options(repos = c(CRAN = 'https://packagemanager.rstudio.com/all/__linux__/focal/latest'), download.file.method = 'libcurl') +options(repos = c(CRAN = 'https://packagemanager.rstudio.com/all/__linux__/noble/latest'), download.file.method = 'libcurl') options(HTTPUserAgent = sprintf("R/%s R (%s)", getRversion(), paste(getRversion(), R.version$platform, R.version$arch, R.version$os))) From b469b47aeac794ebc60b134b141d2104f4e60955 Mon Sep 17 00:00:00 2001 From: Mantra Date: Thu, 4 Jun 2026 15:46:34 +0100 Subject: [PATCH 3/5] disable array bounds checking --- R/api.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/api.R b/R/api.R index 74d620d..ae246f3 100644 --- a/R/api.R +++ b/R/api.R @@ -58,7 +58,7 @@ model_compile2 <- function(data) { data <- jsonlite::fromJSON(data, simplifyDataFrame = FALSE) result <- odin2_js_validate(data$model) if (result$valid) { - code <- odin2::odin_show_js(data$model, "text") + code <- odin2::odin_show_js(data$model, "text", check_bounds = FALSE) code <- c(code, "odin_system;") result$model <- scalar(paste(code, collapse = "\n")) } From 95691e06f08600af45a4ab3de1048ab3d9ff87ee Mon Sep 17 00:00:00 2001 From: Mantra Date: Thu, 4 Jun 2026 15:49:18 +0100 Subject: [PATCH 4/5] disable array bounds validate function --- R/odin.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/odin.R b/R/odin.R index 13b7632..eae1bcc 100644 --- a/R/odin.R +++ b/R/odin.R @@ -7,7 +7,7 @@ odin_js_model <- function(code) { odin2_js_validate <- function(code) { - result <- odin2::odin_validate(code, "text") + result <- odin2::odin_validate(code, "text", check_bounds = FALSE) if (!result$success) { err <- result$error From 5f3a3d80bf8b58dc9b5d365f77748efc77b00b0b Mon Sep 17 00:00:00 2001 From: Mantra Date: Wed, 10 Jun 2026 16:43:43 +0100 Subject: [PATCH 5/5] update odin2 ref --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5b466ac..56dae90 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -67,7 +67,7 @@ RUN install2.r --error \ RUN install2.r --error \ remotes -RUN installGithub.r mrc-ide/odin2@dc3c925 +RUN installGithub.r mrc-ide/odin2@9db29f1 COPY DESCRIPTION /tmp/DESCRIPTION