# Manifest: the Bots Tab

> The manifest editor's Bots tab - import and check AI models, declare scripted and AI bots, wire their handlers and difficulties, and open the training page.

- Source: https://diceytable.com/docs/editor/manifest/bots
- Last verified: 2026-10-02

**Mod ▸ Mod Details… ▸ Bots** is where a game declares its opponents ([`bots`](https://diceytable.com/docs/mods/manifest/reference#bots))
and the models its AI bots run ([`aiModels`](https://diceytable.com/docs/mods/manifest/reference#aimodels)). The tab exists only on a
**game pack** — the host loads models and seats bots for the room's game, and nothing else. Its badge counts the
problems publish would refuse. [Game bots](https://diceytable.com/docs/mods/concepts/bots) explains the feature; this page is the
reference for the controls.

## AI models

The totals line shows how many models you have of the 4 allowed and their combined size against the 128 MiB cap.
Each model is a row: id, path, size, node count, opset, min tier and a **status** — *not checked*, *checking…*,
*valid*, *file missing*, *could not check*, or the refusal codes the scanner would report, each listed underneath
with its message verbatim (`ai-model-op-not-allowed: Operator "Erf" is not one a model may use.`). The checks are
the publish scanner's own code, run here, so a model that shows *valid* passes publish.

- **Import model…** — pick an `.onnx` file. See [The Import model dialog](https://diceytable.com/docs/editor/manifest/bots#the-import-model-dialog).
- **Re-check files** — validate every model file in the draft again (after replacing one outside the editor, say).
- **Label** and **Min tier** per model; **Remove** deletes the declaration and the file.
- **Difficulty** presets per model — **Difficulty** (id), **Label**, **Sims** (1–4096, 0 for unset) and **Temp**
  (0–2). ⚠ These are recorded with the model, not applied: your script decides its own simulations. The one value
  the table reads is a preset's `params.turnBudgetMs`, which this tab does not edit.

A model over 45 MiB gets a note: the in-app Publish cannot send a file that large, so push it to the game's GitHub
repository with git (`git add <path>`, commit, push), then publish.

An `.onnx` file in the project that no model declares is listed with **`ai-model-undeclared`**: publish refuses it.
Import it as a model or delete it on the Files tab.

**Trained models ready to import.** When the [training page](https://diceytable.com/docs/editor/training) has exported a checkpoint for
this project on this device, it is listed here with its size and generation. **Import…** opens the import dialog on
it — the same checks as a picked file, so a file that changed since the export is refused — and **Discard** deletes
the export without importing it. Following the training page's *Open the editor to add it* link opens this dialog
directly.

## The Import model dialog

1. **Model file (.onnx)** — the file is checked at once, in a background worker, exactly as publish will check it.
   A format that is not ONNX (`.pt`, `.h5`, `.safetensors`, …) is refused as `ai-model-format`. A refused model shows
   every issue code and message; fix them in the tool that exported it and pick the new file.
2. The card shows what the model is: size, opset, IR version, inputs and outputs, an operator histogram, the
   parameter count, and an **estimated tier** — measured on this device when it can be, otherwise the estimate for a
   fast processor. Above about 12 MB, or 50 ms per position, it suggests `gpu`.
3. **Label**, **Id** (lowercase, digits, hyphens) and **Min tier**. Picking a tier below the estimate is allowed
   with a warning — choose it only if you have measured it. Importing over an existing id **replaces** that model:
   its file and hash are overwritten and the bots using it keep pointing at it.
4. For a model that looks like a policy/value network, **Difficulties** offers the presets Easy 16, Normal 64 and
   Hard 256 simulations per move.
5. **Add to project** (or **Replace model**). The [import credit gate](https://diceytable.com/docs/mods/publishing/attribution) asks
   where the file came from first; then the file is written to `ai/<id>.onnx` and the manifest entry — with the
   file's exact size and SHA-256 — is added, and `assets` updated.

## Bots

**Add bot** (up to 8) adds a row; **Remove** deletes one (an AI bot that named it as its fallback loses the
fallback, and the tab says so). Each row:

| Control | Field | Notes |
|---|---|---|
| Label | `label` | The name in the lobby and at the table. |
| Id | `id` | Rooms remember a seated bot by it. |
| Kind | `kind` | **Scripted** or **AI**. |
| Model | `modelId` | AI only — one of the models above: the bot's default model. |
| Fallback bot | `fallbackBotId` | AI only — a scripted bot of this game offered to a new host that cannot run the model. |
| Handler | `handler` | The id the scene script passes to `bots.register({ id, onTurn })`. |
| Registered | — | The handler ids your scene scripts actually register; picking one fills in **Handler**. |
| Default difficulty | `defaultDifficulty` | Which difficulty a room starts on; "the first" when unset. |
| Seats (most) | `seats.max` | The most of this bot one table may seat; 0 leaves it unset (as many as the game has seats). |
| Description | `description` | One line, shown where the bot is offered. |
| Difficulty / Label / About | `difficulties[]` | Named strengths players pick; **Add difficulty** (up to 8). |
| Difficulty ▸ Model | `difficulties[].modelId` | AI only — the model this difficulty plays with, for a separate network per Easy / Medium / Hard. **Bot's default model** leaves it unset and uses **Model** above. The table loads, gates the host's device on, ranks and reports (`ctx.modelId`) the model picked here for that difficulty. |

**Handler warnings.** The **Registered** list is read from each scene script's **compiled** body — what publish
reads. A handler no script registers is flagged (publish refuses it as `bot-handler-missing`). A handler that is in
a script's source but not in its compiled body gets its own warning: the script was edited but not saved; open it
and save it so it compiles. Only `bots.register({ id: "…", … })` with the id written as a plain string inside the
call is seen.

**Train a model…** opens the [training page](https://diceytable.com/docs/editor/training) for this project in the same tab.

## See also

- [Game bots](https://diceytable.com/docs/mods/concepts/bots) — what all of this does at the table.
- [Training a bot model](https://diceytable.com/docs/mods/guides/training-bots) — where models come from.
- [What gets rejected](https://diceytable.com/docs/mods/scanner/what-gets-rejected#ai-model-rules) — every model and bot refusal.
- [Bots and AI](https://diceytable.com/docs/scripting-api/bots-and-ai) — `bots.register` and the `ai` calls.
