Homejev
Open sourceGo 1.27+

jev

A Go client for the TypeSafe Jev model

Jev does not write text. You send it a state, a ticket, a log line, the current state of your program, and a set of questions, and it answers each one with a typed value and a probability. jev is how you ask from Go.

jev is an independent client built by Hellenic Development and is not affiliated with TypeSafe AI, whose own SDKs are JavaScript and Python. It is one of the first Go libraries for the model, and it speaks the same System One API the official ones do.

One state, three questions, one call

Name each question yourself and read the answer back under that name. Adding a question to the call does not add a round trip.

main.go
client, err := jev.New() // reads TYPESAFE_API_KEY
resp, err := client.SystemOne(ctx, jev.Request{
    State: "I was charged twice. Please fix this ASAP.",
    Questions: jev.Questions{
        "billing": jev.Noul{Instructions: "Is this ticket about billing?"},
        "team": jev.Choice{
            Instructions: "Which team should handle this?",
            Criteria: map[string]any{
                "billing":   "Payments, invoices, refunds",
                "technical": nil, // null means the label has no description
            },
        },
        "urgency": jev.Score{
            Instructions: "How urgent is this ticket?",
            Criteria:     []string{"can wait", "this week", "today"},
        },
    },
})

billing, _ := resp.Noul("billing")
team, _ := resp.Choice("team")
urgency, _ := resp.Score("urgency")

For one question there are shortcuts: Noul, Classify and Rate each send a single question and return its answer directly. The default model is jev-latest, an alias that moves; pin jev-1.13.0 when a later run has to hit the same model.

3 kinds of question

Each one is a Go type with an Instructions field and, where it needs them, the criteria. The client checks the caps before anything is sent.

Yes or no

jev.Noul

You give
A yes/no question about the state.
You get back
The probability of yes, from 0 to 1.

Pick one

jev.Choice

You give
Named options, up to 255, each with an optional description.
You get back
The picked option, a probability for every option, and a confidence.

Rate it

jev.Score

You give
An ordered list of levels, up to 10.
You get back
A position on that list, which can fall between levels, a probability per level, and a confidence.

A pick-one question takes at most 255 options and a rate-it question at most 10 levels. Those are the API's caps, and jev rejects a request that exceeds them before it costs you a token.

What the client does for you

An HTTP call is the easy part. These are the parts a production program needs and would otherwise write itself.

It stays under the limit

TypeSafe allows 1,200 requests a minute and 250,000 input tokens a second per account. Every attempt waits on a client-side limiter set to those figures before it sends, so a busy program paces itself instead of learning the limit from 429 responses. Tokens are estimated from the body before the call and corrected from the usage the API reports after it.

Retries, the way the official SDK does them

2 retries on 408, 429 and every 5xx, with a 500ms to 5s backoff and up to a quarter subtracted at random, which is the JavaScript SDK's policy. Retry-After is honoured, and when the server sends one, every client sharing the limiter pauses, since a 429 is about the account and not about one call. Each attempt has its own 10s timeout; your context still wins.

Typed answers, or your own struct

Read an answer by the name you gave the question and get a typed value back: the probability of yes, the chosen option with its confidence, the position on your scale. Or hand the call a struct and decode the whole response into it with one generic method. That method is the reason the library asks for Go 1.27.

13 errors you can test for

A 401 is ErrUnauthorized, a 429 after the retries is ErrRateLimited, a dropped connection is ErrConnection, and a body that does not match the contract is ErrResponse. All of them work with errors.Is, and the API error behind them carries the status, the request id and the message, so the line you log is the line TypeSafe support can look up.

The bytes the model reads are the bytes you sent

The request is encoded with the standard library's encoding/json/v2, with object keys sorted so the same request always produces the same body, and with the characters <, > and & left as they are. The older encoder rewrites those three to escape sequences, and the text the model would read is then not the text you passed.

Your coding assistant already knows it

The repository ships an Agent Skill with the real signatures, the option names and the mistakes to avoid. One command installs it for Claude Code, Cursor, Codex and Plexon at once, so an assistant writing Go against jev reads the library instead of guessing at it.

One account, one budget

The limits are per account, not per client. Two clients on the same key should share one limiter, and then a Retry-After seen by either pauses both.

shared := jev.NewLimiter(jev.DefaultRateLimit())
latest, err := jev.New(jev.WithRateLimiter(shared))
pinned, err := jev.New(jev.WithModel("jev-1.13.0"), jev.WithRateLimiter(shared))

Enterprise plans get higher limits and TypeSafe adjusts them without notice, so the figures are yours to set. A zero limit turns pacing off, and a plain rate.Limiter from golang.org/x/time/rate works too.

For coding assistants

The repository carries an Agent Skill: a file that tells a coding assistant what the library is for, its real signatures, its option names and the mistakes it has seen assistants make. Install it once and every agent on the machine reads it.

$ npx skills add kataras/jev --skill jev -g -y
  • Claude Code, Cursor, Codex and Plexon read the same file
  • Also installable as a Claude Code plugin from the repository
  • A test in the repository keeps the skill in step with the Go API
Read the skill

Checked against the contract

The exported types and functions carry links to the section of the TypeSafe documentation they implement, so a reader can hold the client up against the API it speaks. Where the Go client makes a different choice from the official SDKs, the README says which and why.

  • Retry policy and version header match the JavaScript SDK
  • Rate-it criteria are a list, as the SDKs require since their v0.6.0
  • Live tests run only when an API key is present, so a fork spends nothing
  • Released under the MIT license
About TypeSafe and Jev

Install it

Go 1.27 or later, and one dependency, golang.org/x/time/rate. The client reads TYPESAFE_API_KEY from the environment, so jev.New with no options is ready to send.

$ go get github.com/kataras/jev

Documentation at pkg.go.dev/github.com/kataras/jev. Released under the MIT license.

jev: common questions

What is jev?

jev is an open-source Go client for the TypeSafe System One API and its model, Jev, written by Gerasimos Maropoulos at Hellenic Development. Jev does not write text. You send it a state, which can be a string or JSON such as a support ticket, a log line or the current state of a program, together with one or more named questions, and it returns a typed answer and a probability for every question in one round trip. The library covers the 3 question kinds the API offers, yes/no, pick-one and rate-it, adds retries, client-side rate limiting and 13 error sentinels on top, and ships an Agent Skill so coding assistants use it correctly. It is published as the Go module github.com/kataras/jev, licensed MIT, and requires Go 1.27 or later. The current release is 0.1.0.

Is jev the official TypeSafe SDK for Go?

No. jev is an independent client and is not affiliated with TypeSafe AI. TypeSafe publishes 2 official SDKs, in JavaScript and Python, and jev speaks the same HTTP contract from Go, one of the first libraries to do so. Where the official SDKs make a choice a Go client should follow, jev follows it: the retry policy matches the JavaScript SDK, the same version header is sent, and the rule that a rate-it question takes a list rather than a map matches the SDK change that introduced it. Where Go differs, jev says so in its documentation, and its exported types and functions link to the section of the TypeSafe documentation they implement, so a reader can check the client against the contract.

What can I ask Jev through jev?

Through jev you can ask Jev 3 kinds of question about one state. A yes/no question returns the probability of yes, from 0 to 1. A pick-one question takes up to 255 named options, each with an optional description, and returns the chosen option, a probability for every option and a confidence. A rate-it question takes an ordered list of up to 10 levels and returns a position on that list, which can fall between two levels, with a probability per level and a confidence. Several questions about the same state belong in one call, because adding a question does not add a round trip. Question names are yours and come back as the answer keys. The model defaults to jev-latest, an alias that moves; pin a version such as jev-1.13.0 when a later run has to hit the same model.

How does jev handle rate limits and errors?

jev stays under the TypeSafe account limits rather than discovering them through 429 responses. Every attempt, retries included, first waits on a client-side limiter built with golang.org/x/time/rate, set by default to the documented 1,200 requests a minute and 250,000 input tokens a second. Input tokens are estimated from the request body before the call and corrected from the usage the API reports after it. When the server does answer with Retry-After, every client sharing that limiter pauses, since the limit is per account, and clients on one key can share a single limiter. Behind the limiter sit 2 retries with a 500ms to 5s backoff, the policy the official JavaScript SDK uses. Failures come back as one of 13 sentinels for errors.Is, such as ErrUnauthorized or ErrRateLimited, and the API error carries the HTTP status, the request id and the message.

How do I install jev?

Install jev by running go get github.com/kataras/jev inside a Go module, then import github.com/kataras/jev. Go 1.27 or later is required, because decoding a response straight into your own struct is a generic method, and the only dependency is golang.org/x/time/rate. The client reads TYPESAFE_API_KEY from the environment, so a program that calls jev.New with no options is ready to send. The API reference is at pkg.go.dev/github.com/kataras/jev and the repository carries a runnable quickstart. If you write Go with a coding assistant, run npx skills add kataras/jev --skill jev -g -y once and Claude Code, Cursor, Codex and Plexon all learn the library's real signatures instead of guessing them.