---
title: "Teams and background work"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Teams and background work}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
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
``` r
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](execution-model.html) 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:
``` r
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
``` r
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. Keep exports and returned values
self-contained: a fan-out child that succeeds keeps only the values it returned with
`gptr_return()`, and a worker sends back only its exports, so an exported function or plot
finds the caller's objects but none of the child's other objects.
At the console each child of a foreground call prints one line when it starts,
`sub-agent A started ()`, and one when it ends, `sub-agent A done (1/3)` with its turns
and seconds, or `sub-agent A failed (1/3): `. A child's approval prompts and questions
name it (`sub-agent A > r ...`; a nested `peter()` call by its prompt and number,
`'Summarise ...' #2`), and its printed output stays in its own result. A `peter()` call that
the agent's own R code makes without `envir =` reads your objects and keeps the ones it creates
in `res$envir`. R code that wraps such calls in `capture.output()` or `sink()` also captures
their prompts.
## Independent calls with gptr_parallel()
``` r
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](token-efficiency.html). `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.
``` r
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:
``` r
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:
``` r
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:
``` r
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](script-as-history.html) for recording and summary recipes, and
[Token efficiency](token-efficiency.html) for shared budgets and usage.