# How the OpenHuman TinyAgents Adapter Seam Integrates Providers and Tools into the Harness

> Discover how the tinyagents adapter seam seamlessly integrates OpenHuman Providers and Tools into the harness. Learn how to leverage existing model clients and tool registries without duplicating logic.

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

---

**The OpenHuman tinyagents adapter seam is a thin translation layer in `openhuman/agent/tinyagents` that implements `tinyagents` harness traits for OpenHuman's native `Provider` and `Tool` types, enabling the external harness to execute turns using OpenHuman's existing model clients and tool registry without duplicating policy or execution logic.**

OpenHuman by tinyhumansai/openhuman is an open-source AI agent framework that leverages the `tinyagents` harness for its core execution loop. To avoid rebuilding policy enforcement, tool execution, and LLM request handling from scratch, the project uses a dedicated **tinyagents adapter seam** that makes OpenHuman's **Provider** and **Tool** abstractions appear as native `tinyagents` components. This integration preserves OpenHuman's rich approval gates, logging, and tool schemas while delegating turn management to the battle-tested harness.

## Three Components of the TinyAgents Adapter Seam

The seam lives in the `openhuman/agent/tinyagents` package and consists of three distinct integration points:

- **Provider adapter** ([`src/openhuman/agent/tinyagents/provider.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/provider.rs)): Converts an OpenHuman `Provider` into a `tinyagents::harness::model::ModelProvider`, translating **ModelRequest** and **ModelResponse** between the two frameworks.

- **Tool adapter** ([`src/openhuman/tools/traits.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/traits.rs)): Wraps concrete OpenHuman tool implementations from `src/openhuman/tools/impl/*` as `tinyagents::harness::tool::Tool` instances, mapping **ToolSchema** to the harness format.

- **Middleware bridge** ([`src/openhuman/agent/tinyagents/middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/middleware.rs)): Stacks tinyagents middleware such as `RepeatedToolFailureMiddleware` with OpenHuman's `ToolPolicy` and `ApprovalGate` to produce a unified **RunPolicy**.

## Provider Adapter: Bridging Model Requests in [`provider.rs`](https://github.com/tinyhumansai/openhuman/blob/main/provider.rs)

The provider adapter converts an OpenHuman `Provider` into a `tinyagents::harness::model::ModelProvider` that the harness can invoke during each turn. In [`src/openhuman/agent/tinyagents/provider.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/provider.rs), the `OpenHumanProvider` struct implements the `request` method, which receives a `tinyagents::harness::model::ModelRequest` and returns a `tinyagents::harness::model::ModelResponse`.

Inside the method, the adapter translates the tinyagents request into an OpenHuman `ProviderRequest` using `ProviderRequest::from(req)`, forwards it through the existing HTTP client, and maps the raw JSON response back with `ModelResponse::from(resp)`. This approach reuses OpenHuman's connection pooling, authentication, and retry logic rather than spawning a second HTTP stack.

```rust
// src/openhuman/agent/tinyagents/provider.rs
impl tinyagents::harness::model::ModelProvider for OpenHumanProvider {
    async fn request(
        &self,
        req: tinyagents::harness::model::ModelRequest,
    ) -> tinyagents::error::Result<tinyagents::harness::model::ModelResponse> {
        // Translate tinyagents request → OpenHuman request
        let oh_req = ProviderRequest::from(req);
        let resp = self.http_client.send(oh_req).await?;
        // Translate back
        Ok(ModelResponse::from(resp))
    }
}

```

## Tool Adapter: Wrapping OpenHuman Tools for the Harness

Every OpenHuman tool implementation under `src/openhuman/tools/impl/*` is exposed to the harness via the adapter in [`src/openhuman/tools/traits.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/traits.rs). The generic `OpenHumanTool<T>` struct implements the harness's **Tool** trait, exposing `name`, `schema`, and an async `run` method. The file also re-exports tinyagents helper functions such as `humanize_tool_name`, `context_detail_from_args`, and `run_tool` to streamline adapter development.

The `schema` method performs a direct translation from OpenHuman's native `ToolSchema` to `tinyagents::harness::tool::ToolSchema`, including parameter definitions. When the harness invokes `run`, the adapter extracts arguments from the **ToolExecutionContext**, delegates to the inner `OpenHumanToolImpl`, and lifts the result into a `tinyagents::harness::tool::ToolResult` so the harness can append it to the conversation history.

```rust
// src/openhuman/tools/traits.rs – tool adapter skeleton
pub struct OpenHumanTool<T> {
    inner: T,
}
impl<T> tinyagents::harness::tool::Tool for OpenHumanTool<T>
where
    T: OpenHumanToolImpl,
{
    fn name(&self) -> &str { self.inner.name() }
    fn schema(&self) -> tinyagents::harness::tool::ToolSchema {
        // Map OpenHuman's schema → tinyagents schema
        tinyagents::harness::tool::ToolSchema {
            description: self.inner.description().into(),
            parameters: tinyagents::harness::tool::ToolParams::from(
                self.inner.parameters(),
            ),
            ..Default::default()
        }
    }
    async fn run(
        &self,
        ctx: tinyagents::harness::tool::ToolExecutionContext,
    ) -> tinyagents::error::Result<tinyagents::harness::tool::ToolResult> {
        // Forward arguments to the real OpenHuman tool
        let args = ctx.args();
        let result = self.inner.run(args).await?;
        Ok(ToolResult::from(result))
    }
}

```

## Middleware Bridge: Unifying Policies in [`middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/middleware.rs)

OpenHuman maintains its own `ToolPolicy` and `ApprovalGate` hooks, while the tinyagents harness expects its own middleware types such as `RepeatedToolFailureMiddleware`. The bridge in [`src/openhuman/agent/tinyagents/middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/middleware.rs) resolves this by constructing a unified **RunPolicy**.

It layers OpenHuman's budget and timeout constraints with tinyagents-specific middleware into a single policy object. This combined policy is then passed to `tinyagents::harness::run::run_harness`, ensuring that OpenHuman's approval gates and logging remain active even as the harness applies its native failure-handling rules.

## Harness Orchestration: Wiring Adapters into `RunConfig`

The final assembly happens in [`src/openhuman/agent/tinyagents/harness.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/harness.rs), where the orchestration code loads the workspace descriptor, instantiates the provider adapter, populates the tool registry with `OpenHumanTool<T>` wrappers, and bundles the stacked middleware.

The resulting **RunConfig** is handed to `tinyagents::harness::run::run_harness`, which kicks off the turn loop. Because every component speaks the harness's native trait language, the core execution loop requires no knowledge of OpenHuman's internal `ProviderConfig` or concrete tool structs.

```rust
// src/openhuman/agent/tinyagents/harness.rs – building the final harness
let provider = OpenHumanProvider::new(config.clone());
let tool_registry = OpenHumanToolRegistry::new(); // registers all OpenHumanTool<T>
let middleware = tinyagents::middleware::RepeatedToolFailureMiddleware::default();

let run_cfg = tinyagents::harness::run::RunConfig {
    provider: Box::new(provider),
    tools: Box::new(tool_registry),
    policy: tinyagents::harness::run::RunPolicy::default()
        .with_middleware(middleware),
    ..Default::default()
};

tinyagents::harness::run::run_harness(run_cfg).await?;

```

## Turn Execution Flow: How Data Moves Through the Seam

During a single turn, the tinyagents harness delegates to the adapter seam at three critical points:

1. The harness queries the **provider adapter** for a `ModelRequest`. The adapter builds the payload from OpenHuman's `ProviderConfig`—including model name, temperature, and system prompt—and issues the request through its HTTP client.

2. When the LLM response contains a tool call, the **tinyagents core** parses the call and resolves the tool name against the registry.

3. The matching **tool adapter** receives a `ToolExecutionContext`, extracts the arguments via `ctx.args()`, and invokes the underlying implementation from `crate::openhuman::tools::impl::...`.

4. The tool result is wrapped as a `tinyagents::harness::tool::ToolResult` and returned to the model, while OpenHuman's observation logging and approval-gate handling remain intact.

For a complete end-to-end example, [`src/openhuman/web_chat/run_task.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/web_chat/run_task.rs) demonstrates a full turn passing through the tinyagents harness with all adapter layers active.

## Summary

- The adapter seam lives in `openhuman/agent/tinyagents` and consists of provider, tool, and middleware bridges.
- [`src/openhuman/agent/tinyagents/provider.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/provider.rs) implements **ModelProvider** to translate requests and responses between OpenHuman and the harness.
- [`src/openhuman/tools/traits.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/traits.rs) wraps each OpenHuman tool as a harness-native **Tool** with bidirectional schema mapping.
- [`src/openhuman/agent/tinyagents/middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/middleware.rs) unifies OpenHuman policies with tinyagents middleware in a single **RunPolicy**.
- [`src/openhuman/agent/tinyagents/harness.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/harness.rs) orchestrates the **RunConfig** and delegates execution to `tinyagents::harness::run::run_harness`.

## Frequently Asked Questions

### What is the tinyagents adapter seam in OpenHuman?

The tinyagents adapter seam is a thin integration layer in the `openhuman/agent/tinyagents` package that lets OpenHuman's native `Provider` and `Tool` abstractions plug into the external `tinyagents` harness. It preserves OpenHuman's existing logging, approval gates, and tool schemas while allowing the harness to manage turn execution.

### How does the provider adapter translate LLM requests?

In [`src/openhuman/agent/tinyagents/provider.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/tinyagents/provider.rs), the `OpenHumanProvider` implements the **ModelProvider** trait's `request` method. It converts the incoming `ModelRequest` into an OpenHuman `ProviderRequest`, forwards it via the existing HTTP client, and maps the response back into a `ModelResponse` that the harness understands.

### Where does the tool schema mapping occur?

Schema mapping occurs in [`src/openhuman/tools/traits.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/traits.rs), where the `OpenHumanTool<T>` wrapper translates OpenHuman's `ToolSchema` into `tinyagents::harness::tool::ToolSchema`. This ensures parameter definitions and descriptions remain compatible with the harness's tool-calling parser.

### How does middleware from both systems coexist?

The [`middleware.rs`](https://github.com/tinyhumansai/openhuman/blob/main/middleware.rs) bridge stacks `tinyagents::middleware::RepeatedToolFailureMiddleware` alongside OpenHuman's `ToolPolicy` and `ApprovalGate` inside a single `RunPolicy`. The unified policy is passed to `run_harness`, so both policy enforcement layers execute during every turn.