> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brussle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Judge images

> Have a judgment read a document's photos and scans beside its text, see which images each answer read, and keep the cost in a known size class.

Name a document's [images](/guides/images) in a judgment's recipe, and an engine that reads images judges them beside the text. Every evaluation names the images it read, and each image counts toward the judgment's size class by its pixels, so you can bound the cost.

<Note>
  Only some engines read images. Every engine reads the text, numbers and attributes of a document the same way. A judgment reads an image only where its [context recipe](/guides/context-recipes) names it. An engine that does not read images gets the image's digest in its place, and the evaluation says so. Each [engine's page](/engines/index) has an **Images** row that says how many images the engine reads in one request, and the [engines](/engines/index#versions) table shows which versions read images. An engine reads images when its `limits` in `GET /engines` have `images`.
</Note>

## Name images in a recipe

You do not send images to a judgment. You write them to documents, and the judgment's [context recipe](/guides/context-recipes) names each image that it reads as `images.<name>`. Choose an engine that reads images. This judgment reads each claim's description and its photos:

```json POST /v1/namespaces/acme%2Fclaims/judgments theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "name": "damage_matches",
  "type": "bool",
  "applies_to": {"attributes.kind": "claim"},
  "question": "Do the photos show damage that matches the description?",
  "criteria": "Yes only if a photo shows damage in the place and of the kind that the description gives. Blurred or unrelated photos are no.",
  "context": {"fields": ["state.description", "images.photos"]},
  "engine": {"name": "gpt-6-luna", "version": "current"},
  "freshness": {"policy": "on_change"}
}
```

`images.photos` names the claim's list of photos. The engine reads each image in the list, and the text of `state.description`. On an engine that reads no images, the same recipe works, but the engine gets each image's digest in its place, and the create [warns you](#on-an-engine-that-reads-no-images).

A recipe can name `images.<name>` in these places:

* **The recipe's `fields`**, as above.
* **`previous.fields`**, to show the image that the last verdict saw beside the current one. See [judge what changed](/guides/change-detection).
* **A relation's `fields`**, to read the images of related documents, such as the policy's photo of the car when it was insured.
* **A relation's `latest` aggregate**, to read the image of the newest related document that has one.

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "context": {
    "fields": ["state.description", "images.photos"],
    "related": {
      "policy": {
        "match": {"attributes.kind": "policy"},
        "join": {"theirs": "id", "mine": "attributes.policy_id"},
        "fields": ["state.vehicle", "images.vehicle_photo"]
      },
      "earlier_claims": {
        "match": {"attributes.kind": "claim"},
        "join": {"theirs": "attributes.policy_id", "mine": "attributes.policy_id"},
        "window": "365d",
        "aggregate": {"count": true, "latest": ["images.photos"]}
      }
    }
  }
}
```

### What an image path cannot do

An image path names a whole image, or a whole list. Brussle refuses these with `invalid_request`:

* `max_chars` on an image path. An image is never cut.
* `last_n` or `window` on an image path.
* A path inside an image, such as `images.photo.width`.
* `sum`, `avg`, `min`, `max` or a band over an image.
* An image in the `label` of `options.from`. A label is text.
* `order.by` on an image.
* An image in `features`, such as `claims.latest(images.photo)`. A feature is a number. The error's `details.path` names the image path.

A judgment without a recipe (`"use_context": "whole_state"`) reads `state` only, never images. A change to an image that the recipe does not name judges nothing again.

## On an engine that reads no images

Create a judgment whose recipe names image paths on an engine that reads no images, and the response has a warning naming the paths, such as `images.photo`, or `images.photo of relation claims` for a relation. Activating such a version warns again, with the code `images_unread` and the `paths`.

Brussle decides this from the definition alone, so the warning comes even when you create the judgment before you write any documents. Setting the namespace's `default_engine` gives no warning.

## What the evaluation shows

An evaluation's context is keyed by path. It names each image by its reference, never by its bytes, so you can always see which images an answer read. Get each one that you did not erase with `GET /namespaces/{ns}/images/{digest}`.

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "state.description": "Rear door dented in a car park.",
  "images.photos": [
    {"digest": "sha256:e0db47...", "type": "image/jpeg", "width": 1440, "height": 960},
    {"digest": "sha256:9b1c2e...", "omitted": "over_the_limit"}
  ]
}
```

Images are only at these paths: `images.<name>`, `previous.images.<name>`, `images.<name>` in a relation's `records`, and a relation's `latest(images.<name>)`. Everything else, `state` included, is JSON as you wrote it.

When the engine does not read an image, it reads the image's digest instead, and the context shows `{"digest": "sha256:e0db47...", "omitted": "..."}` with one of three reasons:

* **`engine_reads_no_images`.** The engine reads no images. This is not a cut, so it does not make `context_truncated` true.
* **`over_the_limit`.** The engine reads images, but this one did not fit. A context holds at most 16 images. When a context is over its token limit, Brussle leaves out whole images before it cuts any text: the images of relations first, then those of `previous`, then those of the recipe's own fields, from the last field. If the text alone is over the limit, Brussle leaves out every image and then cuts the text. `context_truncated` is then true.
* **`erased`.** You [erased](/guides/images#erase-an-image) the image before this judging. This does not make `context_truncated` true.

The answer is the engine's answer to exactly what the context shows.

## What images cost

An image counts toward the judgment's [size class](/pricing#judgments) by its size in pixels, as text counts by its tokens. An engine reads an image in tiles of 32 × 32 pixels, each about one token. A photo of 1440 × 960 pixels is 45 × 30 tiles, or 1,350 tokens, plus about 10 tokens for the label that names its path. With a short record beside it, that is still a standard judgment. Two such photos are more than 2,000 tokens, so the judgment is large. To bound the cost, name fewer images in the recipe.

* **A full-size photo makes a large judgment.** Brussle scales a large photo, such as one from a phone camera, to about 2,048 tiles, about 2,048 tokens. That alone is more than a standard judgment, so with any question the judgment is large and counts as 4. To keep a judgment standard, send photos of at most about 1,500 × 1,000 pixels, with a short record beside them.
* **The engine's weight applies.** On gpt-6-luna, whose [weight](/pricing#judgments) is 2.5, a standard judgment counts as 2.5 judgments, and a large one as 10.
* An image that the engine does not read costs only its digest, about 100 characters.
* A document whose read images and other read fields did not change is not judged again. Sending the same image again gives the same reference, so it changes nothing. When a field that the recipe reads changes, the engine reads the images again, and they count again.
* An estimate counts images as the bill does. It still counts an image that you [erased](/guides/images#erase-an-image), so it can be a little higher than the bill.

A [suggested context recipe](/guides/context-recipes#when-you-leave-out-context) makes each name in `images` one candidate field, `images.<name>`. On an engine that reads images, it keeps them only while the context stays a standard judgment. When it has to choose, it keeps the text and leaves out image names, the last one first, with the reason `image`, so it leaves out full-size photos. On an engine that reads no images, it leaves out each image name with the reason `image`. To judge full-size photos, name them in your recipe yourself, and do not copy the suggested `max_tokens`: a context over `max_tokens` leaves images out as `over_the_limit`.

Storage, writes and reads of images are billed as [images in documents](/guides/images#billing) describes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.