A C++20 SDK for TypeSafe AI.
It configures clients through a Builder and types every question (Choice, Score, Noul) so answers arrive as fields, not strings to parse. It targets System One models such as Jev.
#include <typesafe/typesafe.h>
#include <iostream>
using namespace typesafe;
int main() {
// 1. Automatically picks up TYPESAFE_API_KEY from the environment
auto client = TypeSafeClient::builder().build();
// 2. Prepare the Request
SystemOneRequest req;
req.state = nlohmann::json{{"document", "I was charged twice. Please fix this ASAP."}};
req.add("category", Choice{
"What is this ticket about?",
{{"billing", std::nullopt}, {"technical", std::nullopt}, {"other", std::nullopt}}
});
req.add("urgency", Score{
"How urgent is this ticket?",
{"can wait", "this week", "today"}
});
// 3. Make the API Call
try {
SystemOneResponse response = client.systemOne(req);
std::cout << "Category: " << response.choices["category"].choice << "\n";
std::cout << "Urgency Score: " << response.scores["urgency"].score << "\n";
} catch (const TypeSafeError& e) {
std::cerr << "API Error: " << e.what() << "\n";
}
return 0;
}The SDK uses standard CMake. It depends on nlohmann/json.
add_subdirectory(typesafe-sdk-cpp)
target_link_libraries(your_target PUBLIC typesafe_cpp)Added as a subdirectory, the SDK builds only the library: its tests, examples and include(CTest) run only when it is the top-level project. If your project already defines the nlohmann_json::nlohmann_json target (for example through FetchContent), the SDK uses it instead of calling find_package.
By default, the SDK compiles with a built-in libcurl transport so it works out of the box.
To embed the SDK in an application that owns its HTTP stack, turn libcurl off in your CMake:
set(TYPESAFE_USE_LIBCURL OFF)
add_subdirectory(typesafe-sdk-cpp)Then, implement the typesafe::Transport interface and pass it to the builder:
auto client = TypeSafeClient::builder()
.transport(std::make_unique<MyCustomTransport>())
.build();If your application is built on Boost.Asio and does not use libcurl, compile with Boost.Beast and OpenSSL:
set(TYPESAFE_USE_LIBCURL OFF)
set(TYPESAFE_USE_BOOST_ASIO ON)
add_subdirectory(typesafe-sdk-cpp)#include <typesafe/boost_transport.h>
auto client = TypeSafeClient::builder()
.transport(std::make_unique<BoostTransport>())
.build();examples/ holds eleven small, self-contained programs, one idea each:
| Example | Shows |
|---|---|
01_classify_message |
The smallest call: one yes/no question, answer read from noul |
02_triage_ticket |
Every question type in one request (Noul; Choice with option descriptions; Score against an ordered rubric), plus probabilities, legend and usage |
03_custom_model |
systemOneAs<T> parsing the response straight into your own struct via a from_json adapter |
04_parallel_classification |
Concurrent requests with systemOneAsync |
05_error_handling |
The typed error ladder; runs offline |
06_custom_transport |
Injecting your own Transport; runs offline against a canned response |
07_list_models |
Querying model names and aliases with listModels |
08_openrouter |
Evaluating Jev 1.13 live through OpenRouter's System One endpoint |
09_openrouter_sync |
Sequential evaluation of 5 messages with per-request latency |
10_openrouter_async |
The same 5 messages concurrently, comparing the timings |
11_openrouter_suite |
A six-part live suite exercising every primitive end to end |
cmake -S . -B build && cmake --build build
./build/examples/05_error_handling # runs offline
./build/examples/06_custom_transport # runs offline
OPENROUTER_API_KEY="sk-..." ./build/examples/08_openrouter # runs live against OpenRouter JevOpenRouter also serves Jev, at https://openrouter.ai/api/v1/systemone. One builder call sets the key, that base URL, and the model typesafe/jev-1.13:
auto client = typesafe::TypeSafeClient::builder()
.openrouter("sk-or-v1-...")
.build();Call .model(...) after .openrouter(...) to choose another model; OpenRouter maps bare IDs such as jev-latest onto typesafe/. OpenRouter-only fields such as session_id, provider or trace go in extra_body. listModels() throws ValidationError on such a client, because OpenRouter's /v1/models is a different endpoint.
OpenRouter's schema is stricter than TypeSafe's in two places the SDK does not check: every question needs instructions, and a Noul's criteria, when given, needs both true and false. Its error bodies ({"error": {"message": ...}}) are read into the exception message like TypeSafe's.
listModels() (and listModelsAsync()) call GET /v1/models and return every name your account may send in the model field:
for (const typesafe::ModelMetadata &model : client.listModels().models)
std::cout << model.name << " " << model.release_date << "\n";The endpoint returns aliases and versioned model identifiers:
- Aliases (
jev-latest,jev-preview): Point to the most recent release and update over time. - Versioned IDs (
jev-1.13.0): Fixed versions suitable for production pinning via.model("jev-1.13.0").
The model field in SystemOneResponse always reports the exact versioned model that evaluated the request.
Every failure is a TypeSafeError; the specific class says what went wrong and what to do about it. Messages carry the service's explanation: the field paths from a 422 detail list, or OpenRouter's error.message.
| Exception | Meaning |
|---|---|
ValidationError |
The request broke the contract: refused locally, or by the service as HTTP 422. Fix the request; do not retry. |
AuthenticationError |
The key was rejected (401/403). Fix the key, not the call. |
RateLimitError |
429, already retried with backoff (honouring Retry-After / retry-after-ms within the 30 s call budget) and still limited. Back off longer. |
APIError |
The service refused or failed the request otherwise (5xx are retried, 529 included). |
APIConnectionError |
Could not reach the service at all; retried, still unreachable. |
<typesafe/testing.h> ships the recording transport the SDK's own suite uses. Inject it to see exactly what your code would send and to script what comes back, with no network. It is header-only and does not depend on any test framework:
#include <typesafe/testing.h>
auto transport = std::make_unique<typesafe::testing::RecordingTransport>();
auto *recording = transport.get();
recording->responses.push_back(typesafe::testing::ValidResponse(
{{"spam", {{"type", "noul"}, {"noul", 0.97}}}}));
auto client = typesafe::TypeSafeClient::builder()
.api_key("test-key")
.transport(std::move(transport))
.build();
// ... run the code under test with `client`, then inspect the request:
const nlohmann::json sent = nlohmann::json::parse(recording->requests.at(0).body);An empty response queue makes the next request throw, which the client reports as APIConnectionError; use .max_retries(0) to see it on the first attempt. The transport is not synchronized, so use one per test.
The suite is GoogleTest, fetched and built by CMake when tests are on (the default):
cmake -S . -B build -DTYPESAFE_USE_LIBCURL=OFF # the tests inject their own transport
cmake --build build
ctest --test-dir build --output-on-failureConfiguring with libcurl on also builds the CurlTransport tests, which check connection reuse against a loopback server.
It covers the wire contract (default model, structured instructions, per-request headers, extra body fields), the error mapping and retry policy (which statuses retry, which throw which type, Retry-After handling), strict response validation, the models endpoint, and the async future's independence from the client that made it.
Detailed documentation and architecture guides are available in the docs/ directory:
- Documentation Index: Overview of all guides and technical documentation.
- API Reference: Detailed specification for
TypeSafeClientBuilder, question types (Choice,Score,Noul), response containers, error classes, and asynchronous execution. - Pluggable Transports: HTTP networking layer,
libcurldefault,Boost.Beastbackend, and creating custom transports. - Examples: Self-contained reference applications illustrating each core feature.
- Header Documentation: All public C++ headers in
include/typesafe/are fully documented with Doxygen comments.
For cloud platform documentation and API reference, visit the official TypeSafe Documentation.
Copyright (c) 2026 Bontal LLC. Licensed under the MIT License.