Skip to main content
A grounded answer contains annotation text in message.content. To draw it, first check that the response completed, parse the requested geometry, and resolve its asset selector to the correct image. See annotation format for the complete grammar and coordinates for scaling and crop transforms.

Save a completed grounding answer

The image Q&A, captioning, detection, and OCR guides include grounded examples. Request point_box elements with labels in mention for this walkthrough. You can request explicit asset_idx values to help map boxes to images, but the attribute is optional and the model may omit it even when requested. Save the completed output as response.txt, and keep the exact input image locally. For example, after a non-streaming request has returned response:
For streaming, accumulate the answer and require successful completion before saving it. Receiving a closing box tag does not establish that the whole response succeeded.

Draw image boxes

Install perceptron>=0.4.0 and pillow and save the following as render_boxes.py. Provide the index of the image you want to render and the index of the last asset available when the saved answer was produced. For a request with one image, both indices are 0:
For a request with two images, render the second image with:
The parser preserves a missing asset_idx as None; the renderer resolves it to --last-asset-idx. After checking inherited selectors, an omitted selector refers to the last asset available when the answer was produced. An explicit selector, including 0, takes precedence. Count supplied media in message history and tool results up to that answer. Do not include media added later in the conversation when rendering an earlier answer. To render the first image from the two-image request, use --asset-idx 0 --last-asset-idx 1; boxes without selectors still belong to asset 1 and are filtered out. The SDK parser handles boxes and inherited collection selectors. This renderer accepts a small, explicit subset: static point_box elements, optionally grouped in collection elements, with only mention and asset_idx attributes. Write coordinates as (x1,y1) (x2,y2), quote attribute values, and XML-escape label characters such as & and ". Tracks, timestamps, and other geometry need a different renderer. strict=True raises reported SDK parse errors; it is not a complete syntax or geometry validator. The example checks each annotation fragment with Python’s XML parser, rejects leftover annotation markup, and validates coordinates and selectors before drawing.
Within this supported subset, malformed markup, empty or duplicate selectors, unsupported attributes, and invalid coordinates raise an error rather than silently changing the prediction. Keep the original model output for inspection if parsing fails; do not repair an unfinished answer into an apparent detection. A successful run can draw zero boxes when the answer contains no detections for that asset. Inspect the answer and your asset mapping to distinguish an absent object from a selector mismatch. A valid box and a successful drawing do not establish that the model identified the correct object.

Extend to other geometry

For points and polygons, apply the same asset selection and coordinate conversion to each coordinate. For collections, resolve inherited asset selectors before rendering children. For tracks, first select the video and timestamp; the tracking guide explains sparse observations and interpolation. Use image drawing primitives for geometry and plain text for labels. Do not insert the model’s annotation string as executable HTML; ordinary HTML parsers also treat track as a void element and can lose its children.