Install and select a provider
Add {:typesafe_sdk, "~> 0.4.1"} to your application's dependencies once this
release is published. For a local checkout before publication, use
{:typesafe_sdk, path: "../typesafe_sdk"}. Pristine ~> 0.4.0 owns HTTP
execution, retries, and verified unary cancellation.
The default API root/model are https://api.typesafe.ai and jev-latest. To use
another TypeSafe-compatible deployment, provide the URL, credential, and model
issued for that provider:
export TYPESAFE_API_KEY='credential-issued-for-this-endpoint'
export TYPESAFE_BASE_URL='https://provider.example/deployments/my-typesafe-root'
export TYPESAFE_DEFAULT_MODEL='provider-model-id'
TYPESAFE_BASE_URL is the root before /v1/models and /v1/systemone; path
prefixes are retained. The provider must implement the TypeSafe request/response
and bearer-auth contract. An arbitrary OpenAI-compatible endpoint is not
automatically TypeSafe-compatible. Do not send a credential from one provider to
a guessed third-party host.
Map the environment in your host application's config/runtime.exs:
import Config
config :typesafe_sdk,
api_key: System.fetch_env!("TYPESAFE_API_KEY"),
base_url: System.get_env("TYPESAFE_BASE_URL", "https://api.typesafe.ai"),
default_model: System.get_env("TYPESAFE_DEFAULT_MODEL", "jev-latest")A dependency's runtime config does not configure its host. Runtime modules in TypeSafeSDK do not read OS environment values themselves.
For code that should make provider selection explicit, construct the client directly instead:
client =
TypeSafeSDK.new_client(
api_key: provider_key,
base_url: "https://provider.example/deployments/my-typesafe-root",
model: "provider-model-id"
)Explicit client options win over host config. Blank/invalid endpoint or model values fail instead of silently falling back to a different provider.
First semantic evaluation
client = TypeSafeSDK.new_client()
questions = [
urgent: TypeSafeSDK.noul("Does this need immediate attention?"),
team:
TypeSafeSDK.choice("Which team owns this request?",
billing: "Payments and invoices",
technical: "Bugs and outages"
),
severity: TypeSafeSDK.score("Severity?", ["Routine", "Important", "Blocking"])
]
{:ok, response} =
TypeSafeSDK.evaluate(client, "The API is down for every customer", questions)
response.answers.team.choice
response.answers.severity.label
response.usage.input_tokensAtom question/option keys return as the same atoms. String keys remain strings;
unknown remote strings never create atoms. The SDK returns the same
SystemOneResponse struct for both semantic and legacy APIs.
Use Question.Choice.new/3, Question.Noul.new/2 and Question.Score.new/3 for
runtime input that should return validation errors rather than raise. Top-level
helpers and new! constructors raise for invalid programmer-defined questions.
evaluate! and system_one! similarly raise normalized request errors.
Run bash examples/run_all.sh for the complete live walkthrough, or see the
live example catalog for a 0.2-0.4 feature matrix and
alternate-endpoint workflow.
Legacy parity surface
Existing TypeSafeSDK.Noul, Choice and Score constructors remain unchanged.
system_one(client, state, %{q: legacy_question}) still returns string-keyed
answers. Use the 0.3 migration guide for runtime controls and
the 0.2 migration guide when moving legacy application code
to evaluate; do not replace legacy constructor calls with tuple destructuring.
Continue with client configuration, questions, answers, batching, testing, the evaluation workflow, and runtime controls.