The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
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[@]dogado.de.

Typed decisions

gptr logo

Generative models (System 2) produce text and code. Typed decision models (System 1) answer with a logical value, a choice, or a numeric score, together with their probabilities. gptr returns R vectors that can be used in comparisons, filters and control flow. A batch returns one answer per input.

The executed examples use judge, a fake classifier with synthetic probabilities. Its setup is hidden to keep the examples short. The outputs show the interface, not model accuracy or calibration.

library(gptr)

Logical decisions and probabilities

abstracts = c(
  trial = "We randomized 200 adults to drug A or placebo for 12 weeks.",
  cohort = "We followed 5000 nurses for 20 years and recorded incident diabetes.",
  pilot = "A pilot study of 30 patients; the allocation method is not reported."
)
is_rct = peter("Is this a randomized trial?", abstracts, model = judge)

is_rct
#>          trial         cohort          pilot
#>  TRUE (p=0.94) FALSE (p=0.07)  TRUE (p=0.55)
#> judge-s1-1.0 . calibration unknown . 2026-10-30
gptr_prob(is_rct)
#>  trial cohort  pilot
#>   0.94   0.07   0.55
gptr_prob(is_rct, "probabilities")
#>        FALSE TRUE
#> trial   0.06 0.94
#> cohort  0.93 0.07
#> pilot   0.45 0.55

The gptr_decision result is logical. Its value is TRUE when P(yes) is at least threshold (0.5 by default). gptr_prob(x) returns P(yes), while gptr_prob(x, "confidence") returns abs(2 * P(yes) - 1); confidence is different from the probability of a positive answer.

Structured inputs and batch shape

A data frame supplies one record per row. An atomic vector or an unnamed list supplies one state per element. A fully named list supplies one structured state; I(x) also requests one state for the whole object. Multiple inputs must have compatible batch sizes: a single state can accompany every item in a batch.

studies = data.frame(
  abstract = unname(abstracts),
  sample_size = c(200L, 5000L, 30L),
  row.names = names(abstracts)
)
peter("Was allocation randomized?", studies, model = judge)
#>          trial         cohort          pilot
#>  TRUE (p=0.94) FALSE (p=0.07)  TRUE (p=0.55)
#> judge-s1-1.0 . calibration unknown . 2026-10-30

record = list(abstract = abstracts[["trial"]], sample_size = 200L,
              allocation = "randomized")

length(peter("Was allocation randomized?", record, model = judge))
#> [1] 1

System 1 sends the input states to the selected provider, rather than only an object name. Small structured values can be serialized in full; large or unsupported values are described or truncated. For a large object already loaded in R, derive the small text or structured records the decision needs locally before calling. Check what information the question needs before sending sensitive data; see ?gptr_egress, ?gptr_security and Execution model for how inline R computation differs from these inputs.

Uncertainty in filters, functions and loops

min_confidence turns answers below the requested confidence into NA by default. Unknown confidence is also uncertain when the minimum is positive. For yes/no decisions, min_confidence = 0.8 requires P(yes) at least 0.9 or at most 0.1. For choices and scores, confidence is each engine’s own measure of concentration: the probabilities 0.90, 0.05 and 0.05 give 0.85 on Jev and 0.64 on Clef (see ?gptr_prob), so re-tune min_confidence when you switch engine.

screened = peter("Is this a randomized trial?", abstracts, model = judge,
                 min_confidence = 0.8)

screened
#>          trial         cohort          pilot
#>  TRUE (p=0.94) FALSE (p=0.07)    NA (p=0.55)
#> judge-s1-1.0 . calibration unknown . 2026-10-30
names(abstracts)[!is.na(screened) & screened]
#> [1] "trial"
names(abstracts)[is.na(screened)]
#> [1] "pilot"

Use isTRUE() for a scalar decision in if or while when abstention is possible. Decide separately what to do with an uncertain result; treating NA as exclusion is a workflow choice, not a negative model answer.

is_trial = function(text) {
  peter("Is this a randomized trial?", text, model = judge, min_confidence = 0.8)
}

included = character()
review = character()
for (id in names(abstracts)) {
  decision = is_trial(abstracts[[id]])
  if (isTRUE(decision)) included = c(included, id)
  if (is.na(decision)) review = c(review, id)
}

list(included = included, review = review)
#> $included
#> [1] "trial"
#>
#> $review
#> [1] "pilot"

The same function can live in an ordinary R file. A call inside a function or loop does not own a recorded document block. A matching System 1 cache entry can avoid another request; that is distinct from document replay. See Record, replay and export.

uncertain = "stop" signals gptr_error_s1_uncertain. TRUE or FALSE supply a yes/no fallback; a function function(state, answer) can return one replacement of the answer’s type. Such a function may make another model request if you explicitly implement that escalation, with its own egress, permissions and usage.

Choices and fractional scores

samples = c(s1 = "Biopsy of the right lower lobe",
            s2 = "Hepatocytes from a resected liver segment",
            s3 = "Cortex from an epilepsy resection")
tissue = peter("Which tissue is this?", samples, model = judge,
               choices = c("liver", "lung", "brain", "other"))

tissue
#>             s1             s2             s3
#>  lung (p=0.91) liver (p=0.88) brain (p=0.80)
#> judge-s1-1.0 . calibration unknown . 2026-10-30
gptr_prob(tissue)
#>   s1   s2   s3
#> 0.91 0.88 0.80
gptr_prob(tissue, "probabilities")
#>    liver lung brain other
#> s1  0.03 0.91  0.03  0.03
#> s2  0.88 0.04  0.03  0.05
#> s3  0.05 0.05  0.80  0.10

severity = peter("Rate this event's severity.",
                 c(a = "Mild headache", b = "Anaphylaxis requiring adrenaline"),
                 model = judge, levels = c("mild", "moderate", "severe"))

severity
#>                a                b
#> 0.20 (conf 0.85) 1.88 (conf 0.90)
#> judge-s1-1.0 . calibration unknown . 2026-10-30
gptr_prob(severity, "probabilities")
#>   mild moderate severe
#> a 0.85     0.10   0.05
#> b 0.02     0.08   0.90

gptr_choice is character; gptr_prob(tissue) gives the probability of the selected label. gptr_score is numeric, the expected zero-based level: here mild = 0, moderate = 1, severe = 2. The example scores are 0.20 and 1.88, not rounded labels. For scores, gptr_prob(x) gives confidence; use "probabilities" to inspect the distribution over levels. Choice and score confidence are provider-defined measures of concentration, not established probabilities that the answer is correct. Giving choices as a factor, or .opts = list(output = "factor"), returns a plain factor with those levels instead, without probabilities or confidence.

Provenance and caching

meta = attr(is_rct, "meta")
meta[c("model", "engine", "calibrated", "execution", "locality", "cached")]
#> $model
#> [1] "judge-s1-1.0"
#>
#> $engine
#> [1] "fake"
#>
#> $calibrated
#> [1] NA
#>
#> $execution
#> [1] "native"
#>
#> $locality
#> [1] "local"
#>
#> $cached
#> [1] FALSE FALSE FALSE

The metadata also includes usage, request IDs and, when available, a model digest, server version and calibration provenance. calibrated = NA means unknown; native decision models do not become calibrated merely by returning probabilities. Emulated answers have calibrated = FALSE. Slicing a typed result keeps its probabilities aligned.

Matching answers are cached per input and question. With a .gptr/ workspace the System 1 cache is durable; without one it is held in memory for the R session. Cache identity also depends on the model and answer options. Changed model identity, live mode or a missing entry can require a request. A mutable local model without verified immutable identity cannot promise durable reuse. Use gptr_cache() to inspect the cache.

System 1 routes or judges System 2

tasks = c("Rename a column.", "Choose a mixed model for repeated measurements.")
for (task in tasks) {
  hard = peter("Does this task need the stronger model?", task, model = judge)
  session = peter(task, model = if (isTRUE(hard)) strong else fast)
  print(session$text)
}
#> [1] "A quick answer from the fast model."
#> [1] "A careful answer from the strong model."

session = peter("Summarize mtcars in one sentence.", model = summary_model)

session |> peter("Did the analysis succeed?", model = judge)
#> [1] TRUE (p=0.94)
#> judge-s1-1.0 . calibration unknown . 2026-10-30
session$turns
#> [1] 1

The piped classifier receives a bounded description of the session’s status, designated value and last answer. It adds no agent turn. Judging the answer text is not verification of the calculations or saved artifacts; validate those in R.

Native Jev, local Ollama and explicit emulation

The following examples can contact real providers and are not executed here. Jev reads TYPESAFE_API_KEY; gptr_env() also recognizes jev-key, JEV_KEY, JEV_API_KEY and TYPESAFE_KEY aliases in a key file.

gptr_env("~/keys/jev.env")
peter("Is this a randomized trial?", abstracts, model = jev)

# Prepare an installed native decision model on your local Ollama server first.
peter("Is this a randomized trial?", abstracts, model = "ollama/clef-flash")

Native Clef and Clef Flash use Ollama’s System One API and require server 0.35.1 or later. Their local-only preflight checks the endpoint and discovered model evidence; an unavailable or unsuitable model fails instead of silently falling back to a cloud model. Ollama chat models use a different adapter and return agent sessions.

Emulation uses structured output from a generative model. It must be enabled explicitly in settings; writing an emulate: model name alone does not enable it.

gptr_config(system1 = "emulate:anthropic/claude-haiku-4-5", .scope = "session")
peter("Is this a randomized trial?", abstracts,
      model = "emulate:anthropic/claude-haiku-4-5")

Emulation is a model request, is marked uncalibrated, and does not create native decision capabilities for a chat model. Select a native model when the workflow requires native probabilities, and assess calibration on the data relevant to that workflow.

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.
Health stats visible at Monitor.