Custom HTTP backend
The built-in backend is hyper with rustls: no redirects, no proxies, the
default roots. For an HTTP proxy, client certificates, or other roots,
implement svir::http::Backend over an HTTP client that has them.
Everything above the seam still holds for your backend: the idle timeout, status mapping, compatibility handling, decoding, and layers.
use svir::http::{Backend, BoxBody, HttpRequest, HttpResponse};
use svir::prelude::*;
struct Mine;
impl Backend for Mine {
type Body = BoxBody;
async fn send(&self, request: HttpRequest) -> Result<HttpResponse<BoxBody>, Error> {
// Send `request.method`, `request.url`, `request.headers`, and
// `request.body` with your HTTP client, and return the response
// once its headers have arrived.
let _ = request;
Err(Error::new(ErrorKind::Transport).with_detail("not connected"))
}
}
fn client() -> Result<Client<Mine>, Error> {
// `http` comes before any layer.
Client::openai("https://models.example.com").http(Mine).build()
}
The contract of send
- Send the request as given and return the response as received. No redirects, no retries, no changes to the body.
request.headersholdsauthorizationwhen there is a key, and the headers added with.header(..). Every value but those ofcontent-typeandacceptmay be a credential: keep it out of logs, and send it marked sensitive where your HTTP client can.HttpRequest'sDebugwithholds them.request.bodyisSome(HttpBody { length, stream }). Send it withContent-Length: length, not chunked: not every model server accepts a chunked request.streamyieldsResult<Bytes, Error>.- The response body is any
Stream<Item = Result<Bytes, Error>>that isSend + Unpin + 'static.BoxBodyis the boxed form; a backend that can name its stream type avoids the box. - Report a failure to connect or to read as
ErrorKind::Transport, and a timeout asErrorKind::Timeout. Mark a request that never left with.with_unsent(): that is whatRetry::connectlooks for. - Status codes are svir's to map. Return a
4xxor5xxas a response, not as an error. - Dropping the response body must close the exchange. That is how a cancelled call stops the generation.
The type carries the backend
The client is generic over its backend: Client<Mine>. Client alone means
the built-in one. Code that takes either can be generic over
B: svir::http::Backend.
A layer is tied to the backend it was added for, so .http(..) after a
.layer(..) or .wrap(..) is a Config error at build(). Call http
first.
A scripted backend is also the way to test code that calls a model without a model; see Testing without a server.