Skip to content

Enum Choices

choice<E>() turns an enum into a Choice question: every constant is an option, and the answer's choice is the constant itself.

// Any enum works; each constant's name is the option key, sent undescribed.
enum class Language { KOTLIN, JAVA, PYTHON, GO, OTHER }

object CodeLanguage : JevQuery() {
    val language by choice<Language>("What programming language is this code written in?")
}

Exhaustive handling

Because the answer is an enum constant, a when over it is checked by the compiler:

val language = jev.ask(CodeLanguage, state = snippet)[CodeLanguage.language]

// choice is a Language, so `when` is exhaustive and the compiler checks every case.
val linter =
    when (language.choice) {
        Language.KOTLIN -> "ktlint"
        Language.JAVA -> "checkstyle"
        Language.PYTHON -> "ruff"
        Language.GO -> "golangci-lint"
        Language.OTHER -> null
    }

// Probabilities are keyed by constant, in declaration order.
val kotlinOrJava = language.probability(Language.KOTLIN) + language.probability(Language.JAVA)

probabilities is keyed by constant, in declaration order, and ranked(), probability(...) and topProbability work as they do for string options.

Descriptions

Implement JevOption to describe each option. The description is sent with the option key:

enum class Team(
    override val description: String,
) : JevOption {
    BILLING("Payments, invoicing, refunds"),
    TECHNICAL("Bugs, outages, integrations"),
    SALES("Pricing, upgrades, new accounts"),
}

Option keys

By default, the option key sent to the model, and matched in the answer, is the constant's name. Option names are part of what the model reads, so they should describe the option. Override optionKey to send something else:

// optionKey changes the key that is sent to the model (and matched in the answer).
enum class Plan(
    override val description: String,
) : JevOption {
    FREE("No paid plan"),
    PRO("A paid plan for one person"),
    ENTERPRISE("A company-wide contract"),
    ;

    override val optionKey: String get() = name.lowercase() // the model sees "free", "pro", "enterprise"
}

Structured descriptions

Override entry to send structured JSON, such as a contrastive rubric, instead of a plain description:

// entry replaces the plain description with structured JSON, such as a contrastive rubric.
enum class ReturnTopic(
    override val entry: JsonElement,
) : JevOption {
    RETURN_POLICY(rubric("Whether and how an item can be returned", notFor = "A return already sent")),
    RETURN_STATUS(rubric("Progress of a return already sent", notFor = "Whether an item can be returned")),
}

JevOption at a glance

Member Default Purpose
description null plain-text description of the option
entry description as JSON, or null what's sent as the option's description; override for structured JSON
optionKey null, meaning the constant's name the key sent to the model and matched in the answer

Enums don't have to implement JevOption; without it, options are sent undescribed under their constant names.

In the inline DSL

choice<E>(id, instructions) works in inline queries too. Read the answer back with enumChoice<E>(id):

// Enum choices work in the inline DSL too; read them back with enumChoice<E>(id).
val result =
    jev.query(state = message) {
        choice<Plan>("plan", "Which plan is the customer on?")
        choice<ReturnTopic>("topic", "Which returns topic is the customer asking about?")
    }
val plan: Plan = result.enumChoice<Plan>("plan").choice
val topic: ReturnTopic = result.enumChoice<ReturnTopic>("topic").choice

If the server ever returns an option the enum doesn't define, reading the answer throws a JevResponseValidationException.