Skip to content

feat!: rig-reqwest — cut the bundled transport into its own crate; rig-core has no default transport and no reqwest/tokio - #2397

Merged
gold-silver-copper merged 2 commits into
mainfrom
bevy-prep/rig-reqwest
Aug 21, 2026
Merged

feat!: rig-reqwest — cut the bundled transport into its own crate; rig-core has no default transport and no reqwest/tokio#2397
gold-silver-copper merged 2 commits into
mainfrom
bevy-prep/rig-reqwest

Conversation

@gold-silver-copper

Copy link
Copy Markdown
Contributor

Bevy-prep PR 4, the last of the series (#2394#2395#2396 → this). rig-core is now transport-agnostic all the way down: it names no default HTTP transport and depends on neither reqwest nor tokio. The bundled reqwest transport and everything that depends on it — the default-transport constructors, the aliased provider tree, the OpenAI Responses websocket — live in the new rig-reqwest crate, which the rig facade re-exports behind its (default) reqwest feature. Through the facade the common path is unchanged:

use rig::prelude::*;
let client = rig::providers::openai::Client::from_env()?;            // or ::new(key), ::builder().api_key(..).build()
let agent: Agent<rig::providers::openai::CompletionModel> = client.agent("gpt-4o").build();

rig-core

  • No H = reqwest::Client default anywhere (107 sites: provider aliases, Generic*Model structs, the anthropic_compatible!/model_listing macros, Client<Ext, H>, Capabilities<H>, ModelLister<H>); H is always explicit.
  • Construction is generic: Client::new_with(api_key, http); builder() lives on Client<Ext, Missing> (single inherent impl, so provider::Client::builder() infers H); and the new ProviderFromEnv trait on each provider extension type (from_env_with(http) / from_val_with(input, http)) replaces the 29 reqwest-pinned ProviderClient impls and impl_provider_client! — all the env-var knowledge stays in rig-core, generic over the transport. The ProviderClient trait stays (rig-bedrock/vertexai/gemini-grpc implement it for their own clients). The reqwest-substituting ClientBuilder<…, Missing>::build() is gone; llamacpp::Client::from_url is from_url_with(url, http).
  • http_client/reqwest_transport.rs and the openai websocket module move out; a handful of streaming internals the websocket needs are #[doc(hidden)] pub.
  • Dependencies: reqwest, reqwest-middleware, tokio, tokio-tungstenite removed; default features are ["derive"]. cargo tree -p rig-core -e normal shows no tokio and no reqwest, with default features and with --no-default-features.
  • rig-core's own tests: ~400 construction sites across 61 files now go through the test_utils mock (rig-core cannot dev-depend on rig-reqwest without a dependency cycle). 51 doc examples that demonstrated the default transport are marked ignore; three #[ignore] live Azure tests that needed a real transport are deleted.

rig-reqwest (new crate)

  • ReqwestClient(reqwest::Client) newtype implementing HttpClientExt, plus ReqwestMiddlewareClient behind reqwest-middleware. A newtype is forced: the orphan rule forbids implementing rig-core's trait for reqwest's type from a third crate (the plan's "no wrapper newtype" was wrong, and From<NoBody> for reqwest::Body had to be deleted for the same reason). It derefs to the inner client and converts From<reqwest::Client>.
  • DefaultTransportClient / DefaultTransportBuilder — one blanket impl each, pinned to Client<Ext, ReqwestClient>, giving every provider client new / from_env / from_val and builder().build() with the transport inferred. (Type-alias defaults don't apply in expression position, verified with rustc, so an alias tree alone could not do this; rig-core's ProviderClient and these traits coexist in the prelude without ambiguity because their impl sets are disjoint.)
  • providers: the provider module tree with every transport-generic type aliased to …<ReqwestClient> for type position — 114 aliases, generated from the provider sources and guarded by tests/providers_complete.rs, which cross-reads rig-core's sources (incl. the inline image_generation/audio_generation modules and macro-generated types) so a missing alias fails the build.
  • Runtime bridge: inside a tokio runtime reqwest futures are awaited directly; outside one (Bevy task pools, smol, futures::executor) they run on a lazily started single-worker runtime (LazyLock<Result<Runtime, _>> — a start failure surfaces as a transport error, no panic) and the caller only ever polls runtime-agnostic futures: unary bodies are read eagerly on the tokio side, streamed bodies are forwarded through a bounded futures::channel::mpsc. tests/off_runtime.rs drives a request and a chunked stream entirely from futures::executor::block_on. wasm builds call reqwest's fetch backend directly (no bridge).
  • openai_websocket (feature websocket) with the ResponsesWebSocketExt trait replacing the former inherent impl Client<reqwest::Client>; the websocket conformance suite moves here.

Facade and workspace

  • rig re-exports rig-reqwest behind the reqwest feature: rig::providers, rig::http_client::{ReqwestClient, from_reqwest}, rig::client::{DefaultTransportClient, DefaultTransportBuilder}, and the prelude. The websocket*/rustls/native-tls/socks/reqwest-middleware* features now forward to rig-reqwest. cargo check -p rig --no-default-features --features agent,derive is tokio/reqwest-free — the Bevy configuration.
  • rig-agent's tokio is optional (enabled by rmcp, discord-bot); companion crates' examples and rig-agent's tests/doc examples dev-depend on rig-reqwest.
  • CI: wasm matrix and nextest package lists include rig-reqwest; the serde-wall allowlist, conformance registry and provider-layout guards follow the moved code. MIGRATING.md documents everything, including the import change for code that imported ProviderClient explicitly for core providers.

Verification

  • cargo check --workspace --all-features --all-targets and cargo clippy (same flags): clean.
  • Tests: rig-core 1690, rig-reqwest 62 (incl. the off-runtime bridge, alias completeness, and the moved websocket conformance suite), rig-agent 593 + 13 doctests, facade 3394 (full cassette matrix) — 0 failures.
  • wasm32 cargo check passes for rig-core (all features), rig-reqwest, and rig.
  • cargo tree -p rig-core -e normal: no tokio, no reqwest (both feature sets). cargo tree -p rig -e normal --no-default-features --features agent,derive: likewise.
  • cargo package succeeds for every crate. A full cargo publish --workspace --dry-run cannot verify dependents against the registry at the unbumped 0.42.0 version (rig-core 0.42.0 already exists upstream with a different checksum) — a pre-existing property of the unbumped workspace; the release-plz PR is where that check runs.

…g-core has no default transport and no reqwest/tokio

Bevy-prep PR 4 (final). rig-core is now transport-agnostic all the way
down: it names no default HTTP transport and depends on neither reqwest
nor tokio. The bundled reqwest transport and everything that depends on
it — the default-transport constructors, the aliased provider tree, the
OpenAI Responses websocket — live in the new rig-reqwest crate, which the
rig facade re-exports behind its (default) reqwest feature. Through the
facade, rig::providers::openai::Client::from_env() / ::new(..) /
::builder().build() and Agent<openai::CompletionModel> are unchanged.

rig-core
- No H = reqwest::Client defaults anywhere (107 sites: provider aliases,
  Generic*Model structs, the anthropic_compatible!/model_listing macros,
  Client<Ext, H>, Capabilities<H>, ModelLister<H>); H is always explicit.
- Construction is generic: Client::new_with(api_key, http), builder() on
  Client<Ext, Missing> (single inherent impl, so H infers), and the new
  ProviderFromEnv trait on each provider extension type
  (from_env_with(http) / from_val_with(input, http)) replaces the 29
  reqwest-pinned ProviderClient impls and impl_provider_client!. The
  ProviderClient trait stays for companion crates' own client types.
  The reqwest-substituting ClientBuilder<.., Missing>::build() is gone;
  llamacpp::Client::from_url is from_url_with(url, http).
- reqwest_transport.rs and the openai websocket module move out; a few
  streaming internals the websocket needs become #[doc(hidden)] pub.
- Dependencies: reqwest, reqwest-middleware, tokio, tokio-tungstenite
  removed; default features are ["derive"]. cargo tree -p rig-core
  -e normal shows no tokio and no reqwest (default and no-default).
- Own tests: ~400 construction sites across 61 files now go through the
  test_utils mock (rig-core cannot dev-depend on rig-reqwest without a
  cycle); 51 doc examples that showed the default transport are ignore.

rig-reqwest (new)
- ReqwestClient(reqwest::Client) newtype implementing HttpClientExt —
  a newtype because the orphan rule forbids implementing rig-core's
  trait for reqwest's type from a third crate (the plan's "no wrapper"
  was wrong); ReqwestMiddlewareClient behind reqwest-middleware.
- DefaultTransportClient / DefaultTransportBuilder: one blanket impl
  each, pinned to Client<Ext, ReqwestClient>, giving every provider
  client new/from_env/from_val and builder().build() with the transport
  inferred (type-alias defaults don't apply in expression position, so
  aliases alone could not do this).
- providers: the provider module tree with every transport-generic
  type aliased to <ReqwestClient> for type position (114 aliases,
  generated; tests/providers_complete.rs cross-checks the rig-core
  sources so a missing alias fails the build).
- Runtime bridge: inside tokio, reqwest futures are awaited directly;
  outside (Bevy task pools, smol, futures::executor) they run on a
  lazily started single-worker runtime and the caller only polls
  runtime-agnostic futures — unary bodies are read eagerly on the tokio
  side, streamed bodies are forwarded through a bounded futures mpsc
  channel. Covered by tests/off_runtime.rs driving a request and a
  chunked stream from futures::executor::block_on.
- openai_websocket (feature websocket) with the ResponsesWebSocketExt
  trait replacing the inherent impl; the websocket conformance suite
  moves here.

Facade / workspace
- rig re-exports rig-reqwest (providers, http_client::ReqwestClient /
  from_reqwest, client::{DefaultTransportClient, DefaultTransportBuilder},
  prelude) behind the reqwest feature; websocket*/rustls/native-tls/
  socks/reqwest-middleware* features forward to rig-reqwest.
  cargo check -p rig --no-default-features --features agent,derive is
  tokio/reqwest-free.
- rig-agent's tokio is optional (rmcp, discord-bot); companion crates'
  examples and rig-agent's tests/docs dev-depend on rig-reqwest.
- CI wasm matrix + nextest package lists include rig-reqwest; the serde
  wall allowlist, conformance registry and provider-layout guards follow
  the moved code. MIGRATING.md documents the whole change.

Verification: cargo check/clippy --workspace --all-features
--all-targets clean; rig-core 1690, rig-reqwest 62, rig-agent 593 (+ 13
doctests), facade 3394 tests pass; wasm32 checks pass for rig-core,
rig-reqwest and rig; cargo package succeeds for every crate (a full
publish dry-run cannot verify against the registry at the unbumped
0.42.0 version — pre-existing).
…e rig-reqwest prelude, rig-agent test-utils enables tokio
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant