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

# Multiple assets

> Ground answers across media and conversation history with asset_idx.

Send multiple media parts in a conversation to compare scenes, locate the same object in different inputs, or use a reference image to guide a search. The optional `asset_idx` attribute identifies which input an annotation describes. The model may omit it, including in multi-asset answers and when the prompt asks for it. If no explicit or inherited selector is present, the annotation refers to the last asset available to the model at that point in the conversation.

## How assets are numbered

Assets are numbered from zero in their order of appearance across the messages supplied in the request, then within each message's content array. An annotation refers to the media available up to its position in that conversation. Text does not consume an index. Images, videos, and standalone audio share one sequence.

| Supplied conversation content               | Asset index  |
| ------------------------------------------- | ------------ |
| First user message: reference image         | `0`          |
| First user message: video                   | `1`          |
| Assistant answer containing annotation text | No new asset |
| Later tool result containing an image       | `2`          |
| Later user message containing another image | `3`          |

Each media occurrence has a position. Repeating the same URL or uploaded `file_id` in a separate content part introduces another position; file reuse does not make the selector a persistent file identifier.

Keep these distinctions in mind:

* One `video_url`, `video_file_id`, or `video_frames` group is one asset. Individual frames inside that group are addressed by time.
* One `input_audio`, `audio_url`, or `audio_file_id` part is one asset. For an image followed by a standalone recording, the image is asset `0` and the audio is asset `1`. Enabling a video's soundtrack does not add another asset; its frames and audio belong to the same video.
* Frames supplied as separate `image_url` parts are separate image assets, even if they came from the same video.
* Images returned by functions count in the conversation's asset order. The original image and an image crop returned by a tool are separate assets with their own coordinate systems.
* Numbering does not reset at a new user turn. Resend the relevant conversation history when continuing a conversation.
* Removing or reordering earlier media changes the mapping. Recompute selectors for the actual request instead of reusing an index from a different history.

The request limit is 256 media units. This is a separate count from `asset_idx`: every frame inside a `video_frames` group consumes one media unit, although the group has only one asset index.

## Compare a reference image with a target

Install the Perceptron Python SDK and set `PERCEPTRON_API_KEY`. This example uses two public sample images and requests a box in the second image.

```bash theme={null}
pip install "perceptron>=0.4.0"
export PERCEPTRON_API_KEY="your-api-key"
```

```python theme={null}
import os

from perceptron import Client, image

client = Client(
    api_key=os.environ["PERCEPTRON_API_KEY"],
    provider="perceptron",
)
asset_root = (
    "https://raw.githubusercontent.com/perceptron-ai-inc/perceptron/"
    "main/cookbook/_shared/assets/in-context-learning/single/"
)

response = client.chat.completions.create(
    model="perceptron-mk1.5",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Reference object (asset 0):"},
                image(asset_root + "cake_mixer_example.webp"),
                {"type": "text", "text": "Search this image (asset 1):"},
                image(asset_root + "find_kitchen_item.webp"),
                {
                    "type": "text",
                    "text": (
                        "Find the object in asset 1 that matches the reference in asset 0. "
                        "Return a point_box with asset_idx=\"1\". "
                        "If no matching object is visible, say so without inventing a box."
                    ),
                },
            ],
        }
    ],
    max_completion_tokens=512,
    vision_config={"annotation_format": "box"},
)
choice = response.choices[0]
if choice.finish_reason != "stop":
    raise RuntimeError(f"Incomplete answer: {choice.finish_reason}")
print(choice.message.content or "")
```

The response is text containing annotation markup, not a separate JSON array of boxes. An illustrative annotation is:

```html theme={null}
<point_box mention="matching object" asset_idx="1"> (400,300) (600,650) </point_box>
```

The coordinates above demonstrate the format; they are not measured output for these sample images.

## Resolve selectors before drawing

Select the asset first, then use that asset's dimensions to convert normalized coordinates into pixels. For example, `(500,500)` is `(960,540)` on a 1920×1080 image and `(320,240)` on a 640×480 image.

An annotation can inherit its selector from a collection. An explicit child selector overrides the collection's selector:

```html theme={null}
<collection mention="objects" asset_idx="0">
  <point_box> (100,150) (300,350) </point_box>
  <point_box asset_idx="1"> (400,300) (600,500) </point_box>
</collection>
```

Here the first box belongs to asset `0` and the second to asset `1`. This override is useful for collections; each `<track>` must stay within one asset. For tracking the same object in two videos, request two tracks with explicit selectors. Their presence is not guaranteed.

Resolve an annotation's asset in this order:

1. Use its explicit `asset_idx`, if present.
2. Otherwise, use an inherited selector from its collection or track.
3. If neither is present, use the index of the **last asset available when that annotation was produced**: `number_of_available_assets - 1`.

In the reference-plus-target example above, a box without `asset_idx` refers to target image `1`. With only one media asset, the default is `0`. Count media from earlier supplied messages and tool results up to that annotation's position, across all modalities.

For example, suppose the user supplies images A (`0`) and B (`1`), and the assistant returns a box without a selector. That box refers to B. If a later tool result supplies image C (`2`), an omitted selector in the next assistant answer refers to C. The earlier box still refers to B; adding C does not change its binding. Store each answer with the asset mapping available when it was produced.

Validate explicit and inherited selectors against the asset range available at that annotation's position. An out-of-range or malformed selector is invalid; do not treat it as omitted or replace it with the default. Preserve explicit `0` by testing for a missing value rather than using a truthiness check.

In the SDK, `response.annotations(strict=True)` applies inherited selectors to parsed annotations. Use `response.resolve_asset_idx(annotation)` to resolve the remaining omissions using the asset count captured for that answer. Keep that response with its original asset mapping; do not resolve an older annotation using a newer response.

Check the selected asset's modality before drawing geometry. If an image is followed by standalone audio, an omitted selector resolves to the audio, not automatically to the last image. Request an explicit selector for image annotations and handle an incompatible or missing visual target instead of drawing on a different asset.

## Multilook uses a separate sequence for each prompt

Each Multilook prompt sees the shared context followed by its own content. Asset numbering follows that individual sequence, not the entire batch.

| Shared context      | Prompt content          | Selectors in that result   |
| ------------------- | ----------------------- | -------------------------- |
| One reference image | Prompt A: query image A | Reference `0`, query A `1` |
| One reference image | Prompt B: query image B | Reference `0`, query B `1` |

Both query images are asset `1` in their respective responses. An omitted selector defaults to the last asset in that prompt's context-plus-prompt sequence, so it also selects asset `1` in both examples. Keep the response's `prompt_index` alongside the asset selector when associating results with inputs. The request-wide media-unit limit still includes the shared context and all prompts.

Multilook is for independent prompts. Function calling and tool-result histories belong on `/v1/chat/completions`; see [tool calling](/perceptron-mk1.5/guides/tool-calling).

## Next steps

* Read the [annotation format](/perceptron-mk1.5/concepts/annotations) for geometry, collection, and time syntax.
* Use [video tracking](/perceptron-mk1.5/capabilities/video-tracking) to follow objects within each video.
* Follow [High Fidelity Object Tracking](/perceptron-mk1.5/guides/high-fidelity-object-tracking) to combine a video's model waypoints with local motion and appearance tracking.
