TypeSafe (TypeSafe AI v0.1.0-alpha.1)

Copy Markdown View Source

Unofficial Elixir client for the TypeSafe AI API. Not affiliated with or endorsed by TypeSafe AI.

TypeSafe's System One API answers typed questions about a piece of state: a yes/no probability (noul/2), one option from a set (choice/2), or a position on an ordered scale (score/2). This module is the whole public surface for everyday use; the modules it delegates to hold the details.

client = TypeSafe.new(api_key: "...")

{:ok, result} =
  TypeSafe.evaluate(client, "Help! My payouts have been failing for 3 days.",
    urgent: TypeSafe.noul("Does this convey urgency?"),
    dept:
      TypeSafe.choice("Which team should handle this?",
        billing: "Payments, invoicing, refunds",
        technical: "Bugs, outages, integrations",
        sales: nil
      ),
    anger: TypeSafe.score("How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"])
  )

result.answers.dept.choice   #=> :technical
result.answers.anger.label   #=> "Very angry"
result.usage.input_tokens    #=> 312

The same example, run against TypeSafe.Test stubs instead of the network (this block is a doctest):

iex> client =
...>   TypeSafe.Test.client()
...>   |> TypeSafe.Test.stub(
...>     urgent: {:noul, 0.92},
...>     dept: {:choice, :technical, 0.82},
...>     anger: {:score, 2, 0.65}
...>   )
iex> {:ok, result} =
...>   TypeSafe.evaluate(client, "Help! My payouts have been failing for 3 days.",
...>     urgent:
...>       TypeSafe.noul("Does this convey urgency?",
...>         true: "Explicitly time-sensitive",
...>         false: "No urgency expressed"
...>       ),
...>     dept:
...>       TypeSafe.choice("Which team should handle this?",
...>         billing: "Payments, invoicing, refunds",
...>         technical: "Bugs, outages, integrations",
...>         sales: nil
...>       ),
...>     anger: TypeSafe.score("How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"])
...>   )
iex> result.model
"jev-latest"
iex> result.answers.urgent
%TypeSafe.Answer.Noul{id: :urgent, noul: 0.92}
iex> {result.answers.dept.choice, result.answers.dept.confidence}
{:technical, 0.82}
iex> Map.keys(result.answers.dept.probabilities) |> Enum.sort()
[:billing, :sales, :technical]
iex> {result.answers.anger.level, result.answers.anger.label}
{2, "Very angry"}
iex> Enum.map(result.answers.anger.levels, &elem(&1, 0))
["Calm", "Frustrated", "Very angry"]
iex> TypeSafe.Answer.gate(result.answers.dept, act: 0.8, review: 0.5)
:act
iex> TypeSafe.Answer.gate(result.answers.anger, act: 0.8, review: 0.5)
:review
iex> TypeSafe.Answer.yes?(result.answers.urgent)
true

Question ids and Choice option keys come back exactly as you gave them: atoms stay atoms, strings stay strings. See TypeSafe.Keys for why.

Questions are validated locally on every call, so a malformed one fails with a :validation error before anything is sent. To catch the mistake at the line that wrote it instead, wrap the constructor in TypeSafe.Question.validate!/1, which raises ArgumentError and returns the question unchanged. That is the eager-validation path for questions built once in a module attribute or at application start.

Two layers

TypeSafe.HTTP is the raw layer: maps in, maps out, with auth, retries and telemetry handled. Everything above it (questions, answers, results) is the typed layer. If the API adds something this library does not model yet, the raw layer still works.

Summary

Functions

Pick one option from criteria, a keyword list (or list of pairs) of option key to description; nil when the key speaks for itself. Order is preserved. See TypeSafe.Question.Choice.

Evaluates state against questions and returns a TypeSafe.Result.

Evaluates many states against one question set, concurrently, and returns one outcome per state in input order. See TypeSafe.FanOut for how errors, timeouts and exhausted retries surface.

Lists the models available to the account. See TypeSafe.Models.list/2.

A yes/no question. Optional true: and false: descriptions say what each answer means. See TypeSafe.Question.Noul.

Rate the state along levels, an ordered list of two to ten level descriptions from low to high. See TypeSafe.Question.Score.

Functions

choice(instructions, criteria)

Pick one option from criteria, a keyword list (or list of pairs) of option key to description; nil when the key speaks for itself. Order is preserved. See TypeSafe.Question.Choice.

evaluate(client, state, questions, opts \\ [])

Evaluates state against questions and returns a TypeSafe.Result.

state is a string or JSON-shaped map or list. questions is a keyword list or map of id to question.

Options

  • :model (String.t/0) - Model for this call; defaults to the client's model.

  • :timeout (pos_integer/0) - Timeout in milliseconds for this call.

  • :retry - Retry policy for this call; see TypeSafe.Retry.new/1.

  • :req_options (keyword/0) - Extra Req options for this call.

  • :telemetry (map/0) - Extra metadata merged into telemetry events.

evaluate!(client, state, questions, opts \\ [])

Like evaluate/4 but returns the result or raises the TypeSafe.Error.

evaluate_many(client, states, questions, opts \\ [])

Evaluates many states against one question set, concurrently, and returns one outcome per state in input order. See TypeSafe.FanOut for how errors, timeouts and exhausted retries surface.

Options

  • :max_concurrency (pos_integer/0) - Maximum number of in-flight requests. A guess; see the module docs. The default value is 8.

  • :timeout - Per-state task timeout in milliseconds, covering every retry of that state. Defaults to the retry budget plus one attempt timeout, or 60 000 when the budget is disabled.

  • :attempt_timeout (pos_integer/0) - Timeout in milliseconds for one HTTP attempt; defaults to the client's.

  • :ordered (boolean/0) - Return results in input order. false yields them as they finish. The default value is true.

  • :on_error - :collect returns error tuples in place; :raise raises the first error. The default value is :collect.

Every other option is passed through to each individual call, so :model, :retry, :req_options and :telemetry mean the same here as in evaluate/4. The one exception is :timeout, which is the per-state task timeout above; use :attempt_timeout for the single HTTP attempt.

models(client, opts \\ [])

@spec models(TypeSafe.Client.t(), keyword()) ::
  {:ok, [TypeSafe.Model.t()]} | {:error, TypeSafe.Error.t()}

Lists the models available to the account. See TypeSafe.Models.list/2.

new(opts \\ [])

@spec new(keyword()) :: TypeSafe.Client.t()

Builds a TypeSafe.Client.

With no options, configuration comes from application config and the TYPESAFE_API_KEY, TYPESAFE_BASE_URL, and TYPESAFE_DEFAULT_MODEL environment variables. See TypeSafe.Client.new/1 for all options.

noul(instructions, criteria \\ [])

A yes/no question. Optional true: and false: descriptions say what each answer means. See TypeSafe.Question.Noul.

score(instructions, levels)

Rate the state along levels, an ordered list of two to ten level descriptions from low to high. See TypeSafe.Question.Score.