Skip to content
MrJev

TypeSafe Swift SDK

SwiftPM client whose behaviour follows the official JavaScript SDK.

View on GitHub →

Hands-on review

A small, well-shaped Swift client for Jev. It has no CI, and it will not compile on the server platform its own README recommends.

Good for

  • Strongly typed questions — noul, choice, score — decoded into your own struct
  • All of a decision's questions in one request, which is how Jev is meant to be used
  • A retry policy you can configure, with the auth header locked against override

Watch out for

  • No `FoundationNetworking` import: 191 errors on Linux, and there is no CI to catch it
  • A timeout retries twice by default with no idempotency key — three billed calls, no answer
  • Every decoded field is non-optional, so a new choice label breaks clients after they've paid

Tested Sep 20, 2026 at 4195213 · swift:6.1-noble in Docker with --network none; built and tested offline

How we reviewed this: we built and tested it ourselves in the swift:6.1-noble image with --network none, and read the request path line by line. We made no Jev calls.

What it does

A small Swift client for Jev. You declare your questions as typed values — noul, choice, score — pass any Encodable as the state, and get back a struct you defined. One POST /v1/systemone carries all of a decision’s questions, which is the shape Jev is designed around and which several clients in this directory get wrong.

The design is tidy. HTTPTransport is a protocol, so tests substitute a stub without touching the network; the retry policy is a value you can configure; and the auth header is deliberately not overridable, with a test asserting it.

It does not build on Linux

Sources/TypeSafe/HTTP.swift, APISession.swift and TypeSafeClient.swift all use URLSession, URLRequest and HTTPURLResponse, and all three open with:

import Foundation

On Apple platforms that is enough. On Linux those types live in a separate module, and the repository contains no FoundationNetworking import anywhere — we grepped: zero occurrences. We built the published 4195213 in swift:6.1-noble, offline:

error: 'HTTPURLResponse' is unavailable: This type has moved to the FoundationNetworking module. Import that module to use it.
error: value of type 'HTTPURLResponse' (aka 'AnyObject') has no member 'statusCode'
--- error count ---
191

We then added the standard three-line guard to those same three files and built again:

Build complete! (5.90s)

Nine lines. What makes this worth writing down is not the bug, which every Swift package hits once, but that the README recommends calling the API “from a server you control” — and Linux is where that server almost certainly runs. There is no .github/ directory and no CI, so nothing has ever compiled this outside macOS.

A timeout costs three inferences and returns nothing

RetryPolicy.default is maxRetries: 2 with retryTimeouts: true, and the client’s default timeout is .seconds(10). Sources/TypeSafe/APISession.swift:74:

} catch let error as TimeoutError {
    logger.info("\(tag) timed out after \(formatMilliseconds(started.duration(to: .now)))")
    if retriesLeft > 0, policy.retryTimeouts {

Nothing identifies the retry to the server as the same request. Searching the sources for an idempotency key or nonce returns two hits, and both are reading x-typesafe-request-id off a response — nothing is ever sent.

So a decision that takes longer than ten seconds is sent three times. The server answers all three and bills all three; the caller gets a TimeoutError and no answer. Ten seconds is not a generous budget for a model that evaluates a dozen questions over a long state, which makes this the default rather than the edge case. Retrying a connection failure is safe; retrying a timeout is not, because the first request may well have completed.

Every field is required

The decoder has no optionals anywhere. Sources/TypeSafe/Result.swift:11:

public struct SystemOneResult<Answers: Decodable & Sendable>: Decodable, Sendable {
    public var model: String
    public var answers: Answers
    public var usage: Usage
}

Usage.inputTokens and outputTokens are plain Int; ModelCard.releaseDate is a plain String; a choice answer’s probabilities and a score answer’s legend are required dictionaries. Swift’s synthesised Decodable throws on any one of them being absent or a different type.

Unknown keys at the top level are tolerated, which is the right call and clearly deliberate. But the day the service adds a value to an existing enum, every deployed copy of this SDK throws — after the request has been made and paid for. There is also an undocumented asymmetry in the score question: the request encodes criteria as an array of level descriptions (Questions.swift:180), and the answer requires a legend object back (Questions.swift:157).

Smaller things

Sources/TypeSafe/HTTP.swift:43 hard-codes what it reports as your toolchain:

private let swiftVersion = "6.0"

We compiled with 6.1.3; the header still says swift/6.0. It is only telemetry, but it is telemetry that is wrong by construction.

Headers you set in Configuration(defaultHeaders:) are merged before the SDK’s own, and later wins (APISession.swift:120), so User-Agent and Accept are silently discarded along with Authorization. Locking the auth header is intentional; locking the other two is probably not, and neither is documented. And debug logging prints the full request body (APISession.swift:131) while redaction (Logging.swift:36) covers headers only — turn it on and your state goes to the log.

Verdict

MIT, 20 commits, one contributor, no CI. The repository was created and pushed within about five minutes, which fits: this is a well-shaped first draft that has never been run anywhere but its author’s machine.

Once the conditional imports land it is a reasonable Swift client, and the typed-question API is nicer to use than hand-rolling the JSON. Until then it is Apple-platforms-only in practice, whatever the README says. We’ve reported the build failure, the timeout retry and the strict decoding.

For the same job in other languages, see jev-repl and the .NET SDK.

See how it compares with other tools in Best Jev tools, tested hands-on.

Review updated Sep 20, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.

More in Community SDKs

laya-php

★ 110

marcreichel/laya-php

Laravel-ready PHP client for Laya, the open System One model, so a typed decision is a PHP enum rather than a parsed sentence. Self-hosted, no Jev key.

PHPReviewed

spring-ai-community/spring-ai-typesafe

Java client for the System One API on Spring RestClient, plus Spring AI integrations that use it as a judge, a guardrail, a RAG post-processor and a tool index.

JavaReviewed

obie/ruby_decision_model

Ruby gem with one client for decision models behind several providers: TypeSafe's Jev, OpenRouter (the default), OpenAI, Cloudflare Clef, Perplexity and any /v1/systemone server.

Ruby

Get new Jev projects every week

New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.

Powered by Buttondown. See our privacy policy.