> ## 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.

# Tokenization guide

> Budget text, images, video, audio, reasoning, and tool conversations using reported token usage.

Perceptron Mk1.5 uses one context window for the input conversation and generated output. Text, visual inputs, audio, tool definitions, and conversation history all contribute to that budget. Use the [model card](/perceptron-mk1.5/models/perceptron-mk1.5#specifications) for the context and output limits, and its [pricing table](/perceptron-mk1.5/models/perceptron-mk1.5#pricing) for token rates.

## Reserve space for output

Set `max_completion_tokens` to the output budget for each request. It is an upper bound, not a requested answer length. Reasoning, the final answer, annotation markup, and generated function arguments consume this budget.

Plan for both constraints:

```text theme={null}
input tokens + output budget <= context window
output budget <= model maximum output
```

Input includes message formatting and tool schemas as well as visible text and media. A character count or text-only tokenizer cannot determine the full size of a multimodal request.

When [reasoning](/perceptron-mk1.5/capabilities/thinking) is enabled, leave room for reasoning and the final answer together. A small output budget can be exhausted before the model finishes a track, JSON response, or function call. Increasing `reasoning_effort` does not increase `max_completion_tokens`.

Chat completions rejects a request if its input plus requested output budget exceeds the context window, or if `max_completion_tokens` exceeds the model's output limit. Leave room for the desired output; increasing the output limit alone cannot make an oversized conversation fit.

## Image token counting

Images are decoded and processed into visual patches. The processed spatial grid determines their visual token count. Resizing, aspect ratio, minimum and maximum image budgets, and patch grouping affect that grid.

* Compressed file size is not a token count. Saving the same dimensions as a smaller JPEG does not proportionally reduce visual tokens.
* Large images may be resized, and small images may be upscaled. Source dimensions alone do not establish the exact processed token count.
* Each supplied image contributes to the input, including reference images and images returned by tools. Uploading a file once avoids repeated upload work; it does not make later model input free.
* Cropping to a relevant region can improve detail per request, but the crop is another input if you also send the original image.

For estimates, measure `usage.prompt_tokens` on representative Mk1.5 requests using the same prompts, tools, image dimensions, and aspect ratios as your application. That number includes the complete input, not only the image. Resolution-to-token tables for another model do not account for Mk1.5's preprocessing.

## Video token counting

Video token use depends on the frames processed and their spatial resolution. Frames are grouped temporally and spatially, so multiplying a standalone image estimate by the source video's frame rate is not an accurate video-token calculation.

For a `video_url` or `video_file_id`, the model processes sampled frames across the selected video. A frame cap reduces sampling density over longer footage; it does not mean that only the beginning of the video is analyzed. The visual budget can also limit per-frame resolution.

With `video_frames`, you choose the input images and timestamps, but those frames still undergo video preprocessing. Supplying fewer frames can omit a brief event; more frames can increase input cost and compete for visual detail. Neither duration nor frame count alone determines the final token total.

Use the [video input guide](/perceptron-mk1.5/capabilities/video-understanding) for frame examples and timestamp conventions. For cost estimates, compare representative short and long clips, aspect ratios, and frame selections using reported usage. Measure the frame-selection strategy you intend to use rather than assuming a fixed number of tokens per second of source video.

## Audio and soundtrack token counting

Standalone audio uses approximately **750 audio tokens per minute**. A two-minute recording therefore contributes about 1,500 audio tokens, plus message formatting and text. File size, compression, and a text transcript's length do not determine the audio-token count.

Setting `vision_config.enable_audio_in_video: true` adds the video's soundtrack to its visual input. Budget approximately 750 audio tokens per minute of processed soundtrack, plus timestamp text, on top of the frame tokens. The option must be explicitly set to `true`; omitting it or setting it to `false` leaves soundtrack processing disabled. When enabled, an encoded audio track consumes audio tokens even if it contains only silence. A video with no audio stream or no soundtrack overlapping the processed interval falls back to visual input; `video_frames` has no soundtrack. See [video soundtracks](/perceptron-mk1.5/capabilities/video-understanding#analyze-video-soundtracks).

The current Mk1.5 limit is **16,384 audio tokens per item**, roughly **21.8 minutes** at that rate. This per-item cap is separate from the model's 36,864-token context window: several individually valid recordings, or a video with both frames and audio, can exceed the shared context. Over-limit audio returns `audio_token_limit_exceeded` instead of being truncated. Split or shorten the recording; lowering the output budget does not change the per-item audio cap.

Use the rate to estimate capacity, then use reported usage for accounting. `usage.prompt_tokens_details.audio_tokens` includes standalone audio and opted-in soundtracks and is already included in `prompt_tokens`. It does not include all surrounding text overhead and is not an extra charge to add again. See [Audio](/perceptron-mk1.5/capabilities/audio) for input examples.

## Tokens, media units, and asset indices

These are separate counts:

| Quantity    | What it controls                                                                                                                                                                              |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Tokens      | Context capacity, generated output, and token-based pricing.                                                                                                                                  |
| Media units | The request's media-input limit. An image, video, or audio reference counts as one unit; each frame inside `video_frames` counts as one unit.                                                 |
| `asset_idx` | Which media occurrence an annotation describes, numbered from the media available up to that point in the conversation. One `video_frames` group is one asset, regardless of its frame count. |

For example, one reference image followed by a `video_frames` group containing 20 frames uses 21 media units and two asset positions: image `0`, video `1`. It does not use 21 tokens. The current request cap is 256 media units; staying below it does not guarantee that the tokenized input fits the context window.

See [multiple assets](/perceptron-mk1.5/guides/multiple-assets) for indexing across user messages and image tool results.

## Read usage from a response

Install `perceptron>=0.4.0` and set `PERCEPTRON_API_KEY`. This example prints the usage returned with an image answer and handles missing usage, cache, or audio breakdowns. The same handling applies to audio and video requests.

```python theme={null}
import os

from perceptron import Client, image

client = Client(
    provider="perceptron",
    api_key=os.environ["PERCEPTRON_API_KEY"],
)
response = client.chat.completions.create(
    model="perceptron-mk1.5",
    messages=[{
        "role": "user",
        "content": [
            image(
                "https://raw.githubusercontent.com/perceptron-ai-inc/perceptron/"
                "main/cookbook/_shared/assets/capabilities/qna/studio_scene.webp"
            ),
            {"type": "text", "text": "Describe the scene in two sentences."},
        ],
    }],
    max_completion_tokens=1024,
)

usage = response.usage
if usage is None:
    print("Usage was not included in this response.")
else:
    print(f"Input tokens: {usage.prompt_tokens}")
    print(f"Output tokens: {usage.completion_tokens}")
    print(f"Total tokens: {usage.total_tokens}")
    cached = usage.cached_tokens
    if cached is not None:
        print(f"Cached input tokens, included in input: {cached}")
    audio_tokens = usage.audio_tokens
    if audio_tokens is not None:
        print(f"Audio tokens, included in input: {audio_tokens}")

choice = response.choices[0]
if choice.finish_reason != "stop":
    raise RuntimeError(f"Answer did not complete: {choice.finish_reason}")
print(choice.message.content or "")
```

`prompt_tokens` measures input; `completion_tokens` measures generated output, including reasoning when used. `total_tokens` combines the two. Count the reported output, not only the length of `message.content`: reasoning and function arguments can be returned in separate fields.

When `prompt_tokens_details.cached_tokens` is present, it is a subset of `prompt_tokens`. Do not add it again to the total. Cached input still occupies context; its pricing is different. An absent breakdown does not establish a cache hit, and repeating a URL or request does not promise one.

The chat-completions audio breakdown is optional too. A missing or `null` `audio_tokens` value means the breakdown was not reported; it is not a measured zero. An explicit zero reports no audio tokens. Multilook instead defaults a missing upstream audio breakdown to zero, so zero alone cannot prove audio was skipped there. For multiple inputs, the aggregate cannot identify which particular video's soundtrack was analyzed.

[Multilook](/perceptron-mk1.5/guides/multilook) reports aggregate usage for independent prompts over shared context. Use its aggregate once when accounting for the request; do not add the same aggregate to every prompt result.

For streaming requests, send `stream_options={"include_usage": True}`. Usage may arrive after the last content delta in a chunk with `choices: []`. The SDK includes it in the completion returned by `stream.get_final_completion()` after consuming the stream. Read it independently of choice deltas if you handle chunks yourself, and avoid summing cumulative usage snapshots within one response. The [tracking stream example](/perceptron-mk1.5/capabilities/video-tracking#stream-a-tracking-answer) shows this pattern.

## Budget a tool conversation

Each model request in a [tool loop](/perceptron-mk1.5/guides/tool-calling) has its own input and output usage. Previous assistant calls and tool results become input on later requests; tool-returned images contribute visual tokens too.

Add usage across model requests to measure the whole workflow. Bound the number of rounds and the size of tool results, and reserve capacity for the final answer. Compact long retrieved text to the relevant evidence while keeping call/result pairs intact. If you remove or reorder media in history, update any affected `asset_idx` references.

## Handle an output limit

`finish_reason: "length"` means the generated response is incomplete. Do not treat a partial JSON object, track, or function argument string as a completed result.

Increase `max_completion_tokens` when both model limits and remaining context allow it. Otherwise reduce the input, shorten the requested answer, or split the task into smaller requests. For tracking, request fewer objects or a narrower interval rather than completing missing markup yourself.

Keep usage from incomplete responses when available: they still generated tokens. Review quality alongside usage when changing image resolution, frame sampling, reasoning effort, or output budgets.
