Rho

R-CMD-check Lifecycle: experimental

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.

An agent run

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"

OpenAI Codex with explicit credentials

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

The packages

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.

Install and develop

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.

License

MIT.