Summary
Complete the tinyagents inference migration ("Motion B") by registering crate-native providers::openai clients directly in the turn ModelRegistry, eliminating the remaining openhuman Provider trait objects in the agent harness and deleting the duplicated in-house provider stack. This is the follow-up to Motion A (PR #4726), which put every agent turn path on a seam-owned TurnModelSource so that no harness struct holds Arc<dyn Provider>.
Problem / Context
After Motion A, the harness holds TurnModelSource everywhere, but the source still wraps openhuman's Box<dyn Provider> stack (inference/provider/compatible*.rs, reliable.rs, router.rs — ~30k lines) that re-implements what tinyagents 1.7 ships natively (harness::providers::openai, harness::retry, harness::model::ModelRegistry). The crate ModelRegistry + RunPolicy.fallback + capability gating are already wired in assemble_turn_harness (see docs/tinyagents-phase3-router-registry-design.md) — the registered models are just ProviderModel wrappers rather than crate-native clients. Consolidating removes the double-maintenance and the isomorphic type-conversion seam.
Context/links:
Scope
In scope — the remaining dyn Provider boundaries in agent/ (all provider-resolution/build sites that Motion A intentionally left):
inference/provider/factory.rs — build crate providers::openai clients (via ProviderSpec) for BYOK slugs, Ollama, LM Studio, the cloud slug, instead of CompatibleProvider; keep the provider-string grammar.
subagent_runner/ops/provider.rs::resolve_subagent_provider — return a TurnModelSource/registry entry instead of Arc<dyn Provider> (this churns its 9 unit tests — deferred from Motion A on purpose).
tools/delegate.rs, triage/routing.rs — build sources/crate clients instead of Box<dyn Provider>.
- Builder
.provider()/.provider_arc() setters and the TurnModelSource::provider() escape hatch — remove once nothing needs a raw provider (incl. the streaming session cap-hit checkpoint in session/turn/session_io.rs, which currently uses source.provider().chat(...) because crate ChatModel::invoke has no delta sink — needs the crate stream API).
- Bespoke providers stay as host
ChatModel impls (openhuman_backend.rs, claude_code/, claude_agent_sdk/, openai_codex.rs) — Phase 4.
- Delete
compatible*.rs, reliable.rs, router.rs after cutover.
Smaller stragglers (can land independently, ahead of the big cutover):
tinyflows/caps.rs — one-shot .chat() caller still on create_chat_provider (round-trips tool_calls + reasoning into a JSON envelope; needs the seam message converter exposed).
src/bin/inference_probe.rs — debug binary still names create_chat_provider.
Not in scope — the inference.* RPC surface, provider-string grammar, local-runtime process management, voice, /v1 server endpoint (all host-owned; unchanged).
Acceptance criteria
Related
https://claude.ai/code/session_018MnMVgnzxchtcs1DDUxTfF
Summary
Complete the tinyagents inference migration ("Motion B") by registering crate-native
providers::openaiclients directly in the turnModelRegistry, eliminating the remaining openhumanProvidertrait objects in the agent harness and deleting the duplicated in-house provider stack. This is the follow-up to Motion A (PR #4726), which put every agent turn path on a seam-ownedTurnModelSourceso that no harness struct holdsArc<dyn Provider>.Problem / Context
After Motion A, the harness holds
TurnModelSourceeverywhere, but the source still wraps openhuman'sBox<dyn Provider>stack (inference/provider/compatible*.rs,reliable.rs,router.rs— ~30k lines) that re-implements whattinyagents1.7 ships natively (harness::providers::openai,harness::retry,harness::model::ModelRegistry). The crateModelRegistry+RunPolicy.fallback+ capability gating are already wired inassemble_turn_harness(seedocs/tinyagents-phase3-router-registry-design.md) — the registered models are justProviderModelwrappers rather than crate-native clients. Consolidating removes the double-maintenance and the isomorphic type-conversion seam.Context/links:
docs/tinyagents-phase3-router-registry-design.md,docs/tinyagents-drift-ledger.md(rows P1-8/P1-9),docs/tinyagents-inference-migration-plan.md(Phases 2–4).Scope
In scope — the remaining
dyn Providerboundaries inagent/(all provider-resolution/build sites that Motion A intentionally left):inference/provider/factory.rs— build crateproviders::openaiclients (viaProviderSpec) for BYOK slugs, Ollama, LM Studio, thecloudslug, instead ofCompatibleProvider; keep the provider-string grammar.subagent_runner/ops/provider.rs::resolve_subagent_provider— return aTurnModelSource/registry entry instead ofArc<dyn Provider>(this churns its 9 unit tests — deferred from Motion A on purpose).tools/delegate.rs,triage/routing.rs— build sources/crate clients instead ofBox<dyn Provider>..provider()/.provider_arc()setters and theTurnModelSource::provider()escape hatch — remove once nothing needs a raw provider (incl. the streaming session cap-hit checkpoint insession/turn/session_io.rs, which currently usessource.provider().chat(...)because crateChatModel::invokehas no delta sink — needs the crate stream API).ChatModelimpls (openhuman_backend.rs,claude_code/,claude_agent_sdk/,openai_codex.rs) — Phase 4.compatible*.rs,reliable.rs,router.rsafter cutover.Smaller stragglers (can land independently, ahead of the big cutover):
tinyflows/caps.rs— one-shot.chat()caller still oncreate_chat_provider(round-trips tool_calls + reasoning into a JSON envelope; needs the seam message converter exposed).src/bin/inference_probe.rs— debug binary still namescreate_chat_provider.Not in scope — the
inference.*RPC surface, provider-string grammar, local-runtime process management, voice,/v1server endpoint (all host-owned; unchanged).Acceptance criteria
ModelRegistryholdsproviders::openaiclients built from config for BYOK/Ollama/LM Studio/cloud;ProviderModelno longer wraps the OpenAI-compat path.inference/provider/compatible*.rs,reliable.rs,router.rsremoved; the 207-testcompatible_tests.rsbehavioral cases re-expressed against the crateproviders::openaifixtures (upstream where generic).dyn Providerinagent/—grep -rl 'dyn Provider' src/openhuman/agent/ --include='*.rs'returns only bespoke hostChatModelimpls (if any); theTurnModelSource::provider()escape hatch and the builder provider setters are gone.session_io.rscap-hit checkpoint streams via the crateChatModelstream path, notsource.provider().chat.tinyflows/caps.rsandinference_probe.rsoffcreate_chat_provider..github/workflows/ci-lite.yml).Related
refactor(agent): hold TurnModelSource across all turn paths)docs/tinyagents-phase3-router-registry-design.md,docs/tinyagents-drift-ledger.mdhttps://claude.ai/code/session_018MnMVgnzxchtcs1DDUxTfF