Skip to contents

gptr logo

Use a team for several roles working on one task, a fan-out for the same task over several inputs, or gptr_parallel() for independently specified calls. Each returns a session whose children can be inspected. Model requests run concurrently; ordinary inline R code still executes in the one R process.

The examples below need a configured model and are shown without running them.

A team of named roles

library(gptr)
session = peter("Review the analysis workflow.", agents = list(
  statistics = gptr_agent(system = "Check the statistical assumptions."),
  code = gptr_agent(system = "Check the code and input/output handling.")
))
session$statistics$text
session$code$status

The member names identify roles. gptr_agent() can specify each role’s model, system text, tools, skills, backend and exported object names. A multi-member inline team uses child workspace overlays: reads can use the parent’s already-loaded objects, while ordinary assignments stay in each child until requested exports are collected. That is not a security boundary or protection against arbitrary code mutating an external resource. Worker backends use separate R processes and serialize selected inputs, with their own memory and temporary file costs; CLI backends need their external CLI and authentication. See Execution model for copy-on-modify and backend boundaries. Role definitions do not waive the usual tool permission checks.

session$text joins member reports under headings; session$value is their named list of values, and session$statistics is the member session. A report is model-generated output, not verification. Inspect each child’s status and the actual results before relying on it.

Continuing a team without agents = asks the container’s model to synthesize those reports:

session |> peter("Reconcile the reviews into one prioritized list of fixes.")

This adds a model request. agents = starts new children; it cannot be used to continue a piped session. Teams and fan-outs run in the foreground and cannot be combined with background = TRUE, .run = FALSE, or nested as gptr_parallel() members.

A fan-out over input elements

cohorts = list(A = 1:3, B = 4:6, C = 7:9)
session = peter("Summarize this cohort.", cohorts, parallel = 2)
session$A$text

parallel = 2 limits active children, not the number of inputs processed: every element is queued. Exactly one attached context object must supply the batch, as a list, atomic vector or data-frame rows. Each child receives its own element. Unique nonempty element names become child names; otherwise positions are used. Inspect a child with session$A or session[[1]].

For object-producing work, keep each child’s results separate and specify exports only when needed. Export-name collisions are reported and results are collected in task order; this is not an automatic merge of competing analyses.

Independent calls with gptr_parallel()

session = gptr_parallel(
  planner = peter("Plan the analysis."),
  review = peter("Review the inputs."),
  max_active = 2
)
session$planner$text
session$review$text

The calls are deferred while the members are constructed, then scheduled together. You can also pass .list = a named list of sessions created with .run = FALSE. Member names must not collide with session accessors such as text or status. A member must be idle with queued input, rather than an already completed call passed by variable.

Each member retains its own model and workspace, but member tools =, budget = and per-run .opts (including max_turns) are not applied. Members share the call’s budget. Set applicable defaults before launch or run under an enclosing budget; see Token efficiency. on_error = "return" keeps failed members for inspection; "stop" signals the first failed member’s condition after all members settle.

Deferred sessions and SDK control

.run = FALSE constructs an idle session with queued input without requesting a model yet. gptr_step() advances it by model turns; gptr_wait() runs queued work until it finishes or times out. Returned sessions can be inspected at each stage.

session = peter("Prepare a plan.", .run = FALSE)
session$status
gptr_step(session, turns = 1)
session$text

gptr_steer(session, "Include an input validation step.", as = "follow_up")
gptr_wait(session, timeout = 10)
session$text

A steer reaches a running agent after its current tool results; a follow-up is delivered when it would otherwise stop. On an idle session, either remains queued until the next run. Piping a prompt into a running session uses the same steering mechanism. gptr_wait() timing out leaves the run running; it does not cancel it. gptr_cancel() aborts active runs, keeps partial history and leaves idle sessions alone.

gptr_on(session, event, function(event, ctx) ...) subscribes to cataloged session events and returns a function that removes the listener. Keep reporting state outside the agent’s workspace and remove listeners when finished:

seen = new.env()
seen$roles = character()
off = gptr_on(session, "message_end", function(event, ctx) {
  seen$roles = c(seen$roles, event$message$role)
  NULL
})
session |> peter("Check the plan's assumptions.")
off()
seen$roles

Experimental background work

A background session runs through idle-console callbacks from the optional later package. It is not an independent R execution thread. Given an existing fitted model fit, start with a short task and an explicit workspace:

work = new.env()
work$fit = fit
session = peter("Inspect fit and propose follow-up checks.", fit = work$fit,
                envir = work, background = TRUE)
session$status
gptr_jobs()
session |> peter("Prioritize residual diagnostics.")
gptr_wait(session, timeout = 30)
# Cancel only if the run is still active and you want to stop it.
gptr_cancel(session)

With gptr.background_tools = "idle" (default), an R tool can run at an idle tick and occupy the R console until it finishes. "wait" defers those tools to a blocking gptr call. Rscript, knitr and Quarto have no idle-console progress: use gptr_wait() or another blocking call. Other front ends have the support limits documented in help("gptr-background", package = "gptr"); do not infer support from the mere return of a session object.

An approval or question at an idle tick parks the session as waiting, without performing the action. A later blocking call resumes it and the model repeats its request before the usual prompt. That extra request can use tokens. A waiting session has no active run for gptr_cancel() to abort; inspect its status before choosing how to resume or stop jobs.

Checkpoints, usage and durable history

After a modifying run, inspect checkpoints and preview a rewind before restoring state:

gptr_checkpoints(session)
gptr_rewind(session, preview = TRUE)
gptr_rewind(session)
gptr_usage(session, by = "agent")

restore = "conversation" changes the conversation; "workspace" restores captured workspace state; "all" requests both. Restoration covers captured objects/files and registered checkpointers, with conflicts and unsupported changes reported. It does not undo arbitrary external services or uncaptured side effects. Check the report and restored data. Assigning a session to another name keeps the same object; use gptr_fork() for a branch.

For a top-level team or fan-out call in a designated history document, child code is recorded under that statement and replay can reconstruct the children. Recording, answer caches and usable blocks must be present; a narrative team report alone is not a replayable workflow. See Script as history for recording and summary recipes, and Token efficiency for shared budgets and usage.