TypeSafe Jev is not a chat model. It takes a piece of state and a set of typed questions, and returns typed answers with probabilities: a label from a list, a level on a scale, or the probability that a statement is true. It never generates text. That makes it a decision primitive, and decision primitives belong in code that already knows how to route on data.

The reply is a message

The official SDKs are promise-shaped: call, await, read fields off a response object. On the BEAM the natural shape is different. A process sends a message and, some time later, receives one. Jev.Server takes that literally. You reply to Jev from a callback, and its answer arrives at handle_answer/3, where the reply is a plain map:

def handle_answer(%{security: p}, from, s) when p > 0.5, do: done(from, [:security], s)
def handle_answer(%{kind: k, confidence: %{kind: c}}, from, s) when c > 0.85, do: done(from, [k], s)
def handle_answer(%{kind: k}, from, s), do: done(from, [k, :"needs-triage"], s)

Clause order is the routing. Confidence thresholds are guards. Nothing in that module touches the network, so tests construct maps and call the function.

Why not structs for answers

Every version of this library that wrapped answers in structs made the callbacks worse. %{kind: :bug} matches; %{kind: %Answer{value: :bug}} is noise in every clause. So the reply is a map with your question names as keys, and the distribution lives beside them under confidence and probabilities for the clauses that want it.

Why not helpers

The TypeSafe patterns are confidence gating, fan-out, composite scoring, and intent routing. In Python they are chains of if. In Elixir they are function heads, with clauses that accept guards, and Enum over the probabilities map. There is nothing for a helper module to add, and the library has none.

Why not a chat client

Jev fits an LLM client library as plumbing: keys, retries, telemetry, cost. It does not fit as a paradigm. Questions as data, fan-out over one state, and confidence as a routing axis are not things a chat abstraction expresses, and forcing them into a chat response struct hides what makes Jev useful. Jev is therefore standalone. Its transport, Jev.HTTP, is a single function that you can swap for another client if you already run one.

What OTP adds

  • A server holds any number of requests in flight; tags tell the answers apart.
  • Requests run under a task supervisor, so a crashed request becomes an error answer instead of taking the server down.
  • Recursion is a clause that replies again. Tree descent, bisection, and verify-and-repair loops are a few lines each. See Recursive Workflows.
  • Telemetry carries tokens, cost, and every confidence value, so calibration monitoring and evaluation harnesses attach without changing call sites.