typesafe

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 14 Imported by: 0

README

typesafeai-go

CI Go Reference Go Report Card

An unofficial, dependency-free Go SDK for the TypeSafe System One API (API reference).

System One evaluates a piece of content (state) against one or more typed questions and returns calibrated, probability-backed answers:

  • Noul — a yes/no question, answered with a 0-1 probability.
  • Choice — pick one option from a fixed set, with per-option probabilities and a confidence score.
  • Score — rate content against an ordered list of descriptive levels.

Contents

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

resp, err := client.SystemOne(ctx, &typesafe.Request{
    State: "Hi, I've been trying to connect my Stripe account for 3 days.",
    Questions: map[string]typesafe.Question{
        "is_urgent": typesafe.Noul{
            Instructions: "The message conveys urgency or time-sensitivity.",
        },
    },
})
if err != nil {
    log.Fatal(err)
}

fmt.Println(resp.Answers["is_urgent"].(typesafe.NoulAnswer).Noul)

Why this SDK

  • Zero dependencies. Only the Go standard library — nothing to audit in your dependency tree, nothing to break on an upstream release.
  • Secure by default. The API key is never logged; it's only ever sent as Authorization: Bearer <key> over HTTPS. Every call takes a context.Context, so timeouts and cancellation are always in your control.
  • Automatic, configurable retries. Rate limits (429) and transient server errors (5xx, including TypeSafe's 529 "overloaded" response) are retried with jittered exponential backoff and honor the Retry-After header, matching the retry behavior TypeSafe's own SDKs document.
  • Typed errors you can errors.As against, mirroring TypeSafe's JavaScript SDK error hierarchy: AuthenticationError, BadRequestError, PermissionDeniedError, NotFoundError, UnprocessableEntityError, RateLimitError, InternalServerError, APIConnectionError, APITimeoutError.
  • Forward-compatible responses. An answer type this SDK version doesn't know about yet decodes to RawAnswer instead of failing the whole response — upgrading the API server can't break older clients.
  • Client-side validation. Choice's 255-option limit and Score's 2-10 level bounds are checked before a request ever leaves the process.
  • Fast by construction, not by shortcut. Responses are decoded straight from the wire (no intermediate byte-slice buffering), the underlying http.Client is reused and connection-pooled across calls, and every claim here is backed by a benchmark you can run yourself — see Performance.
  • Open source, MIT-licensed, and built to be contributed to — see Contributing.

Install

go get github.com/refaldyrk/typesafeai-go
import typesafe "github.com/refaldyrk/typesafeai-go"

Requires Go 1.22 or later.

Authentication

export TYPESAFE_API_KEY=sk-...
// Reads TYPESAFE_API_KEY automatically:
client, err := typesafe.NewClient()

// Or set it explicitly:
client, err := typesafe.NewClient(typesafe.WithAPIKey("sk-..."))

Get a key from the TypeSafe console.

The TYPESAFE_API_KEY environment variable name itself is fixed — it matches TypeSafe's own Python/JS SDKs — but you can always bypass it entirely with WithAPIKey.

Usage

Noul (yes/no)
resp, err := client.SystemOne(ctx, &typesafe.Request{
    State: state,
    Questions: map[string]typesafe.Question{
        "is_urgent": typesafe.Noul{
            Instructions: "Does this convey urgency?",
            Criteria: &typesafe.NoulCriteria{
                True:  "explicitly time-sensitive",
                False: "no urgency expressed",
            },
        },
    },
})
answer := resp.Answers["is_urgent"].(typesafe.NoulAnswer)
fmt.Println(answer.Noul) // 0.95
Choice (pick one)
resp, err := client.SystemOne(ctx, &typesafe.Request{
    State: state,
    Questions: map[string]typesafe.Question{
        "category": typesafe.Choice{
            Instructions: "Which category best fits this message?",
            Criteria: map[string]any{
                "billing":   "Payments, charges, refunds, invoices",
                "technical": "Bugs, errors, outages",
                "account":   "Login, profile, permissions",
            },
        },
    },
})
answer := resp.Answers["category"].(typesafe.ChoiceAnswer)
fmt.Println(answer.Choice, answer.Confidence, answer.Probabilities)
Score (rate against levels)
resp, err := client.SystemOne(ctx, &typesafe.Request{
    State: state,
    Questions: map[string]typesafe.Question{
        "quality": typesafe.Score{
            Instructions: "Rate the response quality.",
            Criteria:     []any{"poor", "fair", "good", "excellent"},
        },
    },
})
answer := resp.Answers["quality"].(typesafe.ScoreAnswer)
fmt.Println(answer.Score, answer.Legend, answer.Probabilities)
Multiple questions in one call

Questions is a map, so you can ask several things about the same state in a single request:

resp, err := client.SystemOne(ctx, &typesafe.Request{
    State: state,
    Questions: map[string]typesafe.Question{
        "is_urgent": typesafe.Noul{Instructions: "..."},
        "category":  typesafe.Choice{Instructions: "...", Criteria: map[string]any{...}},
        "quality":   typesafe.Score{Instructions: "...", Criteria: []any{...}},
    },
})
Structured state and instructions

State and Instructions both accept a string, or any JSON-serializable map/slice value. Structured instructions can reference fields of a structured State using backtick notation:

resp, err := client.SystemOne(ctx, &typesafe.Request{
    State: map[string]any{
        "complaint_text": "Still no refund after two weeks.",
        "customer_tier":  "enterprise",
    },
    Questions: map[string]typesafe.Question{
        "needs_escalation": typesafe.Noul{
            Instructions: "Does `complaint_text` warrant escalation, given `customer_tier`?",
        },
    },
})

See TypeSafe's structured questions guide for the full syntax.

Handling unknown answer types safely

Type-switch instead of a blind type assertion if you want to handle a future answer type gracefully instead of panicking:

switch a := resp.Answers["is_urgent"].(type) {
case typesafe.NoulAnswer:
    fmt.Println(a.Noul)
case typesafe.ChoiceAnswer:
    fmt.Println(a.Choice)
case typesafe.ScoreAnswer:
    fmt.Println(a.Score)
case typesafe.RawAnswer:
    log.Printf("unrecognized answer type %q: %s", a.Type, a.Raw)
}

Error handling

Every error from SystemOne can be inspected with errors.As:

resp, err := client.SystemOne(ctx, req)
if err != nil {
    var rateLimit *typesafe.RateLimitError
    if errors.As(err, &rateLimit) {
        log.Printf("rate limited, retry after %s", rateLimit.RetryAfter)
        return
    }

    var authErr *typesafe.AuthenticationError
    if errors.As(err, &authErr) {
        log.Fatal("invalid API key")
    }

    var apiErr *typesafe.APIError
    if errors.As(err, &apiErr) {
        log.Printf("api error: %s (status %d)", apiErr.Message, apiErr.StatusCode)
        return
    }

    log.Printf("request failed: %v", err) // network error, timeout, etc.
}
Type HTTP status Meaning
BadRequestError 400 Malformed request
AuthenticationError 401 Missing or invalid API key
PermissionDeniedError 403 Key valid but not authorized
NotFoundError 404 Resource not found
UnprocessableEntityError 422 Well-formed but invalid (bad questions, criteria out of bounds)
RateLimitError 429 Rate limit exceeded — carries RetryAfter
InternalServerError 5xx Server error, including 529 "overloaded"
APIConnectionError Request never reached the API, or the response never arrived
APITimeoutError Client timeout or context deadline exceeded

All of the HTTP-status errors wrap a shared *APIError (StatusCode, Message, Code, Body, Header), so errors.As(err, &apiErr) with var apiErr *typesafe.APIError matches any of them.

Retries

Requests retry automatically on connection failures, timeouts, HTTP 429, and HTTP 5xx (including 529), using jittered exponential backoff that starts at 500ms and caps at 8s, honoring the Retry-After header when the API sends one.

// Customize:
client, err := typesafe.NewClient(
    typesafe.WithRetryPolicy(typesafe.RetryPolicy{
        MaxRetries:           5,
        RetryableStatusCodes: []int{429, 500, 502, 503, 529},
        BaseDelay:            250 * time.Millisecond,
        MaxDelay:             10 * time.Second,
    }),
)

// Disable entirely:
client, err := typesafe.NewClient(typesafe.WithRetryPolicy(typesafe.NoRetry()))

RetryPolicy bounds retry delay, not the whole operation — wrap the call in context.WithTimeout to cap total wall-clock time across all attempts.

Client configuration

client, err := typesafe.NewClient(
    typesafe.WithAPIKey("sk-..."),                    // default: $TYPESAFE_API_KEY
    typesafe.WithBaseURL("https://api.typesafe.ai/v1/systemone"), // default shown
    typesafe.WithTimeout(30*time.Second),              // default: 60s, per attempt
    typesafe.WithHTTPClient(&http.Client{ /* ... */ }), // bring your own transport/proxy
    typesafe.WithRetryPolicy(typesafe.DefaultRetryPolicy()),
    typesafe.WithUserAgent("my-app/1.0"),
    typesafe.WithDefaultModel("jev-preview"),          // default: "jev-latest"
    typesafe.WithMaxRequestBodySize(8*1024*1024),      // default: 4MB
    typesafe.WithMaxResponseBodySize(128*1024*1024),   // default: 64MB
)

Every default in the SDK is overridable this way — none of them require editing a constant in the source. WithTimeout always takes effect regardless of whether it's passed before or after WithHTTPClient.

Models

Requests default to jev-latest (typesafe.DefaultModel), the alias TypeSafe's own SDKs default to. Override per-request:

req := &typesafe.Request{
    State:     state,
    Model:     "jev-1.13.0", // pin an exact version instead of an alias
    Questions: questions,
}

resp.Model always reports the exact versioned model that produced the answer (e.g. "jev-1.13.0"), even when the request used an alias — log it if you need to know which version generated a given result. See docs.typesafe.ai/models for current limits (context window, rate limits, pricing).

Performance

The SDK is built to add as little overhead as possible on top of the network round trip, which dominates real-world latency:

  • One HTTP client, reused and connection-pooled. *Client wraps a single *http.Client; keep-alives mean repeated calls reuse the same TCP/TLS connection instead of renegotiating one per request.
  • Streaming JSON decode. A successful response is decoded directly from the response body (json.NewDecoder(...).Decode(&out)) instead of being buffered into a []byte first and then unmarshaled — one fewer full-body copy and allocation per call.
  • No reflection-heavy middleware, no interface{} soup. Requests and responses use concrete, statically-typed structs; the only dynamic dispatch is the necessarily-polymorphic Question/Answer decoding.
  • Zero dependencies means zero indirect allocation or CPU cost from a transitive HTTP or JSON library layered on top of the standard one.

Measured on a typical dev laptop (go test -bench=. -benchmem -run=^$), encoding/decoding a 3-question request/response and a full round trip against an in-process server:

Benchmark Time/op Bytes/op Allocs/op
BenchmarkRequestMarshal (3 questions) ~11 µs ~2.5 KB 25
BenchmarkResponseUnmarshal (3 answers) ~28 µs ~4.5 KB 96
BenchmarkSystemOneRoundTrip (in-process HTTP) ~173 µs ~12 KB 151

Run it yourself — numbers vary by hardware, and the benchmarks live in bench_test.go so you can track regressions on your own machine or in CI:

go test -bench=. -benchmem -run=^$ ./...

In practice, the real API's network latency (typically tens of milliseconds) will dwarf all of the above — these numbers exist so that the SDK itself is never the bottleneck, and so any future PR that regresses them gets caught.

API Reference

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

Builds a *Client. Reads TYPESAFE_API_KEY if WithAPIKey isn't passed; returns ErrMissingAPIKey if no key is available from either source. A *Client is safe for concurrent use by multiple goroutines.

func (*Client) SystemOne(ctx context.Context, req *Request) (*Response, error)

Evaluates req.State against req.Questions. Validates the request client-side before sending it. Retries transient failures per the client's RetryPolicy; honors ctx cancellation/deadline across all attempts.

Options
Option Purpose Default
WithAPIKey(string) Set the API key $TYPESAFE_API_KEY
WithBaseURL(string) Override the API endpoint https://api.typesafe.ai/v1/systemone
WithHTTPClient(*http.Client) Replace the transport &http.Client{Timeout: 60s}
WithTimeout(time.Duration) Per-attempt HTTP timeout 60s
WithRetryPolicy(RetryPolicy) Retry behavior DefaultRetryPolicy()
WithUserAgent(string) Override the User-Agent header typesafeai-go/<version>
WithDefaultModel(string) Model used when Request.Model is empty (a request-level Model still wins) DefaultModel ("jev-latest")
WithMaxRequestBodySize(int) Client-side cap on the encoded request body, in bytes 4MB
WithMaxResponseBodySize(int) Cap on how much of a response body is read, in bytes 64MB
Types
  • RequestState any, Model string (defaults to DefaultModel), Questions map[string]Question.
  • Question — implemented by Noul, Choice, Score (closed set).
    • Noul{Instructions any, Criteria *NoulCriteria}
    • Choice{Instructions any, Criteria map[string]any} — 1-255 options.
    • Score{Instructions any, Criteria []any} — 2-10 ordered levels.
  • ResponseModel string, Answers map[string]Answer, Usage Usage{InputTokens, OutputTokens int}.
  • Answer — implemented by NoulAnswer, ChoiceAnswer, ScoreAnswer, and RawAnswer (unknown types).
    • NoulAnswer{Noul float64}
    • ChoiceAnswer{Choice string, Probabilities map[string]float64, Confidence float64}
    • ScoreAnswer{Score float64, Legend map[string]string, Probabilities map[string]float64, Confidence float64}
    • RawAnswer{Type string, Raw json.RawMessage}
  • RetryPolicyMaxRetries int, RetryableStatusCodes []int, BaseDelay, MaxDelay time.Duration. Build with DefaultRetryPolicy() or NoRetry().
  • ErrorsAPIError (base) and BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, UnprocessableEntityError, RateLimitError, InternalServerError, APIConnectionError, APITimeoutError.
Constants
  • typesafe.DefaultBaseURL"https://api.typesafe.ai/v1/systemone"
  • typesafe.DefaultModel"jev-latest"
  • typesafe.Version — SDK version string

Full generated documentation: go doc github.com/refaldyrk/typesafeai-go, or browse it on pkg.go.dev/github.com/refaldyrk/typesafeai-go.

Examples

Runnable, end-to-end examples live under examples/:

export TYPESAFE_API_KEY=sk-...
go run ./examples/basic

Development

go build ./...
go vet ./...
gofmt -l .   # should print nothing
go test ./...

The test suite runs entirely against an in-process httptest server — no network access or real API key required.

Contributing

Contributions are welcome — bug reports, docs fixes, and PRs alike. Please read CONTRIBUTING.md first: it covers the dev workflow, the project's "zero dependencies, no breaking changes without discussion" ground rules, and the PR checklist. See also CODE_OF_CONDUCT.md and, for vulnerabilities, SECURITY.md. Released changes are tracked in CHANGELOG.md.

Upstream documentation

This SDK wraps the single POST /v1/systemone endpoint described at docs.typesafe.ai/api. For question-design guidance, model details, and advanced patterns, see:

License

MIT — copyright (c) 2026 refaldyrk. This is an unofficial, community-maintained SDK and is not affiliated with or endorsed by TypeSafe.

Documentation

Overview

Package typesafe is an unofficial Go SDK for the TypeSafe System One API (https://docs.typesafe.ai/api).

System One evaluates a piece of content ("state") against one or more typed questions — Noul (yes/no), Choice (pick one option) or Score (rate against ordered levels) — and returns calibrated, probability-backed answers.

Quick start

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

resp, err := client.SystemOne(ctx, &typesafe.Request{
	State: "Hi, I've been trying to connect my Stripe account for 3 days...",
	Questions: map[string]typesafe.Question{
		"is_urgent": typesafe.Noul{
			Instructions: "The message conveys urgency or time-sensitivity",
		},
	},
})
if err != nil {
	var rl *typesafe.RateLimitError
	if errors.As(err, &rl) {
		time.Sleep(rl.RetryAfter)
	}
	log.Fatal(err)
}

fmt.Println(resp.Answers["is_urgent"].(typesafe.NoulAnswer).Noul)

The client never logs or echoes the API key, retries idempotent failures with jittered exponential backoff by default, and every network call accepts a context.Context so callers control cancellation and deadlines.

Index

Constants

View Source
const DefaultBaseURL = "https://api.typesafe.ai/v1/systemone"

DefaultBaseURL is the production TypeSafe API endpoint.

View Source
const DefaultModel = "jev-latest"

DefaultModel is the model alias the SDK sends when Request.Model is left empty. It always resolves to TypeSafe's most recent stable release.

View Source
const Version = "0.1.0"

Version is the SDK version, sent as part of the User-Agent header.

Variables

View Source
var ErrMissingAPIKey = errors.New("typesafe: no API key provided (use WithAPIKey or set TYPESAFE_API_KEY)")

ErrMissingAPIKey is returned by NewClient when no API key was supplied via WithAPIKey or the TYPESAFE_API_KEY environment variable.

Functions

This section is empty.

Types

type APIConnectionError

type APIConnectionError struct{ Err error }

APIConnectionError indicates the request never reached the API, or its response never came back — a DNS failure, refused connection, or a closed connection mid-response. Err holds the underlying network error.

func (*APIConnectionError) Error

func (e *APIConnectionError) Error() string

func (*APIConnectionError) Unwrap

func (e *APIConnectionError) Unwrap() error

type APIError

type APIError struct {
	// StatusCode is the HTTP status code returned by the API.
	StatusCode int

	// Message is a human-readable description of the failure. It is
	// extracted from the response body on a best-effort basis (the exact
	// error body shape isn't part of TypeSafe's published API reference),
	// falling back to the raw body text.
	Message string

	// Code is the API's machine-readable error code, if the response body
	// included one. May be empty.
	Code string

	// Body is the raw, unparsed response body, for callers that need
	// access to fields this SDK doesn't surface.
	Body []byte

	// Header is the response's HTTP headers.
	Header http.Header
}

APIError is returned for any non-2xx response from the TypeSafe API. Use errors.As to check for one of the more specific subtypes below, which all wrap *APIError:

var rl *typesafe.RateLimitError
if errors.As(err, &rl) {
	time.Sleep(rl.RetryAfter)
}

func (*APIError) Error

func (e *APIError) Error() string

type APITimeoutError

type APITimeoutError struct{ Err error }

APITimeoutError indicates the request exceeded the client's configured timeout or the request context's deadline.

func (*APITimeoutError) Error

func (e *APITimeoutError) Error() string

func (*APITimeoutError) Unwrap

func (e *APITimeoutError) Unwrap() error

type Answer

type Answer interface {
	// AnswerType returns the API's discriminator value: "noul", "choice"
	// or "score" (or the raw value verbatim for a RawAnswer).
	AnswerType() string
	// contains filtered or unexported methods
}

Answer is the result for one question: a NoulAnswer, ChoiceAnswer, ScoreAnswer, or RawAnswer for an answer type this SDK version does not yet recognize.

Type-switch on the concrete type to read a result:

switch a := resp.Answers["is_urgent"].(type) {
case typesafe.NoulAnswer:
	fmt.Println(a.Noul)
case typesafe.ChoiceAnswer:
	fmt.Println(a.Choice)
case typesafe.ScoreAnswer:
	fmt.Println(a.Score)
}

type AuthenticationError

type AuthenticationError struct{ *APIError }

AuthenticationError is returned for HTTP 401: the API key is missing or invalid.

func (*AuthenticationError) Unwrap

func (e *AuthenticationError) Unwrap() error

Unwrap allows errors.As(err, &(*APIError)(nil)) to match the embedded base error; embedding *APIError alone does not provide this for free.

type BadRequestError

type BadRequestError struct{ *APIError }

BadRequestError is returned for HTTP 400: the request was malformed.

func (*BadRequestError) Unwrap

func (e *BadRequestError) Unwrap() error

type Choice

type Choice struct {
	// Instructions tells the model how to choose. Accepts a string or a
	// structured object/array with backtick data references.
	Instructions any

	// Criteria maps each option name to a description of what it means.
	// A description may be nil if the option name is self-explanatory.
	// Must contain between 1 and 255 entries.
	Criteria map[string]any
}

Choice asks the model to select exactly one option from a fixed set. The API returns the selected option along with a probability for every option and a derived confidence score.

func (Choice) MarshalJSON

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

type ChoiceAnswer

type ChoiceAnswer struct {
	// Choice is the selected option name, one of the keys from the
	// request's Choice.Criteria.
	Choice string `json:"choice"`

	// Probabilities maps every offered option to its probability.
	Probabilities map[string]float64 `json:"probabilities"`

	// Confidence is a 0-1 certainty metric derived from Probabilities.
	Confidence float64 `json:"confidence"`
}

ChoiceAnswer is the result of a Choice question.

func (ChoiceAnswer) AnswerType

func (ChoiceAnswer) AnswerType() string

type Client

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

Client calls the TypeSafe System One API. Construct one with NewClient. A *Client is safe for concurrent use by multiple goroutines.

func NewClient

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

NewClient builds a Client. Without WithAPIKey, it reads the API key from the TYPESAFE_API_KEY environment variable and returns ErrMissingAPIKey if that's also unset.

func (*Client) SystemOne

func (c *Client) SystemOne(ctx context.Context, req *Request) (*Response, error)

SystemOne evaluates req.State against req.Questions and returns the model's answers. It retries transient failures (connection errors, timeouts, 429 and 5xx responses) per the client's RetryPolicy, and honors ctx cancellation/deadline across all attempts.

type InternalServerError

type InternalServerError struct{ *APIError }

InternalServerError is returned for HTTP 5xx, including 529 (service overloaded).

func (*InternalServerError) Unwrap

func (e *InternalServerError) Unwrap() error

type NotFoundError

type NotFoundError struct{ *APIError }

NotFoundError is returned for HTTP 404.

func (*NotFoundError) Unwrap

func (e *NotFoundError) Unwrap() error

type Noul

type Noul struct {
	// Instructions tells the model what to evaluate. It accepts a plain
	// string or a structured object/array that references fields of State
	// using backtick notation, e.g. "Does `complaint_text` convey urgency?".
	Instructions any

	// Criteria optionally clarifies what counts as true/false. Either field
	// may be omitted.
	Criteria *NoulCriteria
}

Noul asks a yes/no question. The API returns a 0-1 probability that the answer is "yes".

func (Noul) MarshalJSON

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

type NoulAnswer

type NoulAnswer struct {
	// Noul is the probability, from 0 to 1, that the answer is "yes".
	Noul float64 `json:"noul"`
}

NoulAnswer is the result of a Noul (yes/no) question.

func (NoulAnswer) AnswerType

func (NoulAnswer) AnswerType() string

type NoulCriteria

type NoulCriteria struct {
	True  any
	False any
}

NoulCriteria clarifies the true/false boundary for a Noul question.

type Option

type Option func(*Client)

Option configures a Client. Pass one or more to NewClient.

func WithAPIKey

func WithAPIKey(apiKey string) Option

WithAPIKey sets the API key explicitly, overriding TYPESAFE_API_KEY.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL overrides the API endpoint. Mainly useful for testing against a local mock server.

func WithDefaultModel

func WithDefaultModel(model string) Option

WithDefaultModel overrides the model used when a Request leaves Model empty (default: DefaultModel, "jev-latest"). A Request that sets its own Model still takes precedence over this client-level default.

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) Option

WithHTTPClient replaces the underlying *http.Client, e.g. to install a custom transport, proxy, or TLS configuration. Combine with WithTimeout (rather than setting httpClient.Timeout yourself) if you also want the SDK's per-attempt timeout applied — WithTimeout always wins regardless of which of the two options is passed first.

func WithMaxRequestBodySize

func WithMaxRequestBodySize(maxBytes int) Option

WithMaxRequestBodySize overrides the client-side cap on the encoded request body (default 4MB, sized for the model's ~64k token context window). Requests larger than this fail locally instead of round-tripping to the API only to be rejected there.

func WithMaxResponseBodySize

func WithMaxResponseBodySize(maxBytes int) Option

WithMaxResponseBodySize overrides the cap on how much of a response body the client will read (default 64MB). Guards against unbounded memory use if a response is unexpectedly huge, e.g. WithBaseURL pointed somewhere untrusted or misbehaving.

func WithRetryPolicy

func WithRetryPolicy(policy RetryPolicy) Option

WithRetryPolicy overrides the default retry behavior. Pass NoRetry() to disable retries entirely.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the per-attempt HTTP timeout (default 60s). It does not bound retries as a whole — wrap the call's context with context.WithTimeout for that. Order-independent with WithHTTPClient: it always applies last, whichever order the two options are passed in.

func WithUserAgent

func WithUserAgent(userAgent string) Option

WithUserAgent overrides the User-Agent header sent with every request.

type PermissionDeniedError

type PermissionDeniedError struct{ *APIError }

PermissionDeniedError is returned for HTTP 403: the API key is valid but not authorized for this operation.

func (*PermissionDeniedError) Unwrap

func (e *PermissionDeniedError) Unwrap() error

type Question

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

Question is one named question sent to the System One API. The three concrete implementations are Noul, Choice and Score. The set is closed — there is no need to implement this interface yourself.

type RateLimitError

type RateLimitError struct {
	*APIError
	RetryAfter time.Duration
}

RateLimitError is returned for HTTP 429. RetryAfter is parsed from the response's Retry-After header when present; the SDK's built-in retry logic already honors it, so most callers only need this when they've disabled retries.

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

type RawAnswer

type RawAnswer struct {
	Type string
	Raw  json.RawMessage
}

RawAnswer preserves an answer whose "type" this SDK version does not recognize, so that upgrading the API server never breaks decoding of responses to older SDK clients. Inspect Raw yourself, or upgrade the SDK.

func (RawAnswer) AnswerType

func (a RawAnswer) AnswerType() string

type Request

type Request struct {
	// State is the content to evaluate. It may be a string, or a
	// structured map/slice — anything JSON-serializable. Required.
	State any

	// Model selects the model version. Defaults to DefaultModel
	// ("jev-latest") when empty.
	Model string

	// Questions maps a caller-chosen question ID to a Noul, Choice or
	// Score question. Required, must be non-empty.
	Questions map[string]Question
}

Request is the body of a POST /v1/systemone call: a piece of content ("state") evaluated against one or more named questions.

func (Request) MarshalJSON

func (r Request) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler, applying DefaultModel when Model is unset.

type Response

type Response struct {
	// Model is the specific versioned model ID that produced the answers
	// (e.g. "jev-1.13.0"), even when the request used an alias.
	Model string

	// Answers holds one entry per question ID from the request. Each
	// value is a NoulAnswer, ChoiceAnswer, ScoreAnswer, or — for
	// forward-compatibility with answer types this SDK version doesn't
	// know about yet — a RawAnswer.
	Answers map[string]Answer

	Usage Usage
}

Response is the body of a successful System One call.

func (*Response) UnmarshalJSON

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

type RetryPolicy

type RetryPolicy struct {
	// MaxRetries is the number of retry attempts after the initial try.
	// 0 disables retrying entirely.
	MaxRetries int

	// RetryableStatusCodes lists HTTP status codes worth retrying.
	// Anything else fails immediately.
	RetryableStatusCodes []int

	// BaseDelay is the delay before the first retry.
	BaseDelay time.Duration

	// MaxDelay caps the exponential backoff delay, before jitter.
	MaxDelay time.Duration
}

RetryPolicy controls how the client retries failed requests. The zero value is not usable directly — start from DefaultRetryPolicy().

func DefaultRetryPolicy

func DefaultRetryPolicy() RetryPolicy

DefaultRetryPolicy retries connection failures, timeouts, HTTP 429 (rate limited) and 5xx responses (including 529, "service overloaded") up to twice, with jittered exponential backoff starting at 500ms and capped at 8s — matching the retry behavior TypeSafe's own SDKs document.

func NoRetry

func NoRetry() RetryPolicy

NoRetry disables retries: every request is attempted exactly once.

type Score

type Score struct {
	// Instructions tells the model how to rate. Accepts a string or a
	// structured object/array with backtick data references.
	Instructions any

	// Criteria lists each level's description in ascending order, e.g.
	// []any{"poor", "fair", "good", "excellent"}. Must contain between 2
	// and 10 entries.
	Criteria []any
}

Score asks the model to rate the state against an ordered list of descriptive levels, from lowest to highest. The API returns a probability-weighted score plus a per-level probability distribution.

func (Score) MarshalJSON

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

type ScoreAnswer

type ScoreAnswer struct {
	// Score is the probability-weighted rating across the requested
	// levels.
	Score float64 `json:"score"`

	// Legend maps each level's index (as a string) to its description
	// from the request's Score.Criteria.
	Legend map[string]string `json:"legend"`

	// Probabilities maps each level's index (as a string) to its
	// probability.
	Probabilities map[string]float64 `json:"probabilities"`

	// Confidence is a 0-1 certainty metric derived from Probabilities.
	Confidence float64 `json:"confidence"`
}

ScoreAnswer is the result of a Score question.

func (ScoreAnswer) AnswerType

func (ScoreAnswer) AnswerType() string

type UnprocessableEntityError

type UnprocessableEntityError struct{ *APIError }

UnprocessableEntityError is returned for HTTP 422: the request was well-formed JSON but failed validation (missing fields, malformed questions, criteria out of bounds, etc).

func (*UnprocessableEntityError) Unwrap

func (e *UnprocessableEntityError) Unwrap() error

type Usage

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

Usage reports token consumption for a single System One call. Per the API's pricing model only InputTokens are billed.

Directories

Path Synopsis
examples
basic command
Command basic shows the minimal Noul (yes/no) question flow.
Command basic shows the minimal Noul (yes/no) question flow.
choice command
Command choice shows classifying content into one of several categories, with per-option probabilities and a confidence score.
Command choice shows classifying content into one of several categories, with per-option probabilities and a confidence score.
score command
Command score shows rating content against an ordered set of levels.
Command score shows rating content against an ordered set of levels.

Jump to

Keyboard shortcuts

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