Community-maintained Rust SDK for the TypeSafe AI API.
This is not an official TypeSafe AI product. For official SDKs, see the TypeSafe SDKs page.
TypeSafe answers typed questions about text or structured state. Create a client, describe the questions you want answered, and read the typed answers.
[dependencies]
typesafe-sdk-rs = "0.1"The library is imported as typesafe_sdk:
use typesafe_sdk::{Client, SystemOneRequest};Set your API key as an environment variable:
export TYPESAFE_API_KEY="sk-..."use typesafe_sdk::{Client, SystemOneRequest, noul, choice, score};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
let response = client
.system_one(
SystemOneRequest::new("I was charged twice. Please fix this ASAP.")
.question("is_billing", noul("Is this about billing?"))
.question(
"department",
choice(
"Which department should handle this?",
[
("billing", Some(json!("Payments"))),
("technical", Some(json!("Bugs"))),
("support", None::<serde_json::Value>),
],
),
)
.question(
"frustration",
score(
"How frustrated is the customer?",
[
Some(json!("Calm")),
Some(json!("Frustrated")),
Some(json!("Very angry")),
],
),
),
)
.await?;
// Yes/no answers
for (name, a) in response.nouls() {
println!("{name}: noul = {:.2}", a.noul);
}
// Choice answers
for (name, a) in response.choices() {
println!("{name}: choice = {}, confidence = {:.2}", a.choice, a.confidence);
}
// Score answers
for (name, a) in response.scores() {
println!("{name}: score = {:.2}, confidence = {:.2}", a.score, a.confidence);
}
Ok(())
}use typesafe_sdk::{noul, noul_with_criteria, NoulCriteria};
use serde_json::json;
// Simple yes/no
let q = noul("Is this about billing?");
// With criteria descriptions
let q = noul_with_criteria(
"Is this urgent?",
NoulCriteria {
r#true: Some(json!("Action required within 24 hours")),
r#false: Some(json!("No immediate action needed")),
},
);use typesafe_sdk::choice;
use serde_json::json;
let q = choice(
"Which department?",
[
("billing", Some(json!("Payments and invoicing"))),
("technical", Some(json!("Bugs and outages"))),
("support", None::<serde_json::Value>), // undescribed label
],
);use typesafe_sdk::score;
use serde_json::json;
// At least two rubric levels are required
let q = score(
"How frustrated is the customer?",
[
Some(json!("Calm")),
Some(json!("Frustrated")),
Some(json!("Very angry")),
],
);use std::time::Duration;
use typesafe_sdk::{ClientBuilder, RetryPolicy};
let client = ClientBuilder::new("sk-...")
.base_url("https://custom.api.example.com")
.default_model("jev-1.13.0")
.timeout(Duration::from_secs(30))
.retry(RetryPolicy {
max_retries: 3,
..Default::default()
})
.build()?;| Variable | Description | Default |
|---|---|---|
TYPESAFE_API_KEY |
API key (required) | — |
TYPESAFE_BASE_URL |
API root URL | https://api.typesafe.ai |
TYPESAFE_DEFAULT_MODEL |
Default model | jev-latest |
TYPESAFE_LOG_LEVEL |
Log verbosity (debug, info, warn, error, off) |
warn |
Configuration priority: explicit builder settings > environment variables > SDK defaults.
use typesafe_sdk::Error;
match client.system_one(req).await {
Ok(response) => { /* use response */ }
Err(Error::Api(err)) => {
eprintln!("API error {}: {}", err.status, err.message);
if err.is_rate_limit() {
eprintln!("Rate limited; retry after {:?}", err.retry_after());
}
}
Err(Error::Connection(err)) => {
eprintln!("Connection error: {}", err);
}
Err(Error::Timeout(err)) => {
eprintln!("Timeout after {:?}", err.timeout);
}
Err(Error::Response(err)) => {
eprintln!("Invalid response ({}): {}", err.status, err.raw_body);
}
Err(Error::Config(msg)) => {
eprintln!("Configuration error: {}", msg);
}
}| Method | Status |
|---|---|
is_bad_request() |
400 |
is_authentication() |
401 |
is_permission_denied() |
403 |
is_not_found() |
404 |
is_unprocessable_entity() |
422 |
is_rate_limit() |
429 |
is_server_error() |
500–599 |
use std::time::Duration;
use typesafe_sdk::RetryPolicy;
let policy = RetryPolicy {
max_retries: 3, // default: 2
backoff_initial: Duration::from_secs(1),// default: 500ms
backoff_max: Duration::from_secs(10), // default: 5s
backoff_jitter: 0.5, // default: 0.25
respect_retry_after: true, // default: true
max_retry_after: Duration::from_secs(30), // default: 60s
retry_connection_errors: true, // default: true
retry_timeout_errors: true, // default: true
..Default::default()
};The client retries 408, 429, and 5xx responses by default. Server retry delays
from retry-after-ms or Retry-After headers are honored up to
max_retry_after.
Unknown answer types returned by a future API are preserved as
Answer::Unknown(UnknownAnswer { kind, raw }), so your code doesn't break
when new answer types are added.
Set TYPESAFE_LOG_LEVEL=debug to see request and response headers and bodies.
Credential-bearing headers (Authorization, Cookie, etc.) are redacted.
use typesafe_sdk::ClientBuilder;
use typesafe_sdk::logging::LogLevel;
let client = ClientBuilder::new("sk-...")
.log_level(LogLevel::Debug)
.build()?;The SDK uses the tracing crate. If you
don't set up a subscriber, log output is silently discarded.
MIT. See LICENSE.