SystemOneResponse is the single public response. evaluate enriches the
existing NoulAnswer, ChoiceAnswer and ScoreAnswer; there is no second result
wrapper. Responses retain raw, unknown_answers, raw HTTP response, request
ID, model, usage, retry count and elapsed time. Semantic elapsed_ms includes
local validation, Pristine execution and decoding; runtime_elapsed_ms retains
Pristine's reported timing. Batch responses also carry zero-based batch_index.
team = TypeSafeSDK.Response.fetch!(response, :team)
{:ok, team} = TypeSafeSDK.Response.fetch(response, :team)
TypeSafeSDK.Response.choices(response)
TypeSafeSDK.Response.nouls(response)
TypeSafeSDK.Response.scores(response)Fetch uses exact keys, not atom/string coercion. fetch! lists available IDs in
its KeyError. Raw/parity responses use strings; semantic responses preserve the
caller key type. Every typed answer retains its own raw payload and ID.
Relational validation
Known answers must match requested IDs and types. Choice selections and the
probability-key domain must match submitted options. Score must lie in the
requested range; legend and probability keys must match level indices. All
probabilities/confidence lie in [0,1]. Distribution sums must be within
probability_tolerance (default 0.02, permitted range 0..0.1); values are never
silently renormalized. Tighten to zero when your application requires exact sums
(up to the implementation's floating-point comparison epsilon).
A requested ID missing entirely is an error. An entry with an unknown future tag is retained/skipped, even for a known question; applications must handle a missing typed answer in that case. Unexpected known IDs fail. Negative or noninteger usage counts fail; absent optional usage counts remain nil. A structural/relationally valid answer can still make an incorrect judgment.
Information helpers
TypeSafeSDK.Answer.Choice.ranked(team)
TypeSafeSDK.Answer.Choice.margin(team)
TypeSafeSDK.Answer.confidence(team)
severity = response.answers.severity
TypeSafeSDK.Answer.Score.ranked(severity)
TypeSafeSDK.Answer.Score.expected_level(severity)
TypeSafeSDK.Answer.Score.max_level(severity)
TypeSafeSDK.Answer.Score.normalized(severity)Choice ranking is descending; ties follow caller order (deterministic key order
for unenriched legacy answers). Margin is the difference between the top two
probabilities, NOT the provider's confidence. For a Score with probabilities
%{0 => 0.45, 1 => 0.1, 2 => 0.45} and expected score 1, the rounded expected
level is 1 but the modal helper returns 0, breaking equal probabilities toward
the lowest index. Normalization divides by the highest legend index; legacy
single-level rubrics return 0.0. Semantic scores retain original rubric labels
and descriptions while preserving the server's legend separately.
Noul has no wire confidence. Answer.confidence(noul) derives max(p, 1-p);
that describes certainty, not whether the answer is true or safe to act on.
Answer.yes?(noul, threshold) compares probability of true; its convenience
default is 0.5 and is not a recommended risk policy.
No hidden gate policy
case TypeSafeSDK.Answer.gate(team, act: 0.90, review: 0.70) do
:act -> :application_may_continue
:review -> :request_review
:escalate -> :use_fallback
endBoth thresholds are mandatory, with 0 <= review <= act <= 1. These numbers are
illustrative. A confident Noul false answer can return :act: separately inspect
yes? before deciding which business action to perform. Confidence is not a
calibration guarantee. Choose thresholds from task-specific evidence and costs.
Run mix run examples/live_semantic.exs for a complete live walkthrough.
See the live example catalog for setup and API-call costs.