linger-openai-sdk 0.1.1

Rust-native async SDK for OpenAI APIs with typed requests, streaming, uploads, retries, and pluggable transports.
Documentation

linger-openai-sdk

Rust-native async SDK for OpenAI APIs.

linger-openai-sdk provides typed request builders, typed responses, streaming, uploads, structured errors, conservative retries, and a pluggable HTTP transport boundary.

Installation

[dependencies]
dotenvy = "0.15"
linger-openai-sdk = "0.1"
tokio = { version = "1", features = ["full"] }

Default features enable the reqwest transport, rustls, and OS/system proxy discovery. Disable default features only when you want to provide your own transport:

[dependencies]
linger-openai-sdk = { version = "0.1", default-features = false }

Minimum supported Rust version: 1.82.

Quick Start

use linger_openai_sdk::{
    Client, ClientConfig, CreateResponseRequest, LingerError,
};

#[tokio::main]
async fn main() -> Result<(), LingerError> {
    let _ = dotenvy::dotenv();

    let api_key = std::env::var("OPENAI_API_KEY")
        .map_err(|_| LingerError::invalid_config("OPENAI_API_KEY is required"))?;
    let model = std::env::var("OPENAI_MODEL").unwrap_or_else(|_| "gpt-5.4-mini".to_string());

    let mut config = ClientConfig::builder().api_key(api_key);
    if let Ok(base_url) = std::env::var("OPENAI_BASE_URL") {
        config = config.base_url(base_url);
    }

    let client = Client::with_config(config.build()?)?;
    let response = client
        .responses()
        .create(
            CreateResponseRequest::builder()
                .model(&model)
                .input("Hello")
                .build()?,
        )
        .await?;

    println!("{}", response.output_text());
    Ok(())
}

Run the same core example from this repository:

echo 'OPENAI_API_KEY="sk-..."' > .env
cargo run --example responses_create

The example runs the same simple Responses tests shown below.

On Windows PowerShell:

Set-Content -Path .env -Value 'OPENAI_API_KEY="sk-..."'
cargo run --example responses_create

Optional .env values:

OPENAI_BASE_URL="https://api.openai.com"
OPENAI_MODEL="gpt-5.4-mini"

Supported APIs

  • Responses
  • Chat Completions
  • Completions
  • Audio
  • Embeddings
  • Files and Uploads
  • Images
  • Models
  • Moderations
  • Realtime
  • Assistants and Threads
  • Vector Stores
  • Batches
  • Fine-tuning
  • Evals
  • Containers
  • Skills
  • ChatKit
  • Webhooks

Organization, Admin, and Projects management APIs are outside the current supported scope.

Streaming

use futures_util::StreamExt;
use linger_openai_sdk::{Client, CreateResponseRequest, LingerError};

async fn stream_response(client: &Client) -> Result<(), LingerError> {
    let mut stream = client
        .responses()
        .create_stream(
            CreateResponseRequest::builder()
                .model("gpt-5.4-mini")
                .input("Stream one short sentence.")
                .build()?,
        )
        .await?;

    while let Some(item) = stream.next().await {
        if let Some(delta) = item?.output_text_delta() {
            print!("{delta}");
        }
    }

    Ok(())
}

Simple Responses Tests

The repository example runs these simple, non-destructive smoke tests:

  • create a response
  • retrieve the response by id
  • list the response input items
  • count input tokens
  • stream a short response
use futures_util::StreamExt;
use linger_openai_sdk::{
    Client, CreateResponseInputTokensRequest, CreateResponseRequest, LingerError,
};

async fn create_response(client: &Client, model: &str) -> Result<String, LingerError> {
    let response = client
        .responses()
        .create(
            CreateResponseRequest::builder()
                .model(model)
                .input("Hello")
                .build()?,
        )
        .await?;

    println!("response id: {}", response.id);
    println!("response text: {}", response.output_text());
    Ok(response.id)
}

async fn retrieve_response(client: &Client, response_id: &str) -> Result<(), LingerError> {
    let response = client.responses().retrieve(response_id).await?;
    println!("retrieved response id: {}", response.id);
    Ok(())
}

async fn list_input_items(client: &Client, response_id: &str) -> Result<(), LingerError> {
    let input_items = client.responses().list_input_items(response_id).await?;
    println!("input item count: {}", input_items.data.len());
    Ok(())
}

async fn count_input_tokens(client: &Client, model: &str) -> Result<(), LingerError> {
    let tokens = client
        .responses()
        .count_input_tokens(
            CreateResponseInputTokensRequest::builder()
                .model(model)
                .input("Count the input tokens in this short sentence.")
                .build()?,
        )
        .await?;

    println!("input tokens: {}", tokens.input_tokens);
    Ok(())
}

async fn stream_response(client: &Client, model: &str) -> Result<(), LingerError> {
    let mut stream = client
        .responses()
        .create_stream(
            CreateResponseRequest::builder()
                .model(model)
                .input("Stream one short sentence.")
                .build()?,
        )
        .await?;

    print!("stream text: ");
    while let Some(item) = stream.next().await {
        if let Some(delta) = item?.output_text_delta() {
            print!("{delta}");
        }
    }
    println!();

    Ok(())
}

Environment Variables

Create a local .env file:

OPENAI_API_KEY="sk-..."

.env is ignored by git in this repository. Use .env.example as the committed template. You can also set the variable in the current shell:

export OPENAI_API_KEY="sk-..."
$env:OPENAI_API_KEY = "sk-..."

If you set a persistent User or Machine environment variable on Windows, open a new terminal before running Cargo so the process inherits the new value.

Live Smoke Test

cargo run --example responses_create

The example loads .env, creates a client, and runs the simple Responses tests above using OPENAI_MODEL or gpt-5.4-mini.

If the command returns an OpenAI API error such as 429 insufficient_quota or a temporary 5xx service error, the SDK reached the API and parsed the error correctly; check quota/billing or retry later for transient service errors. If it returns a transport error, check local network/proxy settings. The default features include system-proxy so reqwest can discover OS-level proxy settings where supported.

Features

Feature Default Description
reqwest-transport yes Default HTTP transport backed by reqwest.
rustls-tls yes Uses rustls for TLS.
system-proxy yes Lets reqwest discover OS/system proxy settings where supported.
native-tls no Uses the platform native TLS backend instead of rustls.

More Examples

License

MIT OR Apache-2.0