Installation
Add Jev to your dependencies:
def deps do
[{:jev, "~> 0.1"}]
endJev requires Elixir 1.18 or later, for the built-in JSON module, and Erlang/OTP 27 or later.
API key
Set TYPESAFE_API_KEY in the environment, or configure it:
# config/runtime.exs
config :jev, api_key: System.fetch_env!("TYPESAFE_API_KEY")Keys come from the TypeSafe dashboard.
Without a key, a self-hosted model that speaks the same wire format works too. Name it as an endpoint and make it the default:
config :jev, endpoint: :laya, endpoints: [laya: [base_url: "http://localhost:8000"]]Endpoints are covered in the Server guide.
First call
Jev.HTTP.post/3 sends questions about a piece of state and returns a plain map:
{:ok, reply} =
Jev.HTTP.post("My flight was cancelled. Can I get a refund?",
refund: "Does the customer want money returned?",
request: {"What is the main request?", %{refund: nil, rebooking: nil, information: nil}},
frustration: {"How frustrated is the customer?", ["Calm", "Concerned but civil", "Angry"]}
)
reply.refund #=> 0.97
reply.request #=> :refund
reply.frustration #=> 0.6
reply.confidence #=> %{request: 0.95, frustration: 0.7}A bare string is a yes/no question and comes back as a probability. A tuple with a map of labels is a choice and comes back as the label. A tuple with a list of levels is a score and comes back as the expected level. See Questions.
First server
The point of the library is Jev.Server. Instead of calling Jev, a server
replies to it and gets the answer back as a callback:
defmodule Refunds do
use Jev.Server
def init(_), do: {:ok, %{}}
def handle_call({:route, message}, from, s) do
{:reply, {from, message,
request: {"What is the main request?", %{refund: nil, rebooking: nil, information: nil}}}, s}
end
def handle_answer(%{request: r, confidence: %{request: c}}, from, s) when c > 0.85 do
GenServer.reply(from, {:route_to, r})
{:noreply, s}
end
def handle_answer(%{request: r}, from, s) do
GenServer.reply(from, {:confirm_with_user, r})
{:noreply, s}
end
end{:ok, pid} = Jev.Server.start_link(Refunds, [])
GenServer.call(pid, {:route, "My flight was cancelled. Can I get a refund?"})
#=> {:route_to, :refund}The server does not block while Jev thinks. The caller's from rides along as
the tag, and the matching clause answers it when the reply lands. See
Server for the full callback contract.
Running the example
The repository ships a triage example that sends four issues through one server concurrently:
TYPESAFE_API_KEY=... mix run examples/triage.exs