Module graphs provide one cycle-safe protocol for inspecting and transforming
nested dsprrr programs. Built-in adapters understand pipelines, wrappers,
ensembles, and multi-chain modules. Custom Module subclasses can opt in by
implementing two public methods:
graph_children()returns a list whose leaves are childModuleobjects. Lists can be nested and can be named or unnamed.set_graph_children(children)replaces that complete child structure and returns the program invisibly. This method is required only for replacement and mapping operations; read-only traversal needs onlygraph_children().
A custom module can additionally implement graph_is_parameter() and return
TRUE or FALSE to control whether named_parameters() includes it.
Paths use a JSON-pointer-like form. "$" is the root, named list elements
use their names, and unnamed or ambiguously named lists use one-based numeric
positions. / and ~ in names are escaped as ~1 and ~0.
With boundaries = "respect", compiled and frozen modules are included as
boundary nodes but neither they nor their descendants are mutated. A shared
module reachable through any protected path is protected at every alias.
Mapping visits each R6 identity once in post-order and rewires all aliases to
the same replacement. replace_module() defaults to replacing one path and
can opt into identity-wide replacement with shared = "all". Mapping can
safely traverse cycles, but replacing a module that participates in a cycle
is rejected; use path-specific replace_module() to break a cycle edge.
This is primarily useful when implementing or testing a custom module graph
adapter. Custom modules should normally implement graph_children() rather
than call this function from that method.
Usage
module_graph(
program,
boundaries = c("cross", "respect"),
cycles = c("record", "error")
)
named_modules(
program,
include_root = TRUE,
aliases = FALSE,
boundaries = c("cross", "respect"),
cycles = c("record", "error")
)
named_parameters(
program,
include_root = TRUE,
boundaries = c("respect", "cross")
)
map_modules(
program,
.fn,
...,
include_root = TRUE,
boundaries = c("respect", "cross")
)
replace_module(
program,
path,
replacement,
shared = c("path", "all"),
boundaries = c("respect", "cross")
)
freeze_modules(program, paths = "$", recursive = TRUE, frozen = TRUE)
is_module_frozen(module)
set_module_lm(
program,
chat,
include_root = TRUE,
boundaries = c("cross", "respect"),
clone = TRUE
)
module_children(module)Arguments
- program
A dsprrr
Moduleobject.- boundaries
Whether compiled and frozen modules are crossed. Inspection defaults to
"cross", graph rewrites default to"respect", and recursive LM propagation defaults to"cross"so runtime configuration reaches the complete program.- cycles
Whether cycles are recorded or raise an error.
- include_root
Whether to include or transform the root module.
- aliases
Whether
named_modules()includes shared and cyclic aliases.- .fn
A function called as
.fn(module, path, ...). It must return aModule, orNULLto leave that module unchanged.- ...
Additional arguments passed to
.fn.- path
A stable path returned by
module_graph()ornamed_modules().- replacement
A replacement
Module.Whether replacement affects only
pathor every reference to the same R6 object.- paths
One or more graph paths to freeze or unfreeze.
- recursive
Whether freezing applies to every descendant by identity.
- frozen
Logical;
TRUEfreezes andFALSEunfreezes.- module
A dsprrr
Moduleobject.- chat
An ellmer
Chatobject, orNULLto clear stored Chats.- clone
Whether to give each module an independent deep clone of
chat. The default avoids sharing mutable conversation history.
Value
module_graph()returns a tibble with one row per graph occurrence.named_modules()andnamed_parameters()return named lists.map_modules()andreplace_module()return the resulting root module.freeze_modules()andset_module_lm()returnprograminvisibly.is_module_frozen()returns one logical value.module_children()returns a list whose leaves are child modules.
Examples
first <- module(signature("text -> answer"))
second <- module(signature("answer -> summary"))
program <- pipeline(first = first, second = second)
names(named_modules(program))
#> [1] "$" "$/steps/first" "$/steps/second"
names(named_parameters(program))
#> [1] "$/steps/first" "$/steps/second"
program <- map_modules(program, function(module, path) {
module$config$graph_path <- path
module
})
freeze_modules(program, "$/steps/first")
is_module_frozen(first)
#> [1] TRUE