--- title: "Teams and background work" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Teams and background work} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- 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 ``` 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.