TypeSafe live walkthrough

Copy Markdown View Source
Mix.install([
  {:typesafe_api, "~> 0.1.0-alpha.3"}
])

Client

The client reads TYPESAFE_API_KEY from the environment. If Livebook was launched from the Dock it will not see your zshrc, so it also falls back to a Livebook secret named TYPESAFE_API_KEY (exposed as LB_TYPESAFE_API_KEY).

api_key = System.get_env("TYPESAFE_API_KEY") || System.fetch_env!("LB_TYPESAFE_API_KEY")
client = TypeSafeAPI.new(api_key: api_key)

The question set

Three question types, one state. Keys are atoms and round-trip back as atoms.

state = "Help! My payouts have been failing for 3 days."

questions = [
  urgent: TypeSafeAPI.noul("Does this convey urgency?"),
  dept:
    TypeSafeAPI.choice("Which team should handle this?",
      billing: "Payments, invoicing, refunds",
      technical: "Bugs, outages, integrations",
      sales: nil
    ),
  anger: TypeSafeAPI.score("How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"])
]

What goes over the wire

TypeSafeAPI.prepare/1 validates the question set and serializes it once. The encoded questions keep the order you wrote them in, and a batch splices the same bytes into every request instead of encoding the questions again per state.

{:ok, prepared} = TypeSafeAPI.prepare(questions)
prepared
body = %{"state" => state, "model" => client.model, "questions" => prepared.encoded}

req = TypeSafeAPI.HTTP.build(client, :post, TypeSafeAPI.SystemOne.path(), body)
IO.puts("POST #{client.base_url}#{TypeSafeAPI.SystemOne.path()}")
for {k, v} <- req.headers, k != "authorization", do: IO.puts("#{k}: #{Enum.join(List.wrap(v), ", ")}")
IO.puts("authorization: Bearer sk-***\n")
IO.puts(JSON.encode!(body))

Raw response

The raw HTTP layer returns maps, plus status, headers, request id, and retry count.

{:ok, resp} = TypeSafeAPI.HTTP.request(client, :post, TypeSafeAPI.SystemOne.path(), body)
resp

Typed result

evaluate/3 does all of the above and decodes into typed answer structs.

{:ok, result} = TypeSafeAPI.evaluate(client, state, questions)
result.answers
result.usage

Routing on the answers

alias TypeSafeAPI.Answer

cond do
  Answer.yes?(result.answers.urgent) and result.answers.anger.level >= 1 ->
    {:escalate, result.answers.dept.choice}

  true ->
    {:queue, result.answers.dept.choice}
end

Models

{:ok, models} = TypeSafeAPI.models(client)
models