How Harness::builder() Composes a Core for Library Embedding in OpenHuman
The Harness::builder() static method returns a configurable builder object that assembles a fully-initialized core runtime through step-by-step axis configuration, culminating in a single build() call that produces an embeddable Harness instance for library integration.
The tinyhumansai/openhuman repository provides a library-friendly entry point for embedding AI capabilities directly into Rust applications. Understanding how Harness::builder() composes a core for library embedding reveals a three-stage composition pattern that transforms configuration parameters into a running, in-process AI core capable of executing prompts without network overhead.
The Three-Stage Composition Architecture
The composition process implemented in the OpenHuman source code follows a clear pipeline from empty builder to running core. Each stage corresponds to specific source files and responsibilities within the embedding layer.
Stage 1: Builder Creation
In src/embed/harness/builder.rs, the Harness::builder() static method constructs a new Builder struct. This builder holds mutable configuration fields for every runtime axis, including the provider, workspace, access tier, backend URL, MCP servers, skill directories, and tool groups. The builder starts with sensible defaults but remains uninitialized until the caller specifies required dependencies like the AI provider.
Stage 2: Axis Configuration
The caller configures the runtime through a fluent interface of chaining methods. According to src/embed/harness/builder.rs, each method—provider(), workspace(), access(), backend_url(), mcp(), skills_dir(), and tool_groups()—updates the corresponding internal field and returns &mut self to enable further chaining. These methods perform lightweight validation, such as verifying that a Workspace::Dir(path) exists on disk, before returning the builder for the next configuration step.
Stage 3: Core Construction
When the caller finally invokes build(), the builder transfers its collected configuration to CoreBuilder located in src/core/runtime/builder.rs. This internal constructor performs three critical selections:
- Service set: Configures infrastructure components including HTTP RPC, socket handlers, cron schedulers, channels, and heartbeat monitors.
- Domain set: Registers business logic domains such as agent, memory, threads, config, and security controllers, plus optional families like flows, skills, and web3.
- Tool groups: Determines tool visibility—advertised to the LLM, withheld, or turned off entirely.
CoreBuilder then spawns the core's asynchronous task system, registers all domain controllers, installs the tool registry, and wraps the runtime inside an embed::Core handle. This handle is stored within the Harness struct defined in src/embed/harness/mod.rs, exposing high-level methods like run(prompt) and turn(prompt) for library consumers.
Configuring Runtime Axes
The builder pattern provides granular control over the embedding environment through type-safe configuration methods. Each method targets a specific axis of the runtime:
provider(): Accepts aProviderenum variant, such asProvider::openai_compatible(base_url, api_key), configuring the LLM backend.workspace(): Defines storage semantics usingWorkspace::Ephemeral,Workspace::Dir(path), orWorkspace::Inheritfor persistent or temporary session storage.access(): Sets the access tier and origin permissions, typically viaAccess::full()for unrestricted library usage.backend_url(): Specifies the TinyHumans API endpoint for authentication and cloud services.mcp(): Registers Model Context Protocol servers viaMcpServer::stdio(name, command, args)for external tool integration.tool_groups(): Controls tool advertisement usingToolGroups::packed()orToolGroups::with("media", GroupMode::Off)to hide specific capabilities.
Because the core is embedded in the same process, there is no network hop between the library caller and the runtime—all RPC calls resolve to direct method invocations on the embed::Core handle.
Practical Implementation Examples
The following examples demonstrate common patterns for library embedding found in examples/run_turn.rs and tests/harness_embed.rs.
Basic Single-Turn Usage
This pattern creates a harness, composes the core, and executes a single prompt:
use openhuman::embed::harness::Harness;
use openhuman::provider::Provider;
use openhuman::workspace::Workspace;
use openhuman::access::Access;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let harness = Harness::builder()
.provider(Provider::openai_compatible(
"https://api.openai.com/v1",
std::env::var("OPENAI_API_KEY")?,
))
.workspace(Workspace::Ephemeral)
.access(Access::full())
.backend_url("https://api.tinyhumans.ai")
.build()?; // ← core is now created and running
let response = harness.run("Explain how Harness::builder works.").await?;
println!("AI says: {}", response.message);
Ok(())
}
Multi-Turn Session Management
For conversational continuity, reuse the same core across multiple turns:
let harness = Harness::builder()
.provider(Provider::openai_compatible("https://api.openai.com/v1", "sk-..."))
.workspace(Workspace::Dir("./my_workspace".into()))
.access(Access::full())
.backend_url("https://api.tinyhumans.ai")
.build()?; // core lives for the entire program duration
let mut session = harness.session(); // optional explicit session handle
for prompt in ["First question?", "Second question?"] {
let reply = session.turn(prompt).send().await?;
println!("→ {}", reply.message);
}
Customizing Tool Visibility
Control which tool schemas the LLM receives by configuring tool groups before composition:
let harness = Harness::builder()
.provider(Provider::openai_compatible("...", "..."))
.tool_groups(ToolGroups::with("media", GroupMode::Off))
.skills_dir("./skills")
.build()?;
Summary
Harness::builder()insrc/embed/harness/builder.rsinitiates a three-stage composition process for library embedding.- Axis configuration uses fluent methods to set providers, workspaces, access tiers, and tool groups before core instantiation.
CoreBuilderinsrc/core/runtime/builder.rstranslates builder state into a running core by selecting service sets, domain sets, and tool visibility rules.- The resulting embedded core runs in-process, eliminating network latency while maintaining parity with the full OpenHuman desktop runtime.
Frequently Asked Questions
What source file implements the Harness::builder() method?
The Harness::builder() static method and the associated Builder struct are implemented in src/embed/harness/builder.rs. This file contains the fluent API for configuring providers, workspaces, and access tiers before core construction.
How does workspace configuration affect the embedded core?
The workspace() method accepts Workspace::Ephemeral for temporary storage, Workspace::Dir(path) for persistent state on disk, or Workspace::Inherit to reuse an existing workspace. The builder validates that directory paths exist during the configuration phase, ensuring the core receives valid storage parameters.
What is the difference between service sets and domain sets during core construction?
According to src/core/runtime/builder.rs, service sets configure infrastructure capabilities like HTTP RPC, socket communication, and cron scheduling, while domain sets register business logic controllers for agents, memory, threads, and optional features like skills or web3 integrations. Both sets are selected based on the builder's accumulated configuration before the asynchronous runtime spawns.
Can I run multiple independent prompts on the same composed core?
Yes. After calling build(), the Harness instance owns a long-lived core. You can execute harness.run(prompt) for single-turn interactions or create a persistent session via harness.session() and chain session.turn(prompt).await calls to maintain conversational context across multiple prompts without reconstructing the core.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →