pub struct ProviderLayer { /* private fields */ }Expand description
A tower layer that sends requests to a provider: another host (and path prefix) serving the same API, with its own headers, query parameters and paths.
A request is the provider’s when its URL starts with the provider’s base URL, or with the base declared with
ProviderLayer::rewrite_from, which is then replaced with the provider’s: same scheme, host and port, and a path
that starts with the base’s path at a segment boundary (/v1 holds /v1 and /v1/models, not /v10). To such a
request the layer, in this order:
- replaces the base (scheme, host, port and path prefix) with the provider’s, keeping the rest of the path and the query;
- rewrites the rest of the path with
ProviderLayer::map_path, if set; - sets the headers, each replacing every value the request has under its name;
- sets the query parameters, each replacing the parameters of its name the request has.
Any other request (a download from another host, say) is passed on unchanged, so the provider’s credentials are never sent elsewhere.
Per-call RequestOptions win over the layer:
- a header the options set is not replaced by the layer’s header of that name;
- a query parameter the options added is not replaced by the layer’s parameter of that name (with an
api-versionin both, the options’ is sent, once); - a call the options sent to a base URL of their own is not moved: the layer
ignores
ProviderLayer::rewrite_fromfor it. When that base is the provider’s (or under it), the layer still rewrites the path and adds its headers and query parameters; any other base gets nothing from the layer, so its credentials do not go there.
There are two ways to point a generated client at a provider:
- Set
Configuration.base_pathto the provider’s base URL and addProviderLayer::new(provider_base)for the headers, query parameters and paths. The requests already start with the provider’s base. - Keep the generated
base_pathand declare it with.rewrite_from(generated_base): the layer moves every request under it to the provider. This suits aClientshared by severalConfigurations, or a crate that hands out a ready-made client for a provider.
use timvw_rust_runtime::provider::ProviderLayer;
// An OpenAI-compatible provider under another base URL, with headers of its own.
let openrouter_like = ProviderLayer::new("https://openrouter.example/api/v1")?
.rewrite_from("https://api.openai.com/v1")?
.sensitive_header("authorization", "Bearer sk-or-123")?
.header("x-title", "my app")?;
assert_eq!(
openrouter_like.rewrite_uri(&"https://api.openai.com/v1/models?limit=2".parse()?)?.to_string(),
"https://openrouter.example/api/v1/models?limit=2"
);
// A provider that puts a deployment in the path and wants an `api-version` on every request.
let azure_like = ProviderLayer::new("https://my-resource.example.com/openai")?
.rewrite_from("https://api.openai.com/v1")?
.sensitive_header("api-key", "secret")?
.query(&[("api-version", "2024-10-21")])?
.map_path(|path| format!("/deployments/my-deployment{path}"));
assert_eq!(
azure_like.rewrite_uri(&"https://api.openai.com/v1/chat/completions".parse()?)?.to_string(),
"https://my-resource.example.com/openai/deployments/my-deployment/chat/completions?api-version=2024-10-21"
);Add it with Client::layer, to Client::default() or to a generated Configuration.client.
Added last, it sees each request first and rewrites it once, before the retries.
Implementations§
Source§impl ProviderLayer
impl ProviderLayer
Sourcepub fn new(base_url: &str) -> Result<Self, Error>
pub fn new(base_url: &str) -> Result<Self, Error>
A provider whose API is at base_url, such as https://openrouter.ai/api/v1. Fails, with an error whose
is_builder() is true, on a URL that is not absolute or that has a query or fragment.
Sourcepub fn rewrite_from(self, base_url: &str) -> Result<Self, Error>
pub fn rewrite_from(self, base_url: &str) -> Result<Self, Error>
Moves the requests under base_url, typically a generated client’s default base_path such as
https://api.openai.com/v1, to the provider: base_url is replaced with the provider’s base URL, and the rest
of the path and the query are kept. Fails like ProviderLayer::new. Calling it again replaces the base.
Sourcepub fn header<K, V>(self, name: K, value: V) -> Result<Self, Error>where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<Error>,
pub fn header<K, V>(self, name: K, value: V) -> Result<Self, Error>where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<Error>,
Sets a header on every request to the provider, replacing every value the request has under that name (the
generated client’s included). Calling it again for the same name replaces the value. Fails, with an error
whose is_builder() is true, on an invalid name or value.
Sourcepub fn sensitive_header<K, V>(self, name: K, value: V) -> Result<Self, Error>where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<Error>,
pub fn sensitive_header<K, V>(self, name: K, value: V) -> Result<Self, Error>where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<Error>,
Like ProviderLayer::header, and marks the value sensitive (see HeaderValue::set_sensitive): for an API
key, so that it is left out of Debug output.
Sourcepub fn headers(self, headers: HeaderMap) -> Self
pub fn headers(self, headers: HeaderMap) -> Self
Sets every header of a map, replacing the values set before under its names. A name with several values keeps them all. Values marked sensitive stay sensitive.
Sourcepub fn query<T: Serialize + ?Sized>(self, query: &T) -> Result<Self, Error>
pub fn query<T: Serialize + ?Sized>(self, query: &T) -> Result<Self, Error>
Sets query parameters on every request to the provider, such as api-version, serialized as
application/x-www-form-urlencoded: a sequence of pairs, a map or a struct. Each replaces the parameters of its
name the request has; a name given twice here is sent twice. Fails, with an error whose
is_builder() is true, on a value that is not a flat list of pairs.
Sourcepub fn map_path<F>(self, f: F) -> Self
pub fn map_path<F>(self, f: F) -> Self
Rewrites the path of each request to the provider, relative to the base URL: f gets the path after the base
(/chat/completions for https://api.openai.com/v1/chat/completions with base https://api.openai.com/v1;
empty for the base itself), percent-encoded, and returns the new one, which is put after the provider’s base.
Sourcepub fn header_map(&self) -> &HeaderMap
pub fn header_map(&self) -> &HeaderMap
The headers set on each request to the provider.
Sourcepub fn query_pairs(&self) -> &[(String, String)]
pub fn query_pairs(&self) -> &[(String, String)]
The query parameters set on each request to the provider, decoded, in order.
Sourcepub fn rewrite_uri(&self, uri: &Uri) -> Result<Uri, Error>
pub fn rewrite_uri(&self, uri: &Uri) -> Result<Uri, Error>
The URL a request to uri is sent to: moved to the provider, its path rewritten and its query parameters set,
or uri unchanged when it is not the provider’s. Fails, with an error whose
is_builder() is true, when the rewritten URL is invalid.