Expand description
REST API client module for OpenAI interface
This module provides the core HTTP functionality for making requests to OpenAI-compatible APIs. It includes traits and implementations for both streaming and non-streaming API calls.
§Overview
The rest module contains:
post: HTTP POST request functionality with streaming and non-streaming supportget: HTTP GET request functionality with various parameter handling optionsdelete: HTTP DELETE request functionalityoptions: Per-request authentication (RequestOptions,Auth) taken by every request methoddefault_client: Areqwest::Clientconstructor shared by all request traitscheck_status: Shared non-2xx response handling which parses the error bodyskip_deserialization_errors: A stream adapter that drops chunks which fail to deserialize
§Usage
The module is designed to be used through the higher-level API modules (chat, completions,
etc.). However, you can use the traits directly if needed:
§POST Requests
use openai_interface::rest::{post::{Post, PostNoStream}, RequestOptions};
use openai_interface::errors::OapiError;
use serde::{Serialize, Deserialize};
use std::str::FromStr;
#[derive(Serialize)]
struct MyRequest {
prompt: String,
stream: bool,
}
#[derive(Deserialize)]
struct MyResponse {
// Define the fields of your response here
id: String,
}
impl FromStr for MyResponse {
type Err = OapiError;
fn from_str(content: &str) -> Result<Self, Self::Err> {
let parse_result: Result<Self, _> = serde_json::from_str(content)
.map_err(|e| OapiError::DeserializationError(e.to_string()));
parse_result
}
}
impl Post for MyRequest {
fn is_streaming(&self) -> bool {
self.stream
}
fn build_url(&self, base_url: &str) -> Result<String, OapiError> {
Ok(format!("{}/service", base_url))
}
}
impl PostNoStream for MyRequest {
type Response = MyResponse;
}
// Send it with a client:
// let client = openai_interface::rest::default_client();
// let response: MyResponse = request
// .get_response(&client, "https://api.openai.com/v1/chat/completions", &RequestOptions::bearer("API_KEY"))
// .await?;§GET Requests
use openai_interface::rest::get::Get;
use openai_interface::errors::OapiError;
// GET request with URL building
struct ComplexRequest {
resource_id: String,
limit: Option<u32>,
}
impl Get for ComplexRequest {
fn build_url(&self, base_url: &str) -> Result<String, OapiError> {
let mut url = format!("{}/{}", base_url, self.resource_id);
if let Some(limit) = self.limit {
url.push_str(&format!("?limit={}", limit));
}
Ok(url)
}
}§Client configuration
Every request method takes the client as its first argument, so callers
control proxies, timeouts and connection pooling. Use
default_client for a sensible default, or build your own, e.g. with a
proxy:
// Building a client needs an installed provider: this one comes from the
// `ferritls` feature (see "TLS crypto provider" below).
openai_interface::rest::install_crypto_provider().ok();
let client = reqwest::Client::builder()
.proxy(reqwest::Proxy::http("http://127.0.0.1:10808")?)
.timeout(std::time::Duration::from_secs(60))
.build()?;§TLS crypto provider
This crate depends on reqwest with its rustls-no-provider feature, so
the rustls stack is compiled without a crypto backend. That leaves the
choice of backend to the application: exactly one
rustls::crypto::CryptoProvider must be installed as the process
default before any reqwest::Client is built, otherwise reqwest panics at
construction time.
Nothing in this crate installs a provider for you — neither
default_client nor any request method touches the global state, so an
application that picked a provider first keeps it, and an application that
never builds a client through this crate is free to install its own
whenever it likes.
This example is the regression lock for that promise. It runs as
should_panic, deliberately with no provider installed and no hidden
ferritls setup: if default_client() ever learns to install one on the
side, it stops panicking and cargo test fails.
let _client = openai_interface::rest::default_client();§With the ferritls feature
The optional ferritls feature (off by default) adds the pure-Rust
ferritls-rustls backend and the install_crypto_provider helper.
Enable it when you are happy to let this crate pick a provider for you:
[dependencies]
openai-interface = { version = "0.10", features = ["ferritls"] }Then call it once at startup, before the first client:
openai_interface::rest::install_crypto_provider()
.expect("a rustls crypto provider was already installed");
let client = openai_interface::rest::default_client();§Without it
With the feature off, ferritls-rustls is not in the dependency tree at
all and install_crypto_provider does not exist. Install a provider
yourself instead — first install wins, so do it before any client is
built:
// In the application crate, with `rustls = "0.23"` (feature `ring` or
// `aws-lc-rs`) as one of its own dependencies:
rustls::crypto::ring::default_provider()
.install_default()
.expect("a rustls crypto provider was already installed");§When reqwest already has a backend
Because Cargo features are additive, a project that depends on reqwest
itself with default-tls / rustls (its defaults, which fall back to the
bundled aws-lc-rs provider) or with native-tls (which skips the rustls
path entirely) needs no provider installed here at all. That backend is
then picked by feature unification instead of by you; see the
“TLS Crypto Provider” section
of the crate docs for the trade-off.
Re-exports§
pub use options::Auth;pub use options::RequestOptions;
Modules§
- delete
- Delete request
- get
- GET request functionality for OpenAI interface
- options
- Per-request authentication and header options.
- post
Functions§
- check_
status - Checks a response status, turning a non-2xx response into an
OapiError::ApiErrorthat carries the parsed error body. - default_
client - Builds a
reqwest::Clientwith library defaults. - install_
crypto_ provider - Installs the pure-Rust
ferritls-rustlscrypto provider as the process-wide default for rustls. - skip_
deserialization_ errors - Drops the items of a parsed stream that failed to deserialize.