typesafe_rs/lib.rs
1//! Rust client for [TypeSafe](https://typesafe.ai)'s System One API.
2//!
3//! System One evaluates a `state` against a map of named questions (`noul`,
4//! `choice`, `score`) and returns one typed answer per question. This crate is
5//! the HTTP client: configuration, retries, errors, `POST /v1/systemone`, and
6//! `GET /v1/models`.
7//!
8//! This is a community SDK, not an official TypeSafe product. Env vars, defaults,
9//! retries, identification headers, and error kinds match the official Python
10//! and TypeScript SDKs.
11//!
12//! Set `TYPESAFE_API_KEY`. Optional: `TYPESAFE_BASE_URL`, `TYPESAFE_DEFAULT_MODEL`.
13//!
14//! # Quick start
15//!
16//! ```no_run
17//! use typesafe_rs::{questions, Client, Question};
18//!
19//! # async fn run() -> Result<(), typesafe_rs::Error> {
20//! let client = Client::from_env()?;
21//! let response = client
22//! .system_one(
23//! "Help! My payouts have been failing for 3 days.",
24//! questions! {
25//! "urgent" => Question::noul("Does this convey urgency?"),
26//! },
27//! )
28//! .await?;
29//! println!("{:?}", response.noul("urgent"));
30//! # Ok(())
31//! # }
32//! ```
33//!
34//! For tests, use [`typesafe-rs-mock`](https://docs.rs/typesafe-rs-mock) instead
35//! of the live API. See the `quickstart` example.
36//!
37//! # Crate features
38//!
39//! | Feature | Default | Enables |
40//! |---|---|---|
41//! | `rustls` | yes | TLS via rustls (platform verifier) |
42//! | `native-tls` | no | Platform TLS instead of, or in addition to, rustls |
43//! | `tracing` | yes | `typesafe.request` spans and `retry_scheduled` events |
44//! | `blocking` | no | [`BlockingClient`] |
45//!
46//! Enable `rustls` (the default) or `native-tls`. HTTPS will not compile with
47//! neither.
48
49#![cfg_attr(docsrs, feature(doc_cfg))]
50#![forbid(unsafe_code)]
51#![deny(missing_docs)]
52#![warn(missing_debug_implementations)]
53
54#[cfg(not(any(feature = "rustls", feature = "native-tls")))]
55compile_error!("Enable the `rustls` feature (default) or `native-tls`.");
56
57mod backend;
58#[cfg(feature = "blocking")]
59mod blocking;
60mod client;
61mod config;
62mod error;
63mod headers;
64mod retry;
65mod transport;
66pub mod types;
67
68pub use backend::Backend;
69#[cfg(feature = "blocking")]
70#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))]
71pub use blocking::{BlockingClient, BlockingModels};
72pub use client::{Client, Models};
73pub use config::{
74 CallOptions, ClientConfig, DEFAULT_BASE_URL, DEFAULT_MODEL, DEFAULT_TIMEOUT, ENV_API_KEY,
75 ENV_BASE_URL, ENV_DEFAULT_MODEL, SecretString,
76};
77pub use error::{ApiError, ApiErrorKind, Error, ErrorBody, TransportError};
78pub use http::{HeaderMap, HeaderValue, StatusCode};
79pub use indexmap::IndexMap;
80pub use retry::{RetryPolicy, StatusSet};
81pub use types::{
82 Answer, ChoiceView, Entry, ListModelsResponse, MAX_CHOICE_OPTIONS, ModelCard, NoulCriteria,
83 NoulView, Question, Questions, ResponseMeta, ScoreView, SystemOneRequest, SystemOneResponse,
84 Usage,
85};
86pub use url::Url;
87
88/// Crate version, used in `User-Agent` and `X-TypeSafe-SDK`.
89pub const VERSION: &str = env!("CARGO_PKG_VERSION");
90
91/// Result alias for this crate's [`Error`].
92pub type Result<T, E = Error> = std::result::Result<T, E>;
93
94/// Build an ordered map of named questions.
95///
96/// # Examples
97///
98/// ```
99/// use typesafe_rs::{questions, Question};
100///
101/// let qs = questions! {
102/// "urgent" => Question::noul("Does this convey urgency?"),
103/// "team" => Question::choice("Which team?")
104/// .option("billing", "Payments")
105/// .option("technical", "Bugs"),
106/// };
107/// assert_eq!(qs.len(), 2);
108/// ```
109#[macro_export]
110macro_rules! questions {
111 ( $($key:expr => $value:expr),* $(,)? ) => {{
112 let mut map = $crate::IndexMap::new();
113 $(
114 map.insert(
115 ::std::string::ToString::to_string(&$key),
116 ::core::convert::Into::<$crate::Question>::into($value),
117 );
118 )*
119 map
120 }};
121}