# `TypeSafeAPI.Answer`
[🔗](https://github.com/typesend/typesafe_ai/blob/v0.1.0-alpha.3/lib/typesafe_api/answer.ex#L1)

Decodes one wire answer against the question that produced it, and turns
any answer into a routing decision.

Decoding needs the question (to know which type is expected, and to check the
answer against the options or levels that were actually sent) and the
`TypeSafeAPI.Keys` registry built for the request (to hand ids and Choice
options back under the caller's own atoms or strings, never by calling
`String.to_atom/1` on anything the API sent).

Decoding is strict: a probability that is not a number, an option or level
the caller never sent, a Noul outside `0.0..1.0`, or a `choice` that is not
the highest-probability option all decode to an `Error.unexpected/2` rather
than to a struct that is confidently wrong. The typed layer exists to catch
exactly these, and `TypeSafeAPI.Result`'s `raw` still holds the response as
it arrived.

`gate/2` and `confidence/1` work the same way across all three answer types,
so a caller who only cares about "should I trust this" doesn't need to branch
on which question it was. `yes?/2` is Noul-only: a Choice or Score has no
yes/no reading.

# `t`

```elixir
@type t() ::
  TypeSafeAPI.Answer.Noul.t()
  | TypeSafeAPI.Answer.Choice.t()
  | TypeSafeAPI.Answer.Score.t()
```

# `confidence`

```elixir
@spec confidence(t()) :: float()
```

The confidence value `gate/2` uses: `max(noul, 1 - noul)` for Noul, and the
wire `confidence` for Choice and Score.

The API returns no confidence for a Noul answer, only the probability, so
the Noul value is this library's convention rather than something the model
reported. It reads distance from 0.5 as certainty: 0.92 and 0.08 both give
0.92, and 0.5 gives 0.5. That makes a Noul comparable to a Choice or Score
in `gate/2`, but it is not the same measurement, so do not tune one
threshold against numbers from the other. It is also the `confidence` field
on `TypeSafeAPI.Answer.Noul`, so all three answer structs have the same shape.

# `decode`

```elixir
@spec decode(map(), String.t(), TypeSafeAPI.Question.t(), TypeSafeAPI.Keys.t()) ::
  {:ok, t()} | {:error, TypeSafeAPI.Error.t()}
```

Decodes one answer from the wire `answers` object.

`wire_id` is the string key the answer was found under; `question` is the
normalized question it answers. A `question`/`raw_answer` type mismatch, an
unrecognized `"type"`, a missing or invalid field, or a value that disagrees
with the question all decode to an `Error.unexpected/2`.

# `gate`

```elixir
@spec gate(t(), keyword()) :: :act | :review | :escalate
```

Routes an answer into `:act`, `:review`, or `:escalate` by comparing
`confidence/1` against two thresholds.

Both `:act` and `:review` are required. Raises `ArgumentError` if `:act` is
lower than `:review`, since that would make the review band unreachable.

A Noul answer's confidence is `max(noul, 1 - noul)`, which has a floor of
0.5, so `:escalate` is unreachable for a Noul unless `:review` is above 0.5.
Rather than silently never escalating, `gate/2` raises `ArgumentError` when a
Noul answer is given a `:review` threshold of 0.5 or lower. A Noul sitting at
0.5 is the most uncertain answer the model can give, so the useful escalation
threshold is just above it (`review: 0.55`, say). Choice and Score
confidences come from the API and can be anything in `0.0..1.0`, so they take
any thresholds.

# `yes?`

```elixir
@spec yes?(TypeSafeAPI.Answer.Noul.t(), number()) :: boolean()
```

Whether a Noul answer clears a probability threshold. Defaults to 0.5.

Noul answers only. A Choice or Score answer has no yes/no reading, so this
raises `ArgumentError` rather than inventing one; use `gate/2` to route on
confidence, or compare the fields you care about instead.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
