--- title: "Extending gptr" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Extending gptr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- gptr logo 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](skills-and-plugins.html). For existing helpers, see [Tools and artifacts](tools-and-artifacts.html); choose a model in [Language models](language-models.html). The displayed responses below are scripted offline, so building this guide needs no credentials or network. ``` r 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. ``` r 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) #> 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] 5 ``` Ask Peter to use it: ``` r session = peter("Add 40 and 2 with the demo tool.") session$text ``` ``` #> [1] "The sum is 42." ``` ``` r 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](execution-model.html) 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. ``` r 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) ``` ``` r session = peter("Remove the session's objects.", envir = new.env(), mode = auto) session$text ``` The offline example requests `rm(list = ls())`, which this policy denies: ``` #> [1] "I was not allowed to remove the objects." ``` ``` r 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](configuration.html) 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: ``` r 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. ``` r 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")] #> 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()`: ``` r gptr_spec("env_alias", "SLACK_BOT_TOKEN", aliases = "slack-token") #> #> aliases: slack-token #> api_version: 1.0 ``` A provider for an OpenAI-compatible endpoint is data: ``` r 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. ``` r 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](skills-and-plugins.html). 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.