Skip to content

Add the Haskell SDK for the TypeSafe API - #1

Merged
mageshb merged 9 commits into
mainfrom
initial-sdk
Sep 22, 2026
Merged

mageshb merged 9 commits into
mainfrom
initial-sdk

Conversation

@mageshb

@mageshb mageshb commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Adds a Haskell SDK for TypeSafe AI's System One API (https://docs.typesafe.ai), as two Hackage packages plus runnable examples.

Packages

  • typesafe-ai-core: no HTTP dependency (only aeson, http-types and boot packages), so it works with servant or any other HTTP stack.
    • TypeSafe.Wire: every schema of the OpenAPI specification (0.2.0) with JSON codecs. Unknown question and answer types are preserved.
    • TypeSafe.Question: typed Noul, Choice and Score questions. Choice options and Score levels are user types (derivable for enumerations), so an answer outside them never reaches user code. Questions combine with Applicative.
    • TypeSafe.Call: API calls as values that a transport renders and decodes.
    • TypeSafe.Error and TypeSafe.Retry: classified errors and the retry policy of the official SDKs, as pure functions.
  • typesafe-ai: an http-client transport. It has a shared TLS connection pool, per-attempt timeouts, and retries with backoff that honour Retry-After. It reads the same environment variables as the official SDKs, validates and redacts the API key, and has a logging hook. It re-exports the core modules and includes TypeSafe.Tutorial.
  • examples/: quick start, fan-out with composite scoring, re-ranking, confidence routing, concurrency, models, and forward compatibility. Not published.

http-client was chosen over req and wreq; the README explains why.

Keeping up with the API

  • The OpenAPI specification is vendored in typesafe-ai-core/spec/openapi.json. Conformance tests check every operation, schema, property, required field and discriminator against it. They validate generated values against the schemas and decode the specification's own examples.
  • spec-drift.yml runs daily. When the published specification changes, it opens a PR with a summary of the changes and the failing conformance tests.
  • release.yml uploads Hackage candidates on a version tag, and publishes on a manual run.
  • MAINTAINING.md describes the update and release process.

Testing

  • 151 core tests and 18 client tests, the client ones against a local mock server. All pass on GHC 9.6.7, 9.12.2 and 9.14.1 with -Werror.
  • doctest runs 82 documentation examples, and all pass.
  • cabal check is clean for both packages, and both build and pass their tests from their source distributions.
  • One live request with a deliberately invalid key. It returned a correctly classified 401 with its request id, and the key was not leaked.
  • CI passes on GHC 9.6 to 9.14 on Linux, and on GHC 9.12 on macOS and Windows, including the doctest and Hackage-readiness jobs.
  • Against the live API with a real key (TYPESAFE_LIVE_TESTS=1 cabal test typesafe-ai), all 21 tests pass, including listing models, a typed question and bad-key rejection. typesafe-quickstart returns typed answers from jev-1.13.0.

Setup after merging

  • Add a HACKAGE_TOKEN secret, in a hackage environment, for releases.
  • Optionally add a SPEC_SYNC_TOKEN secret, so that CI runs on the automated specification PRs.

A transport-agnostic package with no HTTP dependency:

- TypeSafe.Wire mirrors every schema of the TypeSafe OpenAPI
  specification (0.2.0) with hand-written, order-preserving JSON codecs.
  Unknown question and answer types are preserved.
- TypeSafe.Question provides typed Noul, Choice and Score questions whose
  options and levels are user types (derivable for enumerations), combined
  with the Applicative instance of Questions.
- TypeSafe.Call describes API calls as values that any transport can
  render and decode.
- TypeSafe.Error classifies error responses and parses their bodies;
  TypeSafe.Retry implements the retry policy of the official SDKs as pure
  functions.

The vendored specification drives conformance tests that check every
operation, schema, property, required field and discriminator, validate
generated values against the schemas and decode the specification's own
examples.
A client with a shared TLS connection pool, per-attempt timeouts, retries
with exponential backoff and jitter that honour Retry-After and
retry-after-ms, protected headers, API key validation and redaction, and a
logging hook. Configuration can come from TYPESAFE_API_KEY,
TYPESAFE_BASE_URL and TYPESAFE_DEFAULT_MODEL.

The package re-exports the modules of typesafe-ai-core and includes a
tutorial module. Tests run the transport against a local mock of the API.
Quick start, speculative fan-out with composite scoring and re-ranking, confidence-gated routing, concurrent requests, model listing and pinning, and the forward-compatibility escape hatches. CI builds them; they are not published.
- README describes the packages, the design and the choice of
  http-client, and how the bindings keep up with the API.
- MAINTAINING.md covers updating the bindings, versioning and releasing
  to Hackage.
- scripts/sync-spec.sh compares the published OpenAPI specification with
  the vendored copy and summarises structural changes.
- CI builds and tests on GHC 9.6 to 9.14, runs doctest, and builds and
  tests the source distributions. A daily workflow opens a pull request
  when the specification changes, and a release workflow uploads to
  Hackage.
With --with-compiler=doctest, cabal also uses doctest to build any dependency that is not built yet, and doctest cannot compile them. CI starts from an empty store, so build the dependencies first.
With cabal 3.16, --with-compiler makes cabal reconfigure the local typesafe-ai-core library and rebuild it with doctest, which cannot compile. --with-repl only replaces GHCi, so GHC keeps building everything, and building the dependencies beforehand is no longer needed.
@mageshb
mageshb merged commit 238bda9 into main Sep 22, 2026
9 checks passed
@mageshb
mageshb deleted the initial-sdk branch September 22, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant