refactor(client)!: single canonical CompletionClient + AgentClientExt - #2205
Merged
Conversation
Collapse the duplicated `rig-agent` `CompletionClient` trait — which re-declared `completion_model` and hand-forwarded to core with a "keep this in sync" maintainer comment — into `AgentClientExt`, an extension trait that inherits `completion_model` / `CompletionModel` from its supertrait `rig_core::client::completion::CompletionClient`. There is now one canonical `CompletionClient` (in `rig-core`); the classic `agent()` / `extractor()` constructors live on `AgentClientExt`, and nothing needs forwarding if the portable trait grows. Drop the `rig::client::CompletionClient` facade path entirely for purity: the facade `client` module re-exports the portable provider surface minus `CompletionClient`, which is reached via `rig::prelude::*` or `rig_core::client::completion::CompletionClient`. This also removes the deliberate trait shadowing in the facade `client` module and prelude — `AgentClientExt` and `CompletionClient` share no method names, so both resolve without ambiguity. Migrate all call sites to `use rig::prelude::*;` (or `rig_agent::prelude::*` for crates depending on `rig-agent` directly).
…docs
Review of the exclusion approach found it unnecessary and net-negative:
`pub use rig_core::client::*;` alongside `AgentClientExt` compiles with no
ambiguity (the names never collide), so the hand-curated re-export list only
added drift risk and broke real imports.
- Facade `rig::client` uses `pub use rig_core::client::*;` again, restoring the
canonical `CompletionClient` path (still a single trait, no shadow).
- Fix root README example: it imported the removed `rig::client::CompletionClient`
and failed to compile — now `use rig::prelude::*;`.
- Fix MIGRATING.md: the "explicit imports" note suggested
`rig_core::client::completion::CompletionClient`, which a `rig`-only consumer
cannot name; use facade paths (`rig::client::{CompletionClient, AgentClientExt}`)
and drop the now-false "no facade path" wording.
- Fix CHANGELOG wording accordingly.
- Reword the `AgentClientExt` doc: it does not "inherit" `completion_model`;
the supertrait bound makes it callable inside default bodies, and callers need
both traits in scope (importing `AgentClientExt` alone does not bring
`completion_model` into method-resolution scope).
Verified: clippy --all-features --all-targets -D warnings, doctests, and a
pure-`rig` consumer compiling the README/explicit/prelude import forms.
…tClientExt} import Add a regression test alongside the prelude-surface test that imports the documented explicit facade pair and exercises completion_model, agent, and extractor. Guards the restored `rig::client::CompletionClient` path referenced by README.md / MIGRATING.md, without depending on rig-core.
Merged
This was referenced Jul 27, 2026
gold-silver-copper
added a commit
that referenced
this pull request
Jul 28, 2026
…split MIGRATING.md was scoped to the runtime split (#2197/#2205/#2207) and covered only unreleased work. It said nothing about 0.40, which carried 31 breaking changes -- so a user on 0.39 had a flat CHANGELOG section and nothing else. 0.39 added 2 more and the unreleased section adds 13. Rewrite it as a full guide: per-release sections newest-first, the old runtime-split content absorbed into the unreleased section, and a symbol-reference appendix. The lead section is "Silent behavior changes" -- the seven changes that leave code compiling and behaving differently, which no compiler upgrade will point at. `max_turns` is the headline: it now bounds the exact total number of model calls including the initial call and retries, so an existing `max_turns(n)` means something different than it did. Also there: `max_tokens` now actually forwarded by deepseek, together, hyperbolic, and azure after being silently dropped; three providers switching from empty-text to an error; and the `chat_streaming` -> `chat` span rename. Two breaking changes were missing from the CHANGELOG and are documented here, both found by diffing the public API rather than reading PRs: - #1944 is absent entirely. Its commit body is marked [breaking], and the tree confirms it: RerankError is #[non_exhaustive] and gained ProviderResponse(ProviderResponseError). - #1950 has only its sibling #1951 listed. Output::Unknown is now Output::Unknown(Value), so hosted-tool payloads that were previously discarded at the typed-decode boundary now survive. Symbol names are verified against the tree; the before/after snippets are transcribed from CHANGELOG prose and PR descriptions and are not compiled. See the PR body for the method and its limits.
pull Bot
pushed a commit
to appelgriebsch/rig
that referenced
this pull request
Jul 28, 2026
…split (0xPlaygrounds#2216) * docs(migrating): cover 0.38 through unreleased, not just the runtime split MIGRATING.md was scoped to the runtime split (0xPlaygrounds#2197/0xPlaygrounds#2205/0xPlaygrounds#2207) and covered only unreleased work. It said nothing about 0.40, which carried 31 breaking changes -- so a user on 0.39 had a flat CHANGELOG section and nothing else. 0.39 added 2 more and the unreleased section adds 13. Rewrite it as a full guide: per-release sections newest-first, the old runtime-split content absorbed into the unreleased section, and a symbol-reference appendix. The lead section is "Silent behavior changes" -- the seven changes that leave code compiling and behaving differently, which no compiler upgrade will point at. `max_turns` is the headline: it now bounds the exact total number of model calls including the initial call and retries, so an existing `max_turns(n)` means something different than it did. Also there: `max_tokens` now actually forwarded by deepseek, together, hyperbolic, and azure after being silently dropped; three providers switching from empty-text to an error; and the `chat_streaming` -> `chat` span rename. Two breaking changes were missing from the CHANGELOG and are documented here, both found by diffing the public API rather than reading PRs: - 0xPlaygrounds#1944 is absent entirely. Its commit body is marked [breaking], and the tree confirms it: RerankError is #[non_exhaustive] and gained ProviderResponse(ProviderResponseError). - 0xPlaygrounds#1950 has only its sibling 0xPlaygrounds#1951 listed. Output::Unknown is now Output::Unknown(Value), so hosted-tool payloads that were previously discarded at the typed-decode boundary now survive. Symbol names are verified against the tree; the before/after snippets are transcribed from CHANGELOG prose and PR descriptions and are not compiled. See the PR body for the method and its limits. * docs(migrating): account for PRs merged since the guide was written Rebased onto main and reworked for the five commits that landed after d612ba7. The guide said `dynamic_context` was removed with no replacement. 0xPlaygrounds#2174 removed it and 0xPlaygrounds#2219 restored it, so that section was actively wrong -- it told readers to rewrite working code. Rewritten: the call is unchanged, but it is now a thin wrapper over a private `AgentHook` on the ordinary completion-call lifecycle rather than a separate retrieval pipeline. Two consequences are called out, since neither is visible from the signature: registration order now decides whether an application hook can suppress retrieval, and multiple registrations run sequentially through `HookStack` instead of concurrently. `DynamicContextStore` really is gone, so it moves to the symbol table as removed-with-no-replacement rather than disappearing from the guide alongside the helper that came back. Two new silent behavior changes: - Ollama now honors `max_tokens` (0xPlaygrounds#2185). Anyone who set it, saw no effect and left it there gets truncated responses at a budget they may have chosen long ago. - Multipart tool results reach OpenAI intact (0xPlaygrounds#2217). Mixed text/JSON/rich output now presents as distinct blocks rather than one merged blob, so prompts tuned against the flattened shape are worth re-checking. 0xPlaygrounds#2166 (URL-backed PDF filename) and 0xPlaygrounds#2218 (test-only) are deliberately omitted: the first turned a hard 400 into a working request, so there is nothing to migrate, and the second changes no public surface. Re-verified every symbol the guide names against the current tree, not the tree it was written against: `FinalResponse` and `DynamicContextStore` absent, `dynamic_context` present again, and the rest unchanged. All 9 internal links resolve against 37 headings. * docs(migrating): name the upcoming release 0.41 instead of "unreleased" This is the last PR before 0.41, so the section that has been called "unreleased" is just 0.41. Retitle it and settle the version label everywhere it appeared: the intro range, the navigation table and its anchor (#40--unreleased -> #40--041), the three silent-change subsection tags, the two cross-references from the older sections, and the Version column of the symbol appendix. Rewrapped the three prose paragraphs the substitution left ragged. Two links now wrap inside their link *text*, which renders normally; no link target is split. All 9 internal links still resolve against 37 headings, and no occurrence of "unreleased" remains.
This was referenced Jul 28, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR (net change): Collapse the duplicate
rig-agentCompletionClientinto a supertrait-based
AgentClientExtextension trait, leavingrig-core'sCompletionClientas the single canonical trait.rig::client::CompletionClientstill resolves and
use rig::prelude::*;keeps working unchanged; no sourcechanges are required for facade users. No behavior change, no shadow, no
hand-maintained forward.
Follow-up to #2197. That PR, to preserve the pre-split
client.agent(m)ergonomics, introduced a second, same-named
CompletionClienttrait inrig-agentthat re-declaredcompletion_model, hand-forwarded to the canonicalrig-coretrait (with a "add a matching forward if core grows" maintainercomment), and shadowed the core trait in the facade and preludes. This
removes that duplication while keeping the ergonomics.
What changed
CompletionClient(rig-core). The classicagent()/extractor()constructors now live on a newAgentClientExtextension traitwhose supertrait is
rig_core::client::completion::CompletionClient— so itbuilds on
completion_model/CompletionModelvia the supertrait bound andthere is nothing to keep in sync if the portable trait grows a method. The
hand-written forward and its maintainer comment are gone.
AgentClientExtandCompletionClientshare no methodnames, so both resolve without ambiguity. The deliberate same-name shadow in
the facade
clientmodule and both preludes is removed.rig::clientre-exports the fullrig-coreclient surface, including thecanonical
CompletionClient(pub use rig_core::client::*;).AgentClientExtis a distinct name layered on top, so this is one canonical trait with no
shadow — not a re-introduced duplicate.
trait by bare path now use
use rig::prelude::*;(orrig_agent::prelude::*for crates depending on
rig-agentdirectly).Migration
Facade users (most): no change required.
use rig::prelude::*;gives thefull
completion_model+agent+extractorsurface, andrig::client::CompletionClientstill resolves. If you imported the client traitexplicitly, use
rig::client::{CompletionClient, AgentClientExt}—CompletionClientprovidescompletion_model,AgentClientExtprovidesagent/extractor.AgentModelExt::into_agent_builderis unchanged.Provider clients no longer carry inherent
.agent()/.extractor()methods;they come from
AgentClientExt(a blanket impl over every completion client),which the prelude brings into scope.
Verification
cargo check --workspace --all-features --all-targets— clean (0 warnings)cargo clippy --workspace --all-features --all-targets -- -D warnings— exit 0(no ambiguous-glob-reexport)
cargo test --doc -p rig -p rig-agent -p rig-core --all-features— passcargo test -p rig-agent --features test-utils,cargo test -p rig-runtime-conformance— passcargo check -p rig --no-default-features --features {agent,derive,test-utils}— passcargo check -p rig{-agent,} --target wasm32-unknown-unknown— passtests/tool_facade_traits.rscovers both the singleuse rig::prelude::*;surface and the explicit
rig::client::{CompletionClient, AgentClientExt}pair,each exercising
completion_model+agent+extractor.rigconsumer (norig-coredep) compiles the README, prelude, andexplicit import forms.