How to Embed OpenHuman Core as a Rust Library Using Harness
You can embed OpenHuman's AI agent core in any Rust application by using the Harness builder API to configure the LLM provider, workspace, and access tier, then calling run() or turn() to execute agent turns.
The openhuman crate from the tinyhumansai/openhuman repository exposes its core engine as a reusable Rust library. The primary entry point for programmatic integration is the Harness type, which provides a fluent builder interface to assemble a fully functional AI agent that matches the behavior of the desktop product.
Understanding the Harness Architecture
Harness encapsulates everything required to run an AI agent turn, including the LLM provider configuration, persistence layer, security gates, and optional cloud services. According to the source code in src/embed/harness/mod.rs, this design ensures that the core is instantiated once per process and can be reused across any number of conversational turns.
The builder pattern is implemented in src/embed/harness/builder.rs and enforces compile-time safety for required configuration components. Once constructed via Harness::builder().build().await, the instance manages its own async runtime and security boundaries, making it safe to embed into existing applications without disrupting their execution model.
Configuring the Harness Builder
Setting Up the LLM Provider
The Provider struct (defined in src/embed/harness/provider.rs) supports OpenAI-compatible endpoints, Claude, and other LLM backends. You must specify the base URL, API key, and model name:
use openhuman::embed::harness::{Harness, Provider, Workspace, Access};
let harness = Harness::builder()
.provider(
Provider::openai_compatible(
"https://api.openai.com/v1",
std::env::var("OPENAI_API_KEY")?,
)
.model("gpt-4o"),
)
.workspace(Workspace::Ephemeral)
.access(Access::full())
.build()
.await?;
Choosing a Workspace Strategy
Workspace configuration (located in src/embed/harness/workspace.rs) determines how the agent handles persistent state and temporary files:
Workspace::Ephemeral– In-memory sandbox with no disk persistenceWorkspace::Dir(path)– Dedicated directory for agent files and tool workspacesWorkspace::Inherit– Reuses the parent process environment
Defining Access Permissions
The access tier (defined in src/embed/harness/access.rs) controls which tools and policies the embedded agent may invoke:
Access::full()– Unrestricted tool accessAccess::supervised()– Requires approval for sensitive operationsAccess::readonly()– Blocks all mutation operations
Optional Cloud Backend and Services
You can optionally connect to TinyHumans cloud services by specifying a backend URL in src/embed/harness/mod.rs. For extended functionality, src/embed/harness/mcp.rs exposes methods like mcp(), services(), and tool_groups() to enable micro-container protocols, cron jobs, and channel communications that mirror the full desktop product capabilities.
Running Agent Turns
Once built, the Harness exposes two primary methods for execution:
run(prompt)– Initiates a new conversation sessionturn(prompt)– Continues an existing session using a session ID
Both methods return a TurnResult containing output_text, generated tool calls, and the session_id for stateful multi-turn conversations. All I/O operations pass through the same security and approval gates used by the official UI.
// First turn creates a session
let first = harness.run("Summarise the repository.").await?;
println!("{}", first.output_text);
// Continue the same session
let second = harness
.turn("Now detail the build process.")
.session(&first.session_id)
.send()
.await?;
Complete Integration Examples
Single-Turn Execution
For one-off queries without persistence, use an ephemeral workspace and the run method:
use openhuman::embed::harness::{Harness, Provider, Workspace, 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")?,
)
.model("gpt-4o"),
)
.workspace(Workspace::Ephemeral)
.access(Access::full())
.build()
.await?;
let result = harness.run("Explain how OpenHuman's architecture works.").await?;
println!("Reply:\n{}", result.output_text);
Ok(())
}
Multi-Turn Session Management
To maintain context across multiple prompts, capture the session_id from the initial turn and pass it to subsequent turn calls:
#[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")?,
)
.model("gpt-4o-mini"),
)
.workspace(Workspace::Dir("my_workspace".into()))
.access(Access::full())
.build()
.await?;
let first = harness.run("List the main modules in this repo.").await?;
println!("First reply: {}", first.output_text);
let second = harness
.turn("Give a brief description of each module.")
.session(&first.session_id)
.send()
.await?;
println!("Second reply: {}", second.output_text);
Ok(())
}
Embedding in a Third-Party Crate
Add the dependency to your Cargo.toml:
[dependencies]
openhuman = { path = "../openhuman", features = ["full"] }
tokio = { version = "1", features = ["full"] }
Then expose the functionality through your library:
pub async fn embed_core() -> anyhow::Result<String> {
let harness = openhuman::embed::harness::Harness::builder()
.provider(
openhuman::embed::harness::Provider::openai_compatible(
"https://api.openai.com/v1",
std::env::var("OPENAI_API_KEY")?,
)
.model("gpt-4o"),
)
.workspace(openhuman::embed::harness::Workspace::Ephemeral)
.access(openhuman::embed::harness::Access::full())
.build()
.await?;
let resp = harness.run("What is the purpose of the src/openhuman/web3 package?").await?;
Ok(resp.output_text)
}
Summary
- Single entry point: The
Harnesstype insrc/embed/harness/mod.rsprovides the primary API for embedding OpenHuman core - Builder pattern: Use
Harness::builder()to configure Provider, Workspace, and Access tiers before callingbuild().await - Session management:
run()creates new conversations whileturn()continues existing ones using session IDs - Security parity: Embedded instances use identical security gates and tool policies as the desktop application
- File locations: Key implementations reside in
src/embed/harness/builder.rs,provider.rs,workspace.rs, andaccess.rs
Frequently Asked Questions
What is the difference between Harness::run() and Harness::turn()?
Harness::run() initiates a fresh conversation session and returns a TurnResult containing a new session_id. Harness::turn() requires chaining .session(&existing_session_id) to continue a previous conversation, maintaining context and accumulated state from prior turns.
How do I configure the Harness to use Claude instead of OpenAI?
According to src/embed/harness/provider.rs, replace Provider::openai_compatible() with the Claude-specific constructor (or compatible endpoint URL) and adjust the model identifier accordingly. The provider system abstracts the underlying LLM implementation while maintaining a consistent interface for the Harness.
Can I use a custom Tokio runtime instead of the one spawned by Harness?
Yes. While Harness::builder().build().await spawns an internal runtime by default, you can supply a custom Tokio runtime configuration through the runtime() method on the builder, as indicated in src/embed/harness/mod.rs. This allows integration with existing async application architectures.
What permissions does the Access::full() tier grant to the embedded agent?
Access::full() enables all available tools and removes policy restrictions, allowing the agent to execute file operations, network calls, and system commands without additional approval gates. For restricted environments, use Access::supervised() or Access::readonly() as defined in src/embed/harness/access.rs.
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 →