ALLM.ClassificationQuestion (allm v0.6.1)

Copy Markdown View Source

One typed question inside an ALLM.ClassificationRequest — Layer A serializable data.

iex> q = ALLM.ClassificationQuestion.choice("Which team should handle this?", ["billing", "technical"])
iex> q.type
:choice
iex> q.criteria
%{"billing" => nil, "technical" => nil}

Question types

There are three, and a request may mix them freely:

  • :choice — pick exactly one option from a named set. The answer reports the chosen option plus a probability per option.
  • :score — place the input on an ordered scale of levels, low to high. The answer reports a (fractional) position on that scale.
  • :yes_no — the probability that the answer is "yes". Some providers use their own name for this type (TypeSafe calls it "noul"); ALLM always spells it :yes_no, and an adapter translates on the wire.

Instructions and criteria

:instructions is the question itself. It is a string, or a JSON object or array when the question is better expressed as structured data.

:criteria depends on the type:

Type:criteria
:choicea map of option name to an optional description (nil, a string, or structured data)
:scorea list of level descriptions, where index = level
:yes_nonil, or a map whose keys are among "true" and "false", describing what each answer means

Use the builders choice/2, score/2 and yes_no/2; they produce these shapes and stringify atom option names so the question survives a JSON round trip unchanged.

Construction

new/1 is a bare struct!/2 pass-through: unknown keys raise KeyError and nothing else is checked. The builders raise on a wrongly-typed argument (FunctionClauseError, or ArgumentError for an unknown yes_no/2 option or colliding choice/2 option names), but counts, emptiness and provider limits are left to ALLM.Validate.classification_request/1, so a hand-built struct and a builder-made one are judged by the same rules.

Summary

Types

A question type. :yes_no is ALLM's neutral name for a yes/no probability.

String, JSON object, or JSON array: structured instructions or criteria.

t()

Functions

Choice over options.

Build a %ClassificationQuestion{} from keyword opts.

Score on ordered levels, low to high. Level N is Enum.at(levels, N).

Yes/no. Optional true: / false: describe what each answer means.

Types

question_type()

@type question_type() :: :choice | :score | :yes_no

A question type. :yes_no is ALLM's neutral name for a yes/no probability.

structured()

@type structured() :: String.t() | map() | list()

String, JSON object, or JSON array: structured instructions or criteria.

t()

@type t() :: %ALLM.ClassificationQuestion{
  criteria:
    %{required(String.t()) => structured() | nil} | [structured()] | map() | nil,
  instructions: structured() | nil,
  type: question_type() | nil
}

Functions

choice(instructions, options)

@spec choice(
  structured(),
  [String.t() | atom()] | %{required(String.t() | atom()) => structured() | nil}
) :: t()

Choice over options.

options is a list of names (each description is nil) or a map of name to description. Atom names are stringified. An options that is neither a list nor a map raises FunctionClauseError. A keyword list is still a list, so choice(q, billing: "Payments") treats each {name, description} pair as a name and raises Protocol.UndefinedError — pass a map for named descriptions. Two distinct names that become the same string (:billing and "billing") raise ArgumentError rather than silently merging into one option; repeating the identical name in a list is harmless.

Examples

iex> q = ALLM.ClassificationQuestion.choice("Which team?", [:billing, "sales"])
iex> q.criteria
%{"billing" => nil, "sales" => nil}

iex> q = ALLM.ClassificationQuestion.choice("Which team?", %{billing: "Payments and invoices"})
iex> q.criteria
%{"billing" => "Payments and invoices"}

new(opts \\ [])

@spec new(keyword()) :: t()

Build a %ClassificationQuestion{} from keyword opts.

Unknown keys raise KeyError via struct!/2. No validation.

Examples

iex> q = ALLM.ClassificationQuestion.new(type: :yes_no, instructions: "Is this spam?")
iex> {q.type, q.criteria}
{:yes_no, nil}

score(instructions, levels)

@spec score(structured(), [structured()]) :: t()

Score on ordered levels, low to high. Level N is Enum.at(levels, N).

A non-list levels raises FunctionClauseError.

Examples

iex> q = ALLM.ClassificationQuestion.score("How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"])
iex> {q.type, Enum.at(q.criteria, 1)}
{:score, "Frustrated"}

yes_no(instructions, opts \\ [])

@spec yes_no(
  structured(),
  keyword()
) :: t()

Yes/no. Optional true: / false: describe what each answer means.

With neither option :criteria is nil; otherwise it carries only the keys given. Any other option raises ArgumentError.

Examples

iex> ALLM.ClassificationQuestion.yes_no("Is a refund requested?").criteria
nil

iex> ALLM.ClassificationQuestion.yes_no("Is a refund requested?", true: "The customer asks for money back").criteria
%{"true" => "The customer asks for money back"}