| Title: | Stateful Agentic Orchestration for Scientific Reproducibility |
|---|---|
| Description: | A high-performance framework for orchestrating complex agentic workflows in R, specifically designed for scientific reproducibility and auditability. HydraR provides a robust, state-managed engine for Directed Acyclic Graphs (DAGs) and iterative state machines, prioritizing CLI-native LLM interactions (e.g., Gemini, Claude, Copilot) and hardened state persistence (DuckDB/SQLite). It features isolated execution via Git Worktrees for parallel file-modifying tasks, autonomous quality control through integrated auditors, and human-readable visualization using Mermaid.js. Ideal for creating auditable, resumable research assistants and complex bioinformatics pipelines. |
| Authors: | Chi Nam Ignatius Pang [aut, cre] (ORCID: <https://orcid.org/0000-0001-9703-5741>), Aidan Tay [aut] (ORCID: <https://orcid.org/0000-0003-1315-4896>), APAF Agentic Workflow [ctb] |
| Maintainer: | Chi Nam Ignatius Pang <[email protected]> |
| License: | LGPL (>= 3) |
| Version: | 0.2.4 |
| Built: | 2026-09-17 09:06:19 UTC |
| Source: | https://github.com/APAF-bioinformatics/HydraR |
A convenience wrapper to instantiate an AgentLLMNode. Useful for
functional-style DAG construction.
add_llm_node(id, role, driver, model = NULL, cli_opts = list(), ...)add_llm_node(id, role, driver, model = NULL, cli_opts = list(), ...)
id |
String. A unique identifier for the node within the DAG. |
role |
String. The system prompt or identity the LLM should assume (e.g., "Python Developer"). |
driver |
AgentDriver.
An R6 driver object (e.g., |
model |
String. Optional model name override (e.g., "gpt-4"). Defaults to driver default. |
cli_opts |
List. Optional parameters passed to the underlying CLI or API call. |
... |
Additional arguments.
Passed directly to the |
An AgentLLMNode object.
## Not run: # Standard API-based agent with model overrides and safety parameters # Use Sys.getenv to securely load your API key from .Renviron driver <- AnthropicAPIDriver$new(api_key = Sys.getenv("ANTHROPIC_API_KEY")) node_coder <- add_llm_node( id = "optimizer", role = "You are an expert at optimizing R code for performance.", driver = driver, model = "claude-3-opus-20240229", cli_opts = list(temperature = 0, max_tokens = 1024), output_path = "output/optimized.R" ) # CLI-based agent with sandbox permissions and tool discovery gemini <- GeminiCLIDriver$new() node_researcher <- add_llm_node( id = "researcher", role = "Find bioinformatics papers on 'single-cell RNA-seq' using PubMed.", driver = gemini, cli_opts = list(allowed_tools = "pubmed_search,read_pdf", yolo = TRUE) ) ## End(Not run)## Not run: # Standard API-based agent with model overrides and safety parameters # Use Sys.getenv to securely load your API key from .Renviron driver <- AnthropicAPIDriver$new(api_key = Sys.getenv("ANTHROPIC_API_KEY")) node_coder <- add_llm_node( id = "optimizer", role = "You are an expert at optimizing R code for performance.", driver = driver, model = "claude-3-opus-20240229", cli_opts = list(temperature = 0, max_tokens = 1024), output_path = "output/optimized.R" ) # CLI-based agent with sandbox permissions and tool discovery gemini <- GeminiCLIDriver$new() node_researcher <- add_llm_node( id = "researcher", role = "Find bioinformatics papers on 'single-cell RNA-seq' using PubMed.", driver = gemini, cli_opts = list(allowed_tools = "pubmed_search,read_pdf", yolo = TRUE) ) ## End(Not run)
A convenience wrapper to instantiate an AgentLogicNode. Nodes created
this way execute pure R code rather than calling an LLM.
add_logic_node(id, logic_fn, ...)add_logic_node(id, logic_fn, ...)
id |
String. A unique identifier for the node. |
logic_fn |
Function.
An R function that accepts an |
... |
Additional arguments.
Passed directly to the |
An AgentLogicNode object.
## Not run: # 1. Validation Logic: Check if previous output meets quality thresholds quality_gate <- function(state) { results <- state$get("researcher") if (length(results$papers) >= 5) { list(status = "success", output = list(proceed = TRUE)) } else { # 'pause' status can trigger a human-in-the-loop or a retry loop list(status = "pause", output = list(reason = "insufficient results")) } } node_gate <- add_logic_node("quality_gate", quality_gate) # 2. Data Transformation Logic: Clean and format LLM output cleaner <- function(state) { raw_text <- state$get("coder") clean_code <- extract_r_code_advanced(raw_text) list(status = "success", output = list(code = clean_code)) } node_clean <- add_logic_node("cleaner", cleaner) ## End(Not run)## Not run: # 1. Validation Logic: Check if previous output meets quality thresholds quality_gate <- function(state) { results <- state$get("researcher") if (length(results$papers) >= 5) { list(status = "success", output = list(proceed = TRUE)) } else { # 'pause' status can trigger a human-in-the-loop or a retry loop list(status = "pause", output = list(reason = "insufficient results")) } } node_gate <- add_logic_node("quality_gate", quality_gate) # 2. Data Transformation Logic: Clean and format LLM output cleaner <- function(state) { raw_text <- state$get("coder") clean_code <- extract_r_code_advanced(raw_text) list(status = "success", output = list(code = clean_code)) } node_clean <- add_logic_node("cleaner", cleaner) ## End(Not run)
Executes a raw bash script. Can run within an isolated worktree if configured.
An 'AgentBashNode' object.
HydraR::AgentNode -> AgentBashNode
scriptCharacter string or Function. The bash script to execute.
env_varsNamed list. Environment variables to inject.
new()
Initialize Bash Node
AgentBashNode$new(id, label = NULL, script, env_vars = list(), params = list())
idNode ID
labelNode Label
scriptString or Function(state) returning a string.
env_varsNamed list of environment variables
paramsList of standard Node parameters Run Bash Execution
run()
AgentBashNode$run(state, working_dir = NULL)
stateAgentState object.
working_dirDirectory context for execution.
List with output, status_code, and success flag.
clone()
The objects of this class are cloneable with this method.
AgentBashNode$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Simple static bash command node_simple <- AgentBashNode$new(id = "bash_ls", script = "ls -la") # 2. Dynamic bash script using state values and environment variables # The script is generated at runtime based on the current AgentState. dynamic_script <- function(state) { input_file <- state$get("raw_data_path") sprintf("cat %s | grep 'ERR' > failure_log.txt", input_file) } node_dynamic <- AgentBashNode$new( id = "bash_filter", script = dynamic_script, env_vars = list(THRESHOLD = "100", CPU_LIMIT = "4") ) # Execute the node res <- node_dynamic$run(state = AgentState$new(list(raw_data_path = "data.csv"))) message(res$output) ## End(Not run)## Not run: # 1. Simple static bash command node_simple <- AgentBashNode$new(id = "bash_ls", script = "ls -la") # 2. Dynamic bash script using state values and environment variables # The script is generated at runtime based on the current AgentState. dynamic_script <- function(state) { input_file <- state$get("raw_data_path") sprintf("cat %s | grep 'ERR' > failure_log.txt", input_file) } node_dynamic <- AgentBashNode$new( id = "bash_filter", script = dynamic_script, env_vars = list(THRESHOLD = "100", CPU_LIMIT = "4") ) # Execute the node res <- node_dynamic$run(state = AgentState$new(list(raw_data_path = "data.csv"))) message(res$output) ## End(Not run)
The core orchestrator in HydraR, AgentDAG defines and executes a
Directed Graph of AgentNode objects. It supports:
Pure DAGs: Parallel execution using furrr.
Cycles & Loops: Iterative execution via conditional edges.
State Isolation: Parallel branch execution in isolated git worktrees.
Persistence: Automatic state checkpointing and restoration.
An AgentDAG R6 object.
nodesList. Named list of AgentNode objects.
edgesList. Pending edge definitions to be bound.
conditional_edgesList. Conditional transition logic.
error_edgesList. Failover transition logic.
resultsList. Execution results for each node.
trace_logList. Execution telemetry and tracing.
graphigraph. Internal graph representation.
start_nodeString. Explicit entry point for cycles.
stateAgentState. Centralized state object.
message_logMessageLog. Optional audit log.
worktree_managerWorktreeManager. Optional isolation manager.
new()
Initialize AgentDAG
AgentDAG$new()
A new AgentDAG instance with empty node and edge lists.
set_start_node()
Set Start Node(s) Explicitly defines the entry point(s) of the graph. If not called, the engine defaults to nodes with an in-degree of 0. Required for graphs with cycles or where execution must start at a specific node.
AgentDAG$set_start_node(node_ids)
node_idsCharacter vector.
A vector of one or more node IDs. These nodes must already exist in the
DAG (added via add_node).
The AgentDAG object (invisibly).
add_node()
Add a Node
Registers an AgentNode object into the graph.
AgentDAG$add_node(node)
nodeAgentNode.
An instance of AgentNode or a subclass (e.g., AgentLLMNode).
The ID of the node must be unique within the DAG.
The AgentDAG object (invisibly).
add_edge()
Add an Edge Creates a directed connection between nodes. Also supports specialized edge types (Error, Test, Fail) via labeling.
AgentDAG$add_edge(from, to, label = NULL)
fromString or Character vector. The source node ID(s).
toString. The destination node ID.
labelString. Optional label for the edge. In HydraR, certain labels trigger logic:
"error" or "failover": Creates an error edge.
"Test": Creates a conditional edge (true path).
"Fail": Creates a conditional edge (false path).
The AgentDAG object (invisibly).
add_conditional_edge()
Add a Conditional Edge (Loop Support)
Adds branching logic to the graph. After the from node executes,
the test function is evaluated against the node's result.
AgentDAG$add_conditional_edge( from, test = NULL, if_true = NULL, if_false = NULL )
fromString. The ID of the source node.
testFunction.
A predicate function that takes the node result list and returns
TRUE or FALSE.
if_trueString. Optional ID of the node to execute if the test passes.
if_falseString. Optional ID of the node to execute if the test fails.
The AgentDAG object (invisibly).
add_error_edge()
Add an Error Edge (Failover Support)
AgentDAG$add_error_edge(from, to)
fromString node ID.
toString node ID.
The AgentDAG object (invisibly).
run()
Run the Graph
The primary execution engine for the AgentDAG. It manages the orchestration
lifecycle, including state initialization / recovery, worktree setup for
isolation, and routing between nodes. It automatically switches between
linear (parallel-capable) and iterative execution modes based on the
graph's complexity.
AgentDAG$run(
initial_state = NULL,
max_steps = 25,
checkpointer = NULL,
thread_id = NULL,
resume_from = NULL,
use_worktrees = FALSE,
repo_root = getwd(),
cleanup_policy = "auto",
fail_if_dirty = TRUE,
packages = c("withr"),
...
)initial_stateList, AgentState, or String.
The starting data for the workflow. Can be a named list of R objects,
an existing AgentState instance, or a path to a checkpoint (if supported).
Required unless resuming from a checkpointer.
max_stepsInteger.
The maximum number of node executions allowed in a single run() call.
Prevents infinite loops in cyclic graphs. Default is 25.
checkpointerCheckpointer.
An optional Checkpointer R6 object. If provided, the state is
automatically saved after every node execution.
thread_idString.
A unique identifier for this execution "thread" or session. Required if
using a checkpointer to resolve the correct state from storage.
resume_fromString. Optional node ID. If provided (or found in a checkpoint), execution will skip completed nodes and start from this point.
use_worktreesLogical.
Enable branch isolation. If TRUE, parallel branches are executed
in separate git worktrees, preventing file-system conflicts between agents.
repo_rootString.
Path to the master git repository. Required if use_worktrees is TRUE.
cleanup_policyString.
One of "auto" (default), "none", or "aggressive".
Determines how worktrees are removed after execution.
fail_if_dirtyLogical.
If TRUE, execution fails if the repo_root has uncommitted
changes (recommended for reproducibility when using worktrees).
packagesCharacter vector.
A list of R packages to load on parallel worker nodes (passed to furrr).
...Additional arguments.
Passed down to individual node$run() calls.
A list containing:
results: A named list of each node's output.
state: The final AgentState object.
status: "completed" or "paused".
Internal: Linear DAG Execution
.run_linear()
AgentDAG$.run_linear( max_steps = 25, checkpointer = NULL, thread_id = NULL, resume_from = NULL, node_ids = NULL, step_count = 0, fail_if_dirty = TRUE )
max_stepsInteger.
checkpointerCheckpointer object.
thread_idString thread ID.
resume_fromNode ID(s) to resume from.
node_idsCharacter vector of nodes to run.
step_countInteger current total step count.
fail_if_dirtyLogical.
Execution result list. Internal: Iterative Execution
.run_iterative()
AgentDAG$.run_iterative(
max_steps,
checkpointer = NULL,
thread_id = NULL,
resume_from = NULL,
step_count = 0,
fail_if_dirty = TRUE,
packages = c("withr")
)max_stepsInteger.
checkpointerCheckpointer object.
thread_idString.
resume_fromString.
step_countInteger.
fail_if_dirtyLogical.
packagesCharacter vector. Packages to load in parallel workers. Visualize the Graph
plot()
Generates a visual representation of the DAG using Mermaid or DOT syntax.
AgentDAG$plot( type = "mermaid", status = FALSE, details = FALSE, include_params = NULL, show_edge_labels = TRUE )
typeString.
Either "mermaid" (default) for web-native rendering or "grViz"
for DiagrammeR/Graphviz.
statusLogical.
If TRUE, colors nodes based on the results in the current trace log
(Green: Success, Red: Failed, Yellow: Paused).
detailsLogical.
If TRUE, injects node parameters into the labels.
include_paramsCharacter vector.
Optional whitelist of parameter names to display when details is TRUE.
show_edge_labelsLogical. Whether to display labels (e.g., "Test", "Fail") on edges.
The Mermaid/DOT source string (invisibly).
\dontrun{
dag <- dag_create()
dag$add_node(AgentNode$new("A"))
dag$add_node(AgentNode$new("B"))
dag$add_edge("A", "B", label = "proceed")
# Generate Mermaid string
m_src <- dag$plot(type = "mermaid", show_edge_labels = TRUE)
cat(m_src)
}
get_terminal_nodes()
Get Terminal Nodes Identifies nodes with no outgoing edges.
AgentDAG$get_terminal_nodes()
Character vector of node IDs.
get_start_nodes()
Get Start Nodes (Roots) Identifies nodes with no incoming edges.
AgentDAG$get_start_nodes()
Character vector of node IDs. Compile the Graph
compile()
Rebuilds the internal igraph representation and performs validation
checks such as cycle detection, start node disambiguation, and
reachability analysis. This must be called before run().
AgentDAG$compile()
Compilation will throw errors if:
Cycles are found in a pure DAG (no conditional edges).
Multiple root nodes are found without an explicit start_node.
Node IDs used in edges do not exist.
The AgentDAG object (invisibly).
Save the Execution Trace
save_trace()
Exports the detailed telemetry from the last execution (durations, status, outputs) to a JSON file.
AgentDAG$save_trace(file = "dag_trace.json")
fileString.
Path to the output JSON file. Defaults to "dag_trace.json".
The AgentDAG object (invisibly).
Create Graph from Mermaid
from_mermaid()
A static-like method to populate a DAG from a Mermaid string using a custom node factory.
AgentDAG$from_mermaid(mermaid_str, node_factory)
mermaid_strString.
A valid Mermaid graph definition (e.g., "graph TD; A-->B").
node_factoryFunction.
A closure mapping Mermaid labels and parameters to AgentNode
instances. See auto_node_factory for the standard
implementation.
The AgentDAG object (invisibly).
\dontrun{
# Use a custom factory to map all nodes to Logic nodes
simple_factory <- function(id, label, params) {
AgentLogicNode$new(id = id, logic_fn = function(s) list(status="ok"))
}
dag <- AgentDAG$new()
dag$from_mermaid("graph LR; Start-->End", simple_factory)
}
clone()
The objects of this class are cloneable with this method.
AgentDAG$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Define a complex DAG with branching, loops, and custom state # Initializing the orchestrator dag <- AgentDAG$new() # Define logic for data retrieval and quality control register_logic("fetcher", function(state) list(status = "success", output = "raw data")) register_logic("gate", function(state) { if (nchar(state$get("fetcher")) > 5) list(status = "true") else list(status = "false") }) # 2. Programmatic Node Construction dag$add_node(AgentLogicNode$new("start", logic_fn = get_logic("fetcher"))) dag$add_node(AgentLogicNode$new("check", logic_fn = get_logic("gate"))) # 3. Connecting nodes with status-based edges # Errors in 'start' go to a terminal cleanup node dag$add_edge("start", "check") dag$add_conditional_edge("check", test = function(res) res$status == "true", if_true = "process", if_false = "retry" ) # 4. Multi-agent execution context # Compiling and running with initial state dag$compile() final_state <- dag$run(initial_state = list(job_id = "123")) # Checking results print(final_state$get_all()) ## End(Not run) ## ------------------------------------------------ ## Method `AgentDAG$plot` ## ------------------------------------------------ ## Not run: dag <- dag_create() dag$add_node(AgentNode$new("A")) dag$add_node(AgentNode$new("B")) dag$add_edge("A", "B", label = "proceed") # Generate Mermaid string m_src <- dag$plot(type = "mermaid", show_edge_labels = TRUE) cat(m_src) ## End(Not run) ## ------------------------------------------------ ## Method `AgentDAG$from_mermaid` ## ------------------------------------------------ ## Not run: # Use a custom factory to map all nodes to Logic nodes simple_factory <- function(id, label, params) { AgentLogicNode$new(id = id, logic_fn = function(s) list(status="ok")) } dag <- AgentDAG$new() dag$from_mermaid("graph LR; Start-->End", simple_factory) ## End(Not run)## Not run: # 1. Define a complex DAG with branching, loops, and custom state # Initializing the orchestrator dag <- AgentDAG$new() # Define logic for data retrieval and quality control register_logic("fetcher", function(state) list(status = "success", output = "raw data")) register_logic("gate", function(state) { if (nchar(state$get("fetcher")) > 5) list(status = "true") else list(status = "false") }) # 2. Programmatic Node Construction dag$add_node(AgentLogicNode$new("start", logic_fn = get_logic("fetcher"))) dag$add_node(AgentLogicNode$new("check", logic_fn = get_logic("gate"))) # 3. Connecting nodes with status-based edges # Errors in 'start' go to a terminal cleanup node dag$add_edge("start", "check") dag$add_conditional_edge("check", test = function(res) res$status == "true", if_true = "process", if_false = "retry" ) # 4. Multi-agent execution context # Compiling and running with initial state dag$compile() final_state <- dag$run(initial_state = list(job_id = "123")) # Checking results print(final_state$get_all()) ## End(Not run) ## ------------------------------------------------ ## Method `AgentDAG$plot` ## ------------------------------------------------ ## Not run: dag <- dag_create() dag$add_node(AgentNode$new("A")) dag$add_node(AgentNode$new("B")) dag$add_edge("A", "B", label = "proceed") # Generate Mermaid string m_src <- dag$plot(type = "mermaid", show_edge_labels = TRUE) cat(m_src) ## End(Not run) ## ------------------------------------------------ ## Method `AgentDAG$from_mermaid` ## ------------------------------------------------ ## Not run: # Use a custom factory to map all nodes to Logic nodes simple_factory <- function(id, label, params) { AgentLogicNode$new(id = id, logic_fn = function(s) list(status="ok")) } dag <- AgentDAG$new() dag$from_mermaid("graph LR; Start-->End", simple_factory) ## End(Not run)
Abstract base class for CLI-based LLM drivers.
An 'AgentDriver' R6 object.
idString. Unique identifier for the driver.
providerString. Provider name (e.g., "google", "ollama").
model_nameString. The specific model identifier.
supported_optsCharacter vector. Allowed CLI option names.
validation_modeString. Either "warning" or "strict".
working_dirString. Optional path to the working directory/worktree.
repo_rootString. Path to the main repository root (for root-locked CLIs).
new()
Initialize AgentDriver
AgentDriver$new( id, provider = "unknown", model_name = "unknown", validation_mode = "warning", working_dir = NULL, repo_root = NULL )
idUnique identifier.
providerString. Provider name.
model_nameString. Model identifier.
validation_modeString. "warning" or "strict".
working_dirString. Optional working directory.
repo_rootString. Path to the main repository root. Get Driver Capabilities
get_capabilities()
AgentDriver$get_capabilities()
List of logicals. Execute Command in Working Directory
exec_in_dir()
Safely executes a system command within the specified 'working_dir' using 'withr::with_dir' to ensure the original CWD is restored.
AgentDriver$exec_in_dir(command, args, ...)
commandString. The command to run.
argsCharacter vector. Command arguments.
...Additional arguments passed to system2.
Result of system2 call. Filter CLI Noise from LLM Output
filter_llm_noise()
Removes common CLI-injected headers, keychain warnings, or MCP status messages that can corrupt the generated model content.
AgentDriver$filter_llm_noise(text)
textString or Character Vector. Raw output from the CLI.
Character vector of cleaned lines. Call the LLM
call()
AgentDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString. The prompt to send.
modelString. Optional model override.
system_promptString. Optional system prompt.
cli_optsList. Named list of CLI options.
...Additional arguments.
String. Cleaned response from the LLM. Validate No Command Injection
validate_no_injection()
Portably prevents command injection by rejecting inputs with shell metacharacters.
AgentDriver$validate_no_injection(x)
xString or character vector.
The original string invisibly, or throws an error. Validate CLI Options
validate_cli_opts()
AgentDriver$validate_cli_opts(cli_opts)
cli_optsList. Named list to validate.
Invisible TRUE if valid. Format CLI Options
format_cli_opts()
AgentDriver$format_cli_opts(cli_opts = list())
cli_optsList. Named list to format.
Character vector of CLI flags.
clone()
The objects of this class are cloneable with this method.
AgentDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: driver <- AgentDriver$new(id = "test", provider = "mock") ## End(Not run)## Not run: driver <- AgentDriver$new(id = "test", provider = "mock") ## End(Not run)
A specialized AgentNode that leverages a Large Language Model (LLM)
to generate outputs from prompts. It manages prompt construction by combining
a persistent role (system prompt) with dynamic context from the
AgentState. It also handles tool injection and automatic context
discovery from local files (e.g., agents.md, skills.md).
An AgentLLMNode object.
HydraR::AgentNode -> AgentLLMNode
roleString. System prompt/role for the agent.
modelString. Default model.
driverAgentDriver object.
output_formatString. Output expectation.
cli_optsList. Default CLI options for the driver.
prompt_builderFunction(state) -> String.
toolsList of AgentTool objects.
agents_filesCharacter vector. Paths to agents context files.
skills_filesCharacter vector. Paths to skills context files.
new()
Initialize AgentLLMNode
AgentLLMNode$new( id, role, driver, model = NULL, cli_opts = list(), prompt_builder = NULL, tools = list(), label = NULL, params = list(), agents_files = NULL, skills_files = NULL )
idString. Unique identifier for the node.
roleString. The primary system prompt or persona the LLM should assume.
driverAgentDriver.
An instance of an AgentDriver subclass (CLI or API based).
modelString. Optional. The specific model to use (overrides driver default).
cli_optsList. Optional. Named list of parameters for the LLM call (e.g., temperature).
prompt_builderFunction.
Optional. A function that takes an AgentState and returns a
string prompt. If omitted, the node serializes the entire state as JSON.
toolsList.
A list of AgentTool objects available for the agent to use.
labelString. Human-readable name for visualization.
paramsList.
Additional configuration (e.g., output_format="r").
agents_filesCharacter vector. Optional paths to markdown files containing agent interaction guidelines.
skills_filesCharacter vector. Optional paths to markdown files containing specialized tool instructions. Run the LLM Node
run()
Executes the LLM call. This method handles prompt construction, tool injection, context file discovery, and driver invocation.
AgentLLMNode$run(state, ...)
stateAgentState. The centralized state object for the workflow.
...Additional arguments.
Passed through to the driver's call() method.
A list containing status, output (the LLM response),
raw (the full driver response), and meta-information.
Swap Driver at Runtime
swap_driver()
AgentLLMNode$swap_driver(driver)
driverAgentDriver object or String ID.
clone()
The objects of this class are cloneable with this method.
AgentLLMNode$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # NOTE: Set ANTHROPIC_API_KEY in your .Renviron file driver <- AnthropicAPIDriver$new() # Create a node with a prompt builder that pulls from state node <- AgentLLMNode$new( id = "summarizer", role = "You are a concise summarizer.", driver = driver, prompt_builder = function(state) { sprintf("Summarise this text: %s", state$get("input_text")) } ) ## End(Not run)## Not run: # NOTE: Set ANTHROPIC_API_KEY in your .Renviron file driver <- AnthropicAPIDriver$new() # Create a node with a prompt builder that pulls from state node <- AgentLLMNode$new( id = "summarizer", role = "You are a concise summarizer.", driver = driver, prompt_builder = function(state) { sprintf("Summarise this text: %s", state$get("input_text")) } ) ## End(Not run)
A node that executes a synchronous R function.
An 'AgentLogicNode' object.
HydraR::AgentNode -> AgentLogicNode
logic_fnFunction(state) -> List(status, output).
new()
Initialize AgentLogicNode
AgentLogicNode$new(id, logic_fn, label = NULL, params = list())
idUnique identifier.
logic_fnFunction that takes an AgentState object and returns a list.
labelOptional human-readable name.
paramsOptional list of parameters. Run the Logic Node
run()
AgentLogicNode$run(state, ...)
stateAgentState object.
...Additional arguments.
List with status, output, and metadata.
clone()
The objects of this class are cloneable with this method.
AgentLogicNode$clone(deep = FALSE)
deepWhether to make a deep clone.
node <- AgentLogicNode$new("start", function(state) list(status = "success"))node <- AgentLogicNode$new("start", function(state) list(status = "success"))
A node that maps over a list in the state and performs an operation.
An 'AgentMapNode' object.
HydraR::AgentNode -> AgentMapNode
map_keyString. Key in state to map over.
logic_fnFunction(item, state) -> List(status, output).
new()
Initialize AgentMapNode
AgentMapNode$new(id, map_key, logic_fn, label = NULL, params = list())
idUnique identifier.
map_keyString identifier for state retrieval.
logic_fnMapping function.
labelOptional label.
paramsOptional parameters. Run the Map Node
run()
AgentMapNode$run(state, ...)
stateAgentState object.
...Additional arguments.
List with status, output (list of results).
clone()
The objects of this class are cloneable with this method.
AgentMapNode$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # Mapping over a list of URLs to fetch data fetch_logic <- function(url, state) { # Custom logic for each item list(status = "success", output = paste0("Data from ", url)) } node_map <- AgentMapNode$new( id = "batch_fetcher", map_key = "url_list", logic_fn = fetch_logic ) # Setup state with items to map over state <- AgentState$new(list(url_list = c("url1", "url2", "url3"))) results <- node_map$run(state) ## End(Not run)## Not run: # Mapping over a list of URLs to fetch data fetch_logic <- function(url, state) { # Custom logic for each item list(status = "success", output = paste0("Data from ", url)) } node_map <- AgentMapNode$new( id = "batch_fetcher", map_key = "url_list", logic_fn = fetch_logic ) # Setup state with items to map over state <- AgentState$new(list(url_list = c("url1", "url2", "url3"))) results <- node_map$run(state) ## End(Not run)
The abstract base R6 class for all nodes within an AgentDAG.
It defines the common interface and fields required for any node to be
orchestrated by the HydraR engine. Subclasses must implement the run()
method.
An AgentNode object.
idString. Unique identifier for the node.
labelString. Human-readable name/label.
last_resultList. Results from most recent execution.
paramsList. Arbitrary metadata/config parameters.
new()
Initialize AgentNode
AgentNode$new(id, label = NULL, params = list())
idString. A unique identifier for the node. Must be unique within a single DAG.
labelString.
An optional human-readable name for the node. Defaults to the id
if not provided. This label is used in Mermaid visualizations.
paramsList. An optional named list of arbitrary metadata or configuration parameters that are stored on the node and can be accessed during execution. Useful for passing static configuration to nodes created via factories. Run the Node
run()
AgentNode$run(state, ...)
stateAgentState.
An AgentState object (typically a RestrictedState) providing
scoped access to the centralized workflow memory.
...Additional arguments.
Arbitrary parameters passed from AgentDAG$run().
A list with at least status (String, e.g., "success", "failed", "pause")
and output (Any R object to be integrated back into the state).
clone()
The objects of this class are cloneable with this method.
AgentNode$clone(deep = FALSE)
deepWhether to make a deep clone.
# Defining a custom subclass of AgentNode CustomNode <- R6::R6Class("CustomNode", inherit = AgentNode, public = list( run = function(state, ...) { message("Executing custom node: ", self$id) list(status = "success", output = "Custom output") } ) ) node <- CustomNode$new("node_1", label = "My First Node")# Defining a custom subclass of AgentNode CustomNode <- R6::R6Class("CustomNode", inherit = AgentNode, public = list( run = function(state, ...) { message("Executing custom node: ", self$id) list(status = "success", output = "Custom output") } ) ) node <- CustomNode$new("node_1", label = "My First Node")
A node that executes logic for side-effects (e.g., logging, notifications). Its output does not modify the primary AgentState.
An 'AgentObserverNode' object.
HydraR::AgentNode -> AgentObserverNode
observe_fnFunction(state) -> void.
new()
Initialize AgentObserverNode
AgentObserverNode$new(id, observe_fn, label = NULL, params = list())
idUnique identifier.
observe_fnFunction that takes an AgentState.
labelOptional label.
paramsOptional parameters. Run the Observer Node
run()
AgentObserverNode$run(state, ...)
stateAgentState or RestrictedState object.
...Additional arguments.
List with status "observer" and NULL output.
clone()
The objects of this class are cloneable with this method.
AgentObserverNode$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # An observer that logs stats to a file without modifying state logger <- function(state) { stats <- state$get_all() writeLines(jsonlite::toJSON(stats), "logs/observation.json") message("Stats observed and written to logs.") } node_obs <- AgentObserverNode$new( id = "status_logger", observe_fn = logger ) # Run with dummy state node_obs$run(AgentState$new(list(x = 10, y = 20))) ## End(Not run)## Not run: # An observer that logs stats to a file without modifying state logger <- function(state) { stats <- state$get_all() writeLines(jsonlite::toJSON(stats), "logs/observation.json") message("Stats observed and written to logs.") } node_obs <- AgentObserverNode$new( id = "status_logger", observe_fn = logger ) # Run with dummy state node_obs$run(AgentState$new(list(x = 10, y = 20))) ## End(Not run)
Executes a python script via system python or reticulate.
An 'AgentPythonNode' object.
HydraR::AgentNode -> AgentPythonNode
scriptCharacter string or Function. The python script to execute.
engineCharacter. Execution engine ("system2" or "reticulate").
new()
AgentPythonNode$new( id, label = NULL, script, engine = "system2", params = list() )
idNode ID
labelNode Label
scriptString or Function(state) returning a string.
engine"system2" (isolated process) or "reticulate" (inline bindings).
paramsList of standard Node parameters Run Python Execution
run()
AgentPythonNode$run(state, working_dir = NULL)
stateAgentState object.
working_dirDirectory context for execution.
List with output, success flag, and optionally result variables.
clone()
The objects of this class are cloneable with this method.
AgentPythonNode$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Pure system-level Python execution (isolated process) node_sys <- AgentPythonNode$new( id = "py_cleaner", script = "print('Cleaning data...'); result = 10", engine = "system2" ) # 2. Reticulate-based execution with shared memory # This allows the 'result' variable in Python to be returned as an R object. node_retic <- AgentPythonNode$new( id = "py_stats", script = " import numpy as np data = np.array([1, 2, 3, 4, 5]) result = data.mean() ", engine = "reticulate" ) # Execute and retrieve the 'result' object res <- node_retic$run(state = AgentState$new()) message("Mean calculated in Python: ", res$result) ## End(Not run)## Not run: # 1. Pure system-level Python execution (isolated process) node_sys <- AgentPythonNode$new( id = "py_cleaner", script = "print('Cleaning data...'); result = 10", engine = "system2" ) # 2. Reticulate-based execution with shared memory # This allows the 'result' variable in Python to be returned as an R object. node_retic <- AgentPythonNode$new( id = "py_stats", script = " import numpy as np data = np.array([1, 2, 3, 4, 5]) result = data.mean() ", engine = "reticulate" ) # Execute and retrieve the 'result' object res <- node_retic$run(state = AgentState$new()) message("Mean calculated in Python: ", res$result) ## End(Not run)
A node that determines the next node in the DAG dynamically. The logic function must return a list with a 'target_node' field.
An 'AgentRouterNode' object.
HydraR::AgentNode -> AgentRouterNode
router_fnFunction(state) -> List(target_node, output).
new()
Initialize AgentRouterNode
AgentRouterNode$new(id, router_fn, label = NULL, params = list())
idUnique identifier.
router_fnFunction that takes an AgentState and returns a list.
labelOptional human-readable name.
paramsOptional list of parameters. Run the Router Node
run()
AgentRouterNode$run(state, ...)
stateAgentState object.
...Additional arguments.
List with status, output, and target_node.
clone()
The objects of this class are cloneable with this method.
AgentRouterNode$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # Routing based on an LLM sentiment classification classifier <- function(state) { sentiment <- state$get("analyst_output") if (grepl("Positive", sentiment)) { list(target_node = "celebrate", status = "success") } else { list(target_node = "investigate", status = "success") } } node_router <- AgentRouterNode$new( id = "sentiment_router", router_fn = classifier ) # Run with dummy state res <- node_router$run(AgentState$new(list(analyst_output = "Positive results!"))) message("Targeting node: ", res$target_node) ## End(Not run)## Not run: # Routing based on an LLM sentiment classification classifier <- function(state) { sentiment <- state$get("analyst_output") if (grepl("Positive", sentiment)) { list(target_node = "celebrate", status = "success") } else { list(target_node = "investigate", status = "success") } } node_router <- AgentRouterNode$new( id = "sentiment_router", router_fn = classifier ) # Run with dummy state res <- node_router$run(AgentState$new(list(analyst_output = "Positive results!"))) message("Targeting node: ", res$target_node) ## End(Not run)
A strongly typed, centrally managed state object for passing data between nodes in an AgentDAG. It supports declarative schemas for validation and functional "reducers" for sophisticated state merging during execution.
The AgentState is the single source of truth for a DAG. It is designed
to be serializable, allowing the entire workflow state to be checkpointed
and restored.
An AgentState R6 object.
dataEnvironment. Stores state variables.
reducersList. Functions applied to merge updates.
schemaList. Expected types for state variables.
new()
Initialize AgentState
AgentState$new(initial_data = list(), reducers = list(), schema = list())
initial_dataList or String.
The starting data for the state. If a list, it is merged into the
environment. If a string, it is stored under the key "input".
reducersNamed list of functions or character names.
Maps state keys to functions that define how new values are merged
with current values (e.g., append or sum).
schemaNamed list of character strings.
Defines the expected class/type for specific keys (e.g.,
list(count = "numeric")).
Get a state variable
get()
AgentState$get(key, default = NULL)
keyString. The name of the variable to retrieve.
defaultValue. The value to return if the key is not found in the state.
The value associated with the key, or the default. Get all state variables as a list
get_all()
AgentState$get_all()
A named list. Validate a single state variable against the schema
validate()
AgentState$validate(key, value)
keyString.
valueAny value.
TRUE if valid, throws error otherwise. Set a state variable directly (bypassing reducers)
set()
AgentState$set(key, value)
keyString.
valueAny value. Update state using reducers
update()
AgentState$update(updates)
updatesList of state updates. Update state from a node's output
update_from_node()
AgentState$update_from_node(output, node_id)
outputThe output from the node.
node_idThe ID of the node. Export state for persistence (logic as names)
to_list_serializable()
AgentState$to_list_serializable()
List.
clone()
The objects of this class are cloneable with this method.
AgentState$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Initialize state with strict schema validation and reducers state <- AgentState$new( initial_data = list(count = 0, logs = list()), schema = list(count = "numeric", logs = "list"), reducers = list(logs = reducer_append) ) # 2. Basic update (replaces current value) state$set("count", 42) # 3. Reducer update (appends to current list) state$update(list(logs = "Process started")) state$update(list(logs = "Processing step 1")) # 4. Schema validation check # This will throw an error because 'count' must be numeric try(state$set("count", "not a number")) # Verify accumulation print(state$get("logs")) # list("Process started", "Processing step 1") ## End(Not run)## Not run: # 1. Initialize state with strict schema validation and reducers state <- AgentState$new( initial_data = list(count = 0, logs = list()), schema = list(count = "numeric", logs = "list"), reducers = list(logs = reducer_append) ) # 2. Basic update (replaces current value) state$set("count", 42) # 3. Reducer update (appends to current list) state$update(list(logs = "Process started")) state$update(list(logs = "Processing step 1")) # 4. Schema validation check # This will throw an error because 'count' must be numeric try(state$set("count", "not a number")) # Verify accumulation print(state$get("logs")) # list("Process started", "Processing step 1") ## End(Not run)
Defines a tool or action that an agent can perform. Used for prompt-based tool discovery.
An 'AgentTool' object.
nameString. The unique name of the tool.
descriptionString. A clear description of what the tool does.
parametersList. A description of the expected parameters.
exampleString. An example of how to use the tool.
new()
Initialize AgentTool
AgentTool$new(name, description, parameters = list(), example = "")
nameString.
descriptionString.
parametersList or String.
exampleString. Format for Prompt
format()
AgentTool$format()
A formatted string describing the tool.
clone()
The objects of this class are cloneable with this method.
AgentTool$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # Define a tool for searching genomic databases tool <- AgentTool$new( name = "blast_search", description = "Perform a BLAST search against the NCBI non-redundant database.", parameters = list( query = "The DNA sequence string", evalue = "The e-value threshold (default 1e-5)" ), example = "blast_search(query='ATGC...', evalue=0.001)" ) # Format for injection into a system prompt message(tool$format()) ## End(Not run)## Not run: # Define a tool for searching genomic databases tool <- AgentTool$new( name = "blast_search", description = "Perform a BLAST search against the NCBI non-redundant database.", parameters = list( query = "The DNA sequence string", evalue = "The e-value threshold (default 1e-5)" ), example = "blast_search(query='ATGC...', evalue=0.001)" ) # Format for injection into a system prompt message(tool$format()) ## End(Not run)
A specialized AgentDriver for the Anthropic Messages API, providing
access to the Claude family of models.
Setup: To use this driver, you must set the ANTHROPIC_API_KEY
environment variable. It is recommended to add this to your .Renviron file:
ANTHROPIC_API_KEY="sk-ant-..."
An AnthropicAPIDriver object.
HydraR::AgentDriver -> AnthropicAPIDriver
api_urlString. Base URL.
new()
Initialize AnthropicAPIDriver
AnthropicAPIDriver$new( id = "anthropic_api", model = "claude-sonnet-4-6", validation_mode = "warning", working_dir = NULL )
idString. Unique identifier for this driver instance.
modelString.
The Anthropic model ID (e.g., "claude-3-sonnet").
validation_modeString. Driver validation strictness level.
working_dirString. Optional. Base directory for file-system operations.
A new AnthropicAPIDriver object.
Get Capabilities
get_capabilities()
AnthropicAPIDriver$get_capabilities()
A list of capabilities. Call Anthropic API
call()
AnthropicAPIDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString. The prompt text.
modelString. Optional model override.
system_promptString. Optional system prompt.
cli_optsList. Additional API options.
...Additional arguments.
String. LLM response.
clone()
The objects of this class are cloneable with this method.
AnthropicAPIDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Initialize the Claude Messages API driver driver <- AnthropicAPIDriver$new(model = "claude-3-opus-20240229") # 2. Perform a research task with reasoning constraints response <- driver$call( prompt = "Summarize the differences between S3 and R6 classes in R.", system_prompt = "You are a technical documentarian. Use markdown tables.", cli_opts = list( temperature = 0.2, max_tokens = 1024, stop_sequences = list("### Conclusion") ) ) message(response) ## End(Not run)## Not run: # 1. Initialize the Claude Messages API driver driver <- AnthropicAPIDriver$new(model = "claude-3-opus-20240229") # 2. Perform a research task with reasoning constraints response <- driver$call( prompt = "Summarize the differences between S3 and R6 classes in R.", system_prompt = "You are a technical documentarian. Use markdown tables.", cli_opts = list( temperature = 0.2, max_tokens = 1024, stop_sequences = list("### Conclusion") ) ) message(response) ## End(Not run)
A specialized AgentDriver for the Anthropic claude (Claude Code)
CLI. Optimized for terminal-based engineering tasks.
Setup: Requires claude (Anthropic CLI) to be installed and
authenticated. Configure the path via options(HydraR.claude_path = "...")
or in your .Renviron.
An AnthropicCLIDriver object.
HydraR::AgentDriver -> AnthropicCLIDriver
modelString. Default model.
new()
Initialize AnthropicCLIDriver
AnthropicCLIDriver$new( id = "claude_cli", model = "sonnet", validation_mode = "warning", working_dir = NULL )
idUnique identifier.
modelString. Default model.
validation_modeString. "warning" or "strict".
working_dirString. Optional. Path to isolated Git worktree.
A new 'AnthropicCLIDriver' object. Call the LLM
call()
AnthropicCLIDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString.
modelString override.
system_promptString. Optional system prompt.
cli_optsList.
...Additional arguments.
String. Cleaned result.
clone()
The objects of this class are cloneable with this method.
AnthropicCLIDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Initialize the Claude Code CLI driver driver <- AnthropicCLIDriver$new(model = "sonnet") # 2. Execute a complex engineering task with budget constraints # and permission skipping for non-interactive automation. response <- driver$call( prompt = "Refactor R/dag.R to use R6 private methods.", cli_opts = list( dangerously_skip_permissions = TRUE, max_budget_usd = 5.0, verbose = TRUE ) ) ## End(Not run)## Not run: # 1. Initialize the Claude Code CLI driver driver <- AnthropicCLIDriver$new(model = "sonnet") # 2. Execute a complex engineering task with budget constraints # and permission skipping for non-interactive automation. response <- driver$call( prompt = "Refactor R/dag.R to use R6 private methods.", cli_opts = list( dangerously_skip_permissions = TRUE, max_budget_usd = 5.0, verbose = TRUE ) ) ## End(Not run)
Generates a closure that can resolve Mermaid node labels into fully
instantiated AgentNode objects based on inline annotations.
auto_node_factory(driver_registry = NULL)auto_node_factory(driver_registry = NULL)
driver_registry |
DriverRegistry.
An optional registry used to resolve drivers specified in Mermaid
annotations (e.g., |
The factory supports the following type= parameters in Mermaid labels:
llm: Creates an AgentLLMNode. Requires role or role_id.
logic: Creates an AgentLogicNode. Requires logic_id.
router: Creates an AgentRouterNode. Requires logic_id.
map: Creates an AgentMapNode. Requires logic_id and map_key.
A function that takes (id, label, params) and returns an AgentNode.
## Not run: # 'Low Code' workflow: resolve complex attributes from a Mermaid string mermaid_src <- ' graph TD A["Data Fetcher | type=llm | role=Expert Bioinformatician | model=gpt-4o"] B["Quality Gate | type=logic | logic_id=validate_data"] C["Reporter | type=llm | role=Technical Writer | driver=claude_cli"] A --> B B --> C ' # 1. Register the required logic in the registry register_logic("validate_data", function(state) { raw <- state$get("A") if (nchar(raw) > 100) list(status = "success") else list(status = "failed") }) # 2. Create the DAG using the automatic factory factory <- auto_node_factory() dag <- mermaid_to_dag(mermaid_src, node_factory = factory) # Result: n1 is an AgentLLMNode (OpenAI), n2 is Logic, n3 is AgentLLMNode (Claude) ## End(Not run)## Not run: # 'Low Code' workflow: resolve complex attributes from a Mermaid string mermaid_src <- ' graph TD A["Data Fetcher | type=llm | role=Expert Bioinformatician | model=gpt-4o"] B["Quality Gate | type=logic | logic_id=validate_data"] C["Reporter | type=llm | role=Technical Writer | driver=claude_cli"] A --> B B --> C ' # 1. Register the required logic in the registry register_logic("validate_data", function(state) { raw <- state$get("A") if (nchar(raw) > 100) list(status = "success") else list(status = "failed") }) # 2. Create the DAG using the automatic factory factory <- auto_node_factory() dag <- mermaid_to_dag(mermaid_src, node_factory = factory) # Result: n1 is an AgentLLMNode (OpenAI), n2 is Logic, n3 is AgentLLMNode (Claude) ## End(Not run)
An abstract base class defining the contract for state persistence and
recovery in HydraR. Checkpointers allow a DAG to be paused and resumed
across sessions by saving the AgentState after each node execution.
A Checkpointer object.
put()
Persist state to the checkpointer.
Checkpointer$put(thread_id, state)
thread_idString. A unique identifier for the execution thread or session.
stateAgentState.
The AgentState object to be persisted.
NULL (called for side effect). Load state from the checkpointer
get()
Checkpointer$get(thread_id)
thread_idString. The unique identifier associated with the saved state.
AgentState object or NULL if no checkpoint is found for the thread.
clone()
The objects of this class are cloneable with this method.
Checkpointer$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Abstract interface usage (internal) # Checkpointers are passed to dag_create() to enable state persistence. dag <- dag_create( checkpointer = RDSSaver$new(dir = "checkpoints"), message_log = JSONLMessageLog$new(file = "logs/messages.jsonl") ) ## End(Not run)## Not run: # 1. Abstract interface usage (internal) # Checkpointers are passed to dag_create() to enable state persistence. dag <- dag_create( checkpointer = RDSSaver$new(dir = "checkpoints"), message_log = JSONLMessageLog$new(file = "logs/messages.jsonl") ) ## End(Not run)
Identifies and optionally deletes stale remote branches on GitHub. Specifically targets branches authored by 'google-labs-jules[bot]' that have been inactive beyond a specified threshold, or any branch already merged into the main branch.
cleanup_jules_branches( repo_root = getwd(), threshold_hours = 24, dry_run = TRUE, verbose = TRUE )cleanup_jules_branches( repo_root = getwd(), threshold_hours = 24, dry_run = TRUE, verbose = TRUE )
repo_root |
String. Path to the git repository. Defaults to 'getwd()'. |
threshold_hours |
Numeric. Inactivity threshold for Jules's branches (hours). Defaults to 24. |
dry_run |
Logical. If TRUE (default), identifies candidates but does not perform deletion. |
verbose |
Logical. If TRUE, prints detailed status messages. |
A character vector of branches identified for deletion.
## Not run: # 1. Identify stale branches without deleting (Dry Run) stale_branches <- cleanup_jules_branches(dry_run = TRUE) # 2. Perform aggressive cleanup of stale bot branches (> 48 hours) cleanup_jules_branches( threshold_hours = 48, dry_run = FALSE, verbose = TRUE ) ## End(Not run)## Not run: # 1. Identify stale branches without deleting (Dry Run) stale_branches <- cleanup_jules_branches(dry_run = TRUE) # 2. Perform aggressive cleanup of stale bot branches (> 48 hours) cleanup_jules_branches( threshold_hours = 48, dry_run = FALSE, verbose = TRUE ) ## End(Not run)
Handles semantic or git-level conflicts during branch merges. Used by Conflict Harmonizer nodes.
A 'ConflictResolver' R6 object.
strategyString. "llm", "human", or "ours".
driverAgentDriver. The driver to use for "llm" strategy.
new()
Initialize ConflictResolver
ConflictResolver$new(strategy = "llm", driver = NULL)
strategyConflict resolution strategy.
driverOptional LLM driver for semantic resolution.
A new 'ConflictResolver' object. Resolve a Conflict
resolve()
ConflictResolver$resolve(repo_root, branch_a, branch_b, files)
repo_rootPath to repo.
branch_aBranch A.
branch_bBranch B (usually base).
filesList of conflicting files.
List with status and details.
clone()
The objects of this class are cloneable with this method.
ConflictResolver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Initialize a resolver with an LLM strategy resolver <- ConflictResolver$new( strategy = "llm", driver = AnthropicAPIDriver$new(model = "claude-3-sonnet") ) # 2. Resolve a detected conflict during a merge res <- resolver$resolve( repo_root = ".", branch_a = "feature-updates", branch_b = "main", files = c("R/core.R") ) ## End(Not run)## Not run: # 1. Initialize a resolver with an LLM strategy resolver <- ConflictResolver$new( strategy = "llm", driver = AnthropicAPIDriver$new(model = "claude-3-sonnet") ) # 2. Resolve a detected conflict during a merge res <- resolver$resolve( repo_root = ".", branch_a = "feature-updates", branch_b = "main", files = c("R/core.R") ) ## End(Not run)
A specialized AgentDriver for the GitHub gh copilot CLI extension.
Setup: Requires the GitHub CLI (gh) and the copilot
extension (gh extension install github/gh-copilot). Must be
authenticated via gh auth login.
A CopilotCLIDriver object.
HydraR::AgentDriver -> CopilotCLIDriver
new()
Initialize CopilotCLIDriver
CopilotCLIDriver$new( id = "copilot_cli", validation_mode = "warning", working_dir = NULL )
idUnique identifier.
validation_modeString. "warning" or "strict".
working_dirString. Optional. Path to isolated Git worktree. Call the LLM
call()
CopilotCLIDriver$call(prompt, system_prompt = NULL, cli_opts = list(), ...)
promptString.
system_promptString. Optional system prompt.
cli_optsList.
...Additional arguments.
String. Cleaned result.
clone()
The objects of this class are cloneable with this method.
CopilotCLIDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Use GitHub Copilot CLI extension driver <- CopilotCLIDriver$new() # 2. Request a terminal command with tool permissions response <- driver$call( prompt = "Find all CSV files larger than 1MB and move them to 'data/'", cli_opts = list( allow_all_tools = TRUE, no_custom_instructions = FALSE ) ) ## End(Not run)## Not run: # 1. Use GitHub Copilot CLI extension driver <- CopilotCLIDriver$new() # 2. Request a terminal command with tool permissions response <- driver$call( prompt = "Find all CSV files larger than 1MB and move them to 'data/'", cli_opts = list( allow_all_tools = TRUE, no_custom_instructions = FALSE ) ) ## End(Not run)
Factory function to create an AgentLogicNode that merges parallel git branches back into the base branch.
create_merge_harmonizer( id = "merge_harmonizer", strategy = "sequential", conflict_resolver = NULL )create_merge_harmonizer( id = "merge_harmonizer", strategy = "sequential", conflict_resolver = NULL )
id |
String. Node identifier. |
strategy |
String. "sequential" or "octopus". |
conflict_resolver |
ConflictResolver. Optional custom resolver. |
An 'AgentLogicNode' R6 object.
## Not run: # 1. Create a harmonizer with a specific conflict resolution strategy resolver <- ConflictResolver$new(strategy = "human") merger <- create_merge_harmonizer( id = "global_merger", strategy = "sequential", conflict_resolver = resolver ) # 2. This node is then added to a DAG that uses parallel execution # and a WorktreeManager in its state. ## End(Not run)## Not run: # 1. Create a harmonizer with a specific conflict resolution strategy resolver <- ConflictResolver$new(strategy = "human") merger <- create_merge_harmonizer( id = "global_merger", strategy = "sequential", conflict_resolver = resolver ) # 2. This node is then added to a DAG that uses parallel execution # and a WorktreeManager in its state. ## End(Not run)
Instantiates an AgentLLMNode and appends it to the provided AgentDAG
in one step.
dag_add_llm_node(dag, id, role, driver, model = NULL, cli_opts = list(), ...)dag_add_llm_node(dag, id, role, driver, model = NULL, cli_opts = list(), ...)
dag |
AgentDAG. The graph object to which the node will be added. |
id |
String. Unique identifier for the node. |
role |
String. System prompt/role for the agent. |
driver |
AgentDriver. The LLM driver instance. |
model |
String. Optional model name override. |
cli_opts |
List. Optional CLI/API parameters. |
... |
Additional arguments.
Passed to |
The modified AgentDAG object (invisibly).
## Not run: # Chaining nodes using the functional API # This pattern is common in complex HydraR orchestration scripts. dag <- dag_create() |> dag_add_llm_node( id = "writer", role = "Academic technical writer", driver = OpenAIAPIDriver$new(api_key = Sys.getenv("OPENAI_API_KEY"), model = "gpt-4-turbo") ) |> dag_add_llm_node( id = "critic", role = "Peer reviewer", driver = AnthropicAPIDriver$new(api_key = Sys.getenv("ANTHROPIC_API_KEY"), model = "claude-3-sonnet"), cli_opts = list(temperature = 0.2) ) ## End(Not run)## Not run: # Chaining nodes using the functional API # This pattern is common in complex HydraR orchestration scripts. dag <- dag_create() |> dag_add_llm_node( id = "writer", role = "Academic technical writer", driver = OpenAIAPIDriver$new(api_key = Sys.getenv("OPENAI_API_KEY"), model = "gpt-4-turbo") ) |> dag_add_llm_node( id = "critic", role = "Peer reviewer", driver = AnthropicAPIDriver$new(api_key = Sys.getenv("ANTHROPIC_API_KEY"), model = "claude-3-sonnet"), cli_opts = list(temperature = 0.2) ) ## End(Not run)
Add an R Logic Node directly to a DAG
dag_add_logic_node(dag, id, logic_fn, ...)dag_add_logic_node(dag, id, logic_fn, ...)
dag |
AgentDAG object. |
id |
String. Unique identifier for the node. |
logic_fn |
Function. Pure R function taking an AgentState object. |
... |
Additional arguments passed to AgentLogicNode$new() |
The modified AgentDAG object (invisibly).
## Not run: # Adding logic nodes to a production pipeline dag <- dag_create() |> dag_add_llm_node("planner", "Project Manager", GeminiCLIDriver$new()) |> dag_add_logic_node("audit_check", function(state) { # Log state for external observability message("Auditing current progress...") list(status = "success", output = list(timestamp = Sys.time())) }) ## End(Not run)## Not run: # Adding logic nodes to a production pipeline dag <- dag_create() |> dag_add_llm_node("planner", "Project Manager", GeminiCLIDriver$new()) |> dag_add_logic_node("audit_check", function(state) { # Log state for external observability message("Auditing current progress...") list(status = "success", output = list(timestamp = Sys.time())) }) ## End(Not run)
Initializes a new AgentDAG object. This is the primary entry point for
building orchestration workflows either programmatically or from definitions.
dag_create(message_log = NULL)dag_create(message_log = NULL)
message_log |
MessageLog.
An optional |
An AgentDAG R6 object.
## Not run: library(HydraR) # 1. Simple Audit-only DAG dag <- dag_create() # 2. Production DAG with persistent DuckDB audit log and custom metadata # This pattern ensures every agent interaction is recorded for reproducibility. log <- DuckDBMessageLog$new(db_path = "audit_trail.duckdb") dag <- dag_create(message_log = log) # 3. Advanced setup with a pre-configured memory saver saver <- MemorySaver$new() cp <- Checkpointer$new(saver = saver) # Note: Checkpointer is typically assigned to the DAG after creation dag$checkpointer <- cp ## End(Not run)## Not run: library(HydraR) # 1. Simple Audit-only DAG dag <- dag_create() # 2. Production DAG with persistent DuckDB audit log and custom metadata # This pattern ensures every agent interaction is recorded for reproducibility. log <- DuckDBMessageLog$new(db_path = "audit_trail.duckdb") dag <- dag_create(message_log = log) # 3. Advanced setup with a pre-configured memory saver saver <- MemorySaver$new() cp <- Checkpointer$new(saver = saver) # Note: Checkpointer is typically assigned to the DAG after creation dag$checkpointer <- cp ## End(Not run)
A singleton-like registry for managing AgentDriver instances. Enables runtime recovery and hot-swapping of drivers across nodes.
A 'DriverRegistry' R6 object.
driversList. Storage for registered drivers.
new()
Initialize DriverRegistry
DriverRegistry$new()
A new 'DriverRegistry' object. Register a Driver
register()
DriverRegistry$register(driver, overwrite = FALSE)
driverAgentDriver object.
overwriteLogical. Whether to overwrite existing driver with same ID.
The registry object (invisibly). Get a Driver
get()
DriverRegistry$get(id)
idString. Driver identifier.
AgentDriver object or NULL. List Registered Drivers
list_drivers()
DriverRegistry$list_drivers()
Data frame of registered drivers and their metadata. Remove a Driver
remove()
DriverRegistry$remove(id)
idString.
The registry object (invisibly). Clear Registry
clear()
DriverRegistry$clear()
The registry object (invisibly).
clone()
The objects of this class are cloneable with this method.
DriverRegistry$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Create a registry and register multiple models reg <- DriverRegistry$new() reg$register(GeminiCLIDriver$new(id = "fast_model", model = "gemini-1.5-flash")) reg$register(GeminiCLIDriver$new(id = "smart_model", model = "gemini-1.5-pro")) # 2. Audit the registered drivers summary <- reg$list_drivers() print(summary) # 3. Fetch a specific driver by its shorthand ID my_drv <- reg$get("smart_model") ## End(Not run)## Not run: # 1. Create a registry and register multiple models reg <- DriverRegistry$new() reg$register(GeminiCLIDriver$new(id = "fast_model", model = "gemini-1.5-flash")) reg$register(GeminiCLIDriver$new(id = "smart_model", model = "gemini-1.5-pro")) # 2. Audit the registered drivers summary <- reg$list_drivers() print(summary) # 3. Fetch a specific driver by its shorthand ID my_drv <- reg$get("smart_model") ## End(Not run)
A persistent implementation of MessageLog that writes messages to a
centralized DuckDB database. This is the recommended logger for production
and audit-heavy workflows.
A DuckDBMessageLog object.
HydraR::MessageLog -> DuckDBMessageLog
db_pathString. Path to DuckDB file.
new()
Finalizer to clean up the cached connection.
Initialize DuckDBMessageLog.
DuckDBMessageLog$new(db_path = "~/.gemini/memory/bot_history.duckdb")
db_pathString.
Path to the DuckDB file. If the file does not exist, it will be
created upon the first message log. Defaults to the master
bot_history.duckdb.
A new DuckDBMessageLog object.
log()
Store a message.
DuckDBMessageLog$log(msg)
msgList. Message object.
The log object (invisibly).
get_all()
Get all logs.
DuckDBMessageLog$get_all()
List of logs.
clone()
The objects of this class are cloneable with this method.
DuckDBMessageLog$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Persistent audit logging to DuckDB audit_log <- DuckDBMessageLog$new( db_path = "data/agent_audit.duckdb" ) # 2. Attach to a DAG and run dag <- dag_create(message_log = audit_log) dag$run(thread_id = "experiment_404") # 3. Retrieve and analyze messages from a previous session prev_logs <- audit_log$get_all() ## End(Not run)## Not run: # 1. Persistent audit logging to DuckDB audit_log <- DuckDBMessageLog$new( db_path = "data/agent_audit.duckdb" ) # 2. Attach to a DAG and run dag <- dag_create(message_log = audit_log) dag$run(thread_id = "experiment_404") # 3. Retrieve and analyze messages from a previous session prev_logs <- audit_log$get_all() ## End(Not run)
A production-grade Checkpointer that utilizes DuckDB for high-performance
state persistence. Supports BLOB storage of serialized R objects and
concurrent access patterns.
A DuckDBSaver R6 object.
HydraR::Checkpointer -> DuckDBSaver
conDBIConnection.
table_nameString.
new()
Initialize DuckDBSaver
DuckDBSaver$new(con = NULL, db_path = NULL, table_name = "agent_checkpoints")
conDBIConnection. An existing DBI connection to a DuckDB instance.
db_pathString. Path to a DuckDB file. If provided, the driver will handle the connection internally.
table_nameString.
The name of the table used to store checkpoints. Defaults to
"agent_checkpoints".
Save state
put()
DuckDBSaver$put(thread_id, state)
thread_idString.
stateAgentState object. Load state
get()
DuckDBSaver$get(thread_id)
thread_idString.
AgentState object or NULL.
clone()
The objects of this class are cloneable with this method.
DuckDBSaver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Production-ready persistence using DuckDB saver <- DuckDBSaver$new( db_path = "storage/hydrar_main.duckdb", table_name = "workflow_checkpoints" ) # 2. Orchestrate a long-running DAG dag <- dag_create(checkpointer = saver) dag$run(thread_id = "genomic_alignment_job_42") # 3. Query the internal storage directly if needed library(DBI) DBI::dbGetQuery(saver$con, "SELECT thread_id, updated_at FROM workflow_checkpoints") ## End(Not run)## Not run: # 1. Production-ready persistence using DuckDB saver <- DuckDBSaver$new( db_path = "storage/hydrar_main.duckdb", table_name = "workflow_checkpoints" ) # 2. Orchestrate a long-running DAG dag <- dag_create(checkpointer = saver) dag$run(thread_id = "genomic_alignment_job_42") # 3. Query the internal storage directly if needed library(DBI) DBI::dbGetQuery(saver$con, "SELECT thread_id, updated_at FROM workflow_checkpoints") ## End(Not run)
Extract R Code from LLM Response
extract_r_code_advanced(raw)extract_r_code_advanced(raw)
raw |
String. Raw text response from LLM. |
String. Extracted R code or same text if no blocks found.
## Not run: # 1. Extract a single R block from markdown noise raw_text <- "Here is the code: \n```r\nplot(cars)\n```\nHope it helps!" extract_r_code_advanced(raw_text) # 2. Extract and concatenate multiple blocks multi_blocks <- "First: ```R\nx <- 1\n``` Then: ```r\ny <- x + 1\n```" code <- extract_r_code_advanced(multi_blocks) # Result: \"x <- 1\n\ny <- x + 1\" ## End(Not run)## Not run: # 1. Extract a single R block from markdown noise raw_text <- "Here is the code: \n```r\nplot(cars)\n```\nHope it helps!" extract_r_code_advanced(raw_text) # 2. Extract and concatenate multiple blocks multi_blocks <- "First: ```R\nx <- 1\n``` Then: ```r\ny <- x + 1\n```" code <- extract_r_code_advanced(multi_blocks) # Result: \"x <- 1\n\ny <- x + 1\" ## End(Not run)
Format Toolset for Prompt
format_toolset(tools)format_toolset(tools)
tools |
List of AgentTool objects. |
A formatted string containing all tool descriptions.
## Not run: # Format a collection of tools for an agent tools <- list( search = AgentTool$new("google_search", "Search the web"), run_r = AgentTool$new("r_exec", "Execute R code locally") ) prompt_appendix <- format_toolset(tools) cat(prompt_appendix) ## End(Not run)## Not run: # Format a collection of tools for an agent tools <- list( search = AgentTool$new("google_search", "Search the web"), run_r = AgentTool$new("r_exec", "Execute R code locally") ) prompt_appendix <- format_toolset(tools) cat(prompt_appendix) ## End(Not run)
A specialized AgentDriver for the Google Gemini API (AI Studio / Vertex AI).
Setup: Requires the GOOGLE_API_KEY environment variable to
be set. Add it to your .Renviron: GOOGLE_API_KEY="AIza..."
A GeminiAPIDriver R6 object.
HydraR::AgentDriver -> GeminiAPIDriver
api_baseString. Base URL.
new()
Initialize GeminiAPIDriver
GeminiAPIDriver$new( id = "gemini_api", model = "gemini-3.1-flash-lite-preview", validation_mode = "warning", working_dir = NULL )
idString. Unique identifier for this driver.
modelString.
Google model ID (e.g., "gemini-1.5-flash").
validation_modeString. Controls schema enforcement behavior.
working_dirString. Optional path for isolated execution.
A new GeminiAPIDriver object.
Get Capabilities
get_capabilities()
GeminiAPIDriver$get_capabilities()
A list of capabilities supported by the Gemini API. Call Gemini API
call()
GeminiAPIDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString. The prompt or query provided by the user.
modelString. Optional override for the model ID.
system_promptString. Optional system-level instructions for the model.
cli_optsList.
Advanced API parameters (e.g., generationConfig).
...Additional arguments passed to the requester.
String. The text response generated by Gemini.
clone()
The objects of this class are cloneable with this method.
GeminiAPIDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Initialize Gemini Pro API driver <- GeminiAPIDriver$new(model = "gemini-1.5-pro") # 2. Advanced call with custom generation config and safety filtering response <- driver$call( prompt = "Write a high-performance R function for matrix multiplication.", cli_opts = list( generationConfig = list( temperature = 0.9, topP = 0.8, topK = 40, maxOutputTokens = 2048 ), safetySettings = list( list(category = "HARM_CATEGORY_HARASSMENT", threshold = "BLOCK_LOW_AND_ABOVE") ) ) ) ## End(Not run)## Not run: # 1. Initialize Gemini Pro API driver <- GeminiAPIDriver$new(model = "gemini-1.5-pro") # 2. Advanced call with custom generation config and safety filtering response <- driver$call( prompt = "Write a high-performance R function for matrix multiplication.", cli_opts = list( generationConfig = list( temperature = 0.9, topP = 0.8, topK = 40, maxOutputTokens = 2048 ), safetySettings = list( list(category = "HARM_CATEGORY_HARASSMENT", threshold = "BLOCK_LOW_AND_ABOVE") ) ) ) ## End(Not run)
A specialized AgentDriver that invokes the Google gemini CLI
tool. This is the preferred driver for workflows requiring local tool use
and filesystem interaction via the Google-native MCP bridge.
Setup: Requires the gemini CLI to be installed and in your
PATH. You can override the binary path using:
options(HydraR.gemini_path = "/path/to/gemini") or by setting the
HYDRAR_GEMINI_PATH environment variable in your .Renviron.
A GeminiCLIDriver object.
HydraR::AgentDriver -> GeminiCLIDriver
modelString. Default model. Omit to use CLI default.
new()
Initialize GeminiCLIDriver
GeminiCLIDriver$new( id = "gemini_cli", model = "gemini-2.5-flash", validation_mode = "warning", working_dir = NULL, repo_root = NULL )
idString. Unique identifier for this driver.
modelString.
The Gemini model ID (e.g., "gemini-1.5-flash").
validation_modeString.
Either "warning" or "strict".
working_dirString. Optional. Path to an isolated git worktree where the CLI will execute.
repo_rootString. Optional. Path to the main repository to enable cross-worktree context. Call the LLM
call()
GeminiCLIDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString.
modelString override.
system_promptString. Optional system prompt.
cli_optsList. Named list of CLI options.
...Additional arguments.
String. Cleaned result.
clone()
The objects of this class are cloneable with this method.
GeminiCLIDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Standard CLI-based agent with model selection driver <- GeminiCLIDriver$new(model = "gemini-1.5-pro") # 2. Advanced call with MCP tool discovery and 'YOLO' mode enabled # This allows the agent to execute tools without interactive confirmation. response <- driver$call( prompt = "Analyze the R scripts in this directory and suggest performance fixes.", cli_opts = list( allowed_tools = "ls,grep,read_file", allowed_mcp_server_names = "filesystem,github", yolo = TRUE, include_directories = c("R", "tests") ) ) ## End(Not run)## Not run: # 1. Standard CLI-based agent with model selection driver <- GeminiCLIDriver$new(model = "gemini-1.5-pro") # 2. Advanced call with MCP tool discovery and 'YOLO' mode enabled # This allows the agent to execute tools without interactive confirmation. response <- driver$call( prompt = "Analyze the R scripts in this directory and suggest performance fixes.", cli_opts = list( allowed_tools = "ls,grep,read_file", allowed_mcp_server_names = "filesystem,github", yolo = TRUE, include_directories = c("R", "tests") ) ) ## End(Not run)
A specialized driver for Gemini's multimodal and image generation capabilities (supporting Imagen and native Gemini 3.x modalities).
Setup: Requires GOOGLE_API_KEY in your .Renviron.
A GeminiImageDriver R6 object.
HydraR::AgentDriver -> HydraR::GeminiAPIDriver -> GeminiImageDriver
output_dirString. Directory to save generated images.
aspect_ratioString. Default "16:9".
new()
Initialize GeminiImageDriver
GeminiImageDriver$new( id = "gemini_image", model = "gemini-3.1-flash-image-preview", output_dir = "images", aspect_ratio = "1:1", validation_mode = "warning", working_dir = NULL )
idString. Unique identifier for the image driver.
modelString. Google model ID (defaults to multimodal flash).
output_dirString. The directory where generated images will be saved.
aspect_ratioString.
The default aspect ratio for generated images (e.g., "16:9", "1:1").
validation_modeString. Controls schema enforcement.
working_dirString. Optional path for isolated execution.
A new GeminiImageDriver instance.
Call Gemini Image API (Multimodal Unified)
call()
Sends a prompt to Gemini for image generation.
GeminiImageDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString. Image prompt.
modelString. Optional override.
system_promptString. Optional system prompt.
cli_optsList. Parameters (aspectRatio, etc).
...Additional arguments.
String. Local path to the generated image.
clone()
The objects of this class are cloneable with this method.
GeminiImageDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Generate a high-resolution laboratory illustration # Multimodal Gemini 3.1 models infer dimensions from prompt + config driver <- GeminiImageDriver$new(output_dir = "assets/media") # 2. Request a specific aspect ratio and filename img_path <- driver$call( prompt = "A futuristic bioinformatics lab with DNA holograms, hyper-realistic, 8k", cli_opts = list( aspectRatio = "16:9", sampleCount = 1, filename = "hero_dna_lab.png" ) ) message("Hero image saved to: ", img_path) ## End(Not run)## Not run: # 1. Generate a high-resolution laboratory illustration # Multimodal Gemini 3.1 models infer dimensions from prompt + config driver <- GeminiImageDriver$new(output_dir = "assets/media") # 2. Request a specific aspect ratio and filename img_path <- driver$call( prompt = "A futuristic bioinformatics lab with DNA holograms, hyper-realistic, 8k", cli_opts = list( aspectRatio = "16:9", sampleCount = 1, filename = "hero_dna_lab.png" ) ) message("Hero image saved to: ", img_path) ## End(Not run)
List Registered Roles
get_agent_roles()get_agent_roles()
Character vector of role names.
## Not run: # List all personas currently registered in the system personas <- get_agent_roles() stopifnot("bio_analyst" %in% personas) ## End(Not run)## Not run: # List all personas currently registered in the system personas <- get_agent_roles() stopifnot("bio_analyst" %in% personas) ## End(Not run)
Get the Default Agent Driver
get_default_driver()get_default_driver()
AgentDriver object or NULL.
## Not run: # Resolve the driver to be used when none is explicitly specified drv <- get_default_driver() if (!is.null(drv)) { message("Using default provider: ", drv$provider) } ## End(Not run)## Not run: # Resolve the driver to be used when none is explicitly specified drv <- get_default_driver() if (!is.null(drv)) { message("Using default provider: ", drv$provider) } ## End(Not run)
Global Driver Registry Accessor
get_driver_registry()get_driver_registry()
The global DriverRegistry instance.
## Not run: # Access the singleton registry used across the entire package reg <- get_driver_registry() # Register a global driver that any node can reference by name 'default' reg$register(OpenAIAPIDriver$new(id = "default"), overwrite = TRUE) ## End(Not run)## Not run: # Access the singleton registry used across the entire package reg <- get_driver_registry() # Register a global driver that any node can reference by name 'default' reg$register(OpenAIAPIDriver$new(id = "default"), overwrite = TRUE) ## End(Not run)
Get Logic Function
get_logic(name)get_logic(name)
name |
String. Unique identifier. |
Function or NULL.
## Not run: # Retrieve a function by name for manual node construction logic_fn <- get_logic("validate_results") if (!is.null(logic_fn)) { node <- AgentLogicNode$new(id = "validator", logic_fn = logic_fn) } ## End(Not run)## Not run: # Retrieve a function by name for manual node construction logic_fn <- get_logic("validate_results") if (!is.null(logic_fn)) { node <- AgentLogicNode$new(id = "validator", logic_fn = logic_fn) } ## End(Not run)
Get an LLM Role (System Prompt)
get_role(name)get_role(name)
name |
String. Unique identifier. |
String prompt text, or NULL if not found.
## Not run: # Resolve a role for an LLM node manually role_prompt <- get_role("bio_analyst") node <- AgentLLMNode$new(id = "a1", role = role_prompt, driver = GeminiCLIDriver$new()) ## End(Not run)## Not run: # Resolve a role for an LLM node manually role_prompt <- get_role("bio_analyst") node <- AgentLLMNode$new(id = "a1", role = role_prompt, driver = GeminiCLIDriver$new()) ## End(Not run)
Get a Role-specific System Prompt
get_role_prompt(name)get_role_prompt(name)
name |
String. Role identifier. |
String prompt text.
## Not run: # Convenience helper to get role prompts from the Logic Registry register_role("assistant", "You are a helpful assistant.") p <- get_role_prompt("assistant") ## End(Not run)## Not run: # Convenience helper to get role prompts from the Logic Registry register_role("assistant", "You are a helpful assistant.") p <- get_role_prompt("assistant") ## End(Not run)
Connects to the master DuckDB for telemetry and logs. Follows APAF Bioinformatics mandatory pattern for database persistence.
init_bot_history(read_only = FALSE)init_bot_history(read_only = FALSE)
read_only |
Logical. If TRUE, connects in read-only mode. |
A DBIConnection object.
## Not run: # Connect to the master bot history for auditing con <- init_bot_history(read_only = TRUE) # Use DBI to query the execution logs library(DBI) dbListTables(con) dbDisconnect(con) ## End(Not run)## Not run: # Connect to the master bot history for auditing con <- init_bot_history(read_only = TRUE) # Use DBI to query the execution logs library(DBI) dbListTables(con) dbDisconnect(con) ## End(Not run)
Helper to verify that an object is a list (but not a data.frame) and that all its elements have non-empty names.
is_named_list(x)is_named_list(x)
x |
Any object. |
Logical.
## Not run: # Valid named list is_named_list(list(a = 1, b = 2)) # TRUE # Invalid: contains unnamed elements is_named_list(list(a = 1, 2)) # FALSE # Invalid: data.frame (technically a list, but we want a record/dict) is_named_list(data.frame(a = 1)) # FALSE ## End(Not run)## Not run: # Valid named list is_named_list(list(a = 1, b = 2)) # TRUE # Invalid: contains unnamed elements is_named_list(list(a = 1, 2)) # FALSE # Invalid: data.frame (technically a list, but we want a record/dict) is_named_list(data.frame(a = 1)) # FALSE ## End(Not run)
A file-based implementation of MessageLog that appends messages to
a JSON Lines file. This implementation is safe for parallel execution across
git worktrees as it uses atomic line appending.
A JSONLMessageLog object.
HydraR::MessageLog -> JSONLMessageLog
pathString. Path to JSONL file.
new()
Initialize JSONLMessageLog.
JSONLMessageLog$new(path = tempfile(fileext = ".jsonl"))
pathString. The output file path for the JSON Lines log. Defaults to a temporary file.
log()
Store a message (atomic append).
JSONLMessageLog$log(msg)
msgList. Message object.
get_all()
Get all logs.
JSONLMessageLog$get_all()
List of logs.
clone()
The objects of this class are cloneable with this method.
JSONLMessageLog$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Create a file-based logger using JSONL format file_log <- JSONLMessageLog$new(path = "logs/pipeline_audit.jsonl") # 2. Orchestrate a DAG with file-level auditing dag <- dag_create(message_log = file_log) dag$run(initial_state = list(x = 1)) # 3. Read back the logs from the file logs <- file_log$get_all() print(logs[[1]]) ## End(Not run)## Not run: # 1. Create a file-based logger using JSONL format file_log <- JSONLMessageLog$new(path = "logs/pipeline_audit.jsonl") # 2. Orchestrate a DAG with file-level auditing dag <- dag_create(message_log = file_log) dag$run(initial_state = list(x = 1)) # 3. Read back the logs from the file logs <- file_log$get_all() print(logs[[1]]) ## End(Not run)
List Registered Components
list_logic()list_logic()
Character vector of names.
## Not run: # Check which custom logic functions are currently available available_logic <- list_logic() message("Registered logic: ", paste(available_logic, collapse = ", ")) ## End(Not run)## Not run: # Check which custom logic functions are currently available available_logic <- list_logic() message("Registered logic: ", paste(available_logic, collapse = ", ")) ## End(Not run)
Parses a declarative workflow definition from a YAML or JSON file. This function handles the resolution of external logic files and role-playing identities referenced in the definition.
load_workflow(file_path)load_workflow(file_path)
file_path |
String.
The absolute or relative path to a |
A list representing the workflow structure, including graph,
initial_state, roles, and logic mapping.
## Not run: # 1. Prepare a YAML workflow with relative logic paths # my_workflow.yaml: # roles: # analyst: "You are an analyst." # graph: | # graph TD # A[type=llm | role_id=analyst]-->B[type=logic | logic_id=clean_data] # logic: # clean_data: "scripts/clean.R" wf <- load_workflow("my_workflow.yaml") # 2. Key components are resolved during loading print(wf$roles$analyst) print(wf$logic$clean_data) # Returns the function sourced from scripts/clean.R ## End(Not run)## Not run: # 1. Prepare a YAML workflow with relative logic paths # my_workflow.yaml: # roles: # analyst: "You are an analyst." # graph: | # graph TD # A[type=llm | role_id=analyst]-->B[type=logic | logic_id=clean_data] # logic: # clean_data: "scripts/clean.R" wf <- load_workflow("my_workflow.yaml") # 2. Key components are resolved during loading print(wf$roles$analyst) print(wf$logic$clean_data) # Returns the function sourced from scripts/clean.R ## End(Not run)
A non-persistent, in-memory implementation of MessageLog. Messages
are stored in an internal list that exists only for the duration of the
R session.
A MemoryMessageLog object.
HydraR::MessageLog -> MemoryMessageLog
logsList. Storage for message logs.
log()
Store a message.
MemoryMessageLog$log(msg)
msgList. Message object.
The log object (invisibly).
get_all()
Get all logs.
MemoryMessageLog$get_all()
List of logs.
clone()
The objects of this class are cloneable with this method.
MemoryMessageLog$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Create a new memory logger for rapid prototyping log <- MemoryMessageLog$new() # 2. Run a DAG and inspect logs after execution dag <- dag_create(message_log = log) dag$run(initial_state = list(input = "Hello")) # Inspect total messages captured all_messages <- log$get_all() message("Captured ", length(all_messages), " messages.") ## End(Not run)## Not run: # 1. Create a new memory logger for rapid prototyping log <- MemoryMessageLog$new() # 2. Run a DAG and inspect logs after execution dag <- dag_create(message_log = log) dag$run(initial_state = list(input = "Hello")) # Inspect total messages captured all_messages <- log$get_all() message("Captured ", length(all_messages), " messages.") ## End(Not run)
An in-memory implementation of the Checkpointer interface. Stores
checkpoints in a dedicated R environment. This is useful for testing or
short-lived sessions where persistence to disk is not required.
A MemorySaver object.
HydraR::Checkpointer -> MemorySaver
storageEnvironment. Stores the states.
new()
Initialize MemorySaver
Creates a new environment for in-memory checkpoint storage.
MemorySaver$new()
put()
Persist state to the checkpointer.
MemorySaver$put(thread_id, state)
thread_idString.
stateAgentState object.
get()
Load state from the checkpointer.
MemorySaver$get(thread_id)
thread_idString.
AgentState object or NULL.
clone()
The objects of this class are cloneable with this method.
MemorySaver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Use MemorySaver for transient testing or short sessions saver <- MemorySaver$new() dag <- dag_create(checkpointer = saver) # 2. States are saved in the 'storage' environment under thread_id dag$run(thread_id = "test_run_01") ls(saver$storage) # Returns "test_run_01" ## End(Not run)## Not run: # 1. Use MemorySaver for transient testing or short sessions saver <- MemorySaver$new() dag <- dag_create(checkpointer = saver) # 2. States are saved in the 'storage' environment under thread_id dag$run(thread_id = "test_run_01") ls(saver$storage) # Returns "test_run_01" ## End(Not run)
Create AgentDAG from Mermaid
mermaid_to_dag(mermaid_str, node_factory = auto_node_factory())mermaid_to_dag(mermaid_str, node_factory = auto_node_factory())
mermaid_str |
String. Mermaid syntax. |
node_factory |
Function(id, label) -> AgentNode. Defaults to 'auto_node_factory()'. |
The AgentDAG object.
## Not run: # 1. High-level 'Logic-First' graph # Mapping Mermaid node labels to registered R functions dag1 <- mermaid_to_dag("graph TD; A[logic:fetch]-->B[logic:validate];") # 2. Complex 'Agent-First' graph with extended metadata # Explicitly defining roles, drivers, and models inside attributes mermaid_src <- ' graph TD A["Researcher | type=llm | role=Expert Analyst | model=sonnet"] B["Critic | type=llm | role=Scientific Reviewer | temp=0.2"] C["Refiner | type=logic | logic_id=refine_markdown"] A --> B B -- (feedback_required) --> A B -- (approved) --> C ' # Spawning the DAG dag2 <- mermaid_to_dag(mermaid_src) # Verification print(dag2$nodes$A$role) # "Expert Analyst" ## End(Not run)## Not run: # 1. High-level 'Logic-First' graph # Mapping Mermaid node labels to registered R functions dag1 <- mermaid_to_dag("graph TD; A[logic:fetch]-->B[logic:validate];") # 2. Complex 'Agent-First' graph with extended metadata # Explicitly defining roles, drivers, and models inside attributes mermaid_src <- ' graph TD A["Researcher | type=llm | role=Expert Analyst | model=sonnet"] B["Critic | type=llm | role=Scientific Reviewer | temp=0.2"] C["Refiner | type=logic | logic_id=refine_markdown"] A --> B B -- (feedback_required) --> A B -- (approved) --> C ' # Spawning the DAG dag2 <- mermaid_to_dag(mermaid_src) # Verification print(dag2$nodes$A$role) # "Expert Analyst" ## End(Not run)
An abstract base class defining the interface for message logging in HydraR. Subclasses provide concrete storage implementations (Memory, File, Database).
A MessageLog base object.
log()
Store a message.
MessageLog$log(msg)
msgList. Message object.
The log object (invisibly).
get_all()
Get all logs.
MessageLog$get_all()
List of logs.
clone()
The objects of this class are cloneable with this method.
MessageLog$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Abstract interface usage (internal) # Message logs are passed to dag_create() for audit trails. dag <- dag_create( message_log = DuckDBMessageLog$new(db_path = "audit.duckdb") ) ## End(Not run)## Not run: # 1. Abstract interface usage (internal) # Message logs are passed to dag_create() for audit trails. dag <- dag_create( message_log = DuckDBMessageLog$new(db_path = "audit.duckdb") ) ## End(Not run)
A specialized AgentDriver for local execution of Open source models
via the ollama CLI. Ideal for air-gapped or privacy-sensitive workflows.
Setup: Ensure the ollama server is running locally. You can
specify the binary path via options(HydraR.ollama_path = "...").
An OllamaDriver object.
HydraR::AgentDriver -> OllamaDriver
modelString. Default model.
new()
Initialize OllamaDriver
OllamaDriver$new( id = "ollama", model = "llama3.2", validation_mode = "warning", working_dir = NULL )
idUnique identifier.
modelString. Default model.
validation_modeString. "warning" or "strict".
working_dirString. Optional. Path to isolated Git worktree. Format CLI Options for Ollama
format_cli_opts()
OllamaDriver$format_cli_opts(cli_opts = list())
cli_optsList.
Character vector. Call the LLM
call()
OllamaDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString.
modelString override.
system_promptString. Optional system prompt.
cli_optsList.
...Additional arguments.
String. Cleaned result.
clone()
The objects of this class are cloneable with this method.
OllamaDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Use llama3.2 locally via Ollama driver <- OllamaDriver$new(model = "llama3.2") # 2. Call with high-precision sampling and large context window # Ideal for summarizing long bioinformatics documents. response <- driver$call( prompt = "Summarize the technical specifications for the 'samtools' format.", cli_opts = list( num_ctx = 32768, temperature = 0.1, repeat_penalty = 1.2, seed = 42 ) ) ## End(Not run)## Not run: # 1. Use llama3.2 locally via Ollama driver <- OllamaDriver$new(model = "llama3.2") # 2. Call with high-precision sampling and large context window # Ideal for summarizing long bioinformatics documents. response <- driver$call( prompt = "Summarize the technical specifications for the 'samtools' format.", cli_opts = list( num_ctx = 32768, temperature = 0.1, repeat_penalty = 1.2, seed = 42 ) ) ## End(Not run)
A specialized AgentDriver that interacts with the OpenAI Chat
Completions API. Requires an active OpenAI account and API key.
Setup: To use this driver, you must set the OPENAI_API_KEY
environment variable. It is recommended to add this to your .Renviron file:
OPENAI_API_KEY="sk-..."
An OpenAIAPIDriver object.
HydraR::AgentDriver -> OpenAIAPIDriver
api_urlString. Base URL.
new()
Initialize OpenAIAPIDriver
OpenAIAPIDriver$new( id = "openai_api", model = "gpt-5.4-mini", validation_mode = "warning", working_dir = NULL )
idString. Unique identifier for this driver instance.
modelString.
The OpenAI model ID (e.g., "gpt-4", "gpt-3.5-turbo").
validation_modeString.
Either "warning" or "strict". Controls how schema
mismatches are handled.
working_dirString. Optional. The directory where output files should be generated.
A new OpenAIAPIDriver object.
Get Capabilities
get_capabilities()
OpenAIAPIDriver$get_capabilities()
A list of capabilities. Call OpenAI API
call()
OpenAIAPIDriver$call( prompt, model = NULL, system_prompt = NULL, cli_opts = list(), ... )
promptString. The primary user message or instruction sent to the LLM.
modelString. Optional model override for this specific call.
system_promptString. Optional system-level instruction (role-playing or constraint).
cli_optsList.
Additional parameters passed to the JSON body (e.g., temperature = 0.7).
...Additional arguments. Passed through to the internal request handler.
String. The text content of the LLM's response.
clone()
The objects of this class are cloneable with this method.
OpenAIAPIDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Standard API-based completion driver <- OpenAIAPIDriver$new(model = "gpt-4-turbo") # 2. Advanced call with JSON mode and deterministic sampling # This ensures the LLM returns a strictly formatted JSON string. response <- driver$call( prompt = "Return a JSON list of 3 common R packages for plotting.", cli_opts = list( temperature = 0, response_format = list(type = "json_object"), max_tokens = 500 ) ) message(response) ## End(Not run)## Not run: # 1. Standard API-based completion driver <- OpenAIAPIDriver$new(model = "gpt-4-turbo") # 2. Advanced call with JSON mode and deterministic sampling # This ensures the LLM returns a strictly formatted JSON string. response <- driver$call( prompt = "Return a JSON list of 3 common R packages for plotting.", cli_opts = list( temperature = 0, response_format = list(type = "json_object"), max_tokens = 500 ) ) message(response) ## End(Not run)
A specialized AgentDriver for the official codex CLI tool (v0.118+).
This model is legacy but still supported for specific code-generation tasks.
An OpenAICodexCLIDriver object.
HydraR::AgentDriver -> OpenAICodexCLIDriver
new()
Initialize OpenAICodexCLIDriver
OpenAICodexCLIDriver$new( id = "codex_cli", validation_mode = "warning", working_dir = NULL )
idString. Unique identifier.
validation_modeString. "warning" or "strict".
working_dirString. Optional. Path to isolated Git worktree. Call the LLM
call()
Sends a execution request to the Codex CLI.
OpenAICodexCLIDriver$call(prompt, system_prompt = NULL, cli_opts = list(), ...)
promptString.
system_promptString. Optional system prompt.
cli_optsList.
...Additional arguments.
String. Cleaned result.
clone()
The objects of this class are cloneable with this method.
OpenAICodexCLIDriver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Legacy Codex CLI support driver <- OpenAICodexCLIDriver$new() # 2. Isolated execution with skip-checks for specific environments response <- driver$call( prompt = "Create a data.frame with 5 rows and 2 columns.", cli_opts = list( sandbox = TRUE, skip_git_repo_check = TRUE ) ) ## End(Not run)## Not run: # 1. Legacy Codex CLI support driver <- OpenAICodexCLIDriver$new() # 2. Isolated execution with skip-checks for specific environments response <- driver$call( prompt = "Create a data.frame with 5 rows and 2 columns.", cli_opts = list( sandbox = TRUE, skip_git_repo_check = TRUE ) ) ## End(Not run)
Extracts nodes and edges from 'graph TD' or 'flowchart TD' strings. Supports basic node labels and directed edges.
parse_mermaid(mermaid_str)parse_mermaid(mermaid_str)
mermaid_str |
String. Mermaid syntax. |
A list containing 'nodes' (data.frame) and 'edges' (data.frame).
mermaid <- "graph TD\n A --> B" parse_mermaid(mermaid)mermaid <- "graph TD\n A --> B" parse_mermaid(mermaid)
A lightweight, file-based checkpointer that uses R's native saveRDS
and readRDS functions. Each thread is saved as an individual .rds
file in a specified directory.
An RDSSaver object.
HydraR::Checkpointer -> RDSSaver
dirString. Directory to store .rds checkpoint files.
new()
Initialize RDSSaver
RDSSaver$new(dir = "checkpoints")
dirString. Directory path for checkpoint files.
put()
Persist state to an .rds file.
RDSSaver$put(thread_id, state)
thread_idString.
stateAgentState object.
get()
Load state from an .rds file.
RDSSaver$get(thread_id)
thread_idString.
AgentState object or NULL.
clone()
The objects of this class are cloneable with this method.
RDSSaver$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Persistent checkpointing to a local directory saver <- RDSSaver$new(dir = "vault/checkpoints") # 2. Create a DAG and run it with a specific thread ID dag <- dag_create(checkpointer = saver) dag$run(thread_id = "agent_session_alpha") # 3. Later, resume the same session - HydraR will load the RDS file dag$run(thread_id = "agent_session_alpha") ## End(Not run)## Not run: # 1. Persistent checkpointing to a local directory saver <- RDSSaver$new(dir = "vault/checkpoints") # 2. Create a DAG and run it with a specific thread ID dag <- dag_create(checkpointer = saver) dag$run(thread_id = "agent_session_alpha") # 3. Later, resume the same session - HydraR will load the RDS file dag$run(thread_id = "agent_session_alpha") ## End(Not run)
A functional reducer that appends new elements to an existing vector or list. This is the standard pattern for accumulating results or logs across multiple agent steps.
reducer_append(current, new)reducer_append(current, new)
current |
The current value in the state. |
new |
The new value to be added. |
A combined vector or list containing both current and new.
## Not run: # Accumulate a trace of agent IDs state <- AgentState$new( initial_data = list(visited = character()), reducers = list(visited = reducer_append) ) state$update(list(visited = "agent_a")) state$update(list(visited = "agent_b")) print(state$get("visited")) # [1] "agent_a" "agent_b" ## End(Not run)## Not run: # Accumulate a trace of agent IDs state <- AgentState$new( initial_data = list(visited = character()), reducers = list(visited = reducer_append) ) state$update(list(visited = "agent_a")) state$update(list(visited = "agent_b")) print(state$get("visited")) # [1] "agent_a" "agent_b" ## End(Not run)
A functional reducer that performs a deep merge of two named lists. It
uses utils::modifyList internally, ensuring that existing keys are
overwritten by new values while preserving other keys.
reducer_merge_list(current, new)reducer_merge_list(current, new)
current |
The current list in the state. |
new |
The new list to merge in. |
A single merged list.
## Not run: # Deep merging configuration or results current_cfg <- list(params = list(temp = 0.5, top_p = 1.0), tags = "v1") new_cfg <- list(params = list(temp = 0.7), tags = "v2") # modifyList behavior ensured: tags is replaced, params is merged merged <- reducer_merge_list(current_cfg, new_cfg) str(merged) # list(params = list(temp = 0.7, top_p = 1.0), tags = "v2") ## End(Not run)## Not run: # Deep merging configuration or results current_cfg <- list(params = list(temp = 0.5, top_p = 1.0), tags = "v1") new_cfg <- list(params = list(temp = 0.7), tags = "v2") # modifyList behavior ensured: tags is replaced, params is merged merged <- reducer_merge_list(current_cfg, new_cfg) str(merged) # list(params = list(temp = 0.7, top_p = 1.0), tags = "v2") ## End(Not run)
Stores a pure R function in a centralized logic registry. This allows the function to be referenced by name in Mermaid diagrams or YAML/JSON workflow definitions without needing to pass the function object across environments.
register_logic(name, fn)register_logic(name, fn)
name |
String.
A unique identifier for the function (e.g., |
fn |
Function.
The R function to be registered. It should typically accept an
|
The registry environment (invisibly).
## Not run: # 1. Register a simple math function register_logic("add_one", function(state) list(status = "ok", output = state$get("x") + 1)) # 2. Register a complex validation function that uses the Logic Registry # This function can now be referenced by name 'validate_results' in any YAML workflow. validate_results <- function(state) { results <- state$get("researcher_node") if (is.null(results)) { return(list(status = "failed", message = "No results found")) } list(status = "success", output = list(valid = TRUE)) } register_logic("validate_results", validate_results) ## End(Not run)## Not run: # 1. Register a simple math function register_logic("add_one", function(state) list(status = "ok", output = state$get("x") + 1)) # 2. Register a complex validation function that uses the Logic Registry # This function can now be referenced by name 'validate_results' in any YAML workflow. validate_results <- function(state) { results <- state$get("researcher_node") if (is.null(results)) { return(list(status = "failed", message = "No results found")) } list(status = "success", output = list(valid = TRUE)) } register_logic("validate_results", validate_results) ## End(Not run)
Stores a persistent system prompt or "identity" in the registry. This is useful for centralizing agent personas so they can be reused across multiple DAGs or workflows.
register_role(name, prompt_text)register_role(name, prompt_text)
name |
String.
A unique, logic-friendly identifier for the role (e.g., |
prompt_text |
String. The full text of the system prompt the LLM should assume. |
The registry environment (invisibly).
## Not run: # Define a specific identity for a bioinformatics analyst register_role( name = "bio_analyst", prompt_text = "You are a senior bioinformatician specializing in NGS data. Always provide R code for visualization using ggplot2." ) # Register a reviewer role register_role( name = "reviewer", prompt_text = "You are a rigorous peer reviewer. Check for statistical accuracy." ) ## End(Not run)## Not run: # Define a specific identity for a bioinformatics analyst register_role( name = "bio_analyst", prompt_text = "You are a senior bioinformatician specializing in NGS data. Always provide R code for visualization using ggplot2." ) # Register a reviewer role register_role( name = "reviewer", prompt_text = "You are a rigorous peer reviewer. Check for statistical accuracy." ) ## End(Not run)
Loads a workflow from a file and renders its architecture as a Mermaid diagram. Supports high-fidelity exports to various image formats.
render_workflow_file(file_path, output_file = NULL, status = FALSE, ...)render_workflow_file(file_path, output_file = NULL, status = FALSE, ...)
file_path |
String. Path to the YAML workflow file. |
output_file |
String. Optional path to save the diagram (e.g., "plot.png"). Supported extensions: .png, .pdf, .svg. |
status |
Logical. If TRUE, styling is applied (requires a valid trace log in the workflow state). |
... |
Additional arguments passed to 'dag$run()'. |
A 'DiagrammeR' htmlwidget if 'output_file' is NULL, otherwise saves the file.
## Not run: # 1. View interactive diagram in RStudio render_workflow_file("plans/main.yaml") # 2. Export high-resolution diagram for publication render_workflow_file( file_path = "plans/main.yaml", output_file = "reports/figures/workflow_v1.png", status = TRUE # Colors nodes by their last status ) ## End(Not run)## Not run: # 1. View interactive diagram in RStudio render_workflow_file("plans/main.yaml") # 2. Export high-resolution diagram for publication render_workflow_file( file_path = "plans/main.yaml", output_file = "reports/figures/workflow_v1.png", status = TRUE # Colors nodes by their last status ) ## End(Not run)
Provides a mechanism to quickly obtain a pre-configured AgentDriver
using logic-friendly keys like "gemini", "claude", or "openai".
resolve_default_driver(driver_id, driver_registry = NULL)resolve_default_driver(driver_id, driver_registry = NULL)
driver_id |
String.
A shorthand identifier. Supported values include: |
driver_registry |
DriverRegistry.
An optional registry object to look up custom drivers first. If omitted,
the global |
An AgentDriver object.
## Not run: # 1. Simple shorthand resolution drv1 <- resolve_default_driver("gemini") # GeminiCLIDriver drv2 <- resolve_default_driver("openai_api") # OpenAIAPIDriver # 2. Resolution with a custom registry context reg <- DriverRegistry$new() reg$register(AnthropicCLIDriver$new(id = "my_custom_claude")) # This will find the driver in the provided registry drv3 <- resolve_default_driver("my_custom_claude", driver_registry = reg) ## End(Not run)## Not run: # 1. Simple shorthand resolution drv1 <- resolve_default_driver("gemini") # GeminiCLIDriver drv2 <- resolve_default_driver("openai_api") # OpenAIAPIDriver # 2. Resolution with a custom registry context reg <- DriverRegistry$new() reg$register(AnthropicCLIDriver$new(id = "my_custom_claude")) # This will find the driver in the provided registry drv3 <- resolve_default_driver("my_custom_claude", driver_registry = reg) ## End(Not run)
A security wrapper for AgentState that restricts access based on node_id. Implements "True Privacy" for inter-agent communication.
A 'RestrictedState' object.
stateAgentState. The underlying global state.
node_idString. The ID of the currently executing node.
read_onlyLogical. If TRUE, set() and update() are blocked.
loggerMessageLog. Audit log for communication.
new()
Initialize RestrictedState
RestrictedState$new(state, node_id, logger = NULL, read_only = FALSE)
stateAgentState object.
node_idString node ID.
loggerOptional MessageLog object.
read_onlyLogical. If TRUE, blocks all write operations. Restricted Get
get()
RestrictedState$get(key, default = NULL)
keyString.
defaultDefault value. Restricted Set
set()
RestrictedState$set(key, value)
keyString.
valueAny. Forward Update
update()
RestrictedState$update(updates)
updatesList. Send Private Message
send_message()
RestrictedState$send_message(to, content, ...)
toString. Target node ID.
contentAny. Message content.
...Additional metadata. Receive Private Messages
receive_messages()
RestrictedState$receive_messages()
List of messages for this node. Clear Own Inbox
clear_inbox()
Removes all messages from the node's own inbox. Filtered Get All
RestrictedState$clear_inbox()
get_all()
RestrictedState$get_all()
List. All public state variables (no private inboxes).
clone()
The objects of this class are cloneable with this method.
RestrictedState$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Secure state wrapper for a specific node global_state <- AgentState$new(initial_data = list(x = 1)) node_state <- RestrictedState$new(state = global_state, node_id = "agent_01") # 2. Inter-agent private messaging (True Privacy) node_state$send_message(to = "agent_02", content = "Secret instructions") # 3. Recipient reads their own inbox recipient_state <- RestrictedState$new(state = global_state, node_id = "agent_02") messages <- recipient_state$receive_messages() ## End(Not run)## Not run: # 1. Secure state wrapper for a specific node global_state <- AgentState$new(initial_data = list(x = 1)) node_state <- RestrictedState$new(state = global_state, node_id = "agent_01") # 2. Inter-agent private messaging (True Privacy) node_state$send_message(to = "agent_02", content = "Secret instructions") # 3. Recipient reads their own inbox recipient_state <- RestrictedState$new(state = global_state, node_id = "agent_02") messages <- recipient_state$receive_messages() ## End(Not run)
Set the Default Agent Driver
set_default_driver(driver)set_default_driver(driver)
driver |
AgentDriver object or ID string. |
NULL (invisibly)
## Not run: # 1. Set a global default driver for all LLM nodes set_default_driver(AnthropicCLIDriver$new(model = "claude-3-opus")) # 2. Or set it using an ID already present in the registry get_driver_registry()$register(GeminiCLIDriver$new(id = "fast-gen")) set_default_driver("fast-gen") ## End(Not run)## Not run: # 1. Set a global default driver for all LLM nodes set_default_driver(AnthropicCLIDriver$new(model = "claude-3-opus")) # 2. Or set it using an ID already present in the registry get_driver_registry()$register(GeminiCLIDriver$new(id = "fast-gen")) set_default_driver("fast-gen") ## End(Not run)
A high-level helper that orchestrates the "Low Code" lifecycle: it takes
a workflow definition (from load_workflow), parses the
internal graph structure, instantiates all nodes via the provided factory,
applies conditional/error edges, and performs a final compilation check.
spawn_dag(wf, node_factory = auto_node_factory())spawn_dag(wf, node_factory = auto_node_factory())
wf |
List.
A workflow object previously returned by |
node_factory |
Function.
An optional factory function to map Mermaid labels to nodes. Defaults to
|
A compiled and ready-to-run AgentDAG object.
## Not run: # 1. Load the declarative plan # This registers roles and logic from the YAML into the global registry. wf <- load_workflow("plans/pipeline.yaml") # 2. Spawn the executable DAG # Uses auto_node_factory() to parse the Mermaid string in the YAML. dag <- spawn_dag(wf) # 3. Execute with initial data results <- dag$run(initial_state = wf$initial_state) ## End(Not run)## Not run: # 1. Load the declarative plan # This registers roles and logic from the YAML into the global registry. wf <- load_workflow("plans/pipeline.yaml") # 2. Spawn the executable DAG # Uses auto_node_factory() to parse the Mermaid string in the YAML. dag <- spawn_dag(wf) # 3. Execute with initial data results <- dag$run(initial_state = wf$initial_state) ## End(Not run)
Default mapping of Mermaid labels to AgentNodes. Convention: "type:name" or "name" Types supported: "logic", "llm" (requires global driver).
standard_node_factory(id, label, driver = NULL)standard_node_factory(id, label, driver = NULL)
id |
String. Node ID. |
label |
String. Node label. |
driver |
AgentDriver. Optional driver for LLM nodes. |
AgentNode object.
## Not run: # 1. Fast resolve from a Mermaid string with defaults drv <- resolve_default_driver("gemini") node <- standard_node_factory("n1", "logic:clean_data") # 2. Using the LLM type with a provided driver node_llm <- standard_node_factory("n2", "llm:Technical Writer", driver = drv) ## End(Not run)## Not run: # 1. Fast resolve from a Mermaid string with defaults drv <- resolve_default_driver("gemini") node <- standard_node_factory("n1", "logic:clean_data") # 2. Using the LLM type with a provided driver node_llm <- standard_node_factory("n2", "llm:Technical Writer", driver = drv) ## End(Not run)
A high-level helper that performs a comprehensive check on a YAML/JSON workflow file. This includes schema validation, topological consistency checks (Mermaid vs YAML), and R logic syntax linting.
validate_workflow_file(file_path)validate_workflow_file(file_path)
file_path |
String. Path to the workflow definition file. |
Logical TRUE if valid (invisibly). Throws a detailed error on failure.
## Not run: # Holistic check: schema + topology + R linting validate_workflow_file("inst/workflows/bioinfo.yaml") ## End(Not run)## Not run: # Holistic check: schema + topology + R linting validate_workflow_file("inst/workflows/bioinfo.yaml") ## End(Not run)
Performs a holistic check on the instantiated DAG and the source workflow. Ensures all roles, logic, and edges are synchronized and syntactically correct.
validate_workflow_full(dag, wf)validate_workflow_full(dag, wf)
dag |
AgentDAG object. |
wf |
List. The workflow object from 'load_workflow'. |
Logical TRUE if valid, otherwise throws an error.
## Not run: # Perform a deep audit of an instantiated DAG against its YAML source wf_data <- load_workflow("orchestration_plan.yaml") dag <- spawn_dag(wf_data) # This ensures no 'ghost' edges exist in the Mermaid graph validate_workflow_full(dag, wf_data) ## End(Not run)## Not run: # Perform a deep audit of an instantiated DAG against its YAML source wf_data <- load_workflow("orchestration_plan.yaml") dag <- spawn_dag(wf_data) # This ensures no 'ghost' edges exist in the Mermaid graph validate_workflow_full(dag, wf_data) ## End(Not run)
Manages the lifecycle of isolated git worktrees for parallel DAG branches. This ensures that each parallel worker has its own filesystem checkout, preventing file-level conflicts between concurrent CLI agents.
A 'WorktreeManager' R6 object.
repo_rootString. Path to the main git repository.
worktreesList. Active worktrees indexed by node_id.
base_branchString. The branch to fork worktrees from.
branch_prefixString. Prefix for auto-generated branch names.
cleanup_policyString. "auto", "none", or "aggressive".
thread_idString. Optional identifier for the current DAG execution.
new()
Initialize the WorktreeManager.
WorktreeManager$new( repo_root = getwd(), base_branch = "main", branch_prefix = "hydra", cleanup_policy = "auto", thread_id = NULL )
repo_rootPath to the main git repository. Defaults to 'getwd()'.
base_branchBranch to branch from. Defaults to "main".
branch_prefixPrefix for new branches.
cleanup_policyCleanup strategy.
thread_idOptional string to group worktrees. Defaults to random UUID.
A new 'WorktreeManager' object. Validate a branch name
validate_branch_name()
WorktreeManager$validate_branch_name(branch)
branchString. The branch name to validate.
Logical or error. Create an Isolated Worktree
create()
WorktreeManager$create(node_id, branch_name = NULL, fail_if_dirty = TRUE)
node_idString. Identifier for the DAG node.
branch_nameOptional specific branch name.
fail_if_dirtyLogical. Whether to stop if repo has uncommitted changes.
String. The absolute path to the new worktree. Get Worktree Path for a Node
get_path()
WorktreeManager$get_path(node_id)
node_idNode identifier.
String path or NULL. Get Worktree Branch for a Node
get_branch()
WorktreeManager$get_branch(node_id)
node_idNode identifier.
String branch name or NULL. Remove the Physical Worktree Directory
remove_worktree()
WorktreeManager$remove_worktree(node_id, force = TRUE)
node_idNode identifier.
forceLogical. Force removal.
Logical (invisibly). Delete the branch and unregister node
delete_branch()
WorktreeManager$delete_branch(node_id)
node_idNode identifier.
Logical (invisibly). Legacy remove method for backward compatibility
remove()
WorktreeManager$remove(node_id, force = TRUE, delete_branch = FALSE)
node_idNode identifier.
forceLogical. Force removal.
delete_branchLogical. Delete the branch.
NULL (called for side effect). Cleanup Worktree based on Policy and Status
cleanup_node()
WorktreeManager$cleanup_node(node_id, status = "success")
node_idNode identifier.
statusString. "success", "failure", or "conflict".
NULL (called for side effect). Cleanup All Worktrees
cleanup()
Removes all worktrees managed by this instance.
WorktreeManager$cleanup()
Logical (invisibly). Check if a branch exists
branch_exists()
WorktreeManager$branch_exists(branch)
branchString.
Logical. Check if the Repository is Clean
is_repo_clean()
WorktreeManager$is_repo_clean()
Logical.
clone()
The objects of this class are cloneable with this method.
WorktreeManager$clone(deep = FALSE)
deepWhether to make a deep clone.
## Not run: # 1. Initialize WorktreeManager for a specific repository wt_manager <- WorktreeManager$new( repo_root = "~/Projects/my_repo", base_branch = "develop", cleanup_policy = "auto" ) # 2. Create an isolated worktree for a parallel node wt_path <- wt_manager$create(node_id = "parallel_worker_A") message("Worktree created at: ", wt_path) # 3. Cleanup after success wt_manager$cleanup_node("parallel_worker_A", status = "success") ## End(Not run)## Not run: # 1. Initialize WorktreeManager for a specific repository wt_manager <- WorktreeManager$new( repo_root = "~/Projects/my_repo", base_branch = "develop", cleanup_policy = "auto" ) # 2. Create an isolated worktree for a parallel node wt_path <- wt_manager$create(node_id = "parallel_worker_A") message("Worktree created at: ", wt_path) # 3. Cleanup after success wt_manager$cleanup_node("parallel_worker_A", status = "success") ## End(Not run)