Add a tool, a policy or a hook when an existing capability needs a
small change. These features, providers, commands and prompt sections
share the extension registry with gptr’s built-ins.
gptr_api()$features lists the supported extension kinds and
services.
For reusable instructions and packaged extensions, see Skills and plugins. For existing helpers, see Tools and artifacts; choose a model in Language models. The displayed responses below are scripted offline, so building this guide needs no credentials or network.
library(gptr)
gptr_api()$version
#> [1] '1.0'
head(grep("^kind[.]", gptr_api()$features, value = TRUE))
#> [1] "kind.adapter" "kind.agent" "kind.artifact_type"
#> [4] "kind.backend" "kind.cache_policy" "kind.checkpointer"A tool the model calls as an R function
Most capabilities are members of the peter$ namespace
rather than separate model-visible tools: the model calls them from R
code, which costs a few tokens per tool instead of a full schema.
gptr_check() runs the conformance checks on a spec before
you register it.
add_tool = gptr_tool(
"add", "Add two numbers",
parameters = list(
type = "object", required = I(c("a", "b")),
properties = list(a = list(type = "number"), b = list(type = "number"))
),
fun = function(a, b) a + b,
exposure = "r", namespace = "demo"
)
gptr_check(add_tool)
#> <gptr_check> 4 check(s), 0 failed
#> ok tool:add spec.class
#> ok tool:add spec.fields
#> ok tool:add tool.schema
#> ok tool:add tool.empty_input: empty input fails schema validation before execute()
off_tool = gptr_register(add_tool)
peter$demo$add(2, 3)
#> [1] 5Ask Peter to use it:
session = peter("Add 40 and 2 with the demo tool.")
session$text#> [1] "The sum is 42."
off_tool()gptr_register() returns a function that unregisters the
record. To add a tool to one session only, pass it as
peter(..., tools = list(add_tool)).
exposure = "r" makes a callable
peter$demo$add() member. A "direct" tool goes
into the model’s tool declarations; give it an
execute = function(input, ctx) handler. A
"deferred" tool is found through
peter$search(), while "hidden" omits it from
the model’s catalog. An R package’s tools should use the package name as
their namespace. Use peter$help("demo/add") to inspect a
registered member’s schema.
An R helper runs in the current R process and can compute on already-loaded objects without a file export. Its allocations follow ordinary R rules; an external backend may need its own serialization. See Execution model for these boundaries.
Choose execution = "sequential" for handlers that
evaluate R or change files. A tool’s
risk = function(input, ctx) classifies its actions; its
result still passes the active permission policies. A custom tool does
not grant permission to run its own side effects.
A permission policy
A policy sees every tool call before it runs and can deny, ask, modify or allow it. The strictest answer of all policies wins, and a policy that throws an error denies.
keep_data = gptr_policy("keep_data", check = function(call, ctx) {
if (identical(call$name, "r") && grepl("rm(", call$input$code, fixed = TRUE)) {
list(decision = "deny", reason = "removing objects is not allowed in this session")
} else {
NULL
}
})
off_policy = gptr_register(keep_data)The offline example requests rm(list = ls()), which this
policy denies:
#> [1] "I was not allowed to remove the objects."
off_policy()The denial reached the model as an error result, even in
auto mode. This text match illustrates the callback; it is
not a general R code security filter. See Configuration for modes, trust and
permission rules.
A hook on a session
gptr_on() attaches a handler to one session’s events;
gptr_hook() specs registered with
gptr_register() apply to every session.
This example uses an offline provider so the event sequence is reproducible:
fake = gptr_fake_provider(list("hello"))
session = peter("Say hello.", model = fake, .run = FALSE)
seen = new.env()
seen$roles = character()
off_hook = gptr_on(session, "message_end", function(event, ctx) {
seen$roles = c(seen$roles, event$message$role)
NULL
})
gptr_step(session)
off_hook()
seen$roles
#> [1] "user" "assistant"Prompt sections, context blocks and commands
A prompt section adds text to the system prompt of new sessions; a context block adds data to the first message or to every turn; a command adds a slash command to the console.
rules = gptr_prompt_section(
"house_rules", "Report every quantity with its unit.", tier = "T1", order = 780L
)
notebook = gptr_context_block(
"lab_notebook", function(ctx, budget) "Experiment 12: cohort B.",
placement = "first", order = 650L
)
rows = gptr_command(
"rows", function(args, ctx) paste("rows:", nrow(mtcars)),
description = "Show the rows of mtcars"
)
offs = lapply(list(rules, notebook, rows), gptr_register)
reg = gptr_registry(c("prompt_section", "context_block", "command"))
reg[reg$name %in% c("house_rules", "lab_notebook", "rows"), c("kind", "name", "source")]
#> <gptr_registry> 3 record(s)
#> kind name source
#> prompt_section house_rules user
#> context_block lab_notebook user
#> command rows user
for (off in offs) off()gptr_prompt() shows what a new session would freeze,
section by section, with token estimates. The command’s optional
complete callback is metadata for frontend integrations;
the built-in console does not currently invoke it for Tab
completion.
Other kinds
The eleven exported constructors cover the common kinds:
gptr_tool(), gptr_provider(),
gptr_adapter(), gptr_router(),
gptr_hook(), gptr_policy(),
gptr_agent(), gptr_command(),
gptr_prompt_section(), gptr_context_block()
and gptr_backend(). Every other kind uses
gptr_spec():
gptr_spec("env_alias", "SLACK_BOT_TOKEN", aliases = "slack-token")
#> <gptr_env_alias SLACK_BOT_TOKEN>
#> aliases: slack-token
#> api_version: 1.0A provider for an OpenAI-compatible endpoint is data:
gptr_register(gptr_provider(
"corp", api = "openai-completions",
base_url = "https://llm.corp.example/v1", auth = "CORP_LLM_KEY",
models = list(list(id = "corp-large", context = 128000))
))
peter("Summarize this table.", mtcars, model = "corp/corp-large")Extensions and plugins
An extension is a function of the extension API object. It registers
records with gptr$register() and can be passed to one call
with extensions =, or saved in
.gptr/extensions/ of a trusted project.
lab_rules = function(gptr) {
gptr$register(gptr_prompt_section(
"lab_rules", "Report concentrations in mmol/L.", tier = "T1", order = 790L
))
}
peter("Summarize the assay results.", assay, extensions = lab_rules)In an extension file, the last expression must be this factory
function. Review the whole file: earlier expressions are evaluated when
it loads. The API object has $register(spec),
$on(event, handler, matcher = NULL),
$require(">= 1.0, < 2") and
$has(feature), along with plugin-local $state.
Check a factory with gptr_check(lab_rules) before enabling
it.
Use gptr_registry(kind = NULL, diagnostics = FALSE) to
inspect records, or gptr_registry(diagnostics = TRUE) to
see validation and collision messages. For the same kind and name,
session records win over trusted project records, user records, plugins
and built-ins, in that order. A tie keeps the first registration and
reports a collision. Additive kinds such as hooks keep multiple records.
Removing a user record exposes the next matching record.
gptr_reload() rereads configuration and extensions for
later use; existing sessions keep their frozen prompt. Plugin manifests,
activation and compatibility are covered in Skills and plugins.
Packages can teach gptr about their classes through methods for
gptr_describe() (compact object descriptions) and
gptr_preimage() (undo support). These are separate from a
tool registration.