The typed answer to one ALLM.ClassificationQuestion — Layer A
serializable data.
iex> a = ALLM.ClassificationAnswer.new(type: :choice, choice: "billing", confidence: 0.88)
iex> ALLM.ClassificationAnswer.value(a)
"billing"One struct, three shapes
An answer is a tagged union carried in one struct: :type says which
question type it answers, and that decides which fields are populated.
| Field | :choice | :score | :yes_no |
|---|---|---|---|
:choice | the chosen option name | nil | nil |
:score | nil | a float between 0.0 and the number of levels minus one | nil |
:yes_probability | nil | nil | a float between 0.0 and 1.0 |
:probabilities | a map of option name to probability, one key per option | a list of probabilities, one per level | nil |
:legend | nil | a list of level descriptions, one per level | nil |
:confidence | a float between 0.0 and 1.0 | a float between 0.0 and 1.0 | nil |
value/1 returns the headline field for each type.
Why score lists, but choice maps
Score levels are ordered, so :probabilities and :legend are lists where
index = level: Enum.at(answer.probabilities, 2) is the probability of
level 2. A list round-trips through JSON unchanged, whereas an
integer-keyed map would come back with string keys. Choice options are
caller-chosen names with no order, so their probabilities stay a
string-keyed map.
Confidence is reported, never computed
:confidence is whatever the provider reports; ALLM does not derive it.
A :yes_no answer carries confidence: nil because the probability is
itself the answer. There is no built-in threshold either: the decision
of what probability counts as "yes" belongs to the caller.
Floats
An adapter coerces every probability, score and confidence to a float
when it decodes a provider response, since JSON 1 and 0 decode as
integers. Decoding a persisted answer does not coerce: the maps and lists
come back exactly as they were stored.
Construction
:type is enforced, so new/1 without it raises ArgumentError. Unknown
keys raise KeyError.
Summary
Functions
Build a %ClassificationAnswer{} from keyword opts.
The headline value: choice → option name, score → position, yes_no → P(yes).
Types
@type t() :: %ALLM.ClassificationAnswer{ choice: String.t() | nil, confidence: float() | nil, legend: [term()] | nil, metadata: map(), probabilities: %{required(String.t()) => float()} | [float()] | nil, score: float() | nil, type: ALLM.ClassificationQuestion.question_type(), yes_probability: float() | nil }
Functions
Build a %ClassificationAnswer{} from keyword opts.
:type is required (ArgumentError without it); unknown keys raise
KeyError.
Examples
iex> a = ALLM.ClassificationAnswer.new(type: :yes_no, yes_probability: 0.93)
iex> {a.type, a.yes_probability, a.confidence}
{:yes_no, 0.93, nil}
The headline value: choice → option name, score → position, yes_no → P(yes).
Examples
iex> ALLM.ClassificationAnswer.value(ALLM.ClassificationAnswer.new(type: :score, score: 1.05))
1.05
iex> ALLM.ClassificationAnswer.value(ALLM.ClassificationAnswer.new(type: :yes_no, yes_probability: 0.2))
0.2