Skip to content

Configuration

Defaults

With TYPESAFE_API_KEY set, a no-argument JevClient() is ready to use:

// With TYPESAFE_API_KEY set, the defaults are all you need.
JevClient().use { jev ->
    println(jev.config) // the key is redacted
}

JevClient holds a Ktor HTTP client, so close it when you're done. use { } does that for you.

The configuration builder

val jev =
    JevClient {
        apiKey = System.getenv("MY_TYPESAFE_KEY")      // default: TYPESAFE_API_KEY
        baseUrl = "https://api.typesafe.ai"            // default: TYPESAFE_BASE_URL, then this
        defaultModel = "jev-1.14.0"                    // default: TYPESAFE_DEFAULT_MODEL, then "jev-latest"
        timeout = 15.seconds                           // per HTTP attempt; default 10 s
        retry = RetryPolicy(maxRetries = 4)            // see "Retries & Errors"
        headers["X-Request-Source"] = "support-triage" // sent with every request
    }

Each setting resolves in this order: the explicit value, then the environment variable, then the default. String settings are trimmed, so the trailing newline of a value read from a file is harmless, and a value that is blank after trimming counts as unset.

Setting Environment variable Default
apiKey TYPESAFE_API_KEY none; required
baseUrl TYPESAFE_BASE_URL https://api.typesafe.ai
defaultModel TYPESAFE_DEFAULT_MODEL jev-latest
timeout 10 seconds per HTTP attempt
retry RetryPolicy(), matching the JS SDK; see Retries & Errors
engine the platform's default engine, created by the client (CIO on the JVM)
headers none; extra headers sent with every request
allowInsecureHttp false; allows a plain http:// baseUrl on a host other than this machine

A missing API key throws a JevConfigException that names TYPESAFE_API_KEY, on its own; the other settings are checked, and reported together, once there is a key. JevConfig.toString() redacts the key, so it's safe to log.

Running with Ollaya

jev4k works with Ollaya, which serves the same API from your own machine. Ollaya runs Laya, a different model from Jev, so answers can differ from TypeSafe's. No code changes are needed; point the client at it with three environment variables:

TYPESAFE_API_KEY=demo
TYPESAFE_BASE_URL=http://localhost:11435
TYPESAFE_DEFAULT_MODEL=laya

For an Ollaya server on another host, such as http://ollaya:11435 in Docker, also set allowInsecureHttp = true: plain http:// is accepted without it only for this machine (localhost, 127.x.x.x, ::1).

Retry policies

// More patience for a batch job:
JevClient {
    retry =
        RetryPolicy(
            maxRetries = 5,
            initialBackoff = 1.seconds,
            maxBackoff = 20.seconds,
        )
},
// Fail fast in an interactive request path:
JevClient { retry = RetryPolicy.NONE },
// Retry rate limits and overloads, but not other server errors:
JevClient { retry = RetryPolicy(retryStatuses = setOf(429, 529)) },

To use a different timeout, retry policy or headers for some calls only, pass per-call options rather than building a second client.

A custom HTTP engine

The client creates its own engine unless you pass one: CIO on the JVM, Darwin on Apple platforms, Curl on Linux, WinHttp on Windows, and the fetch-based Js engine on Node.js. To tune connection pooling, TLS, or proxies, pass your own Ktor engine:

// Supply a tuned Ktor engine instead of the default CIO one. The client won't close an engine you pass in.
val jev =
    JevClient {
        engine =
            CIO.create {
                maxConnectionsCount = 64
                endpoint.maxConnectionsPerRoute = 16
                endpoint.connectTimeout = 2_000 // milliseconds
            }
    }

The client doesn't close an engine you pass in, so one engine can be shared by several clients. Close it yourself when you're done with it. timeout still bounds each whole attempt, but the connect and socket timeouts are left to an engine you supply, so settings like endpoint.connectTimeout above take effect.

The builder checks the settings that would otherwise fail every request, or that engines would send differently. With a key present, it throws a JevConfigException listing every problem it found at once:

  • baseUrl must be an absolute https:// URL with a host and, optionally, a path. Plain http:// is accepted for this machine, or anywhere with allowInsecureHttp = true. Credentials, a query or a fragment are refused; send what a gateway needs in headers instead.
  • apiKey must be printable ASCII with no spaces: a zero-width space or other invisible character pasted with it would make the server see a different key. A leading byte-order mark on any setting, as in a file saved with one, is dropped.
  • headers must have HTTP token names and ASCII values without control characters (a tab is allowed). Content-Length, Content-Type, Transfer-Encoding and Upgrade can't be set, since jev4k or the engine sets them.
  • timeout must be at least a millisecond.

The messages never quote the key, a header value, or a URL that could hold credentials.

Security

  • Keep API keys on the server. Never ship them in a browser or mobile client; an Apple-platform app should reach Jev through a backend of your own (baseUrl) that adds the real key.
  • The key is sent as Authorization: Bearer ... on every request, and redacted from JevConfig.toString().
  • Every request carries a User-Agent of jev4k/<version>. A headers entry of the same name replaces the built-in one rather than adding a second value, so a gateway that needs its own Authorization or Accept can have it.
  • Redirects are never followed. A 3xx response is reported as a JevApiException with its status, because following one would send your headers, and on Node.js the request body, to whatever host it names.
  • A response whose declared Content-Length is over 16 MiB is refused before its body is read into memory. A body without a declared length isn't checked; the per-attempt timeout bounds how long it can stream. On Linux, Apple platforms and Node.js a compressed response has no declared length either: Curl, NSURLSession and fetch ask for compression, decompress the body themselves and drop its length. On the JVM and Windows the engine doesn't ask for it.