Expand description
§TypeSafe Rust SDK
An independent Rust client for the TypeSafe AI System One API, maintained at gilljon/typesafe-ai-rs.
Async and blocking clients, typed Noul/Choice/Score questions and answers, model discovery, configurable retries, cancellation, and complete HTTP response metadata. The implementation targets the official Python and JavaScript SDKs at version 0.6.0. See the parity notes for exact source revisions and deliberate Rust differences.
§Install
cargo add typesafe-ai-rs
cargo add tokio --features macros,rt-multi-thread
cargo add serde_jsonThe package is named typesafe-ai-rs, imported as typesafe_ai_rs.
Alternatively, install directly from the GitHub release:
cargo add typesafe-ai-rs --git https://github.com/gilljon/typesafe-ai-rs --tag v0.1.0Rust 1.88 or newer is required. The default TLS backend is Rustls. To use native TLS, disable default features and enable native-tls. Enable blocking for the synchronous client.
§Quick start
Set TYPESAFE_API_KEY to an API key from the TypeSafe console.
use serde_json::json;
use typesafe_ai_rs::{Client, Choice, Noul, Question, Score, SystemOneRequest};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new()?;
let response = client.system_one(SystemOneRequest::new(
json!({"document": "I was charged twice. Please fix this ASAP."}),
[
("billing", Question::from(Noul::new("Is this about billing?"))),
("tone", Choice::new([("calm", json!(null)), ("frustrated", json!(null)), ("angry", json!(null))])
.instructions("What is the customer's tone?").into()),
("urgency", Score::new(["can wait", "this week", "today"])
.instructions("How urgent is this?").into()),
],
)).await?;
println!("billing: {}", response.nouls()["billing"].noul);
println!("tone: {}", response.choices()["tone"].choice);
println!("urgency: {}", response.scores()["urgency"].score);
println!("request ID: {:?}", response.request_id());
Ok(())
}Questions accept JSON instructions and descriptions. Omitted instructions and explicit JSON null remain distinct. Score criteria are an ordered, nonempty sequence, sent as an array on the wire. Score answers expose integer keys for their probabilities and legend. A one-level score rubric follows Python’s validation; JavaScript requires two levels.
§Blocking client
cargo add typesafe-ai-rs --features blockinguse typesafe_ai_rs::{blocking::Client, Noul, SystemOneRequest};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let response = Client::new()?.system_one(SystemOneRequest::new(
"I was charged twice.",
[("billing", Noul::new("Is this about billing?"))],
))?;
println!("{}", response.nouls()["billing"].noul);
Ok(())
}Use the blocking client outside async runtimes, or in tokio::task::spawn_blocking. Clones share connection pools; dropping the last client releases its resources. AsyncTypeSafeClient and, with the feature enabled, TypeSafeClient are aliases matching Python’s naming.
§Configuration and per-call options
Explicit configuration takes precedence over environment variables. Empty environment values are ignored.
| Environment variable | Default |
|---|---|
TYPESAFE_API_KEY | Required |
TYPESAFE_BASE_URL | https://api.typesafe.ai |
TYPESAFE_DEFAULT_MODEL | jev-latest |
TYPESAFE_LOG_LEVEL | warn |
use std::time::Duration;
use typesafe_ai_rs::{Client, Noul, RequestOptions, RetryPolicy, SystemOneRequest};
let client = Client::builder()
.api_key("your-api-key")
.model("jev-latest")
.timeout(Duration::from_secs(10))
.retry(RetryPolicy { max_retries: 3, ..Default::default() })
.build()?;
let result = client.system_one_with_options(
SystemOneRequest::new("A support ticket", [("billing", Noul::new("About billing?"))])
.model("jev-latest"),
RequestOptions {
timeout: Some(Duration::from_secs(5)),
retry: Some(RetryPolicy::no_retries()),
..Default::default()
},
).await?;default_headers and per-call headers use HeaderMap. Per-call headers override defaults case-insensitively, except protected authentication and SDK protocol headers. A custom reqwest::Client (or reqwest::blocking::Client) can be supplied through .http_client(...) for proxy, TLS, and pool configuration. SDK request timeouts still apply. The default HTTP client does not follow redirects; custom clients control their own redirect behavior.
The default policy makes up to two retries for HTTP 408, 429, 5xx, connection errors, and timeouts. Exponential backoff starts at 500ms, caps at 5s, and subtracts up to 25% jitter. Server retry headers accept milliseconds, seconds, and HTTP dates. A 30s retry budget follows Python; the 60s retry-header cap follows JavaScript. The budget prevents scheduling another retry but does not interrupt an already-running attempt. Set timeout: None in RetryPolicy for JavaScript’s unlimited total budget, or max_retry_after: None for Python’s uncapped server delays. A per-call retry policy replaces the client’s policy. Use struct update syntax to inherit selected settings. Custom HTTP statuses and an additional retry predicate are supported.
§Models and metadata
let response = client.models().list().await?;
for model in &response.models {
println!("{}: {}", model.name, model.description);
}
println!("request ID: {:?}", response.request_id());
println!("HTTP status: {}", response.raw_http_response.status);Responses retain status, headers, and body bytes in raw_http_response; .json() and .text() inspect the full body. Required response fields are validated, unknown fields are ignored, and unknown answer kinds are skipped from typed answers while remaining available in the raw body.
§Errors and cancellation
Errors distinguish invalid configuration/requests, transport failures, timeout, cancellation, server errors, and malformed successful responses. API errors expose an ApiErrorKind, status, request ID, headers, body, and extracted server message. Display and Debug omit response bodies and secret headers; call .message() explicitly for server details.
use typesafe_ai_rs::{ApiErrorKind, Error};
match client.models().list().await {
Ok(response) => println!("{} models", response.models.len()),
Err(Error::Api(error)) if error.kind() == ApiErrorKind::RateLimit => {
eprintln!("retry after {:?}; request {:?}", error.retry_after(), error.request_id());
}
Err(error) => eprintln!("{error}"),
}Pass a cloned CancellationToken in RequestOptions::cancellation_token and call token.cancel() from another task to cancel an async request or retry wait. Dropping an in-progress request future also cancels it. Cancellation is never retried. Blocking requests use timeouts and reject cancellation tokens.
§Forward compatibility and logging
Use Question::Raw(json!(...)) for future question types/fields, and .extra_body([("beam_width", json!(4))]) on a request for additional top-level fields. Extra body fields merge last and can override state, model, or questions, matching Python.
The SDK uses the log facade with target typesafe_ai_rs; install an application logger such as env_logger to receive output. TYPESAFE_LOG_LEVEL or .log_level(...) filters transport messages. info emits request summaries; debug adds bodies and redacted headers. Authorization, cookies, API keys, and headers containing token or secret are redacted. Bodies can contain application data and are deliberately not redacted at debug level.
§Development
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo test --no-default-features
cargo doc --all-features --no-deps
cargo packageTests use local HTTP servers and need no TypeSafe API key. Runnable examples live in examples/. Live examples require your key and may consume API credits. See release instructions for registry publication.
MIT licensed. This project is independently maintained and is not an official TypeSafe SDK.
Modules§
- blocking
- Synchronous client, enabled by the
blockingCargo feature.
Structs§
- ApiError
- An unsuccessful HTTP response with its body and request metadata.
- Cancellation
Token - A token which can be used to signal a cancellation request to one or more tasks.
- Choice
- A question that selects among named alternatives.
- Choice
Answer - The selected label, its confidence, and the probability of each label.
- Client
- Asynchronous TypeSafe client. Clone it to share a pooled HTTP connection manager.
- Client
Builder - Construct an asynchronous client with environment fallbacks.
- Header
Map - A specialized multimap for header names and values.
- Header
Name - Represents an HTTP header field name
- Header
Value - Represents an HTTP header field value.
- List
Models Response - Models available to the account, with raw HTTP metadata.
- Model
Card - Metadata for an available model.
- Models
- Models resource associated with an asynchronous client.
- Noul
- A yes/no question. Its answer is a probability between zero and one.
- Noul
Answer - A yes/no answer, expressed as the probability of yes.
- RawResponse
- The complete response before typed parsing, including unrecognized API fields.
- Request
Options - Per-call overrides. An omitted field inherits the client’s setting.
- Retry
Policy - Configuration shared by synchronous and asynchronous request retries.
- Score
- A question that assigns an expected score using an ordered rubric.
- Score
Answer - An expected score, which may fall between integer rubric levels.
- Status
Code - An HTTP status code (
status-codein RFC 9110 et al.). - System
OneRequest - State and named questions to evaluate with System One.
- System
OneResponse - Answers keyed by question name, together with model and token usage metadata.
- Usage
- Token counts, when reported by the API.
Enums§
- Answer
- An answer identified by its
typediscriminator in the API response. - ApiError
Kind - Classification of an unsuccessful HTTP response.
- Error
- An error returned by the TypeSafe SDK.
- Question
- A typed question, or a raw JSON object for additional and future API fields.
Constants§
- DEFAULT_
BASE_ URL - Default API root (without the versioned endpoint path).
- DEFAULT_
MODEL - Default model alias.
- DEFAULT_
TIMEOUT - Timeout for each complete HTTP attempt, including its response body.
- VERSION
- This crate’s version.
Functions§
- parse_
retry_ after - Parse a server retry delay, preferring
retry-after-msoverRetry-After.
Type Aliases§
- Async
Type Safe Client - Python-compatible name for the asynchronous client.
- Choice
Criteria - Labels mapped to descriptions; use
Value::Nullfor an undescribed label. - Entry
- JSON content accepted by the API, including text, objects, arrays, and null.
- LogLevel
- Per-client log filtering. Messages use the
typesafe_ai_rstarget of thelogfacade. Applications install their own logger; this library never installs a global logger. - Model
Metadata - The Python SDK calls model cards
ModelMetadata. - Noul
Criteria - Descriptions for the
trueandfalseoutcomes of a noul. - Questions
- Questions keyed by the names used to identify their answers.
- Retry
Predicate - An additional rule that can opt an error into retries.
- Score
Criteria - An ordered rubric whose positions are the integer score levels.
- Type
Safe Client - Python-compatible name for the synchronous client.