Skip to content

Complete tinyagents inference migration (Motion B): crate-native provider clients, delete duplicated provider stack #4727

Description

@senamakel

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

  • Crate-native clients registered — the turn ModelRegistry holds providers::openai clients built from config for BYOK/Ollama/LM Studio/cloud; ProviderModel no longer wraps the OpenAI-compat path.
  • Provider stack deletedinference/provider/compatible*.rs, reliable.rs, router.rs removed; the 207-test compatible_tests.rs behavioral cases re-expressed against the crate providers::openai fixtures (upstream where generic).
  • No dyn Provider in agent/grep -rl 'dyn Provider' src/openhuman/agent/ --include='*.rs' returns only bespoke host ChatModel impls (if any); the TurnModelSource::provider() escape hatch and the builder provider setters are gone.
  • Streaming checkpoint on the crate APIsession_io.rs cap-hit checkpoint streams via the crate ChatModel stream path, not source.provider().chat.
  • Stragglers migratedtinyflows/caps.rs and inference_probe.rs off create_chat_provider.
  • Behavior parity — streaming deltas, cost footer (charged-USD + cached tokens), tool-call timeline, and the same-family fallback ordering are byte-parity vs. the current stack (golden mock-backend transcripts + per-provider live smoke); regressions Streamed model calls run in detached spawn: thread_id/task-locals lost, cancellation doesn't stop billed provider call #4460 / $0-turn are not reintroduced.
  • Diff coverage ≥ 80% — the implementing PR(s) meet the changed-lines coverage gate (Vitest + cargo-llvm-cov, enforced by .github/workflows/ci-lite.yml).

Related

https://claude.ai/code/session_018MnMVgnzxchtcs1DDUxTfF

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions