typesafe

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 21, 2026 License: MIT Imports: 13 Imported by: 0

README

typesafe-sdk-go

ci Go Reference Docs

An unofficial Go SDK for TypeSafe. Full docs: typesafe-sdk-go.mintlify.site.

typesafe-sdk-go gives Go applications access to the same SystemOne question-answering workflow that exists in the official Python and JavaScript SDKs: typed choice, score, and noul questions, a typed answer union, and model discovery.

Why This Exists

TypeSafe ships official SDKs for Python and JavaScript, but there is no first-class Go SDK today. This project fills that gap with a Go-native client, built to the same wire contract as the other two.

This project is unofficial and is not affiliated with or endorsed by TypeSafe. If an official Go SDK lands upstream, this repo should ideally become unnecessary.

Status

This project is in beta.

  • Both API endpoints (/v1/systemone, /v1/models) are implemented.
  • go test ./... passes against local fixtures with no network access.
  • A live test (live_test.go) is verified against the real API and runs in CI when TYPESAFE_API_KEY is set.
  • The SDK is checked against the upstream OpenAPI spec, committed at openapi.json.

Requirements

  • Go 1.21+
  • A TypeSafe API key

Installation

go get github.com/atharvamhaske/typesafe-sdk-go

See the quickstart for a walkthrough.

Quickstart

export TYPESAFE_API_KEY=sk-...
package main

import (
	"context"
	"fmt"
	"log"

	typesafe "github.com/atharvamhaske/typesafe-sdk-go"
)

func main() {
	client, err := typesafe.NewClient() // reads TYPESAFE_API_KEY
	if err != nil {
		log.Fatal(err)
	}

	resp, err := client.SystemOne(context.Background(),
		"I was charged twice. Please refund the duplicate charge today.",
		map[string]typesafe.Question{
			"category": typesafe.Choice{
				Instructions: "Categorize the message",
				Criteria:     map[string]string{"billing": "Billing issue", "technical": "Technical issue"},
			},
			"urgency": typesafe.Score{
				Instructions: "Rate urgency",
				Criteria:     []string{"Can wait", "Needs attention"},
			},
			"is_dupe": typesafe.Noul{
				Instructions: "Is this a duplicate charge?",
				Criteria:     map[string]string{"true": "Duplicate", "false": "Not a duplicate"},
			},
		})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(resp.Choices()["category"].Choice) // "billing"
	fmt.Println(resp.Scores()["urgency"].Score)    // 1.7
	fmt.Println(resp.Nouls()["is_dupe"].Noul)      // 0.98
}

Feature Coverage

SystemOne
  • Typed Choice, Score, and Noul question builders
  • Typed answer union, decoded from the API's discriminated response
  • Per-call model override with WithRequestModel
  • Token usage on every response
Models
  • List available models with ListModels

Package Overview

For full API documentation, see the docs site, api.md, or pkg.go.dev.

The public surface is organized around one handle:

  • Client for both SystemOne and ListModels, configured with functional options

Configuration

Option Env var Purpose
WithAPIKey TYPESAFE_API_KEY API key, required
WithBaseURL TYPESAFE_BASE_URL API base URL, defaults to https://api.typesafe.ai
WithModel TYPESAFE_DEFAULT_MODEL Default model, defaults to jev-latest
WithHTTPClient Underlying *http.Client
WithMaxRetries Retries for network errors, 429s, and 5xxs, honoring Retry-After. Default 3
WithCache Opt-in in-memory cache for SystemOne responses, keyed by request body. Disabled by default

Error Handling

Non-2xx responses return *typesafe.Error:

Field Purpose
StatusCode HTTP status code
Detail Validation detail, when the API returns one
Body Raw response body

Examples

Runnable examples live in examples/ and are walked through on the examples page:

TYPESAFE_API_KEY=... go run ./examples/systemone
TYPESAFE_API_KEY=... go run ./examples/listmodels
TYPESAFE_API_KEY=... go run ./examples/spamfilter   # Noul spam/moderation classifier
TYPESAFE_API_KEY=... go run ./examples/prlabel      # Choice-based PR/diff labeler
TYPESAFE_API_KEY=... go run ./examples/gamemove     # Choice-based game move picker

Verified Against the Live API

The raw endpoint via curl:

$ curl -s https://api.typesafe.ai/v1/systemone \
    -H "Authorization: Bearer $TYPESAFE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"state":"I was charged twice. Please refund the duplicate charge today.",
         "model":"jev-latest",
         "questions":{"category":{"type":"choice","instructions":"Categorize the message",
           "criteria":{"billing":"Billing issue","technical":"Technical issue"}}}}'
{
  "model": "jev-1.13.0",
  "answers": {
    "category": {
      "type": "choice",
      "choice": "billing",
      "confidence": 1.0,
      "probabilities": {"billing": 1.0, "technical": 0.0}
    }
  },
  "usage": {"input_tokens": 319, "output_tokens": 31}
}

The same call through the SDK:

$ TYPESAFE_API_KEY=... go run ./examples/systemone
category: billing (confidence 1.00)
urgency:  1.00
is_dupe:  0.66
usage:    381 in / 64 out

SystemOne and ListModels examples running against the live TypeSafe API

Use with Braintrust

trace/contrib/typesafe automatically traces every SystemOne call through typesafe.WithHTTPClient, matching the span shape the official Python and JS TypeSafe integrations use. It is not merged upstream yet, so install it from the fork branch:

go get github.com/atharvamhaske/braintrust-sdk-go/trace/contrib/typesafe@feat/typesafe-tracing

See the Use with Braintrust docs page for the field reference, and examples/braintrust for a full runnable example. That example has its own go.mod, so braintrust-sdk-go and OpenTelemetry stay out of the core SDK's dependency graph.

cd examples/braintrust
BRAINTRUST_API_KEY=... TYPESAFE_API_KEY=... go run .

Testing

go test ./...                                    # fixture tests, no network
TYPESAFE_API_KEY=... go test ./...               # includes the live API test

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md.

If you want to extend surface area or align behavior with the upstream SDKs, opening an issue first is helpful so the API shape can stay coherent.

Relationship to Upstream

This repository exists because there is no official Go SDK at the time of writing. If the TypeSafe team decides to ship or adopt one upstream, aligning this project with that effort would be the best long-term outcome.

References

License

MIT. See LICENSE.

Documentation

Overview

Package typesafe is an unofficial Go client for the TypeSafe AI API.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Answer

type Answer interface {
	// contains filtered or unexported methods
}

Answer is one of ChoiceAnswer, ScoreAnswer, or NoulAnswer.

type Choice

type Choice struct {
	Instructions string
	Criteria     map[string]string
}

Choice picks one option from named criteria.

func (Choice) MarshalJSON

func (q Choice) MarshalJSON() ([]byte, error)

type ChoiceAnswer

type ChoiceAnswer struct {
	Choice        string             `json:"choice"`
	Confidence    float64            `json:"confidence"`
	Probabilities map[string]float64 `json:"probabilities"`
}

ChoiceAnswer answers a Choice question.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to the TypeSafe AI API.

func NewClient

func NewClient(opts ...Option) (*Client, error)

NewClient builds a Client from environment variables and options. It fails if no API key is configured.

func (*Client) ListModels

func (c *Client) ListModels(ctx context.Context) ([]Model, error)

ListModels returns the available models via GET /v1/models.

Example
package main

import (
	"context"
	"fmt"
	"log"

	typesafe "github.com/atharvamhaske/typesafe-sdk-go"
)

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

	models, err := client.ListModels(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	for _, m := range models {
		fmt.Println(m.Name, m.Description)
	}
}

func (*Client) SystemOne

func (c *Client) SystemOne(ctx context.Context, state string, questions map[string]Question, opts ...SystemOneOption) (*SystemOneResponse, error)

SystemOne asks questions about a state via POST /v1/systemone. When the client was built with WithCache, an identical request within the cache's TTL is served without a network call.

Example
package main

import (
	"context"
	"fmt"
	"log"

	typesafe "github.com/atharvamhaske/typesafe-sdk-go"
)

func main() {
	client, err := typesafe.NewClient() // reads TYPESAFE_API_KEY
	if err != nil {
		log.Fatal(err)
	}

	resp, err := client.SystemOne(context.Background(),
		"I was charged twice. Please refund the duplicate charge today.",
		map[string]typesafe.Question{
			"category": typesafe.Choice{
				Instructions: "Categorize the message",
				Criteria:     map[string]string{"billing": "Billing issue", "technical": "Technical issue"},
			},
			"urgency": typesafe.Score{
				Instructions: "Rate urgency",
				Criteria:     []string{"Can wait", "Needs attention"},
			},
			"is_dupe": typesafe.Noul{
				Instructions: "Is this a duplicate charge?",
				Criteria:     map[string]string{"true": "Duplicate", "false": "Not a duplicate"},
			},
		})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(resp.Choices()["category"].Choice)
	fmt.Println(resp.Scores()["urgency"].Score)
	fmt.Println(resp.Nouls()["is_dupe"].Noul)
}

type Error

type Error struct {
	StatusCode int
	Detail     json.RawMessage // HTTPValidationError detail, if any
	Body       []byte
}

Error is a non-2xx API response.

func (*Error) Error

func (e *Error) Error() string

type Model

type Model struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	ReleaseDate string `json:"release_date"`
}

Model describes an available TypeSafe model.

type Noul

type Noul struct {
	Instructions string
	Criteria     map[string]string
}

Noul answers a true/false question with a probability.

func (Noul) MarshalJSON

func (q Noul) MarshalJSON() ([]byte, error)

type NoulAnswer

type NoulAnswer struct {
	Noul float64 `json:"noul"`
}

NoulAnswer answers a Noul question.

type Option

type Option func(*Client)

Option configures a Client.

func WithAPIKey

func WithAPIKey(key string) Option

WithAPIKey sets the API key, overriding TYPESAFE_API_KEY.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL sets the base URL, overriding TYPESAFE_BASE_URL.

func WithCache added in v0.2.0

func WithCache(maxEntries int, ttl time.Duration) Option

WithCache enables an opt-in, in-memory cache for SystemOne responses, keyed by the request body and evicted after ttl. Disabled by default.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient sets the underlying *http.Client.

func WithMaxRetries added in v0.2.0

func WithMaxRetries(n int) Option

WithMaxRetries sets how many times a retryable failure (network error, 429, or 5xx) is retried, with backoff honoring any Retry-After header. Default 3.

func WithModel

func WithModel(model string) Option

WithModel sets the default model, overriding TYPESAFE_DEFAULT_MODEL.

type Question

type Question interface {
	// contains filtered or unexported methods
}

Question is one of Choice, Score, or Noul.

type Score

type Score struct {
	Instructions string
	Criteria     []string
}

Score rates on a scale described by ordered criteria.

func (Score) MarshalJSON

func (q Score) MarshalJSON() ([]byte, error)

type ScoreAnswer

type ScoreAnswer struct {
	Score         float64            `json:"score"`
	Confidence    float64            `json:"confidence"`
	Legend        map[string]string  `json:"legend"`
	Probabilities map[string]float64 `json:"probabilities"`
}

ScoreAnswer answers a Score question.

type SystemOneOption

type SystemOneOption func(*systemOneRequest)

SystemOneOption configures a single SystemOne call.

func WithRequestModel

func WithRequestModel(model string) SystemOneOption

WithRequestModel overrides the client's default model for this call.

type SystemOneResponse

type SystemOneResponse struct {
	Model   string            `json:"model"`
	Usage   Usage             `json:"usage"`
	Answers map[string]Answer `json:"-"`
}

SystemOneResponse is the result of a SystemOne call.

func (*SystemOneResponse) Choices

func (r *SystemOneResponse) Choices() map[string]ChoiceAnswer

Choices returns the choice-typed answers by question key.

func (*SystemOneResponse) Nouls

func (r *SystemOneResponse) Nouls() map[string]NoulAnswer

Nouls returns the noul-typed answers by question key.

func (*SystemOneResponse) Scores

func (r *SystemOneResponse) Scores() map[string]ScoreAnswer

Scores returns the score-typed answers by question key.

func (*SystemOneResponse) UnmarshalJSON

func (r *SystemOneResponse) UnmarshalJSON(data []byte) error

type Usage

type Usage struct {
	InputTokens  int `json:"input_tokens"`
	OutputTokens int `json:"output_tokens"`
}

Usage reports token consumption for a request.

Directories

Path Synopsis
examples
gamemove command
Command gamemove picks the next move in a toy Snake game, following the same pattern as the dozens of Jev-plays-a-game demos in the community (jev-snake, jev-tetris, Jev Pac-Man, and others): the game engine owns the rules and state, and one Choice question per tick picks the move.
Command gamemove picks the next move in a toy Snake game, following the same pattern as the dozens of Jev-plays-a-game demos in the community (jev-snake, jev-tetris, Jev Pac-Man, and others): the game engine owns the rules and state, and one Choice question per tick picks the move.
listmodels command
Command listmodels prints the available TypeSafe models.
Command listmodels prints the available TypeSafe models.
prlabel command
Command prlabel labels a pull request diff by its conceptual scope, following the same pattern as jev-pr-labeler and commit-miner in the Jev community: a Choice question over the diff instead of line counts.
Command prlabel labels a pull request diff by its conceptual scope, following the same pattern as jev-pr-labeler and commit-miner in the Jev community: a Choice question over the diff instead of line counts.
spamfilter command
Command spamfilter scores a message for spam, following the same pattern as the many Jev-powered moderation and antispam bots in the community: one Noul question per message, thresholded in code.
Command spamfilter scores a message for spam, following the same pattern as the many Jev-powered moderation and antispam bots in the community: one Noul question per message, thresholded in code.
systemone command
Command systemone asks typed questions about a support message.
Command systemone asks typed questions about a support message.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL