Tutorial 6: Save and reuse a module
Source:vignettes/tutorial-deploy-to-production.Rmd
tutorial-deploy-to-production.RmdCompiling a module, as in Tutorials 4 and 5, produces demos and instructions that you do not want to recompute in every R session. By the end of this tutorial you can save a compiled module’s configuration to a pins board, restore it as a working module, and export the traces the module records as it runs.
The answers on this page were recorded with gpt-4.1 and
are replayed when the site is built, so gpt-6-luna may word
them differently.
Compile a module
This is the sentiment classifier from Tutorial 5, compiled with
LabeledFewShot on nine short reviews:
sig <- signature(
"review -> sentiment: enum('positive', 'negative', 'neutral')",
instructions = "Classify the sentiment of this product review."
)
trainset <- tibble::tribble(
~review, ~sentiment,
"Love it!", "positive",
"Hate it!", "negative",
"It's okay.", "neutral",
"Amazing!", "positive",
"Terrible!", "negative",
"Meh.", "neutral",
"Best ever!", "positive",
"Worst purchase!", "negative",
"Fine I guess.", "neutral"
)
classifier <- module(sig) |> compile(LabeledFewShot(k = 3L), trainset)
run(classifier, review = "This product is fantastic!", .llm = chat)
#> $sentiment
#> [1] "positive"Save the configuration
pin_module_config() writes the module’s signature,
demos, settings and optimization results to a pins board. The board here
is a temporary folder, which R deletes when the session ends:
board <- board_temp()
pin_module_config(board, "sentiment-classifier", classifier)
#> Creating new version '20260930T174635Z-56386'
#> Writing to pin 'sentiment-classifier'
#> ✔ Pinned program artifact: "sentiment-classifier"
#> ℹ Root module: <PredictModule>
#> ℹ Graph nodes: 1
#> ℹ Compiled: TRUEFor real use, pick a board that outlives the session:
board <- board_local() # a folder in your user data directory
board <- board_folder("pins") # or a folder in your projectShared boards such as board_connect(),
board_s3() and board_gcs() work the same way
and let other people load your module. Each call to
pin_module_config() with the same name saves a new version:
pin_versions(board, "sentiment-classifier") lists them, and
pin_read(board, "sentiment-classifier", version = ) reads
an older one.
Restore it
In a new session, connect to the same board, read the pin and rebuild the module:
config <- pin_read(board, "sentiment-classifier")
restored <- restore_module_config(config)
#> ✔ Restored program artifact
#> ℹ Root module: <PredictModule>
#> ℹ Artifact version: 6
identical(restored$demos, classifier$demos)
#> [1] TRUEThe chat is not part of the saved configuration, so pass
.llm when you run the restored module:
run(restored, review = "Worst product ever!", .llm = chat)restore_module_config() reads only the artifact format
of the installed dsprrr (version 6 in the message above), so keep the
code that compiled the module: if a dsprrr upgrade changes the format,
compile and pin again. A module that holds R functions or tools also
needs a registry when you save and restore it; see
pin_module_config(). To use a saved module in a targets
pipeline or a Quarto report, see Run dsprrr
in pipelines.
Keep a reviewed version
A pin keeps every version you save. To also record which version a
person approved for production, and what it was compiled from, keep the
program in graft. Save
the whole program with save_program(), store it with its
training examples as evidence, and accept it for a purpose:
store <- graft::graft_store("programs", create = TRUE)
id <- program_artifact_id(classifier)
path <- save_program(classifier, tempfile(fileext = ".rds"))
examples <- graft::graft_save(
store,
as.character(jsonlite::toJSON(trainset)),
id = "sentiment:trainset",
media_type = "application/json"
)
program <- graft::graft_save_file(
store,
path,
id = "sentiment:program",
dependencies = examples
)
graft::graft_accept(
store,
program,
stream = "sentiment-classifier",
expected = NULL,
key = id,
actor = "reviewer",
reason = "Checked its evaluation on held-out reviews",
purpose = "production"
)In production, load whichever program is accepted now:
recall <- graft::graft_recall(
store,
stream = "sentiment-classifier",
purpose = "production",
eligible = TRUE
)
path <- tempfile(fileext = ".rds")
writeBin(recall@roots[[1]]@bytes, path)
production <- load_program(path)
identical(program_artifact_id(production), id)The ID from program_artifact_id() is also recorded in
every call’s metadata and trace, so a trace points to the exact program
that was reviewed. graft keeps each acceptance, and any later
withdrawal, in the stream’s history.
Save and inspect traces
A module records a trace for each call it makes, with token counts, latency, cost and model. Run the module a few times, then pin its traces next to the configuration:
run(classifier, review = "Great product!", .llm = chat)
#> $sentiment
#> [1] "positive"
run(classifier, review = "Not worth the money.", .llm = chat)
#> $sentiment
#> [1] "negative"
run(classifier, review = "Does the job.", .llm = chat)
#> $sentiment
#> [1] "neutral"
pin_trace(board, "sentiment-traces", classifier)
#> Creating new version '20260930T174637Z-0c98a'
#> Writing to pin 'sentiment-traces'
#> ✔ Pinned 4 traces: "sentiment-traces"
#> ℹ Total tokens: 941export_traces() returns the same data as a tibble with
one row per call, including the call made right after compiling:
export_traces(classifier)
#> # A tibble: 4 × 11
#> timestamp latency_ms input_tokens cached_input_tokens output_tokens
#> <dbl> <dbl> <int> <int> <int>
#> 1 1790790395. 875. 101 0 7
#> 2 1790790397. 427. 200 0 7
#> 3 1790790397. 381. 271 0 7
#> 4 1790790398. 396. 341 0 7
#> # ℹ 6 more variables: total_tokens <int>, cost <dbl>, model <chr>,
#> # prompt_length <int>, program_artifact_id <chr>, trace_context <list>The tibble leaves out prompts and responses, so you can share it
without exposing the text your module handled. Add them with
include_prompts = TRUE and
include_outputs = TRUE; pin_trace() takes the
same two arguments. summarize_traces(classifier) totals the
tokens, cost and latency.
That completes the tutorials; Compile and optimize and Choose an optimizer take optimization further.