An asynchronous agent runtime for R, inspired by Pi.
Rho expresses Pi’s provider and agent architecture through S7 classes and open generics. nanonext provides asynchronous I/O, and mirai provides worker evaluation.
An effectful operation returns a task or stream. Waiting is an explicit edge operation, so a CLI can block while a Shiny application, extension, or another agent keeps composing work.
The deterministic provider is the executable specification for the same
event protocol used by live providers. rho_prompt() returns before the
result is collected; rho_await() is the explicit wait.
library(rho)
agent <- rho_agent(
provider = rho_faux_provider(),
model = rho_model(provider = "faux", id = "faux")
)
run_task <- rho_prompt(agent, "hello from R")
rho_is_task(run_task)
#> [1] TRUE
run <- rho_await(run_task, timeout = 5000)
c(
status = run@status,
answer = run@messages[[2L]]@content[[1L]]@text
)
#> status answer
#> "completed" "faux: hello from R"The transcript contains typed messages and content; the lifecycle is an ordered sequence of typed events rather than callbacks hidden inside the provider.
vapply(run@events, function(event) event@type, character(1))
#> [1] "agent_start" "turn_start" "message_start" "message_end"
#> [5] "message_start" "message_update" "message_update" "message_update"
#> [9] "message_end" "turn_end" "agent_end" "agent_settled"Live authentication is a value passed to the provider catalog. Rho does not search environment variables or process-global credential state. The importer accepts a Pi or Codex auth file only when its path is supplied by the caller.
credential_path <- getOption("rho.openai_codex_credential")
credential <- rho_load_openai_codex_credential(
path = credential_path
) |>
rho_await(timeout = 5000)
codex_provider <- rho_openai_codex_provider()
models <- rho_models(
providers = list(codex_provider),
credentials = rho_memory_credential_store(
list(`openai-codex` = credential)
)
)
codex_model <- rho_openai_codex_model("gpt-5.3-codex-spark")
codex_agent <- rho_agent(
provider = models,
model = codex_model,
tools = list(rho_tool_r()),
stream_options = list(reasoning_effort = "minimal")
)
codex_run <- rho_prompt(
codex_agent,
paste(
"Call the r tool exactly once with code sum((1:100)^2).",
"Then answer with only the integer result."
)
) |>
rho_await(timeout = 120000)
tool_result <- codex_run@tool_results[[1L]]
result <- tool_result@content[[1L]]@text
codex_example <- data.frame(
model = codex_model@id,
status = codex_run@status,
tool = tool_result@tool_name,
result = result
)
codex_exampleRebuild this example by supplying the credential file explicitly:
Rscript scripts/render-readmes.R /absolute/path/to/auth.jsonrho is one installable R package. Its task, HTTP, provider, agent,
extension, compute, graphics, coding, and bioinformatics contracts
remain distinct components within one namespace. DuckDB, mirai, and
httr2/curl backends use optional dependencies. Bounded test assertions
are internal utilities.
The components have separate responsibilities, not separate package
boundaries. The current rho.ext runtime is not connected to
rho.agent; rho.bio.agent only lists registered manifests; the DuckDB
SQL guard is lexical; and coding file/Bash tools still inherit host
filesystem and environment authority. These gaps are tracked in issues
#7,
#9, and
#11.
Provider implementations live in rho. OpenAI Codex, GitHub Copilot,
Z.ai, OpenAI, Anthropic, Ollama, and the deterministic faux provider
therefore share one typed provider surface without a package per API.
The Pi parity
ledger
distinguishes complete wire adapters from request translators whose
normalized stream is not yet complete. OpenAI, OpenAI Codex, GitHub
Copilot, Z.ai, and Anthropic have executable normalized-stream fixtures;
the ledger records the executable and external-account evidence for each
adapter.
Bioinformatics contracts and adapters consume the provider and agent substrate without defining it.
Rho targets R 4.6 or newer. Install the development package from a
checkout. The required nanonext development build comes from the pinned
RGenomicsETL
fork,
not the r-lib release; the Remotes field declares this source.
git clone git@github.com:RGenomicsETL/Rho.git
cd Rho
Rscript -e 'remotes::install_deps(dependencies = TRUE)'
make installThe authored API documentation is roxygen; the authored tests are R
Markdown files under inst/tinytest/rmd/. Generated manuals, the
namespace, executable tests, and the README are reproducible from their
sources.
make format # Air
make rd # roxygen2
make purl-tests # Rmd tests -> executable tinytest files
make rdm # rebuild README.md
make test # run unified tinytest suite
make check # R CMD build and R CMD check --no-manualThe Pi parity ledger records behavioral contracts and the fixtures that verify them. Public release and addition to the RGenomicsETL R-universe follow green package checks, live provider checks, documentation, and secret scanning. The exact sequence is recorded in the publishing guide.
MIT.