Elixir client for the TypeSafe AI API.
Quickstart
Set TYPESAFE_API_KEY in your environment, build a client, and ask questions about some state:
client = TypeSafe.new()
{:ok, response} =
TypeSafe.system_one(client, "I was charged twice. Please fix this ASAP.", %{
"category" =>
TypeSafe.Question.choice(
instructions: "What is this ticket about?",
criteria: %{"billing" => nil, "technical" => nil, "other" => nil}
)
})
response.choices["category"].choice
#=> "billing"A client is a plain immutable value — build it once and share it. Every request returns
{:ok, result} or {:error, exception}; the ! variants (system_one!/4, list_models!/2)
return the result or raise. See TypeSafe.Error for the exception structs.
Application-level clients
Instead of threading a client value through your code, you can start one under your supervision tree and refer to it by name:
children = [
{TypeSafe, name: MyApp.TypeSafe, api_key: System.fetch_env!("TYPESAFE_API_KEY")}
]
Supervisor.start_link(children, strategy: :one_for_one)Then pass the name anywhere a client is accepted:
TypeSafe.system_one(MyApp.TypeSafe, state, questions)The client is built once at startup (a bad key or option fails the boot loudly) and read
lock-free on every call. TypeSafe.client/1 returns the underlying TypeSafe.Client if you need
the value itself.
Questions and answers
Build questions with TypeSafe.Question.noul/1, TypeSafe.Question.choice/1, and
TypeSafe.Question.score/1. Answers come back as TypeSafe.Answer.Noul, TypeSafe.Answer.Choice,
and TypeSafe.Answer.Score structs, grouped on the response by :nouls, :choices, and :scores.
Summary
Types
A built client value, or the atom name of one started under a supervision tree.
State to evaluate: text, a JSON object (map), or a JSON array (list).
Functions
Child spec for starting an application-level client under a supervision tree.
Return the TypeSafe.Client registered under name.
List the models available to the account.
Like list_models/2, but returns the response or raises the exception.
Build a TypeSafe.Client.
Start an application-level client and register it under opts[:name].
Answer named questions about state.
Like system_one/4, but returns the response or raises the exception.
The SDK version string.
Types
@type client() :: TypeSafe.Client.t() | atom()
A built client value, or the atom name of one started under a supervision tree.
State to evaluate: text, a JSON object (map), or a JSON array (list).
Functions
@spec child_spec(keyword()) :: Supervisor.child_spec()
Child spec for starting an application-level client under a supervision tree.
Requires a :name (a non-nil atom); the remaining options are the same as new/1. The child is
keyed by its name, so several named clients can live under one supervisor. Referenced implicitly
when you place {TypeSafe, opts} in a supervisor's child list.
@spec client(atom()) :: TypeSafe.Client.t()
Return the TypeSafe.Client registered under name.
Raises TypeSafe.ConfigError if no client is registered — usually because {TypeSafe, name: name}
is not in the supervision tree, or has not started yet.
@spec list_models(client(), keyword()) :: {:ok, TypeSafe.Response.ListModels.t()} | {:error, TypeSafe.Error.t()}
List the models available to the account.
Options
:timeout— per-call receive timeout in milliseconds.:headers— extra headers for this call (a map).
Returns {:ok, %TypeSafe.Response.ListModels{}} or {:error, exception}.
@spec list_models!(client(), keyword()) :: TypeSafe.Response.ListModels.t()
Like list_models/2, but returns the response or raises the exception.
@spec new(keyword()) :: TypeSafe.Client.t()
Build a TypeSafe.Client.
Explicit options take precedence over environment variables.
Options
:api_key— API key. Defaults to theTYPESAFE_API_KEYenvironment variable. Required.:base_url— API root. Defaults toTYPESAFE_BASE_URLorhttps://api.typesafe.ai.:model— default model. Defaults toTYPESAFE_DEFAULT_MODELorjev-latest.:timeout— per-request receive timeout in milliseconds. Defaults to10_000.:max_retries— retries after the initial attempt. Defaults to2; use0to disable.:retry,:retry_delay— overrides passed through toReq(seeReqretry docs).:headers— extra default headers (a map).:req_options— a keyword list merged intoReq.new/1(escape hatch for e.g.:connect_options).:plug— aPlug/Req.Teststub used instead of the network, for tests.
Raises TypeSafe.ConfigError when the API key is missing or invalid.
@spec start_link(keyword()) :: GenServer.on_start()
Start an application-level client and register it under opts[:name].
Usually invoked for you via {TypeSafe, opts} in a supervision tree rather than called directly.
Raises ArgumentError when :name is missing and TypeSafe.ConfigError on invalid configuration.
@spec system_one(client(), state(), map(), keyword()) :: {:ok, TypeSafe.Response.SystemOne.t()} | {:error, TypeSafe.Error.t()}
Answer named questions about state.
questions is a non-empty map of names (any term you choose, typically a string) to questions built
with TypeSafe.Question, or raw maps carrying a "type" key. The names key the answers in the
response.
Options
:model— model override for this call; defaults to the client's model.:timeout— per-call receive timeout in milliseconds.:headers— extra headers for this call (a map).:extra_body— a map with string keys, shallow-merged over the request body ("state","model","questions"); a colliding key overrides the default.
Returns {:ok, %TypeSafe.Response.SystemOne{}} or {:error, exception}.
Examples
{:ok, response} =
TypeSafe.system_one(client, "I was charged twice. Please help.", %{
"billing" => TypeSafe.Question.noul(instructions: "Is this about billing?"),
"tone" =>
TypeSafe.Question.choice(
instructions: "What is the tone?",
criteria: %{"calm" => nil, "angry" => nil}
)
})
response.nouls["billing"].noul #=> 0.98
response.choices["tone"].choice #=> "angry"
@spec system_one!(client(), state(), map(), keyword()) :: TypeSafe.Response.SystemOne.t()
Like system_one/4, but returns the response or raises the exception.
@spec version() :: String.t()
The SDK version string.