Skip to content
Client Options and Environment

Client Options and Environment

Package: github.com/therealbill/typesafe-go (import path typesafe).

Client

type Client struct {
    // Has unexported fields.
}

Client calls the TypeSafe API. A single Client is safe for concurrent use by multiple goroutines. The zero value is not usable; NewClient builds one.

NewClient

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

NewClient builds a Client. It fails when no API key is configured, returning ErrMissingAPIKey (documented on the errors and exit codes reference).

(*Client) Close

func (c *Client) Close() error

Close releases idle connections held by a client-owned http.Client.

(*Client) SystemOne

func (c *Client) SystemOne(ctx context.Context, state any, questions Questions, opts ...RequestOption) (*SystemOneResponse, error)

SystemOne asks the named questions about state and returns the typed answers. state is a string, map, slice, or struct that encodes to JSON. Questions and SystemOneResponse are documented on the question types and response types references.

(*Client) ListModels

func (c *Client) ListModels(ctx context.Context, opts ...RequestOption) (*ListModelsResponse, error)

ListModels returns the models available to the account. ListModelsResponse is documented on the response types reference.

(*Client) String

func (c *Client) String() string

String renders the client for fmt verbs. It returns typesafe.Client{base_url: <url>, model: <model>} and never includes the API key.

(*Client) LogValue

func (c *Client) LogValue() slog.Value

LogValue renders the client for slog. It returns slog.GroupValue(slog.String("base_url", ...), slog.String("model", ...)), only those two string attributes, never the API key.

Configuration resolution order

Defaults and environment variable names. Explicit options win over the environment, which wins over these defaults.

Setting1. Option2. Environment variable3. Default
API keyWithAPIKeyTYPESAFE_API_KEYnone. NewClient returns ErrMissingAPIKey if neither is set. An empty or whitespace-only value from either the option or the environment variable is treated as not-set and falls through to the next source.
Base URLWithBaseURLTYPESAFE_BASE_URLDefaultBaseURL (https://api.typesafe.ai)
ModelWithModelTYPESAFE_DEFAULT_MODELDefaultModel (jev-latest)
LoggerWithLogger (sets a *slog.Logger directly)TYPESAFE_LOG_LEVEL (selects a text logger on stderr: debug, info, warning/warn, error)no logging (any other value, including off and unset)

Defaults and environment variable constants

const (
    DefaultBaseURL = "https://api.typesafe.ai"
    DefaultModel   = "jev-latest"
    DefaultTimeout = 10 * time.Second

    EnvAPIKey       = "TYPESAFE_API_KEY"
    EnvBaseURL      = "TYPESAFE_BASE_URL"
    EnvDefaultModel = "TYPESAFE_DEFAULT_MODEL"
    EnvLogLevel     = "TYPESAFE_LOG_LEVEL"
)
NameValue
DefaultBaseURLhttps://api.typesafe.ai
DefaultModeljev-latest
DefaultTimeout10 * time.Second
EnvAPIKeyTYPESAFE_API_KEY
EnvBaseURLTYPESAFE_BASE_URL
EnvDefaultModelTYPESAFE_DEFAULT_MODEL
EnvLogLevelTYPESAFE_LOG_LEVEL

Option

type Option func(*Client) error

Option configures a Client.

Client-level option functions

FunctionSignatureDescription
WithAPIKeyfunc WithAPIKey(key string) OptionSets the API key, ignoring surrounding whitespace. Otherwise TYPESAFE_API_KEY is used. An empty or whitespace-only key is treated as unset and the environment is consulted.
WithBaseURLfunc WithBaseURL(u string) OptionSets the API root, for example for a gateway. Otherwise TYPESAFE_BASE_URL or https://api.typesafe.ai is used. Validated by normalizeBaseURL; see Base URL validation below.
WithModelfunc WithModel(m string) OptionSets the default model. Otherwise TYPESAFE_DEFAULT_MODEL or jev-latest is used.
WithRetryPolicyfunc WithRetryPolicy(p RetryPolicy) OptionReplaces the default retry policy.
WithTimeoutfunc WithTimeout(d time.Duration) OptionSets the timeout for each HTTP attempt. Default 10s. Zero disables the per-attempt timeout. A negative duration returns an error immediately (typesafe: timeout must not be negative).
WithHeadersfunc WithHeaders(h http.Header) OptionAdds headers to every request. A header given here replaces the one the client would send, so supplying Authorization replaces the API key header, which some gateways require. The http.Header is cloned when this option is constructed (not when it is later applied to a Client), so a caller that mutates its header value afterward cannot change what the client sends.
WithHTTPClientfunc WithHTTPClient(hc *http.Client) OptionUses a caller-supplied http.Client. The client is copied so the caller’s value is not modified when instrumentation wraps its transport.
WithInstrumentationfunc WithInstrumentation(i Instrumentation) OptionAttaches an observer, such as the otel subpackage’s.
WithLoggerfunc WithLogger(l *slog.Logger) OptionSets the logger. Otherwise TYPESAFE_LOG_LEVEL selects a text logger on stderr, and unset means no logging.

Base URL validation

normalizeBaseURL validates the value given to WithBaseURL and the value read from TYPESAFE_BASE_URL:

ConditionResult
Scheme is not http or httpsError: typesafe: invalid base URL "<url>": scheme must be http or https
No hostError: typesafe: invalid base URL "<url>": missing host
A query string is presentError: typesafe: invalid base URL "<url>": must not carry a query
A fragment is presentError: typesafe: invalid base URL "<url>": must not carry a fragment
Embedded userinfo/credentials (user:pass@host)Error: typesafe: invalid base URL "<url>": must not carry credentials
A trailing / in the pathTrimmed; not an error.

A path prefix in the base URL is preserved: requests are built with url.JoinPath, so WithBaseURL("https://gw.example.com/api") plus a request to /v1/systemone produces https://gw.example.com/api/v1/systemone.

RequestOption

type RequestOption func(*requestConfig) error

RequestOption configures a single call. An option that cannot be applied returns an error, which the call returns before contacting the API. Options are applied before Instrumentation.RequestStart is called, so an option error (for example from WithRequestTimeout) is returned before instrumentation starts and is never recorded by an Instrumentation hook; state and questions validation errors, which happen after RequestStart, are recorded.

Per-call option functions

FunctionSignatureDescription
WithRequestModelfunc WithRequestModel(m string) RequestOptionOverrides the client’s model for this call.
WithRequestRetryfunc WithRequestRetry(p RetryPolicy) RequestOptionOverrides the retry policy for this call.
WithRequestTimeoutfunc WithRequestTimeout(d time.Duration) RequestOptionOverrides the per-attempt timeout for this call. Zero disables the per-attempt timeout. A negative duration returns &typesafe.ValidationError{Path: "timeout", Err: errors.New("timeout must not be negative")} immediately, before any network call.
WithExtraHeadersfunc WithExtraHeaders(h http.Header) RequestOptionAdds headers to this call, replacing client headers with the same name. As with WithHeaders, an Authorization header given here replaces the API key header. The http.Header is cloned when this option is constructed, not when it is applied, so a caller that mutates its header value afterward cannot change the call.
WithExtraBodyfunc WithExtraBody(fields map[string]any) RequestOptionMerges fields into the top level of the request body. Use it for API fields this package does not model yet. The map is copied when this option is constructed, not when it is applied. The keys the client sets itself (state, model, questions) are rejected if present, with &typesafe.ValidationError{Path: "extra_body.<key>", Err: errors.New("field is set by the client and must not be overridden")}.

NewLogger

func NewLogger(w io.Writer, level string) *slog.Logger

NewLogger returns a text logger on w at the named level: debug, info, warning (or warn), error. Any other value, including "off" and "", returns a logger that discards everything.

RetryPolicy

type RetryPolicy struct {
    MaxRetries      int
    InitialDelay    time.Duration
    MaxDelay        time.Duration
    Jitter          float64
    Budget          time.Duration
    RetryStatuses   []int
    RetryOnConnErr  bool
    RetryOnTimeout  bool
    HonorRetryAfter bool
    MaxRetryAfter   time.Duration
    ShouldRetry     func(resp *http.Response, err error) bool
}

RetryPolicy controls how failed requests are retried. DefaultRetryPolicy returns a populated policy whose fields can then be changed. A zero RetryPolicy disables retries.

FieldTypeDescriptionDefault (DefaultRetryPolicy)
MaxRetriesintNumber of retries after the first attempt. 0 disables retries.2
InitialDelaytime.DurationDelay before the first retry.500ms
MaxDelaytime.DurationCaps the exponential backoff.5s
Jitterfloat64Fraction of each delay randomly subtracted, from 0 to 1.0.25
Budgettime.DurationTotal time allowed per call including delays. 0 means unlimited.30s
RetryStatuses[]intHTTP statuses that are retried.408, 429, 500–599
RetryOnConnErrboolRetries connection failures.true
RetryOnTimeoutboolRetries per-request timeouts.true
HonorRetryAfterboolUses Retry-After and retry-after-ms headers as the delay.true
MaxRetryAftertime.DurationCaps a server-requested wait taken from Retry-After or retry-after-ms. Does not apply to the exponential backoff, which MaxDelay bounds instead. A value ≤ 0 is replaced by the default when the policy is normalized before use.5m
ShouldRetryfunc(resp *http.Response, err error) boolWhen set, replaces every other decision. resp may be nil, and when it is not, its Body has already been drained and closed, so only the status and headers are readable.nil (unset)

DefaultRetryPolicy

func DefaultRetryPolicy() RetryPolicy

DefaultRetryPolicy returns the policy used when none is configured. It matches the official Python SDK’s defaults.

Instrumentation

type Instrumentation interface {
    // RequestStart is called once per SystemOne or ListModels call. The
    // returned context is used for every HTTP attempt. The returned function
    // is called exactly once when the call finishes.
    RequestStart(ctx context.Context, info RequestInfo) (context.Context, func(RequestResult))
    // Transport wraps the HTTP round tripper used by the client. It is called
    // once at client construction and may return rt unchanged.
    Transport(rt http.RoundTripper) http.RoundTripper
}

Instrumentation observes client calls. The otel subpackage provides an OpenTelemetry implementation; its span attribute keys are documented on the span attributes reference.

RequestInfo

type RequestInfo struct {
    Operation     string
    Model         string
    QuestionCount int
    NoulCount     int
    ChoiceCount   int
    ScoreCount    int
    State         any
    Questions     Questions
}

RequestInfo describes a call about to be made. It is passed to Instrumentation.RequestStart before the first HTTP attempt.

FieldTypeDescription
Operationstring"system_one" or "list_models".
ModelstringThe requested model name or alias.
QuestionCountintTotal number of questions in the map.
NoulCountintNumber of Noul questions.
ChoiceCountintNumber of Choice questions.
ScoreCountintNumber of Score questions.
StateanyThe request input state. Instrumentation must not record it unless the caller opted in.
QuestionsQuestionsThe request input questions. Instrumentation must not record them unless the caller opted in.

RequestResult

type RequestResult struct {
    Attempts  int
    Status    int
    RequestID string
    Model     string
    Usage     Usage
    Response  *SystemOneResponse
    Err       error
}

RequestResult describes how a call ended. It is passed to the function returned by Instrumentation.RequestStart exactly once.

FieldTypeDescription
AttemptsintNumber of HTTP attempts made, including the first.
StatusintFinal HTTP status, or 0 if no response was received.
RequestIDstringThe x-typesafe-request-id header of the final response.
ModelstringModel reported by the response, if any.
UsageUsageToken usage reported by the response, if any. See the response types reference.
Response*SystemOneResponseDecoded response for system_one, nil otherwise or on error. See the response types reference.
ErrerrorError returned to the caller, nil on success.