--- title: "Execution model" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Execution model} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- gptr logo Peter combines a model with your current R workspace. A language model requests tools and returns a session; a decision model returns a typed vector. `peter()` handles both. An inline agent can compute on a multi-GB object already loaded in R, keep intermediate variables, and reuse them across turns. This guide explains where that computation happens, what the model receives, and where memory or file transfers still occur. ## One language model turn 1. `peter()` captures the prompt and context expressions and selects the model and permission mode. 2. It describes the workspace and attached objects within a token budget, then builds the request. 3. The provider streams a reply. Each gptr tool request passes through the permission gate. 4. R code runs in the selected environment; file and external tools use their own documented paths. 5. Tool results return to the model until it finishes, is interrupted, or reaches a limit. 6. The session retains the conversation, status, usage and a designated result, if there is one. For an ordinary interactive call, start with a prompt and the data: ``` r library(gptr) session = peter("Fit fuel economy against weight.", mtcars) session |> peter("Plot the fitted relationship.") ``` The executed example below uses a scripted `fake` provider. It runs offline and requests the same R code each time; its setup is hidden. An explicit environment keeps the fitted object. ``` r work = new.env() work$d = mtcars session = peter("Fit fuel economy against weight.", d = work$d, model = fake, envir = work, mode = auto) session$text #> [1] "The regression is ready." session$status #> [1] "idle" coef(session$value) #> (Intercept) wt #> 37.285126 -5.344472 ``` `gptr_return(fit)` designates `fit` as the result. The final model text is `session$text`; it does not automatically become the fitted object. The result also remains available as `work$fit`. ## Where objects live The default `envir` is the calling frame. At the console that is usually the global workspace; inside a function it is that function's frame. Supply an environment when you want the work to persist in a particular place. Named context such as `d = work$d` labels an attachment; it does not create a binding called `d` in a different evaluation environment. ``` r gptr_describe(work$d, budget = 60) #> [1] " 32 x 11, 6.9 KB" #> [2] " mpg:dbl cyl:dbl disp:dbl hp:dbl drat:dbl wt:dbl qsec:dbl vs:dbl am:dbl gear:dbl carb:dbl" ls(work) #> [1] "d" "fit" identical(session$value, work$fit) #> [1] TRUE ``` The inline `r` tool evaluates against those actual bindings. It does not need to write a dataset to a file or reload it before the next tool call. Attach a large object as a plain name visible in that environment; gptr captures that name without retaining another copy of the object in the conversation. An expression such as `subset(d, ...)` still performs ordinary R work when evaluated, and may allocate a new object. Workspace context lists names, classes, shapes and sizes within a budget. Attached objects get compact descriptions that can include schema, sample values and summaries. The full local object remains available to R code. Printed tool output is bounded, with an output handle for long results, and captured plots can be sent as images. Those previews and results may contain data values; the permission and egress controls still apply. System 1 decisions instead send encoded input states under their own size rules; see [Typed decisions](system-one.html). `context = "names"` reduces workspace context to names and attachments to headers; `context = "none"` omits both built-in workspace and attached-object descriptions. These settings leave R objects available to the tools and do not suppress prompt text, tool results or every other context block. Project instructions and environment details remain even with `context = "none"`, so remote routes still require provider acknowledgment. In unattended runs, save that consent before calling the model; offline providers and local loopback routes are exempt (Ollama only while local-only inference is on). See [Configuration](configuration.html) and [Token efficiency](token-efficiency.html). Ordinary R copy-on-modify rules still apply. Modifying an object, keeping aliases, fitting a model or calling a package can allocate substantial memory. Checkpoints retain supported pre-images for undo, and workers, bridges and artifact snapshots have their own transfer or copy costs. An overlay lets reads find the parent object's binding while keeping ordinary assignments local; it does not isolate changes to reference objects or external resources. A continued session reuses its kept home environment unless you explicitly supply a new `envir`. When the default environment is a function call frame, gptr uses it during the active run and does not retain it as the session's home afterward. An explicit working environment can retain named results. An explicit `gptr_fork(session)` branches the conversation and uses a workspace overlay. Restoring a session from disk restores its conversation; it does not restore all of your R objects. Each model request has provider and transport latency. R evaluation takes the time and memory its code needs; memory pressure can slow work or make an allocation fail. A file-backed matrix, Arrow dataset or database connection may read from disk through its package even when its R handle is already loaded. Use those packages' normal subset and compute operations rather than assuming every object is a fully resident data frame. ## What the session retains ``` r session$id #> [1] "s6c07b23a2b" session$model #> [1] "fake/fake-1" session$turns #> [1] 1 gptr_usage(session) #> group requests input output cache_read cache_write cost #> 1 s6c07b23a2b 2 3357 20 0 0 0 #> #> # 1 row #> 2 requests, 3.4k tokens in, $0.0000 ``` | Interface | Use | |---|---| | `session$text`, `session$value`, `session$status` | Inspect the answer, R result and state | | `session$messages`, `session$history`, `session$usage` | Inspect conversation, history and usage | | `session |> peter("next question")` | Add a turn to the same conversation | | `gptr_step()`, `gptr_wait()`, `gptr_steer()`, `gptr_cancel()` | Drive or control a session | | `gptr_sessions()`, `gptr_resume()`, `gptr_last()` | Find stored or live sessions | | `gptr_on()` | Attach a session event handler | | `gptr_fork()`, `gptr_checkpoints()`, `gptr_rewind()` | Branch or recover supported state | Printing `session$history` or `summary(session)` shows eight recent messages and session details without a model request. The underlying table keeps every message; unknown token counts stay unknown. ``` r session$history #> turn role preview tools tokens #> 1 1 user Fit fuel economy agai... 8 #> 2 1 assistant r 1690 #> 3 1 tool_result + fit r 5 #> 4 1 assistant The regression is ready. 1687 #> # 4 recorded messages #> idle . fake/fake-1 . 1 turn . 3.4k tokens . $0.0000 . s6c07b23a2b as.data.frame(session$history) #> turn role preview tools tokens #> 1 1 user Fit fuel economy against weight. 8 #> 2 1 assistant r 1690 #> 3 1 tool_result + fit r 5 #> 4 1 assistant The regression is ready. 1687 ``` Without an initialized project workspace, session files use a temporary directory. `gptr_init()` creates `.gptr/` for project storage. Recording an R document is a separate choice; see [Record, replay and export](script-as-history.html). A history document records executed code, decisions and bounded output. It does not automatically archive the full input dataset. Replay runs the recorded R procedure again; provide its input objects, packages and required files in a fresh session. Answer caches, checkpoints and artifact data snapshots are separate storage mechanisms. ## Permissions and concurrency `manual` is the default: actions classified as read-only can run, while changes require approval. `plan` is for inspection and proposals; `edits` allows approved classes of project file edits; `auto` allows ordinary tool work without prompting. The plan a `plan` run proposes goes once, as a `` block, to the next top-level `peter()` call from the same environment, within an hour and in another mode, whatever that call asks: a script runs the plan with `peter("Go ahead.", mode = auto)`. Another `peter()` call first discards it, and `options(gptr.plan_handoff = FALSE)` turns the hand-off off. Critical, secret and control guards remain separate. `yolo`, which only you can select, also runs gptr's own configuration functions and secret reads; deleting protected files, files outside the project or the project itself, moving the working directory out of the project, `rm(list = ls())`, `options(gptr.*)` and sending a secret over the network still ask. In `auto` and `yolo`, a `system()` or `system2()` call whose command the code computes is refused: the model is told to run the command with `peter$sh()`, whose command the permission rules check when it runs. A permission gate is not an isolation boundary. External CLIs also have their own sandbox and tool controls; live R requests still pass through gptr's MCP gate. Use [Configuration](configuration.html) and `?gptr_security` to choose a mode and understand its limits. HTTP requests, CLI processes, MCP connections and workers share one event loop. Multiple requests can wait for I/O together. Inline R code runs serially in the owning R process and pauses event-loop callbacks until evaluation returns. A worker is a separate R process and receives serializable context; it does not share the parent workspace. See [Teams and background work](teams-and-background.html). Model code in a top-level run, and in a plain `peter()` sub-agent it starts, draws from your random-number stream, as your own code would. Members of a team, a fan-out or `gptr_parallel()` draw from their own stream, so your `.Random.seed` is preserved around them, except after a deliberate `RNGkind()` change or a callback R runs while idle.