Endpoint and provider selection
export TYPESAFE_API_KEY="provider-key"
export TYPESAFE_BASE_URL="https://your-typesafe-host.example/deployment"
export TYPESAFE_DEFAULT_MODEL="provider-model-id"
client = TypeSafeSDK.new_client(
api_key: key,
base_url: "https://your-typesafe-host.example/deployment",
model: "provider-model-id"
)The base URL is the root before /v1/models and /v1/systemone; path prefixes are
retained. Use the selected provider's key/model and TypeSafe operation contract. A
generic OpenAI-compatible endpoint is not automatically TypeSafe-compatible.
Client and questions
Strict semantic API
client = TypeSafeSDK.new_client()
questions = [
urgent: TypeSafeSDK.noul("Urgent?"),
team: TypeSafeSDK.choice("Team?", billing: "Payments", technical: "Product"),
severity: TypeSafeSDK.score("Severity?", ["Low", "High"])
]
prepared = TypeSafeSDK.prepare!(questions)
{:ok, response} = TypeSafeSDK.evaluate(client, state, prepared)Tuple constructors
TypeSafeSDK.Question.Noul.new("Urgent?", true: "Time-sensitive")
TypeSafeSDK.Question.Choice.new("Team?", [billing: nil, technical: nil])
TypeSafeSDK.Question.Score.new("Severity?", ["Low", "High"])Each also has new!. Legacy TypeSafeSDK.Noul/Choice/Score.new still returns
legacy structs; system_one still uses string response keys.
Answers and policy
team = TypeSafeSDK.Response.fetch!(response, :team)
TypeSafeSDK.Answer.Choice.ranked(team)
TypeSafeSDK.Answer.Choice.margin(team)
TypeSafeSDK.Answer.confidence(team)
TypeSafeSDK.Answer.gate(team, act: 0.9, review: 0.7)
TypeSafeSDK.Answer.yes?(response.answers.urgent, 0.85)
TypeSafeSDK.Answer.Score.expected_level(response.answers.severity)
TypeSafeSDK.Answer.Score.max_level(response.answers.severity)
TypeSafeSDK.Answer.Score.normalized(response.answers.severity)
TypeSafeSDK.Response.values(response)
# %{urgent: 0.91, team: :billing, severity: 1.7}Thresholds are explicit application policy, not safety guarantees. Expected and modal Score levels differ; Noul confidence is derived as max(p, 1-p).
Batch, errors and tests
TypeSafeSDK.evaluate_many(client, states, prepared,
max_concurrency: 8, max_pending: 32, ordered: true, on_error: :collect,
task_timeout_ms: 40_000, attempt_timeout_ms: 5_000)
TypeSafeSDK.evaluate_stream(client, states, prepared, ordered: false)
TypeSafeSDK.Error.retryable?(error)
TypeSafeSDK.Error.retry_after(error)
client = TypeSafeSDK.Test.client()
client = TypeSafeSDK.Test.stub(client, urgent: {:noul, 0.9})
# Run application code, then verify before the creating process exits.
TypeSafeSDK.Test.verify!(client)
TypeSafeSDK.Test.close(client)Unordered results have batch_index; errors use details.batch_index. Request
timeout and retry policy values still use seconds. Retries can repeat processing.
Contract and operations tools
mix typesafe.schema.export
mix typesafe.schema.verify
mix typesafe.capabilities
mix typesafe.record --output tmp/live --base-url "$TYPESAFE_BASE_URL" --model "$TYPESAFE_DEFAULT_MODEL"
mix reach.check --arch --smells
Recording needs a real API key; no live data is fabricated. Unadvertised transport bounds remain unverified. Semantic telemetry excludes automatic prompt/state data.
0.3 runtime controls
cancel = Pristine.Cancellation.new()
{:ok, response} = TypeSafeSDK.evaluate(client, state, prepared,
cancellation: cancel,
max_request_bytes: 262_144,
response_contract: [on_unknown_answer: :error])
TypeSafeSDK.Prepared.keys(prepared)
TypeSafeSDK.Prepared.fingerprint(prepared)
TypeSafeSDK.Response.metadata(response)
TypeSafeSDK.RuntimeCapabilities.report(client)Per-call retry keys inherit omitted client retry fields; retry: false disables
retry for that call. Cancellation uses Pristine directly and cannot prove that a
remote service did not begin processing a request.
0.4 OTP and answer telemetry
# Successful evaluate/4 calls emit one event per known answer:
[:typesafe_sdk, :answer]
# measurements: confidence, top_probability, distribution_margin
# metadata: answer_type, question_index, model/request ID, Prepared fingerprint
children = [
{Task.Supervisor, name: MyApp.TypeSafeTasks},
{MyApp.Triage,
client: client,
task_supervisor: MyApp.TypeSafeTasks,
max_in_flight: 32}
]MyApp.Triage uses TypeSafeSDK.OTP.Server and returns {:evaluate, ...} from
callbacks. The SDK starts no global task supervisor. See guides/otp-server.md
and guides/recursive-decisions.md.