TypeSafe AI Go SDK

September 17, 2026 · View on GitHub

Go SDK for TypeSafe AI.

Quickstart

Install the SDK (Go 1.23 or newer):

go get serge.ax/go/typesafe-sdk-go

Set TYPESAFE_API_KEY in your environment, then create and use the client:

package main

import (
	"context"
	"fmt"
	"log"

	typesafe "serge.ax/go/typesafe-sdk-go"
)

func main() {
	client, err := typesafe.New()
	if err != nil {
		log.Fatal(err)
	}

	result, err := client.SystemOne(context.Background(),
		"I was charged twice. Please fix this ASAP.",
		typesafe.Questions{
			"category": typesafe.Choice{
				Instructions: "What is this ticket about?",
				Criteria: map[string]typesafe.Content{
					"billing": nil, "technical": nil, "other": nil,
				},
			},
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Choices()["category"].Choice)
}

The import path and the package name differ, so the typesafe alias above is worth keeping for readability.

Questions

Mix question types in one request; each answer comes back under the name you gave its question.

result, err := client.SystemOne(ctx,
	map[string]any{"subject": "Duplicate charge", "message": "I was charged twice."},
	typesafe.Questions{
		"billing": typesafe.Noul{Instructions: "Is this about billing?"},
		"tone": typesafe.Choice{
			Instructions: "What is the tone of this message?",
			Criteria:     map[string]typesafe.Content{"angry": "Upset or hostile", "calm": "Neutral or polite"},
		},
		"urgency": typesafe.Score{
			Instructions: "How urgent is this message?",
			Criteria:     []typesafe.Content{"Can wait", "Needs attention this week", "Needs attention today"},
		},
	},
)

result.Nouls()["billing"].Noul       // probability of yes, 0 to 1
result.Choices()["tone"].Choice      // "angry" or "calm"
result.Scores()["urgency"].Score     // expected score, may fall between levels
QuestionAnswerFields
Noul — yes/no*NoulAnswerNoul
Choice — pick a label*ChoiceAnswerChoice, Confidence, Probabilities
Score — rate against a rubric*ScoreAnswerScore, Confidence, Legend, Probabilities

Instructions and every criterion take a string, a map[string]any, a []any, or nil. Answers of a type this SDK version does not model are dropped with a warning, so a newer API never breaks an older client; reach the raw payload through result.HTTPResponse.

Configuration

Options take precedence over environment variables, which take precedence over the SDK defaults. Empty or whitespace-only environment values are ignored.

OptionEnvironmentDefault
WithAPIKeyTYPESAFE_API_KEYrequired
WithBaseURLTYPESAFE_BASE_URLhttps://api.typesafe.ai
WithDefaultModelTYPESAFE_DEFAULT_MODELjev-latest
WithLoggerTYPESAFE_LOG_LEVELwarn, to standard error
WithTimeout10s per attempt
WithRetrysee below
WithHeader, WithHTTPClient

Per-call overrides: WithModel, WithRequestTimeout, WithRequestRetry, WithRequestHeader, WithExtraBody.

A *Client is safe for concurrent use.

Retries

By default a failed request is retried twice, with exponential backoff from 500ms to 5s minus up to 25% jitter, for HTTP 408, 429 and 5xx responses as well as connection errors and timeouts. Retry-After and retry-after-ms are honored up to a minute.

retry := typesafe.DefaultRetryPolicy()
retry.MaxRetries = 5
retry.RetryStatus = func(status int) bool { return status == 429 || status >= 500 }

client, err := typesafe.New(typesafe.WithRetry(retry))

Pass a zero RetryPolicy to disable retries. There is no separate retry budget: the timeout applies per attempt, and a context deadline bounds the call as a whole, backoff included.

Errors

result, err := client.SystemOne(ctx, state, questions)
switch {
case errors.Is(err, typesafe.ErrRateLimit):
	var apiErr *typesafe.APIError
	errors.As(err, &apiErr)
	delay, ok := apiErr.RetryAfter()
case errors.Is(err, typesafe.ErrTimeout):
	// the attempt exceeded its timeout
case errors.Is(err, context.Canceled):
	// the caller gave up
}
  • *Error — bad configuration or invalid questions, raised before anything is sent.
  • *APIError — an unsuccessful response, carrying Status, Header, Body, RequestID, and Endpoint. Matches ErrBadRequest, ErrAuthentication, ErrPermissionDenied, ErrNotFound, ErrUnprocessableEntity, ErrRateLimit, or ErrInternalServer.
  • *ResponseValidationError — a successful response whose body was missing required data, naming the offending Field.
  • *ConnectionError and *TimeoutError — the request never produced a usable response. A timeout matches both ErrTimeout and ErrConnection.
  • A canceled or expired context matches context.Canceled or context.DeadlineExceeded.

Logging

The SDK logs through log/slog: request summaries at info, headers and bodies at debug. Credential headers are redacted; bodies are not. Set TYPESAFE_LOG_LEVEL (debug, info, warn, error, off), or pass your own logger with WithLogger.

Documentation

Learn what TypeSafe can do in the TypeSafe docs. The API reference for this package is on pkg.go.dev.

Sibling SDKs: JavaScript, Python.

License

MIT