---
title: "Innovate R kernel bindings"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Innovate R kernel bindings}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

run_live_examples <- identical(Sys.getenv("INNOVATE_R_VIGNETTE_RUN_KERNEL"), "true")

load_innovate_r <- function() {
  exported_helpers <- c(
    "kernel_schema_version",
    "kernel_request",
    "kernel_discover_models",
    "kernel_fit_model",
    "kernel_predict_model",
    "kernel_simulate_model",
    "kernel_diagnose_model",
    "kernel_extract_diagnostics",
    "kernel_response_to_r"
  )

  if (requireNamespace("innovate.R", quietly = TRUE)) {
    for (helper in exported_helpers) {
      assign(
        helper,
        getExportedValue("innovate.R", helper),
        envir = knitr::knit_global()
      )
    }
    return(invisible(TRUE))
  }

  candidate <- normalizePath(getwd(), mustWork = FALSE)
  repeat {
    bridge <- file.path(candidate, "R", "kernel_bridge.R")
    if (file.exists(bridge)) {
      source(bridge, local = knitr::knit_global())
      return(invisible(TRUE))
    }

    bridge <- file.path(candidate, "bindings", "r", "R", "kernel_bridge.R")
    if (file.exists(bridge)) {
      source(bridge, local = knitr::knit_global())
      return(invisible(TRUE))
    }

    parent <- dirname(candidate)
    if (identical(parent, candidate)) {
      stop("Unable to locate innovate.R or local R bindings", call. = FALSE)
    }
    candidate <- parent
  }
}

load_innovate_r()
```

This vignette shows how the `innovate.R` package acts as a thin R interface to
the Innovate functional kernel. The examples are designed to build without
network access. Chunks that invoke the Python bridge are guarded and run only
when `INNOVATE_R_VIGNETTE_RUN_KERNEL=true`.

## Installation

Install the R binding from a local checkout or from a prepared package artifact.
The binding also needs a Python environment containing the `innovate` Python
package. In a repository checkout, the bridge can use the local `src/` tree.

```{r install-local, eval = FALSE}
install.packages("bindings/r", repos = NULL, type = "source")
Sys.setenv(INNOVATE_PYTHON_COMMAND = "uv")
```

For a system Python launcher instead of `uv`, point the binding at `python3`:

```{r install-python-command, eval = FALSE}
Sys.setenv(INNOVATE_PYTHON_COMMAND = "python3")
```

The schema version is available without contacting the bridge:

```{r schema-version}
kernel_schema_version()
```

## Backend Discovery

Model and backend discovery is provided by the stable kernel operation
`discover_models`. This live call is disabled by default for vignette builds.

```{r discover-models, eval = run_live_examples}
models <- kernel_discover_models()
head(models[, c("key", "family", "supported_backends", "supports_simulation")])
```

When writing portable code, treat discovery as data. For example, select models
that advertise simulation support before constructing simulation requests:

```{r discover-filter, eval = FALSE}
simulation_models <- subset(models, supports_simulation)
simulation_models$key
```

## Core Request Examples

The R binding builds versioned request envelopes and sends them to the shared
kernel. Request construction is local R work and is safe to run during vignette
builds.

```{r fit-request}
fit_request <- kernel_request(
  operation = "fit_model",
  model_key = "bass",
  payload = list(
    inputs = list(
      time = c(0, 1, 2, 3, 4),
      observed = c(0.01, 0.04, 0.11, 0.23, 0.38)
    ),
    model_kwargs = list()
  )
)

fit_request$operation
fit_request$model_key
fit_request$schema_version
```

Run the fit through the bridge only in an environment that has the Python kernel
available:

```{r fit-model, eval = run_live_examples}
fit_result <- kernel_fit_model(fit_request)
fit_result
```

Prediction and simulation use the same envelope pattern. A fitted-state payload
can be supplied directly when you already have parameters.

```{r predict-request}
predict_request <- kernel_request(
  operation = "predict_model",
  model_key = "bass",
  payload = list(
    state = list(
      model_key = "bass",
      model_name = "BassModel",
      constructor_kwargs = list(),
      parameters = list(p = 0.03, q = 0.38, m = 120),
      predict_kwargs = list()
    ),
    inputs = list(time = c(0, 1, 2, 3, 4))
  )
)

predict_request$payload$state$parameters
```

```{r predict-model, eval = run_live_examples}
kernel_predict_model(predict_request)
```

```{r simulate-request}
simulate_request <- kernel_request(
  operation = "simulate_model",
  model_key = "bass",
  payload = predict_request$payload
)

simulate_request$operation
```

```{r simulate-model, eval = run_live_examples}
kernel_simulate_model(simulate_request)
```

## Diagnostics

Diagnostics use the same fitted-state convention plus observed values. The
helper `kernel_extract_diagnostics()` works on list-like kernel results that
include diagnostics fields.

```{r diagnose-request}
diagnose_request <- kernel_request(
  operation = "diagnose_model",
  model_key = "logistic",
  payload = list(
    state = list(
      model_key = "logistic",
      model_name = "LogisticModel",
      constructor_kwargs = list(),
      parameters = list(L = 100, k = 0.65, x0 = 3),
      predict_kwargs = list()
    ),
    inputs = list(
      time = c(0, 1, 2, 3, 4, 5),
      observed = c(12.4, 21.4, 34.3, 50.0, 65.7, 78.6)
    )
  )
)

diagnose_request$model_key
```

```{r diagnose-model, eval = run_live_examples}
diagnostics_result <- kernel_diagnose_model(diagnose_request)
kernel_extract_diagnostics(diagnostics_result)
```

The extraction helper intentionally fails when the result does not expose
diagnostics:

```{r diagnose-guard}
tryCatch(
  kernel_extract_diagnostics(list(values = c(1, 2, 3))),
  error = conditionMessage
)
```

## Guarded Failure Modes

The binding performs local request validation before bridge invocation. Kernel
operations other than `discover_models` require a model key.

```{r missing-model-key}
tryCatch(
  kernel_request(operation = "predict_model", payload = list()),
  error = conditionMessage
)
```

Kernel error payloads are converted into an `innovate_kernel_error` condition so
callers can catch kernel failures without parsing transport output.

```{r kernel-error-condition}
condition <- tryCatch(
  kernel_response_to_r(list(
    schema_version = "1.0",
    operation = "predict_model",
    result = NULL,
    error = list(
      code = "unsupported_native_operation",
      message = "Native execution is not available for this payload"
    ),
    metadata = list()
  )),
  error = identity
)

inherits(condition, "innovate_kernel_error")
condition$message
```

Bridge execution can also fail if the Python command is unavailable or the
Python environment cannot import the kernel. Keep live calls guarded in
reproducible documents and surface the configured launcher in logs:

```{r bridge-guard, eval = FALSE}
Sys.getenv("INNOVATE_PYTHON_COMMAND", "uv")
kernel_discover_models()
```
