Models
Per-model wire contracts — the shapes the NT inference API enforces for each base model.
Draft — pending review.
The NT inference API supports multiple base models. Each base has a different input shape — different state dimensions, different camera names, different image resolution. These per-model shapes are the contract.
When you call Robot(model="so101").run(...), the API resolves the model, reads its contract, and validates your first observation frame against it. If the shapes don't match, the connection closes with code 4422 before any inference runs.
What a contract is
A contract declares five things about a model's wire interface:
| Field | Type | What it means |
|---|---|---|
state_shape | list of int | Expected dimensions of the joint-state vector your read_state() callback returns |
state_dtype | str | Expected dtype ("float32") |
cameras | object | {required: [...], expected: [...]} — required camera keys must be present in your images dict (missing one closes 4422); an expected camera key missing at the first frame zero-fills, and dropping it after that closes 4422 |
image_shape | list of int | Expected shape of each camera array, CHW ([C, H, W]) |
action_shape | list of int | Shape of each action chunk the server returns ([horizon, action_dim]) |
The first four fields are checked on your first observation frame. action_shape is informational — the server outputs it, not you.
How to read the contract for your model
curl -H "Authorization: Bearer $NT_API_KEY" \
https://nt-registry-production.up.railway.app/v1/models/ft_6341c5_d13da9The response includes the full contract block. Fine-tune entries return the resolved contract inherited from their base. The registry is an always-on service — the curl returns in about a second without waking the GPU container.
Coercion within a contract
The API coerces missing data within a contract, but not for every camera, and only at the first frame. If your images dict is missing a camera listed in cameras.expected (and not in cameras.required) on that first frame, the server fills that slot with zeros and the connection stays alive. A camera missing from cameras.required at the first frame closes 4422 — contract_mismatch.camera_missing — instead. Once a camera has sent a frame, dropping it later closes 4422 — contract_mismatch.camera_dropped — even if it was only ever listed in cameras.expected.
Coercion does not apply across contracts. An [8]-dimensional state vector sent to a model expecting [14] closes 4422, not coerced.
Choosing a model
Pick the model whose embodiment matches your hardware.
- MA2-SO-101 — SO-101 single-arm, 6D state, 2 cameras