Skip to content

State

The state is what Jev reads: the material you'd hand to an expert before asking them to decide. query and ask accept it in three forms.

Text

// Plain text: a message, a document, a transcript.
val result = jev.ask(Triage, state = "My card was charged twice for order A-104.")

A string suits a single message, document or transcript.

JSON

For anything with parts, prefer a JSON object, so each part has a name and the relationships stay clear:

// Named parts keep related context together and let questions point at a specific part.
val state =
    buildJsonObject {
        putJsonObject("ticket") {
            put("subject", "Duplicate charge")
            putJsonArray("messages") {
                addJsonObject {
                    put("from", "customer")
                    put("text", "I was charged twice for order A-104. Please refund the duplicate.")
                }
                addJsonObject {
                    put("from", "support")
                    put("text", "We are checking the charges.")
                }
            }
        }
        putJsonObject("order") {
            put("id", "A-104")
            putJsonArray("charges") {
                add("49.00 USD captured")
                add("49.00 USD captured")
            }
        }
        put("refund_policy", "Duplicate charges are eligible for a refund.")
    }

val result =
    jev.query(state = state) {
        noul("refund_requested", "Does `ticket.messages[0].text` request a refund?")
        noul("policy_supports_refund", "Does `refund_policy` support the refund requested, given `order.charges`?")
    }

This is one state, even though it holds a conversation, an order and a policy. Put information together when the decision needs to compare the parts.

@Serializable values

Any @Serializable value works as state. Fields that equal their defaults are still sent, so the model sees them:

@Serializable
data class Order(
    val id: String,
    val status: String = "open", // fields equal to their default are still sent
    val items: List<String> = emptyList(),
)

@Serializable
data class RefundCase(
    val message: String,
    val order: Order,
    val policy: String,
)

suspend fun refundSupported(
    jev: JevApi,
    case: RefundCase,
): Boolean {
    val result =
        jev.query(state = case) {
            noul("supported", "Does `policy` support the refund requested in `message` for `order`?")
        }
    return result.noul("supported").isTrue(threshold = 0.8)
}

Order("A-104") is sent as {"id":"A-104","status":"open","items":[]}. A value that isn't @Serializable is rejected with a JevValidationException that says so.

Pointing questions at fields

Name the part of the state a question is about with a backticked dot-and-index path, such as `ticket.messages[0].text` or `order.charges`, as in the JSON example above. Explicit paths tell the model which part to judge.

Send only what's needed

Unrelated detail acts as a distractor: accuracy falls as the state fills with content the question doesn't need. Filter in code first. Given a ticket type like this:

@Serializable
data class SupportTicket(
    val subject: String,
    val body: String,
    val customerPlan: String = "free",
)

send only the parts the question reads:

// Send only what the questions need. Unrelated detail lowers accuracy.
suspend fun triageFromTicket(
    jev: JevApi,
    ticket: SupportTicket,
): Team {
    val focused = "${ticket.subject}\n\n${ticket.body}" // leave out account data the question doesn't use
    return jev.ask(Triage, state = focused)[Triage.team].choice
}
  • Text only. Jev reads text: strings, JSON objects, and arrays of text. Convert images, audio and binary data to text or fields first.
  • English works best. Other languages, including CJK scripts, work but less well; test on your own data.
  • Context limits. A request's state plus all its questions must fit in 64k tokens, and the state plus the longest single question in 32k.