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

# Python FAQs

> Common questions about Python requests, media, annotations, and async execution.

## Which model ID should I use?

Use `model="perceptron-mk1.5"` for the workflows in this version of the documentation. Configure the client with Perceptron's API URL and key as shown in [Python setup](/perceptron-mk1.5/guides/python/getting-started).

## Can I pass a local image, video, or audio path?

Yes, wrap the path with `image()`, `video()`, or `audio()` in a message’s content list. The SDK reads the file and encodes it inline; the API never reads a path on your computer. To upload once and reuse the media, call `client.files.upload()` and pass the returned `File` to the matching helper. See [Files](/perceptron-mk1.5/guides/files). A remote URL must return accessible media bytes; a link to an authenticated viewing page is not enough.

The [MCP bridge](/perceptron-mk1.5/guides/mcp) has separate local-file handling: it reads and uploads the file before making a vision request.

## How do I ask a follow-up question?

Resend the conversation history, including the relevant media and prior assistant messages. The API does not recover earlier turns automatically. Preserve complete assistant messages in your client-side history, including `reasoning_content` and function calls when present. Historical reasoning is passed to the model for requests with tool declarations or tool traffic; the gateway omits it from ordinary non-tool requests. See [Request basics](/perceptron-mk1.5/guides/python/request-basics).

## Why are coordinates in the answer text?

Spatial and temporal annotations remain available as markup in `response.text`. Call `response.annotations(strict=True)` to parse boxes, points, polygons, clips, and tracks, then `response.resolve_asset_idx(annotation)` to select the media from that request. Use that asset’s dimensions when [converting to pixels](/perceptron-mk1.5/concepts/coordinates). Parsing does not verify that the prediction is correct.

Use [Rendering annotations](/perceptron-mk1.5/guides/rendering-annotations) for a worked example. For video, preserve timestamped observations and object identity as described in [Video tracking](/perceptron-mk1.5/capabilities/video-tracking).

## Can I send several assets together?

Yes. Put the media parts in the message's content list and keep their order stable across follow-up requests. The optional `asset_idx` attribute is the zero-based index of a media occurrence available at that point in the conversation; it is not a file ID or video frame index. Numbering continues across messages and turns. If the attribute is neither explicit nor inherited, it defaults to the last asset available at that point. Media added later does not change the target of an earlier annotation. See [Multiple assets](/perceptron-mk1.5/guides/multiple-assets) for the ordering rules.

## How do I make asynchronous requests?

Use `AsyncClient` and await the request. This complete text example uses the same API settings as the synchronous client:

```python theme={null}
import asyncio
import os

from perceptron import AsyncClient


async def main():
    client = AsyncClient(
        api_key=os.environ["PERCEPTRON_API_KEY"],
        provider="perceptron",
        timeout=60.0,
    )
    response = await client.chat.completions.create(
        model="perceptron-mk1.5",
        messages=[{"role": "user", "content": "Reply with a brief greeting."}],
        max_completion_tokens=128,
    )
    choice = response.choices[0]
    if choice.finish_reason != "stop" or choice.message.tool_calls:
        raise RuntimeError(f"No completed text answer: {choice.finish_reason}")
    print(choice.message.content or "")


if __name__ == "__main__":
    asyncio.run(main())
```

In a notebook with an event loop already running, use `await main()` instead of `asyncio.run(main())`. Bound concurrent requests as described in [Scaling](/perceptron-mk1.5/guides/scaling).

## Does receiving text mean the response is complete?

No. Check the final `finish_reason` before using an answer. A `length` finish means the output was truncated. Streaming consumers must also handle empty-choice usage chunks, nullable deltas, and API errors after streaming has begun. See the [tracking stream example](/perceptron-mk1.5/capabilities/video-tracking#stream-a-tracking-answer) and [Error messages](/perceptron-mk1.5/guides/error-messages).

For JSON output, parse and validate only a successfully completed result; see [Structured outputs](/perceptron-mk1.5/capabilities/structured-outputs).

## Does the client execute function calls?

Your application validates and executes the returned calls, then sends tool results with the matching call IDs. Neither the chat API nor the Perceptron SDK runs your Python functions automatically. Use the complete [tool-calling loop](/perceptron-mk1.5/guides/tool-calling), including its streaming variant, and [workflow limits](/perceptron-mk1.5/guides/tool-agents).
