Skip to content

Reading Results

ask, query and evaluate return a JevResult: one typed answer per question, plus details about the call.

By handle

With a typed query, index the result with the question's handle:

val result = jev.ask(Triage, state = ticket)

result[Triage.urgent].noul       // Double
result[Triage.team].choice       // Team
result[Triage.frustration].score // Double

By id

With the inline DSL, read answers by the ids you gave them:

result.noul("urgent").noul
result.choice("team").choice           // String: the option key
result.enumChoice<Team>("team").choice // Team
result.score("frustration").score

Answer helpers

val urgent = result[Triage.urgent]
urgent.isTrue()                // noul > 0.5
urgent.isTrue(threshold = 0.8) // noul > 0.8
urgent.band()                  // NoulBand.NO (< 0.30), UNCERTAIN, or YES (> 0.70)

val team = result[Triage.team]
team.topProbability          // the largest option probability
team.probability(Team.SALES) // any option's probability, 0.0 if absent
team.ranked()                // [(TECHNICAL, 0.85), (BILLING, 0.08), (SALES, 0.07)]

val frustration = result[Triage.frustration]
frustration.normalized      // score / (levels - 1): 0..1 whatever the level count
frustration.nearestLevel    // score rounded to a level
frustration.mostLikelyLevel // the level with the highest probability
frustration.legendText(2)   // the description of level 2
Answer Fields Helpers
NoulAnswer noul isTrue(threshold = 0.5), band(no = 0.30, yes = 0.70)
ChoiceAnswer<K> choice, probabilities, confidence topProbability, probability(option), ranked()
ScoreAnswer score, probabilities, legend, confidence, levelCount normalized, nearestLevel, mostLikelyLevel, legendText(level)
  • Choice probabilities are in the order the options were declared.
  • Score probabilities and legend are keyed by level number, and the legend holds the level entries that were sent.

All answers

result.nouls.forEach { (id, answer) -> println("$id: ${answer.noul}") }
result.choices.forEach { (id, answer) -> println("$id: ${answer.choice} (${answer.confidence})") }
result.scores.forEach { (id, answer) -> println("$id: ${answer.score}") }

result.answers       // every answer by id, in the order the questions were asked
result.questions.ids // the ids that were asked

Call details

println("model=${result.model}")              // the version that answered, as reported by the API
println("requested=${result.requestedModel}") // what was sent, e.g. "jev-latest"
println("tokens in=${result.usage.inputTokens} out=${result.usage.outputTokens}")
println("request id=${result.requestId}") // quote this when contacting TypeSafe support
Property Meaning
model The model that answered, as reported by the API; falls back to requestedModel
requestedModel The name that was sent, possibly an alias such as jev-latest
usage inputTokens / outputTokens; either may be null
requestId The x-typesafe-request-id response header; quote it in support requests
raw The response body exactly as received
questions The QuestionSet that was asked

Log model alongside results: an alias such as jev-latest can move to a newer model.

The raw response

// The response body exactly as received, e.g. for logging or fields jev4k doesn't model yet.
val rawAnswers = result.raw["answers"]?.jsonObject

When an answer is missing

A malformed answer to a known question fails while the response is being read. A missing answer fails lazily, when you read it. Either way you get a JevResponseValidationException whose fieldPath points at the problem, such as answers.team.choice:

try {
    result[Triage.team]
} catch (e: JevResponseValidationException) {
    // The server didn't return a usable answer for this question.
    println("bad answer at ${e.fieldPath}, request ${e.requestId}")
}

An answer of a type jev4k doesn't recognize is kept as an UnknownAnswer in answers, rather than failing the whole response.