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.async)
library(rho.ai)
library(rho.agent)
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.
library(rho.coding)
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_example
#> model status tool result
#> 1 gpt-5.3-codex-spark completed r [1] 338350
Rebuild this example by supplying the credential file explicitly:
make rdm-codex CREDENTIAL=/absolute/path/to/auth.json
Rho keeps transport, provider semantics, agent policy, and applications in separate installable packages. Each package has a focused README and reference site.
| package | role | documentation |
|---|---|---|
| rho.async | tasks, streams, cancellation, timeouts, and composition | guide · reference |
| rho.http | typed HTTP requests, nanonext transport, and SSE decoding | guide · reference |
| rho.ai | messages, models, capabilities, credentials, providers, and tools | guide · reference |
| rho.agent | multi-turn execution, session compaction, tool scheduling, queues, cancellation, and events | guide · reference |
| rho.ext | asynchronous extension handlers and capability registration | guide · reference |
| rho.compute | typed mirai expression and function-call tasks | guide · reference |
| rho.graphics | declared graphics devices and hashed artifacts | guide · reference |
| rho.coding | Bash, file, isolated-worker R, and explicit current-session R tools | guide · reference |
| rho.bio | manifests, resolvers, receipts, and database-neutral SQL contracts | guide · reference |
| rho.duckdb | DuckDB implementation of the asynchronous SQL contracts | guide · reference |
| rho.bio.agent | bioinformatics tools registered through the extension API | guide · reference |
| rho.testkit | bounded assertions for asynchronous tests | guide · reference |
Provider implementations with no independent dependency or ABI
constraint live in rho.ai. 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 remains downstream: rho.bio, rho.duckdb, and
rho.bio.agent consume the provider and agent substrate but do not
define it.
Rho targets R 4.4 or newer. Install the development monorepo from a checkout:
git clone git@github.com:sounkou-bioinfo/Rho.git
cd Rho
make deps
make install
The authored API documentation is roxygen; the authored tests are R
Markdown files under each package’s inst/tinytest/rmd/ directory.
Generated manuals, namespaces, executable tests, and READMEs are
reproducible from their sources.
make format # Air
make rd # roxygen2
make purl-tests # Rmd tests -> executable tinytest files
make rdm # rebuild package READMEs
make check-publication
make test
make check # every package must report Status: OK
make public-ready # complete publication gate
The Pi parity ledger records behavioral contracts and the fixtures that verify them. Public release and addition to the sounkou-bioinfo R-universe follow green package checks, live provider checks, documentation, and secret scanning. The exact sequence is recorded in the publishing guide.
MIT.