typesafe-go

September 17, 2026 ยท View on GitHub

an unofficial go SDK for the typesafe AI API, inspired by the official typescript and python clients. independently maintained, with no affiliation with typesafe.

requires go 1.22 or newer. no third-party dependencies.

quickstart

create an api key in the typesafe console, then set TYPESAFE_API_KEY.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	typesafe "github.com/cole-gillespie/typesafe-go"
)

func main() {
	client, err := typesafe.NewClient(typesafe.Config{})
	if err != nil {
		log.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	result, err := client.SystemOne(ctx, typesafe.SystemOneRequest{
		State: "the dashboard is down and our demo starts in ten minutes",
		Questions: typesafe.Questions{
			"category": typesafe.Choice("which team should handle this?", map[string]any{
				"billing": "payments or refunds",
				"technical": "bugs, outages, or integrations",
				"other": "none of these categories fits",
			}),
			"urgency": typesafe.Score("how soon is attention needed?", "can wait", "today", "immediately"),
			"is_urgent": typesafe.Noul("does this request express urgency?"),
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	answer, err := result.Answers.Choice("category")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(answer.Choice, answer.Confidence)
}

the module path is github.com/cole-gillespie/typesafe-go. after the repository is published, install it with:

go get github.com/cole-gillespie/typesafe-go

run the included example from this repository:

read -rs 'TYPESAFE_API_KEY?typesafe api key: '
echo
export TYPESAFE_API_KEY
go run ./examples/triage
go run ./examples/triage 'could you explain the pricing for a team of twenty?'

the hidden zsh prompt keeps the key out of shell history. the SDK does not load .env files. each example run sends its input to typesafe.

questions and answers

questionconstructoranswer accessorresult
choiceChoice(instructions, map[string]any{...})result.Answers.Choice(name)label, probabilities, confidence
scoreScore(instructions, levels...)result.Answers.Score(name)fractional score, legend, probabilities, confidence
noulNoul(instructions)result.Answers.Noul(name)probability of yes

each accessor returns a typed value and an error. missing fields or a mismatched type produce errors rather than silently returning zero. SystemOne checks every requested answer. the original answer JSON remains available at result.Answers[name].

state, instructions, and descriptions accept text, structured objects, arrays, or nil. use maps, structs, or slices for structured input. nil leaves a choice label undescribed. score questions require at least two levels, indexed from zero. the server validates other semantic constraints. to describe yes/no outcomes:

question := typesafe.Noul("is this time sensitive?")
question.Criteria = &typesafe.NoulCriteria{
	True: "an explicit deadline or immediate impact",
	False: "no time constraint is expressed",
}

confidence summarizes the distribution over options or levels; it is not a guarantee of correctness. noul answers have no separate confidence field. see the confidence guide.

configuration and retries

Config.APIKey, BaseURL, and DefaultModel resolve from nonempty config values, then TYPESAFE_API_KEY, TYPESAFE_BASE_URL, and TYPESAFE_DEFAULT_MODEL. defaults are https://api.typesafe.ai and jev-latest. a base URL may contain a proxy path prefix.

policy := typesafe.DefaultRetryPolicy()
policy.MaxRetries = 0 // disable automatic retries

client, err := typesafe.NewClient(typesafe.Config{
	Timeout: 15 * time.Second,
	Retry: &policy,
	HTTPClient: &http.Client{Transport: customTransport},
})

timeouts cover each attempt, including body delivery, and default to ten seconds. use a context deadline to bound the entire operation, including retry waits. a custom HTTP client's timeout also applies. redirects are not followed unless that client supplies an explicit redirect policy.

per-call options leave client defaults unchanged:

result, err := client.SystemOne(ctx, request,
	typesafe.WithTimeout(5*time.Second),
	typesafe.WithRetry(policy),
	typesafe.WithHeaders(http.Header{"X-Correlation-Id": {"my-request"}}),
)

a supplied retry policy replaces all defaults. start from DefaultRetryPolicy() to change one field. defaults:

  • two retries after the first attempt for connection failures, attempt timeouts, HTTP 408, 429, and 5xx.
  • exponential backoff from 500 ms to five seconds, with up to 25% jitter subtracted.
  • retry-after-ms and Retry-After honored up to one minute; longer delays fall back to backoff.
  • no retries for caller cancellation or malformed JSON.

retries can resubmit an evaluation if the server processed it but its response was lost. this SDK provides no idempotency guarantee. disable retries when duplicate evaluations are undesirable.

clients are reusable across goroutines. configuration headers and retry status lists are copied. supplied transports must support concurrent use; request maps and slices must not change during a call.

models and errors

models, err := client.ListModels(ctx)
if err != nil {
	return err
}
for _, model := range models.Models {
	fmt.Println(model.Name, model.Description, model.ReleaseDate)
}

HTTP failures return *typesafe.APIError, retaining StatusCode, Body, Header, and RequestID. use errors.As:

var apiErr *typesafe.APIError
if errors.As(err, &apiErr) {
	fmt.Println(apiErr.StatusCode, apiErr.RequestID)
}

cancellation and deadlines remain detectable with errors.Is; transport errors preserve their wrapped cause. successful results include Metadata with the final status, headers, and request id. evaluations also include Usage.InputTokens and Usage.OutputTokens.

development

go test -race -cover ./...
go vet ./...
go build ./...

tests use local HTTP servers and synthetic responses. no api key is needed. CI is configured to check formatting, vet, build, and race tests on go 1.22 and the current stable version. live service compatibility still needs verification with an api key.

the supported endpoints are POST /v1/systemone and GET /v1/models. this is not a feature-for-feature port: logging and arbitrary extra request-body fields are not exposed.

license and references

MIT, see LICENSE. upstream acknowledgments are in THIRD_PARTY_NOTICES.md.