Skip to content

feat(openhuman): run an embedded turn on a preset's OpenRouter model - #258

Merged
senamakel merged 1 commit into
tinyhumansai:mainfrom
senamakel:oh-openrouter
Aug 10, 2026
Merged

feat(openhuman): run an embedded turn on a preset's OpenRouter model#258
senamakel merged 1 commit into
tinyhumansai:mainfrom
senamakel:oh-openrouter

Conversation

@senamakel

Copy link
Copy Markdown
Member

Why

A [[customHarnesses]] preset with baseHarness = "openhuman" could name a model but not make it reachable.

baseUrl and apiKeyEnv describe an endpoint Medulla points a child process at, and an OpenHuman turn has no child — so they were documented as inert, and the operator had to configure the embedded core with a matching provider separately for the preset to do anything. Naming a model on its own is nearly a no-op: the core resolves it against its own provider bindings, and one no configured provider serves falls through to the default with an "override skipped" diagnostic.

What

Both fields are live now. Medulla resolves the key named by apiKeyEnv, exchanges it at the loopback attribution proxy for a machine-local token, and passes the core the mount and that token as a per-call route on inference_agent_chat. The core applies it to that one turn's in-memory configuration and never persists it, so pointing a workflow step at OpenRouter does not repoint the account's own OpenHuman inference.

So a complete preset needs nothing installed and nothing pre-configured in the core:

[[customHarnesses]]
id = "deepseek-oh"
name = "DeepSeek via OpenHuman"
baseHarness = "openhuman"
model = "deepseek/deepseek-chat"
hostId = "this-device"
apiKeyEnv = "OPENROUTER_API_KEY"

Attribution

As with a spawned harness, the core is handed the token and never the OpenRouter key, so the headers on the wire are Medulla's. The difference is that there is no environment to scrub: the key is read at this seam, exchanged here, and never written anywhere the turn can reach it.

  • inference_proxyroute_embedded and EmbeddedRouting, the in-process counterpart to route_spawn / ProxyRouting. Credential resolution is shared with the spawn path minus its last step: there is no child to inherit a harness's native variable, so ANTHROPIC_AUTH_TOKEN and OPENAI_API_KEY are not consulted. resolve_key keeps its Openhuman guard — the embedded provider must never be routed as if it were a child.
  • providers::openhuman::router — whether a given turn routes, kept beside model because the two answer different halves of one question: which model, and whose endpoint serves it.

Backwards compatibility

Routing is skipped, with no error, in three cases: no key exported under apiKeyEnv, an endpoint that is not OpenRouter, and a turn with no model resolved. Each leaves the turn on the account's own OpenHuman configuration — so a preset naming only a model still works, and is still advertised as capacity without an OpenRouter key, unlike every other base harness.

A proxy bind failure is fatal. The operator asked for a specific endpoint, and quietly running the turn on a different provider — billed to a different account — is worse than failing.

Docs

Three places said the old behaviour and now say the new one: the custom_harnesses and openhuman::model module docs, docs/attribution-proxy.md (coverage section), and gitbooks/developers/configuration.md, which gains a worked example. config.example.toml gains a commented openhuman preset.

Testing

cargo fmt --check, cargo clippy --all-targets -- -D warnings, and cargo test (70 test binaries) all clean.

9 new tests in router_tests.rs cover each skip case (no router, missing key, blank key, non-OpenRouter endpoint, no model), that a routed turn gets a 127.0.0.1 loopback mount on the OpenAI dialect and a mdl- token that is not the key, that a preset's own apiKeyEnv is authoritative and the ambient default cannot stand in for it, and that one credential reuses one token.

Not exercised live: I have no OpenRouter key here, so the first real request over this path is unverified. The core-side PR builds the ChatModel from the route in a test, which proves the resolution chain, but not the round-trip.

Depends on

tinyhumansai/openhuman#5472, which adds the per-call route to the core. The gitlink here moves onto that branch, so this cannot merge until it lands.

A `[[customHarnesses]]` preset with `baseHarness = "openhuman"` could
name a model but not make it reachable. `baseUrl` and `apiKeyEnv`
described an endpoint Medulla points a *child process* at, and an
OpenHuman turn has no child — so they were documented as inert, and the
operator had to configure the embedded core with a matching provider
separately for the preset to do anything.

They are live now. Medulla resolves the key named by `apiKeyEnv`,
exchanges it at the loopback attribution proxy for a machine-local
token, and passes the core the mount and that token as a per-call route
on `inference_agent_chat`. The core applies it to that one turn's
in-memory configuration and never persists it, so pointing a workflow
step at OpenRouter does not repoint the account's own inference.

As with a spawned harness, the core is handed the token and never the
OpenRouter key. The difference is that there is no environment to scrub:
the key is read at this seam, exchanged here, and never written
anywhere the turn can reach it.

- `inference_proxy`: `route_embedded` and `EmbeddedRouting`, the
  in-process counterpart to `route_spawn`/`ProxyRouting`. Credential
  resolution is shared with the spawn path minus its last step — there
  is no child to inherit a harness's native variable, so
  `ANTHROPIC_AUTH_TOKEN` and `OPENAI_API_KEY` are not consulted.
- `providers::openhuman::router`: whether a given turn routes, kept
  beside `model` because the two answer different halves of one question
  — which model, and whose endpoint serves it.

Routing is skipped, with no error, in three cases: no key exported under
`apiKeyEnv`, an endpoint that is not OpenRouter, and a turn with no
model resolved. Each leaves the turn on the account's own OpenHuman
configuration, so a preset naming only a model still works and is still
advertised as capacity without an OpenRouter key.

Requires the matching openhuman change (gitlink moved here).

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@senamakel, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a150b1e-432c-4084-bdae-2765e052d5e8

📥 Commits

Reviewing files that changed from the base of the PR and between 1e8a255 and 3eeeddd.

📒 Files selected for processing (13)
  • config.example.toml
  • docs/attribution-proxy.md
  • gitbooks/developers/configuration.md
  • src/sdk/src/config/custom_harnesses.rs
  • src/sdk/src/daemon/providers/openhuman/mod.rs
  • src/sdk/src/daemon/providers/openhuman/model.rs
  • src/sdk/src/daemon/providers/openhuman/router.rs
  • src/sdk/src/daemon/providers/openhuman/router_tests.rs
  • src/sdk/src/daemon/providers/openhuman/run/execution.rs
  • src/sdk/src/inference_proxy/mod.rs
  • src/sdk/src/inference_proxy/routing.rs
  • src/sdk/src/inference_proxy/types.rs
  • vendor/openhuman

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0191 · 258,934 in / 30,825 out · 101,702 cached (39%) · openrouter/openai/text-embedding-3-small, z-ai/glm-5.2 · 679 embedded
critique:    $0.0124 · 125,382 in / 19,258 out · 9,984 cached (8%)    · z-ai/glm-5.2
security:    $0.0035 · 104,397 in / 4,160 out  · 82,330 cached (79%)  · z-ai/glm-5.2
tests:       $0.0024 · 18,238 in  / 4,997 out  · 0 cached (0%)        · z-ai/glm-5.2
description: $0.0008 · 10,917 in  / 2,410 out  · 9,388 cached (86%)   · z-ai/glm-5.2

@tinysweeper

tinysweeper Bot commented Aug 9, 2026

Copy link
Copy Markdown

What this change touches

12 files, +437 -46 across 7 components. It reaches 6 untouched components (60 graph nodes walked). 6 further components left out to keep the diagram readable.

flowchart LR
  n0["src/sdk/src/daemon/providers/openhuman<br/>4 files +244 -8"]:::changed
  n1["src/sdk/src/inference_proxy<br/>3 files +93 -18"]:::changed
  n2["gitbooks/developers<br/>1 file +41 -10"]:::changed
  n3["src/sdk/src/daemon/providers/openhuman/run<br/>1 file +19 -1"]:::changed
  n4["root<br/>1 file +18 -1"]:::changed
  n5["src/sdk/src/config<br/>1 file +12 -7"]:::changed
  n6["docs<br/>1 file +10 -1"]:::changed
  n7["src/sdk/src/inference_proxy<br/>5 files reached"]:::impacted
  n8["src/link/src<br/>1 file reached"]:::impacted
  n9["src/sdk/src<br/>1 file reached"]:::impacted
  n10["src/sdk/src/client/error<br/>1 file reached"]:::impacted
  n11["src/sdk/src/config<br/>1 file reached"]:::impacted
  n12["src/sdk/src/config/types<br/>1 file reached"]:::impacted
  n7 -->|10 refs| n8
  n11 -->|3 refs| n10
  n7 -->|2 refs| n10
  n11 -->|2 refs| n8
  n0 -->|1 ref| n8
  n7 -->|1 ref| n12
  n9 -->|1 ref| n7
  n9 -->|1 ref| n11
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading

Green: changed. Grey: untouched, reached through an import or a call. Orange: has findings. Red: has a finding that blocks the merge.

Component Files Lines Findings
src/sdk/src/daemon/providers/openhuman changed 4 +244 -8
src/sdk/src/inference_proxy changed 3 +93 -18
gitbooks/developers changed 1 +41 -10
src/sdk/src/daemon/providers/openhuman/run changed 1 +19 -1
(root) changed 1 +18 -1
src/sdk/src/config changed 1 +12 -7
docs changed 1 +10 -1
src/sdk/src/inference_proxy reached 5
src/link/src reached 1
src/sdk/src reached 1
src/sdk/src/client/error reached 1
src/sdk/src/config reached 1
src/sdk/src/config/types reached 1
Changed files

src/sdk/src/daemon/providers/openhuman

  • src/sdk/src/daemon/providers/openhuman/mod.rs
  • src/sdk/src/daemon/providers/openhuman/model.rs
  • src/sdk/src/daemon/providers/openhuman/router.rs
  • src/sdk/src/daemon/providers/openhuman/router_tests.rs

src/sdk/src/inference_proxy

  • src/sdk/src/inference_proxy/mod.rs
  • src/sdk/src/inference_proxy/routing.rs
  • src/sdk/src/inference_proxy/types.rs

gitbooks/developers

  • gitbooks/developers/configuration.md

src/sdk/src/daemon/providers/openhuman/run

  • src/sdk/src/daemon/providers/openhuman/run/execution.rs

(root)

  • config.example.toml

src/sdk/src/config

  • src/sdk/src/config/custom_harnesses.rs

docs

  • docs/attribution-proxy.md

tinysweeper 0.1.0

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Aug 9, 2026
@senamakel senamakel self-assigned this Aug 10, 2026
@senamakel
senamakel merged commit 9758751 into tinyhumansai:main Aug 10, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant