Understanding the tinyagents Crate in OpenHuman's Agent Execution
The tinyagents crate functions as the mandatory execution harness for OpenHuman's agent runtime, providing unified LLM provider abstraction, message-tool protocol handling, and usage tracking through core types like ChatModel, ToolCall, and the TurnModelSource bridge.
The OpenHuman project (tinyhumansai/openhuman) implements its core agent logic on top of the tinyagents crate, a Rust library that delivers a complete "harness" for language-model-driven agents. This integration allows OpenHuman to swap between providers like OpenAI, Claude, and local LLMs without modifying domain-specific code, while standardizing how the system handles conversation transcripts, tool invocations, and billing analytics.
Unified Model Abstraction
The tinyagents crate exposes provider-agnostic types that decouple OpenHuman's business logic from specific LLM implementations. In tests/raw_coverage/agent_harness_raw_coverage_e2e.rs at lines 21-24, the system imports the foundational model types:
use tinyagents::harness::model::{ChatModel, ModelRequest, ModelResponse};
These types—including ModelProfile for configuration—enable OpenHuman to construct requests that work across different backends. The abstraction layer handles provider-specific authentication, request serialization, and response parsing internally, presenting a uniform interface to the agent core.
Message Handling and Tool-Calling Protocol
Beyond model abstraction, tinyagents defines the complete schema for conversation state and tool interactions. The crate provides AssistantMessage, ContentBlock, and MessageDelta structures that represent streaming conversation transcripts, alongside ToolCall and ToolDelta for function invocations. As shown in the test harness, OpenHuman imports these message types to construct prompts:
use tinyagents::harness::message::{AssistantMessage, ContentBlock, Message};
use tinyagents::harness::tool::ToolCall;
When a model requests a tool, the tinyagents runtime routes the ToolCall to OpenHuman's specific implementations, bridging the gap between the LLM's JSON schema and the core's executable functions.
Usage Tracking and Error Handling
The crate includes built-in observability through the Usage type, which aggregates token consumption, cost metrics, and timing data. OpenHuman records this information for billing and analytics purposes, importing the type from tinyagents::harness::usage::Usage. Error handling flows through the TinyAgentsError enum, which OpenHuman maps to its internal result types. The conversion logic appears in the test suite at lines 94-96:
.map_err(|error| tinyagents::TinyAgentsError::Model(error.to_string()))
This pattern ensures that low-level network or serialization failures from the model provider are captured and wrapped consistently before propagating to OpenHuman's error handling layers.
Integration Architecture
The TurnModelSource Bridge
OpenHuman does not interact with tinyagents types directly in business logic; instead, it uses an adapter layer located in src/openhuman/agent/tinyagents/. The entry point is the TurnModelSource::from_model function, which constructs a tinyagents Harness combining model configuration with OpenHuman's policy, security, and approval-gate layers. This harness serves as the entry point for every agent turn: it streams the prompt to the LLM, parses returned tool calls, executes the corresponding OpenHuman tools, and returns the final model reply.
Mandatory Dependency Status
The tinyagents crate is declared as a mandatory dependency in /Cargo.toml at lines 177-182, vendored under vendor/tinyagents with version 2.1 and features sqlite and multimodal enabled. Unlike optional dependencies that can be toggled via feature gates, tinyagents cannot be excluded because more than two dozen internal domains—from memory management to inference and workflow modules—directly import its types. Disabling the crate would break the majority of the system, making it a foundational requirement for compilation.
Practical Implementation Example
The following pattern demonstrates how OpenHuman constructs a turn using the tinyagents harness, adapted from examples/run_turn.rs:
// Create a tinyagents model configuration
let model = tinyagents::harness::model::ChatModel::OpenAI {
api_key: std::env::var("OPENAI_API_KEY").ok(),
model: "gpt-4o".into(),
};
// Build a request for a new turn
let request = tinyagents::harness::model::ModelRequest {
messages: vec![
tinyagents::harness::message::AssistantMessage {
content: vec![tinyagents::harness::message::ContentBlock::Text(
"Summarize the user's last 5 messages".into(),
)],
},
],
tools: /* OpenHuman‑generated tool specs */,
..Default::default()
};
// Run the turn through OpenHuman’s tinyagents bridge
let response = openhuman_core::openhuman::agent::tinyagents::TurnModelSource::from_model(
&model,
openhuman_core::openhuman::agent::Access::full(),
)
.run(request)
.await?;
// Inspect the model’s reply and any tool calls it emitted
println!("Model reply: {:?}", response.message);
if let Some(tool_calls) = response.tool_calls {
// Dispatch each tool call to the appropriate OpenHuman implementation
}
This example shows the complete lifecycle: configuration, request building, execution through the bridge, and tool call dispatch.
Summary
- tinyagents provides the core LLM abstraction layer via types like
ChatModel,ModelRequest, andModelResponsedefined in the harness module. - The crate standardizes message handling through
AssistantMessageandContentBlock, whileToolCallandToolDeltamanage the tool invocation protocol. - OpenHuman integrates via the
TurnModelSource::from_modeladapter insrc/openhuman/agent/tinyagents/, which injects security and approval policies into the harness. - Usage tracking via the
Usagetype and error handling throughTinyAgentsErrorsupport production observability requirements. - The dependency is mandatory (declared in
/Cargo.toml#L177-L182) and vendored undervendor/tinyagents, serving as the non-optional foundation for over two dozen internal system domains.
Frequently Asked Questions
How does tinyagents enable OpenHuman to support multiple LLM providers?
The crate defines provider-agnostic types like ChatModel and ModelRequest that abstract authentication, serialization, and response parsing. OpenHuman configures the specific provider variant (OpenAI, Claude, or local) at initialization, allowing the core agent logic to remain identical regardless of backend.
What is the role of TurnModelSource in the OpenHuman architecture?
TurnModelSource::from_model acts as the primary bridge between OpenHuman's domain-specific logic and the tinyagents harness. It wraps the model configuration with OpenHuman-specific access controls, security policies, and approval gates, then executes the turn by streaming prompts and routing tool calls back to OpenHuman's implementations.
Why is tinyagents a mandatory dependency that cannot be feature-gated?
According to the source analysis, more than two dozen internal domains—including memory, inference, and workflow modules—directly import tinyagents types. Because these imports appear throughout the codebase rather than behind conditional compilation flags, removing the crate would break compilation across the majority of the system, as confirmed by its declaration in /Cargo.toml#L177-L182.
Where does OpenHuman handle error conversion from tinyagents types?
Error conversion occurs at the boundary layer, specifically in test and adapter files like tests/raw_coverage/agent_harness_raw_coverage_e2e.rs at lines 94-96. Here, external errors are mapped into tinyagents::TinyAgentsError variants using .map_err(), ensuring that provider-specific failures are normalized before propagation to OpenHuman's error handling infrastructure.
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 →