Собственный HTTP-бэкенд
Встроенный бэкенд — hyper с rustls: без редиректов, без прокси, с корнями по
умолчанию. Для HTTP-прокси, клиентских сертификатов или других корней
реализуйте svir::http::Backend поверх HTTP-клиента, который всё это умеет.
Всё, что выше этой границы, работает и с вашим бэкендом: таймаут простоя, разбор статусов, обработка совместимости, декодирование и слои.
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()
}
Контракт send
- Отправьте запрос как есть и верните ответ как получен. Без редиректов, без повторов, без изменений тела.
- В
request.headersестьauthorization, если задан ключ, и заголовки, добавленные через.header(..). Любое значение, кромеcontent-typeиaccept, может быть учётными данными: не пишите его в логи и отправляйте с пометкой sensitive, где ваш HTTP-клиент это умеет.DebugуHttpRequestих скрывает. request.body— этоSome(HttpBody { length, stream }). Отправляйте его сContent-Length: length, а не chunked: не каждый сервер модели принимает chunked-запрос.streamотдаётResult<Bytes, Error>.- Тело ответа — любой
Stream<Item = Result<Bytes, Error>>, которыйSend + Unpin + 'static.BoxBody— его упакованная форма; бэкенд, который может назвать тип своего потока, обходится без упаковки. - Ошибку подключения или чтения сообщайте как
ErrorKind::Transport, таймаут — какErrorKind::Timeout. Запрос, который так и не ушёл, помечайте.with_unsent(): именно это ищетRetry::connect. - Статусы разбирает svir. Возвращайте
4xxили5xxкак ответ, а не как ошибку. - Drop тела ответа должен закрывать обмен. Так отменённый вызов останавливает генерацию.
Тип несёт бэкенд
Клиент обобщён по своему бэкенду: Client<Mine>. Client без параметра —
это встроенный. Код, принимающий любой, может быть обобщён по
B: svir::http::Backend.
Слой привязан к бэкенду, для которого его добавили, поэтому .http(..) после
.layer(..) или .wrap(..) — ошибка Config в build(). Вызывайте http
первым.
Скриптованный бэкенд — заодно и способ тестировать код, который вызывает модель, без модели; см. Тесты без сервера.