Structured Reads

Ask a text a fixed set of questions and get a typed answer to each, with a number attached saying how sure the model is. One request, one pass, every question answered together.

What This Is For

The usual way to get a decision out of a model is to ask it in a prompt and parse what comes back. That works until you need the same questions asked of ten thousand documents, and then two things hurt: the answers drift in shape, and you have no honest measure of how sure the model was about any of them.

A structured read fixes both. The questions and their allowed answers are part of the request, so the reply cannot be a sentence when you wanted a yes. And because the answer is read from the model's own probability distribution rather than sampled as text, every answer arrives with a calibrated number beside it. You can route on that number: act on the confident ones, queue the rest for a person.

Which Model Answers This

Three backends serve this endpoint. The request looks the same against all of them; what changes is what it may ask for, and how long it takes.

LayaClef / Clef FlashDiffusionGemma 26B A4B
What it isA small model built only for thisA 9B or 27B backbone under a schema headA diffusion language model that also chats
Memory2.4 GBAbout 16 GB, or 37 GB for Clef27 GB
Speed a requestMillisecondsHundreds of milliseconds; Clef about four times FlashOne pass for the whole set
State it reads512 tokens English, 1K multilingual, windowed beyond16K, text or JSON256K
ImagesNoYes, with its own visionYes, with its vision companion
Options a questionFalls off past about 20Any numberBounded by the canvas
Conditional questionsNoNoYes
Repeated reads, steps, thoughtRefused: it is deterministicRefusedSupported
Runs onNVIDIA onlyNVIDIA and Apple SiliconNVIDIA and Apple Silicon

Laya scores every option at once in a single bidirectional pass under a head trained for exactly this, so a question costs milliseconds and the same request twice returns the same bits. Repeated reads are refused rather than silently ignored, because there is nothing to vary. A runner serving Laya serves nothing else.

Clef and Clef Flash read the whole schema together and decide every field from one read of the state, so the answers are consistent with each other rather than reached independently. That is the reason to pick them over Laya when the fields interact. They take pictures and allow a question any number of options, but not conditional questions. Flash first; Clef when the decision needs more knowledge than Flash has, accepting roughly four times the time.

DiffusionGemma writes a block of text at once rather than one token after another, so every question's answer slot is filled in the same forward pass and sixty questions cost about what one does. It is the only one of the three that supports the extra machinery below, because it is the only one that can vary.

All three report confidence on the same scale, so a client routing on that number does not care which is loaded.

Availability

The endpoint serves only when the loaded chat model generates by block diffusion. Check before you call rather than probing:

curl -s localhost:11540/api/server | jq '{reader, structured_read}'
# {
#   "reader": null,                    # the decision model, when one is loaded
#   "structured_read": {
#     "canvas_width": 256,
#     "max_questions": 64,
#     "max_samples": 32,
#     "max_steps": 8,
#     "images": true,
#     "conditional": true,
#     "think": true,
#     "types": ["noul", "choice", "score"]
#   }
# }

structured_read: null means the loaded model cannot answer this and the endpoint will refuse. The values differ by backend, which is the point of asking: a Laya runner reports one read, one step, no images and no thought, and names itself under reader.

canvas_width is the real limit on how much one call can read: every question writes one id: label line into that canvas, so a long question set with long ids runs out of room before it hits max_questions. Past that the set is read in chunks rather than refused.

Three Question Types

TypeAskCriteria
noulA yes/no questionOptional {"true": ..., "false": ...} describing what each side means
choiceOne option from a setA map of option name to description
scoreA level on an ordered scaleAn ordered list of level names, lowest first

bool and boolean are accepted as spellings of noul.

A Request

curl -X POST localhost:11540/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "state": "The card reader at gate 4 has been declining valid badges since 06:00. Three staff are waiting outside.",
    "questions": {
      "urgent": {
        "type": "noul",
        "instructions": "Does this need attention within the hour?",
        "criteria": {"true": "People are blocked now", "false": "It can wait for the next shift"}
      },
      "area": {
        "type": "choice",
        "instructions": "Which team owns this?",
        "criteria": {
          "access": "Badge readers, doors, physical entry",
          "network": "Connectivity and switching",
          "facilities": "Power, heating, plumbing"
        }
      },
      "severity": {
        "type": "score",
        "instructions": "How disruptive is this?",
        "criteria": ["none", "minor", "moderate", "major", "critical"]
      }
    },
    "samples": "auto"
  }'

Question ids are written into the answer template, so they must be single words without a colon. state is the text being read.

The Answer

{
  "model": "diffusiongemma-26b-a4b",
  "answers": {
    "urgent": {
      "type": "noul",
      "noul": 0.94,
      "confidence": 0.88,
      "agreement": 1.0,
      "outside": 0.002
    },
    "area": {
      "type": "choice",
      "choice": "access",
      "probabilities": {"access": 0.91, "network": 0.06, "facilities": 0.03},
      "confidence": 0.87,
      "agreement": 1.0,
      "outside": 0.004
    },
    "severity": {
      "type": "score",
      "score": 2.57,
      "level": "major",
      "legend": {"0": "none", "1": "minor", "2": "moderate", "3": "major", "4": "critical"},
      "probabilities": {"0": 0.01, "1": 0.09, "2": 0.35, "3": 0.42, "4": 0.13},
      "confidence": 0.28,
      "agreement": 0.75,
      "outside": 0.01
    }
  },
  "usage": {"input_tokens": 218, "output_tokens": 0},
  "diagnostics": { "reads": 4, "canvas": 256, "questions": [...], "timing": {"total_ms": 412} }
}

output_tokens is zero because nothing is generated. The answer is read out of one forward pass over a seeded template; no text is produced and nothing is committed.

The Four Numbers

Each answer carries four measures, and they mean different things. Using the wrong one is the common mistake.

FieldWhat it says
probabilitiesThe model's distribution over the allowed answers, normalized across the label set.
confidenceHow far from a coin flip the distribution is: 0 when every option is equally likely, 1 at certainty. This is the field to route on.
agreementThe share of repeated reads that picked the reported answer. Low agreement with high confidence means the reads disagreed with each other while each was individually sure.
outsideHow much probability mass the model put on tokens that are not any of your labels. A large value means the question did not fit the options you gave it.
stderrPresent when a read was repeated: the standard error of the winning probability across the draws, so you can tell a stable 0.8 from a noisy one.

confidence is not the winning probability. It is the measure the Jev API documents, (n * max - 1) / (n - 1) over the label distribution, so a client written against Jev's act / review / escalate bands reads it the same way here. A yes/no answer at noul: 0.75 has a confidence of 0.5, not 0.75. The raw probabilities are beside it, untouched, if you want those instead.

outside is worth watching in particular. It has no equivalent in a top-k logprobs read, and it is the honest signal that a question was badly posed: if the model wanted to answer "partly" and your options were only yes and no, the probabilities will still sum to 1 across yes and no while outside quietly reports that most of the mass went elsewhere.

Resampling

samples controls how many times the read is repeated with fresh noise:

ValueBehaviour
"auto" (default)Read once. If any answer is uncertain, fire three more draws at once, so the engine batches them into one pass rather than four.
An integer, 1 to 32Read exactly that many times.

Probabilities are averaged over the reads, agreement reports how often they agreed, and stderr says how much the winning probability moved. The diagnostics.questions array lists every read's pick and confidence per question, so when reads disagree you can see which ones did rather than only that some did.

Questions That Depend On Answers

Not every question applies to every text. A question can be asked only when an earlier one came out a particular way, and questions are then run in stages by their dependencies. This is DiffusionGemma's alone; Laya and Clef refuse it.

FieldOn a question
ask_if{"kind": ["bug"]} asks this one only when kind answered bug. A question whose condition fails answers null and says why, rather than vanishing from the reply.
depends_on["severity"] reads this one with severity's answer already known, so it can take it into account.
aloneGives this question a read of its own rather than sharing a pass.
"questions": {
  "kind":     {"type": "choice", "criteria": {"bug": "...", "question": "...", "praise": "..."}},
  "severity": {"type": "score", "criteria": ["low", "high"], "ask_if": {"kind": ["bug"]}},
  "urgent":   {"type": "noul", "depends_on": ["severity"]}
}

Dependencies must not form a cycle; one that does is refused when the request is read rather than discovered part way through. sequential: true at the top level runs a chunked question set in order with earlier answers prefilled, instead of in parallel.

Reading Pictures

DiffusionGemma and both Clef models read a question set over pictures as well as text. Pass images as data URLs, or send the whole thing as multipart with the JSON in a request part and each picture as a file part. Up to 16 images per read.

Size them before sending. A 12 MP photo spends around 20 seconds in Clef's vision tower, which will dominate a request that otherwise takes a fraction of a second.

The Studio's Reads page takes pictures by button, drop or paste.

Getting More Out Of A Read

Three ways to spend more compute on a harder question set. All three are DiffusionGemma's; Laya and Clef refuse them rather than accepting and ignoring them.

FieldEffect
stepsUp to 8 denoising steps with the answer template pinned, so the answer slots condition on each other's partly settled values before the read is taken.
thinkA thought budget in tokens, up to 4096. The model writes that much reasoning first and the read conditions on it.
samplesRepeated reads with fresh noise, as above.

Limits

  • At most 64 questions per request. A set whose template does not fit the canvas is read in chunks rather than refused, and past ten questions the template packs ids into labels to save room.
  • Up to 16 images per read, on DiffusionGemma with its vision companion.
  • The endpoint needs either a diffusion model or a decision model loaded. It is not available on the ordinary chat models.

Laya is narrower, and worth reading before you choose it. It reads text only and runs on NVIDIA GPUs only. It is weak on a task it has no examples of: on the vendor's own typed-decisions benchmark the base checkpoints trail the majority-class baseline. Accuracy falls off past about 20 options, and on a choice with more than ten the calibration is off, because the English checkpoint ships an out-of-range temperature there which Paddock clamps exactly as the reference implementation does.

Laya also reports two numbers of its own: answer_confidence, its own calibrated measure, which is what its temperatures were fitted to, and entropy_confidence in the diagnostics. It carries an act_probability from its action head, which the model card itself reports as unreliable. It is passed through because the model produces it, not as a recommendation.

In the Studio

The Studio's Reads page builds question sets, runs them over a pasted or attached text, and saves the sets for reuse, so a question set can be worked out interactively before anything calls the endpoint. See Studio.