# Understanding the tinyagents Crate in OpenHuman's Agent Execution

> Discover the vital role of the tinyagents crate in OpenHuman's agent execution. It provides LLM provider abstraction, message-tool protocol handling, and usage tracking via core types.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: internals
- Published: 2026-08-26

---

**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`](https://github.com/tinyhumansai/openhuman/blob/main/tests/raw_coverage/agent_harness_raw_coverage_e2e.rs) at lines 21-24, the system imports the foundational model types:

```rust
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:

```rust
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:

```rust
.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`](https://github.com/tinyhumansai/openhuman/blob/main//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`](https://github.com/tinyhumansai/openhuman/blob/main/examples/run_turn.rs):

```rust
// 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`, and `ModelResponse` defined in the harness module.
- The crate standardizes message handling through `AssistantMessage` and `ContentBlock`, while `ToolCall` and `ToolDelta` manage the tool invocation protocol.
- OpenHuman integrates via the `TurnModelSource::from_model` adapter in `src/openhuman/agent/tinyagents/`, which injects security and approval policies into the harness.
- Usage tracking via the `Usage` type and error handling through `TinyAgentsError` support production observability requirements.
- The dependency is mandatory (declared in `/Cargo.toml#L177-L182`) and vendored under `vendor/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`](https://github.com/tinyhumansai/openhuman/blob/main/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.