Unofficial community SDK. This project is not affiliated with, endorsed by, or sponsored by TypeSafe AI. "TypeSafe" and all related names, marks, and logos are trademarks of TypeSafe AI - used here solely to identify the API this client targets. Use at your own risk; the API may change without notice.

Java client for TypeSafe AI's System One API

(POST /v1/systemone).

- Java 21+, Maven build

- java.net.http.HttpClienttransport - no extra HTTP dependency

- Jackson for JSON (only runtime dependency)

- Sync + async (CompletableFuture) APIs

- Automatic retries with exponential backoff + jitter on 429/5xx, honoring

Retry-After

- Sealed Question/Answerhierarchies; unknown answer types degrade to a forward-compatibleUnknownAnswer

import dev.dosa.typesafe.TypeSafeClient;

import dev.dosa.typesafe.model.*;

TypeSafeClient client = TypeSafeClient.builder()

.apiKey(System.getenv("TYPESAFE_API_KEY"))

.defaultModel("jev-latest") // used when a request sets no model

.requestTimeout(Duration.ofSeconds(10))

.build();

SystemOneRequest request = SystemOneRequest.builder()

.state(Map.of("ticket", "I want a refund now!"))

.question("urgent", Question.noul("Does this convey urgency?")

.whenTrue("Explicitly time-sensitive")

.whenFalse("No time pressure"))

.question("dept", Question.choice("Which team?")

.option("billing", "Payment issues")

.option("technical", "Bugs"))

.question("frustration", Question.score("How frustrated?")

.level("Calm")

.level("Frustrated")

.level("Very angry"))

.build();

SystemOneResponse resp = client.systemOne(request); // sync

// CompletableFuture<SystemOneResponse> f = client.systemOneAsync(request); // async

resp.noul("urgent").ifPresent(p -> System.out.println("urgency: " + p));

resp.choice("dept").ifPresent(c -> System.out.println("team: " + c.choice()));

resp.score("frustration").ifPresent(s -> System.out.println("score: " + s.score()));One static factory + fluent builder per question type:

Question.noul("Does this convey urgency?")

.whenTrue("Explicitly time-sensitive")

.whenFalse("No time pressure");

Question.choice("Which team?")

.option("billing", "Payment issues")

.option("technical", "Bugs");

Question.score("How frustrated?")

.level("Calm")

.level("Frustrated")

.level("Very angry");Validation runs client-side in SystemOneRequest.builder().build(), before any

network call, and throws InvalidRequestException: empty questions map, blank

question names, choice questions with fewer than 2 or more than 255 options,

score questions with fewer than 2 levels, blank instructions, and a state

that is not a JSON string, object, or array.

A model is required by the API: set it per-request via .model("jev-latest")

or once on the client via .defaultModel(...). If neither is set, the client

throws InvalidRequestException before sending. Discover valid names with:

List<ModelInfo> models = client.models(); // GET /v1/models

models.forEach(m -> System.out.println(m.name() + " - " + m.description()));Optional<Double> n = resp.noul("urgent"); // NoulAnswer probability

Optional<ChoiceAnswer> c = resp.choice("dept");

Optional<ScoreAnswer> s = resp.score("frustration");

Optional<Answer> a = resp.answer("anything"); // untyped lookup

Stream<Entry<String, NoulAnswer>> allNouls = resp.nouls();

Stream<Entry<String, ChoiceAnswer>> allChoices = resp.choices();

Stream<Entry<String, ScoreAnswer>> allScores = resp.scores();Response metadata (kept separate from answer data):

resp.requestId(); // Optional<String> - x-typesafe-request-id header

resp.httpStatus(); // final HTTP status after retries

resp.attempts(); // number of HTTP attempts made

resp.usage(); // Optional<Usage> - input/output token countsAll exceptions extend the unchecked dev.dosa.typesafe.exception.TypeSafeException:

The API key is only ever sent in the Authorization header and never appears

in exception messages.

TypeSafeClient.builder()

.apiKey(...) // or TYPESAFE_API_KEY env var

.baseUrl(...) // or TYPESAFE_BASE_URL env var, then https://api.typesafe.ai

.defaultModel(...) // fallback model; one is required somewhere

.requestTimeout(Duration) // per attempt, default 30s

.connectTimeout(Duration) // default 10s

.maxAttempts(3) // retries incl. initial attempt

.initialRetryDelay(Duration) // backoff base, default 200ms

.maxRetryDelay(Duration) // backoff cap, default 10s

.httpClient(HttpClient) // custom transport

.build();mvn test # compiles and runs the test suite (JDK-local stub HTTP server)

mvn package # builds the jarWith TYPESAFE_API_KEY set in the environment, mvn test also runs the live

integration tests in TypeSafeClientLiveTest against the real API (they are

skipped otherwise).