#[non_exhaustive]pub struct HttpRequestOptions {
pub headers: Headers,
/* private fields */
}Expand description
Transport options for one request: HTTP headers and credentials to send with it.
Attach them to a ChatRequest,
EmbeddingRequest or
DecisionRequest like any options. Every
bundled HTTP adapter applies them with
HttpRequest::apply_options, and so
should third-party ones. Use them for gateway tags (tenant, route,
trace, a request id), and for per-tenant credentials:
use allama::ChatRequest;
use allama_http::{Credential, HttpRequestOptions};
let request = ChatRequest::new("gpt-4o-mini").user("Hi").with_option(
HttpRequestOptions::new()
.header("x-tenant-id", "acme")
.credential("openai", Credential::api_key("sk-tenant-key")),
);§From a wrapper
Header and credential injection happen at the transport, and these
options are how a wrapper reaches it: a wrapper sees the portable
request, never the HTTP request (ADR 0013). A wrapper that tags every
call, with a request id header for example, adds its header to the
options the request already carries, through
ChatRequest::option_mut, so the
caller’s headers and credentials survive; a fresh HttpRequestOptions
given to with_option would replace them.
use allama::ChatRequest;
use allama_http::{Credential, HttpRequestOptions};
fn tag(request: &ChatRequest, request_id: &str) -> ChatRequest {
let mut request = request.clone();
match request.option_mut::<HttpRequestOptions>() {
Some(options) => options.headers.insert("x-request-id", request_id),
None => {
let options = HttpRequestOptions::new().header("x-request-id", request_id);
request = request.with_option(options);
}
}
request
}
let request = ChatRequest::new("m").user("Hi").with_option(
HttpRequestOptions::new().credential("openai", Credential::api_key("sk-tenant")),
);
let tagged = tag(&request, "req-1");
let options = tagged.option::<HttpRequestOptions>().unwrap();
assert_eq!(options.headers.get("x-request-id"), Some("req-1"));
assert!(options.credential_for("openai").is_some(), "the caller's key survives");§Headers
A header given here replaces every header of the same name (ignoring
case) that the adapter set; headers with other names are added. A plain
header never carries a credential: one named on the exact credential list
(is_credential_header), or one the adapter’s CredentialSlot
writes or replaces, is refused before anything is sent.
§Credentials
credential addresses a Credential to a
credential scope: the account it belongs to. Only the adapter whose
CredentialSlot has that scope sends it, so in a fallback chain each
provider gets its own key and no other. Bundled adapters default their
scope to their provider name (openai, anthropic, groq, …), each
crate publishes it as DEFAULT_CREDENTIAL_SCOPE, and their builders
change it with credential_scope.
- The scope’s
Credential::api_keyreplaces the key the adapter was configured with. An adapter configured with a key still sends its own key when no credential is addressed to its scope, so a mistyped scope falls back to the configured key; an adapter built without a key refuses such a request instead. - A
Credential::headeris added to requests for its scope, replacing a configured header of the same name.
Two accounts of one provider in a fallback chain need two scopes: by default they share one, and both would send the same key.
Which hops are protected: plain headers are refused by each adapter as it
sends, so a plain header that only one adapter owns (x-nimbus-key)
still reaches the providers tried before it in a fallback chain. Only
the exact credential list protects every hop. Send third-party keys as a
Credential::header addressed to their scope.
§Secrets stay in the process
Debugoutput redacts credentials and credential headers (the namesis_sensitive_headermatches), as forHeaders.- Serialization omits credentials and the headers on the exact
credential list (
is_credential_header), so a serialized request, aRecordingProviderexchange, or a fixture never holds them. A request rebuilt from one carries no credentials. Two requests that differ only in credentials compare equal. - The JSON form (
with_options_json("allama-http", ..)) rejects them withError::InvalidRequest: JSON options are exported and printed as given. Set credentials with the typed value.
Every other header is a plain header, exported and accepted in the JSON
form as given, even one whose name only looks like a credential
(Idempotency-Key, x-gateway-token): an idempotency key must survive
a recording and a replay. Debug still redacts those by name. A secret
belongs in a Credential::header, never in a plain header.
Adapters never put header values on tracing spans.
§JSON form
{"headers": {"x-tenant-id": "acme"}} under OPTIONS_NAMESPACE. A
header given more than once is exported once, with its values joined by
", ".
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.headers: HeadersHeaders to send, in order. Each replaces the adapter’s headers of the same name.
Implementations§
Source§impl HttpRequestOptions
impl HttpRequestOptions
Sourcepub fn header(self, name: impl Into<String>, value: impl Into<String>) -> Self
pub fn header(self, name: impl Into<String>, value: impl Into<String>) -> Self
Add a header to send.
A header that carries a credential is refused when the request is
sent; use credential for those.
Sourcepub fn credential(
self,
scope: impl Into<String>,
credential: Credential,
) -> Self
pub fn credential( self, scope: impl Into<String>, credential: Credential, ) -> Self
Address credential to the credential scope scope.
It replaces the scope’s earlier credential of the same kind: its
Credential::api_key, or its Credential::header of the same
name.
Sourcepub fn credential_for(&self, scope: &str) -> Option<&Credential>
pub fn credential_for(&self, scope: &str) -> Option<&Credential>
The credential addressed to scope that the adapter places itself:
its Credential::api_key (header credentials are added by
HttpRequest::apply_options).
An adapter with a CredentialPlacement::Custom slot reads its
credential here, after apply_options.
Sourcepub fn credentials(&self) -> impl Iterator<Item = (&str, &Credential)>
pub fn credentials(&self) -> impl Iterator<Item = (&str, &Credential)>
Every credential, with the scope it is addressed to, in order.
Trait Implementations§
Source§impl Clone for HttpRequestOptions
impl Clone for HttpRequestOptions
Source§impl Debug for HttpRequestOptions
impl Debug for HttpRequestOptions
Source§impl Default for HttpRequestOptions
impl Default for HttpRequestOptions
Source§impl<'de> Deserialize<'de> for HttpRequestOptions
Reads the JSON form, which takes no credentials.
impl<'de> Deserialize<'de> for HttpRequestOptions
Reads the JSON form, which takes no credentials.