Documentation
¶
Overview ¶
Package jev is a Go client for the TypeSafe AI System One API and its flagship model, Jev: send a state and typed questions, get typed answers with probabilities.
client, err := jev.NewClient() // reads TYPESAFE_API_KEY
...
resp, err := client.SystemOne(ctx, jev.Request{
State: "I was charged twice. Please fix this ASAP.",
Questions: jev.Questions{
"urgent": jev.Noul{Instructions: "Does this convey urgency?"},
"department": jev.Choice{
Instructions: "Which team should handle this?",
Criteria: map[string]any{"billing": nil, "technical": nil, "other": nil},
},
"frustration": jev.Score{
Instructions: "How frustrated is the customer?",
Criteria: []string{"Calm", "Frustrated", "Very angry"},
},
},
})
...
if department, ok := resp.Choice("department"); ok {
fmt.Println(department.Choice, department.Confidence)
}
Answers are a sealed interface: type switch on NoulAnswer, ChoiceAnswer, ScoreAnswer, and UnknownAnswer, or use the typed getters on Response. See the README for configuration, errors, retries, and instrumentation.
Index ¶
- Constants
- Variables
- type APIError
- type Answer
- type Choice
- type ChoiceAnswer
- type Client
- type ConnectionError
- type Model
- type ModelsResponse
- type Noul
- type NoulAnswer
- type NoulCriteria
- type Option
- func WithAPIKey(key string) Option
- func WithBaseURL(baseURL string) Option
- func WithDefaultModel(model string) Option
- func WithHTTPClient(client *http.Client) Option
- func WithHeader(key, value string) Option
- func WithHeaders(headers http.Header) Option
- func WithLogger(logger *slog.Logger) Option
- func WithMaxRetries(n int) Option
- func WithRetryPolicy(policy RetryPolicy) Option
- func WithTimeout(timeout time.Duration) Option
- func WithTracer(tracer Tracer) Option
- type Question
- type Questions
- type RawQuestion
- type Request
- type Response
- func (r *Response) Choice(name string) (ChoiceAnswer, bool)
- func (r *Response) Choices() map[string]ChoiceAnswer
- func (r *Response) Noul(name string) (NoulAnswer, bool)
- func (r *Response) Nouls() map[string]NoulAnswer
- func (r *Response) Score(name string) (ScoreAnswer, bool)
- func (r *Response) Scores() map[string]ScoreAnswer
- type ResponseMetadata
- type ResponseValidationError
- type RetryPolicy
- type Score
- type ScoreAnswer
- type SystemOneEndData
- type SystemOneStartData
- type Tracer
- type UnknownAnswer
- type Usage
Examples ¶
Constants ¶
const ( DefaultBaseURL = "https://api.typesafe.ai" DefaultModel = "jev-latest" // DefaultTimeout applies to each HTTP attempt; see // [RetryPolicy.TotalTimeout] for a whole-call deadline. DefaultTimeout = 10 * time.Second )
Defaults, matching the official TypeSafe SDKs.
const ( EnvAPIKey = "TYPESAFE_API_KEY" EnvBaseURL = "TYPESAFE_BASE_URL" EnvDefaultModel = "TYPESAFE_DEFAULT_MODEL" EnvLogLevel = "TYPESAFE_LOG_LEVEL" )
Environment variables read when the matching option is not set. Blank values are ignored.
const Version = "0.3.0"
Version is sent in the User-Agent and X-Typesafe-Sdk headers.
Variables ¶
var ( ErrBadRequest = errors.New("jev: bad request") // HTTP 400 ErrAuthentication = errors.New("jev: authentication failed") // HTTP 401 ErrPermissionDenied = errors.New("jev: permission denied") // HTTP 403 ErrNotFound = errors.New("jev: not found") // HTTP 404 ErrUnprocessableEntity = errors.New("jev: unprocessable entity") // HTTP 422 ErrRateLimit = errors.New("jev: rate limit exceeded") // HTTP 429 ErrOverloaded = errors.New("jev: service overloaded") // HTTP 529 ErrInternalServer = errors.New("jev: internal server error") // HTTP 5xx, including 529 )
Status sentinels matched by *APIError through errors.Is.
var ( // ErrConnection matches every ConnectionError. ErrConnection = errors.New("jev: connection error") // ErrTimeout matches a ConnectionError caused by a timeout: the // per-attempt timeout, [RetryPolicy.TotalTimeout], or the transport's own. ErrTimeout = errors.New("jev: request timed out") )
Transport sentinels matched by *ConnectionError through errors.Is.
var ErrInvalidConfig = errors.New("jev: invalid configuration")
ErrInvalidConfig is wrapped by configuration errors.
var ErrInvalidRequest = errors.New("jev: invalid request")
ErrInvalidRequest is wrapped by errors for requests rejected before they are sent.
var ErrResponseTooLarge = errors.New("jev: response body exceeds 16 MiB")
ErrResponseTooLarge is the Err of a *ResponseValidationError for a response body over 16 MiB. It is never retried.
Functions ¶
This section is empty.
Types ¶
type APIError ¶
type APIError struct {
StatusCode int
Method string
URL string
Header http.Header
// Body is the raw response body, or nil when empty.
Body []byte
// Message is the server's message extracted from Body, or the body
// itself, truncated to 200 characters. It can contain anything the
// server reflected, including request data.
Message string
// RequestID is the X-Typesafe-Request-Id header, or empty when absent.
RequestID string
// Attempts is the number of HTTP attempts made, including retries.
Attempts int
}
APIError is an unsuccessful HTTP response, returned after any retries. It matches the status sentinels through errors.Is:
if errors.Is(err, jev.ErrRateLimit) { ... }
Example ¶
package main
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
jev "github.com/fgn/jevgo"
)
func main() {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Retry-After", "30")
w.WriteHeader(http.StatusTooManyRequests)
fmt.Fprint(w, `{"error":"rate limit exceeded"}`)
}))
defer server.Close()
client, err := jev.NewClient(jev.WithAPIKey("key"), jev.WithBaseURL(server.URL), jev.WithMaxRetries(0))
if err != nil {
panic(err)
}
_, err = client.SystemOne(context.Background(), jev.Request{
State: "hello",
Questions: jev.Questions{"greeting": jev.Noul{Instructions: "Is this a greeting?"}},
})
var apiErr *jev.APIError
if errors.As(err, &apiErr) {
retryAfter, _ := apiErr.RetryAfter()
fmt.Println(errors.Is(err, jev.ErrRateLimit), apiErr.StatusCode, apiErr.Message, retryAfter)
}
}
Output: true 429 rate limit exceeded 30s
type Answer ¶
Answer is one of NoulAnswer, ChoiceAnswer, ScoreAnswer, or UnknownAnswer.
type Choice ¶
type Choice struct {
// Instructions is a string, or a JSON object or array.
Instructions any
// Criteria is a JSON object mapping each option to its description, such
// as a map[string]string or map[string]any. A nil description leaves the
// option undescribed.
Criteria any
}
Choice selects one option from a defined set.
func (Choice) MarshalJSON ¶
MarshalJSON encodes the question in the API wire format.
type ChoiceAnswer ¶
type ChoiceAnswer struct {
// Choice is the highest-probability option.
Choice string `json:"choice"`
// Confidence is the model's certainty in Choice, from 0 to 1.
Confidence float64 `json:"confidence"`
// Probabilities maps every option to its probability.
Probabilities map[string]float64 `json:"probabilities"`
}
ChoiceAnswer answers a Choice.
func (ChoiceAnswer) MarshalJSON ¶
func (a ChoiceAnswer) MarshalJSON() ([]byte, error)
MarshalJSON encodes the answer in the API wire format.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client calls the TypeSafe API. Create it with NewClient; it is safe for concurrent use.
func NewClient ¶
NewClient creates a client. Explicit options take precedence over environment variables, which take precedence over defaults. The API key is required.
func (*Client) DefaultModel ¶
DefaultModel returns the model used when a request does not name one.
func (*Client) ListModels ¶
ListModels returns the models available to the account.
func (*Client) SystemOne ¶
SystemOne evaluates req.State against req.Questions and returns one answer per question under the same names.
Errors are *APIError for unsuccessful responses, *ConnectionError for transport failures and timeouts, both after retries; *ResponseValidationError for a successful response that does not match the contract; an error wrapping ErrInvalidRequest for requests rejected before sending; and the context's error when ctx is done.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
jev "github.com/fgn/jevgo"
)
func main() {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
fmt.Fprint(w, `{"model":"jev-1.13","answers":{
"department":{"type":"choice","choice":"billing","confidence":0.9,"probabilities":{"billing":0.9,"other":0.1}},
"frustration":{"type":"score","score":1.4,"confidence":0.7,
"legend":{"0":"Calm","1":"Frustrated","2":"Very angry"},"probabilities":{"0":0.1,"1":0.4,"2":0.5}}},
"usage":{"input_tokens":100,"output_tokens":10}}`)
}))
defer server.Close()
client, err := jev.NewClient(jev.WithAPIKey("key"), jev.WithBaseURL(server.URL))
if err != nil {
panic(err)
}
resp, err := client.SystemOne(context.Background(), jev.Request{
State: "I was charged twice. Please fix this ASAP.",
Questions: jev.Questions{
"department": jev.Choice{
Instructions: "Which team should handle this?",
Criteria: map[string]any{"billing": nil, "other": nil},
},
"frustration": jev.Score{
Instructions: "How frustrated is the customer?",
Criteria: []string{"Calm", "Frustrated", "Very angry"},
},
},
})
if err != nil {
panic(err)
}
department, _ := resp.Choice("department")
frustration, _ := resp.Score("frustration")
fmt.Println(department.Choice, department.Confidence)
fmt.Println(frustration.Score, frustration.Legend[frustration.Level()])
}
Output: billing 0.9 1.4 Very angry
type ConnectionError ¶
type ConnectionError struct {
Method string
URL string
// Elapsed is how long the failed attempt ran.
Elapsed time.Duration
// Timeout is the SDK limit that elapsed: the per-attempt timeout or
// [RetryPolicy.TotalTimeout]. It is zero when the failure was not an SDK
// timeout, including timeouts raised by the transport itself.
Timeout time.Duration
// StatusCode, Header, and RequestID are set when the response headers
// arrived before the body failed.
StatusCode int
Header http.Header
RequestID string
Attempts int
Err error
}
ConnectionError is a request that failed without a complete HTTP response, returned after any retries. It matches ErrConnection, and ErrTimeout when the failure was a timeout.
func (*ConnectionError) Error ¶
func (e *ConnectionError) Error() string
func (*ConnectionError) Is ¶
func (e *ConnectionError) Is(target error) bool
Is reports whether target is ErrConnection or, for a timeout, ErrTimeout.
func (*ConnectionError) Unwrap ¶
func (e *ConnectionError) Unwrap() error
type Model ¶
type Model struct {
// Name is the model name or alias to send as [Request.Model].
Name string `json:"name"`
Description string `json:"description"`
// ReleaseDate is kept as sent: the API documents YYYY-MM-DD and has
// returned RFC 3339 timestamps.
ReleaseDate string `json:"release_date"`
}
Model describes an available model.
type ModelsResponse ¶
type ModelsResponse struct {
ResponseMetadata
Models []Model
}
ModelsResponse is the result of Client.ListModels.
type Noul ¶
type Noul struct {
// Instructions is a string, or a JSON object or array.
Instructions any
// Criteria optionally describes what yes and no mean.
Criteria *NoulCriteria
}
Noul asks a yes/no question. The answer is the probability of yes.
func (Noul) MarshalJSON ¶
MarshalJSON encodes the question in the API wire format.
type NoulAnswer ¶
type NoulAnswer struct {
// Noul is the probability of yes, from 0 to 1.
Noul float64 `json:"noul"`
}
NoulAnswer answers a Noul.
func (NoulAnswer) MarshalJSON ¶
func (a NoulAnswer) MarshalJSON() ([]byte, error)
MarshalJSON encodes the answer in the API wire format.
type NoulCriteria ¶
NoulCriteria describes the outcomes of a Noul; nil fields are omitted.
type Option ¶
type Option func(*config) error
Option configures a Client. Options apply in order, last wins, and are also accepted per call by Client.SystemOne and Client.ListModels as overrides for that call. An empty string or nil value means "not set" and falls back to the environment or the default.
func WithAPIKey ¶
WithAPIKey sets the API key. Defaults to TYPESAFE_API_KEY.
func WithBaseURL ¶
WithBaseURL sets the API root, for example a proxy. It must be an http or https URL without credentials, query, or fragment. Defaults to TYPESAFE_BASE_URL, then DefaultBaseURL.
func WithDefaultModel ¶
WithDefaultModel sets the model used when Request.Model is empty. Defaults to TYPESAFE_DEFAULT_MODEL, then DefaultModel.
func WithHTTPClient ¶
WithHTTPClient sets the HTTP client, which is where to install a custom or instrumented transport. Defaults to http.DefaultClient. Per-attempt timeouts are applied through the request context; a Timeout on the client also applies and is reported as a transport timeout.
func WithHeader ¶
WithHeader sets a header sent with every request, replacing any earlier value. Authorization, Accept, Content-Type, and the SDK identification headers cannot be overridden.
func WithHeaders ¶
WithHeaders sets headers sent with every request; each key replaces any earlier values for that key.
func WithLogger ¶
WithLogger sets the logger: one record per HTTP attempt at slog.LevelInfo, and headers and bodies at slog.LevelDebug. Credential headers are redacted; bodies, which contain your state and answers, are not. Without a logger, TYPESAFE_LOG_LEVEL (debug, info, warn, error, or off) selects a level on slog.Default; unset means no logging.
func WithMaxRetries ¶ added in v0.2.0
WithMaxRetries changes only the retry count, keeping the rest of the current policy. 0 disables retries.
func WithRetryPolicy ¶
func WithRetryPolicy(policy RetryPolicy) Option
WithRetryPolicy replaces the retry policy.
func WithTimeout ¶
WithTimeout sets the timeout for each HTTP attempt, including reading the response body. Defaults to DefaultTimeout.
func WithTracer ¶
WithTracer sets the Tracer observing SystemOne calls; nil disables it.
type RawQuestion ¶
RawQuestion is sent as-is, for fields or kinds this SDK version does not model. It must contain a non-empty string "type"; known kinds are validated like their typed forms.
func (RawQuestion) MarshalJSON ¶
func (q RawQuestion) MarshalJSON() ([]byte, error)
MarshalJSON encodes the underlying map.
type Request ¶
type Request struct {
// State is the content every question refers to: a string, or a JSON
// object or array such as a map, slice, or struct with json tags.
State any
// Questions is the nonempty set of named questions. Answers are returned
// under the same names.
Questions Questions
// Model overrides the client's default model when non-empty.
Model string
// Extra adds top-level fields this SDK version does not model. Keys are
// merged last-write-wins after validation, so they can replace state,
// model, and questions on the wire.
Extra map[string]any
}
Request is the input to Client.SystemOne. It is not the wire body; SystemOne validates and encodes it.
type Response ¶
type Response struct {
ResponseMetadata
// Model is the model that answered the request.
Model string
// Answers holds one answer per question under the question's name.
Answers map[string]Answer
Usage Usage
}
Response is the result of Client.SystemOne.
func (*Response) Choice ¶
func (r *Response) Choice(name string) (ChoiceAnswer, bool)
Choice returns the answer to the named Choice question.
func (*Response) Choices ¶
func (r *Response) Choices() map[string]ChoiceAnswer
Choices returns every ChoiceAnswer by question name.
func (*Response) Noul ¶
func (r *Response) Noul(name string) (NoulAnswer, bool)
Noul returns the answer to the named Noul question.
func (*Response) Nouls ¶
func (r *Response) Nouls() map[string]NoulAnswer
Nouls returns every NoulAnswer by question name.
func (*Response) Score ¶
func (r *Response) Score(name string) (ScoreAnswer, bool)
Score returns the answer to the named Score question.
func (*Response) Scores ¶
func (r *Response) Scores() map[string]ScoreAnswer
Scores returns every ScoreAnswer by question name.
type ResponseMetadata ¶
type ResponseMetadata struct {
// RequestID is the X-Typesafe-Request-Id header, or empty when absent.
RequestID string
StatusCode int
Header http.Header
// RawBody is the complete response body, including answers of kinds this
// SDK version does not model.
RawBody json.RawMessage
// Attempts is the number of HTTP attempts made, including retries.
Attempts int
}
ResponseMetadata carries transport details of a successful response.
type ResponseValidationError ¶
type ResponseValidationError struct {
StatusCode int
Method string
URL string
// Path is the dotted path of the offending field, such as
// "answers.tone.confidence".
Path string
Header http.Header
Body json.RawMessage
RequestID string
Attempts int
Err error
}
ResponseValidationError is a successful HTTP response whose body does not match the API contract or the questions asked.
func (*ResponseValidationError) Error ¶
func (e *ResponseValidationError) Error() string
func (*ResponseValidationError) Unwrap ¶
func (e *ResponseValidationError) Unwrap() error
type RetryPolicy ¶
type RetryPolicy struct {
// MaxRetries is the number of retries after the initial attempt.
// Default: 2.
MaxRetries int
// InitialBackoff is the first backoff delay, doubled each retry up to
// MaxBackoff. Default: 500ms.
InitialBackoff time.Duration
// MaxBackoff caps the backoff delay. Default: 5s.
MaxBackoff time.Duration
// Jitter is the fraction of each backoff delay randomly subtracted, from
// 0 to 1. Default: 0.25.
Jitter float64
// Statuses lists the HTTP status codes that are retried. Default: 408,
// 429, and 500 through 599.
Statuses []int
// RespectRetryAfter honors Retry-After-Ms and Retry-After response headers
// up to MaxRetryAfter; longer delays fall back to backoff. Default: true.
RespectRetryAfter bool
// MaxRetryAfter is the longest server-requested delay honored. Default: 60s.
MaxRetryAfter time.Duration
// ConnectionErrors retries connection failures, including interrupted
// response bodies. Default: true.
ConnectionErrors bool
// Timeouts retries attempts that timed out. Default: true.
Timeouts bool
// TotalTimeout is a deadline for the whole call, attempts and delays
// included. When it expires during an attempt the call fails with a
// [*ConnectionError] matching [ErrTimeout]; a retry whose delay would
// reach it is skipped and the last error returned. Zero disables it; the
// caller's context deadline always applies. Default: 0.
TotalTimeout time.Duration
// ShouldRetry, when set, is consulted for API and connection errors the
// rules above do not retry. It never sees context errors or response
// validation errors, which are not retried.
ShouldRetry func(error) bool
}
RetryPolicy controls how failed attempts are retried. Start from DefaultRetryPolicy and adjust fields, or use WithMaxRetries; a zero RetryPolicy disables retries.
func DefaultRetryPolicy ¶
func DefaultRetryPolicy() RetryPolicy
DefaultRetryPolicy returns the SDK default policy, which matches the official TypeSafe SDKs.
type Score ¶
type Score struct {
// Instructions is a string, or a JSON object or array.
Instructions any
// Criteria is a nonempty JSON array of level descriptions from lowest to
// highest, such as a []string or []any.
Criteria any
}
Score rates the state against ordered levels. The answer is a probability-weighted position along them.
func (Score) MarshalJSON ¶
MarshalJSON encodes the question in the API wire format.
type ScoreAnswer ¶
type ScoreAnswer struct {
// Score is the probability-weighted position along the levels, from 0 to
// the highest level. It usually falls between levels; see [ScoreAnswer.Level].
Score float64 `json:"score"`
// Confidence is the model's certainty in Score, from 0 to 1.
Confidence float64 `json:"confidence"`
// Legend maps each level to the description sent in the request.
Legend map[int]any `json:"legend"`
// Probabilities maps every level to its probability.
Probabilities map[int]float64 `json:"probabilities"`
}
ScoreAnswer answers a Score.
func (ScoreAnswer) Level ¶ added in v0.2.0
func (a ScoreAnswer) Level() int
Level returns the highest-probability level, the lowest on a tie. Use it instead of truncating Score, which is a weighted average.
func (ScoreAnswer) MarshalJSON ¶
func (a ScoreAnswer) MarshalJSON() ([]byte, error)
MarshalJSON encodes the answer in the API wire format.
type SystemOneEndData ¶
type SystemOneEndData struct {
// Response is nil when Err is non-nil.
Response *Response
Err error
// Attempts is the number of HTTP attempts made; zero when none was sent.
Attempts int
}
SystemOneEndData describes a completed call.
type SystemOneStartData ¶
type SystemOneStartData struct {
Request *Request
// Model is the model on the wire, after [Request.Extra] is applied.
Model string
// Body is the encoded wire request.
Body json.RawMessage
}
SystemOneStartData describes a call about to be sent.
type Tracer ¶
type Tracer interface {
TraceSystemOneStart(ctx context.Context, data SystemOneStartData) context.Context
TraceSystemOneEnd(ctx context.Context, data SystemOneEndData)
}
Tracer observes SystemOne calls that passed local validation. The context returned by TraceSystemOneStart is used for every HTTP attempt and passed to TraceSystemOneEnd, which runs exactly once per traced call.
Implementations must be safe for concurrent use and must not modify the data they receive.
func MultiTracer ¶
MultiTracer composes tracers. Start contexts chain in order, so each tracer sees values set by the ones before it; End runs in reverse order with the context returned by the last Start. A tracer listed more than once must cope with being started twice under the same context.
type UnknownAnswer ¶
type UnknownAnswer struct {
Type string
// Raw is the complete answer object.
Raw json.RawMessage
}
UnknownAnswer is an answer of a kind this SDK version does not model.
func (UnknownAnswer) MarshalJSON ¶
func (a UnknownAnswer) MarshalJSON() ([]byte, error)
MarshalJSON returns the raw answer object.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
contrib
|
|
|
langfuse
module
|
|
|
examples
|
|
|
basic
command
Command basic asks the three question types about a support ticket.
|
Command basic asks the three question types about a support ticket. |
|
structured
command
Command structured evaluates an order against a return policy using structured state, instructions, and criteria, and shows how to type switch over answers.
|
Command structured evaluates an order against a return policy using structured state, instructions, and criteria, and shows how to type switch over answers. |