feat!: BoxedHttpClient — an erased HTTP transport; Client<Ext> defaults to it - #2401
Merged
Conversation
…pClientExt A private ErasedHttpClient mirror collapses the method generics to Bytes (the only bounds are Into<Bytes>/From<Bytes>), a blanket impl erases any H: HttpClientExt + 'static, and BoxedHttpClient re-exposes the generic surface on top. Byte-transparent; Clone is an Arc bump; boxing a boxed client is a clone; Debug never prints the inner transport.
…_env_boxed, from_val_boxed}, Client::boxed The type default moves off the Missing typestate placeholder onto the erased transport, so Client<Ext> in type position means 'any transport'. Expression-position inference is unchanged; the one break is Client::<Ext>::builder(), which now needs Client::<Ext, Missing>::builder().
…ransport; anthropic/openai cassettes replayed through BoxedHttpClient The replay server matches on body bytes, so a recorded scenario passing through Client<Ext, BoxedHttpClient> over the same transport is the byte-transparency proof.
…and CHANGELOG for the erased transport
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.
Bevy-prep follow-up (after #2394 #2395 #2396 #2397 #2398 #2399 #2400); PR 8 of the data-layer series.
What
HttpClientExtis generic at the method level, so it is not object-safe and every client that holds a transport is generic overH. That is right for a library; a host that owns one transport for many providers (a worker pool, an ECS resource, a plugin) does not wantHin every type it holds. This adds the erasure once, at the boundary:rig_core::http_client::BoxedHttpClient—Arc<dyn ErasedHttpClient>where the private mirror collapses the generics toBytes(the only bounds areInto<Bytes>/From<Bytes>); a blanket impl erases anyH: HttpClientExt + 'static;BoxedHttpClientre-implementsHttpClientExt. Byte-transparent;Cloneis anArcbump; boxing a boxed client is a clone, not a second layer;Debugprints only the type name (the inner transport may carry credentials);compile_fail-pinned non-Serialize.Client<Ext, H = BoxedHttpClient>— the type default moves off theMissingtypestate placeholder, soClient<Ext>in type position means "any transport". Expression-position inference is unchanged.ProviderFromEnv::{from_env_boxed, from_val_boxed},Client::boxed(self),ReqwestClient::boxed()/From<ReqwestClient> for BoxedHttpClient.examples/agent_no_tokionow holds the transport erased.Send + Sync + 'staticpin forBoxedHttpClient.The one break
Client::<Ext>::builder()no longer resolves (builder()lives onClient<Ext, Missing>): spell itClient::<Ext, Missing>::builder(), or go through a providerClientBuilder/ the rig-reqwest prelude as before. Documented in MIGRATING.md.Why this lands before the rest of the data-layer series
Every later PR constructs or holds clients (a model registry populated at startup, spec resolution, a Bevy plugin's single transport resource). Fixing the boundary first means they are written against
Client<Ext>once instead ofH-generic signatures that get re-touched.Verification
tests/providers/{anthropic,openai}/cassette/boxed_transport.rs: the recordedagent/completion_smokeandstreaming/streaming_smokescenarios replayed throughClient<Ext, BoxedHttpClient>over the same bundled transport. The replay server matches on body bytes, so this is the byte-transparency proof. Both binaries pass whole (420 + 420).http_client/erased.rs(pass-through of URI/headers/body, body conversion, error pass-through, clone identity, Debug redaction).cargo check --workspace --all-features --all-targets, clippy, fmt,RUSTDOCFLAGS=-D warnings cargo doc, rig-core lib+doc tests, rig-agent, rig-reqwest, facadecore(incl. the dependency-graph guard) — clean.--all-features), rig-reqwest, rig-agent, rig — clean.cargo tree -p agent_no_tokio -e normal | grep -E '^(tokio|reqwest)'unchanged from feat(agent): run_channel/RunEvents, static Send+Sync pins, bevy_tasks example, dependency-graph guard #2399: both enter only via rig-reqwest.