feat(openhuman): run an embedded turn on a preset's OpenRouter model - #258
Conversation
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>
|
Warning Review limit reached
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
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. Comment |
There was a problem hiding this comment.
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
What this change touches12 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
Green: changed. Grey: untouched, reached through an import or a call. Orange: has findings. Red: has a finding that blocks the merge.
Changed files
|
Why
A
[[customHarnesses]]preset withbaseHarness = "openhuman"could name a model but not make it reachable.baseUrlandapiKeyEnvdescribe 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 oninference_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:
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_proxy—route_embeddedandEmbeddedRouting, the in-process counterpart toroute_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, soANTHROPIC_AUTH_TOKENandOPENAI_API_KEYare not consulted.resolve_keykeeps itsOpenhumanguard — the embedded provider must never be routed as if it were a child.providers::openhuman::router— whether a given turn routes, kept besidemodelbecause 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_harnessesandopenhuman::modelmodule docs,docs/attribution-proxy.md(coverage section), andgitbooks/developers/configuration.md, which gains a worked example.config.example.tomlgains a commentedopenhumanpreset.Testing
cargo fmt --check,cargo clippy --all-targets -- -D warnings, andcargo test(70 test binaries) all clean.9 new tests in
router_tests.rscover each skip case (no router, missing key, blank key, non-OpenRouter endpoint, no model), that a routed turn gets a127.0.0.1loopback mount on the OpenAI dialect and amdl-token that is not the key, that a preset's ownapiKeyEnvis 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
ChatModelfrom 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.