Jev.HTTP (Jev v0.2.2)

Copy Markdown View Source

The transport: one POST to /v1/systemone per call.

This is the Jev.Backend for TypeSafe and every server that speaks its wire format, and the default. Jev.Server calls it from a task; scripts and evaluation harnesses call it directly.

Configuration

config :jev,
  api_key: System.get_env("TYPESAFE_API_KEY"),  # or the TYPESAFE_API_KEY env var
  base_url: "https://api.typesafe.ai",
  model: "jev-latest",
  max_retries: 3,
  receive_timeout: 30_000,
  usd_per_million_input: 0.042,
  req_options: []                                 # merged into Req.new/1, e.g. a test plug

Every option except usd_per_million_input and req_options can also be passed per call. Requests that fail with 429 or 529, or with a gateway error (502, 503, 504), are retried with backoff, honouring Retry-After.

Endpoints

The wire format is spoken by more than TypeSafe: self-hosted decision models such as Laya, kev, decider, and jeff serve the same /v1/systemone. Name them under endpoints and pick one per call with endpoint::

config :jev,
  endpoints: [
    laya: [base_url: "http://localhost:8000"],
    jeff: [base_url: "https://jeff.internal", api_key: "...", model: "gliformer-large"]
  ]

Jev.HTTP.post(state, questions, endpoint: :laya)

The top-level configuration is the :typesafe endpoint, which is the default; config :jev, endpoint: :laya changes the default. A named endpoint takes base_url (required), api_key, model, and usd_per_million_input, plus max_retries, receive_timeout, and req_options. It never inherits the TypeSafe key or price: without an api_key no Authorization header is sent, and the price defaults to zero. Transport settings are inherited. See endpoint/1 for the resolved result.

Telemetry

Every call runs under Jev.Telemetry.span/4, with backend: Jev.HTTP, the endpoint name, and the model in the metadata; the stop event adds status and request_id. The state itself is never put in metadata, only its hash.

Summary

Types

A resolved endpoint, as returned by endpoint/1.

Functions

Resolves the endpoint a call with opts would use.

Evaluates questions against state.

Types

endpoint()

@type endpoint() :: %{
  name: atom(),
  base_url: String.t(),
  api_key: String.t() | nil,
  model: String.t(),
  max_retries: non_neg_integer(),
  receive_timeout: timeout(),
  usd_per_million_input: number(),
  req_options: keyword()
}

A resolved endpoint, as returned by endpoint/1.

option()

@type option() ::
  {:endpoint, atom()}
  | {:model, String.t()}
  | {:api_key, String.t() | nil}
  | {:base_url, String.t()}
  | {:max_retries, non_neg_integer()}
  | {:receive_timeout, timeout()}
  | {:tag, term()}

Functions

endpoint(opts \\ [])

@spec endpoint([option()]) :: endpoint()

Resolves the endpoint a call with opts would use.

opts[:endpoint], then config :jev, endpoint:, then :typesafe names the endpoint. :typesafe is built from the top-level configuration; any other name is looked up under config :jev, endpoints:. Per-call base_url, api_key, model, max_retries, and receive_timeout override the result.

iex> Jev.HTTP.endpoint(endpoint: :local, model: "laya-421m").model
"laya-421m"

Raises ArgumentError for an unknown name or a named endpoint without a base_url.

post(state, questions, opts \\ [])

@spec post(
  Jev.entry(),
  keyword(Jev.shorthand()) | %{required(atom()) => Jev.shorthand()},
  [option()]
) ::
  {:ok, Jev.reply()}
  | {:error, Jev.Error.t() | JSONCodec.Error.t() | Exception.t()}

Evaluates questions against state.

Returns {:ok, reply} with the map described in Jev, or {:error, error} where error is a Jev.Error for a non-2xx response, a JSONCodec.Error for a 200 whose body does not fit Jev.Wire, or the transport exception. Raises ArgumentError for malformed questions, an unknown endpoint, or a missing TypeSafe API key.