# Preparing Models with modelctl

> Use the modelctl CLI on a workstation to convert, validate, quantize, and package ML models into an offline ONNX bundle for the Xisom edge box.

The Xisom runtime executes models in **ONNX** format. `modelctl` is a command-line
tool you run **on a developer workstation** — not on the box — to turn a trained
PyTorch or TensorFlow model into a validated, optionally quantized, checksummed
ONNX bundle you can carry onto an air-gapped edge device.

It complements the [offline bundle install](/install-deploy/offline-bundle-install/):
that flow installs the product; `modelctl` produces the per-model artifacts you
[deploy](/configure/models/) afterwards.

## The workflow

```mermaid
flowchart LR
  subgraph WS["Developer workstation — modelctl"]
    direction LR
    SRC["Trained model<br/>PyTorch .pt / TF SavedModel"] --> CV["convert<br/>→ ONNX"]
    CV --> VL["validate<br/>schema · opset · runtime · inference"]
    VL --> QZ["quantize<br/>dynamic INT8 (optional)"]
    QZ --> PK["package<br/>checksummed bundle"]
  end
  PK -->|"copy bundle (USB / offline)"| BOX["Edge box<br/>upload + activate"]
```

## Install

`modelctl` requires **Python 3.11+**. The heavy ML frameworks are optional — the
core install runs `validate`, `quantize`, and `package` with no PyTorch or
TensorFlow. You only need the extras to **convert** from those frameworks.

  
**Core**

    ```bash
    pip install modelctl
    ```
    `convert` from PyTorch or TensorFlow is unavailable until you add an extra below.
  
  
**PyTorch**

    ```bash
    pip install 'modelctl[pytorch]'
    ```
  
  
**TensorFlow**

    ```bash
    pip install 'modelctl[tensorflow]'
    ```
  
  
**Both**

    ```bash
    pip install 'modelctl[all]'
    ```
  
  
**Quantize**

    ```bash
    pip install 'modelctl[quantize]'
    ```
    Needed for `quantize` and `package --quantize` (pulls `ml_dtypes`).
  

**quantize and tensorflow are mutually exclusive**

The `[quantize]` extra needs a newer `ml_dtypes` than the `[tensorflow]`/`[all]`
extra allows. Install one profile or the other — see
[modelctl Best Practices](/configure/modelctl-best-practices/).

## Convert → validate → package

1. ### Convert to ONNX

   PyTorch input is a **TorchScript `.pt`** (`torch.jit.save`) — self-contained, so
   the convert box needs no model class. TensorFlow input is a **SavedModel**
   directory.

   
     
**PyTorch**

       ```bash
       modelctl convert --source model.pt --format pytorch --output model.onnx \
         --input-shape 1,3,224,224 --opset 18
       ```
     
     
**TensorFlow**

       ```bash
       modelctl convert --source saved_model/ --format tensorflow --output model.onnx
       ```
     
     
**YAML config**

       ```bash
       modelctl convert --config model.yaml
       ```
       CLI flags override YAML, which overrides defaults.
     
   

2. ### Validate against the edge runtime

   Validation checks ONNX schema, **opset vs. the edge ONNX Runtime**, the edge
   input contract (single input · float32 · rank), runtime load, and a test
   inference. Pin the device's ORT version so a too-new model is caught **before**
   it reaches an air-gapped box.

   ```bash
   modelctl validate --model model.onnx --target-ort 1.18
   # ✓ ONNX schema valid
   # ✓ Opset supported
   # ✓ Single input tensor
   # ✓ Input dtype float32
   # ✓ Input rank
   # ✓ ONNX Runtime load successful
   # ✓ Test inference successful
   ```

3. ### Package into a bundle

   ```bash
   modelctl package --model model.onnx --name pump-anomaly-v1
   ```

   This emits a portable directory:

   ```
   pump-anomaly-v1/
   ├── model.onnx
   ├── metadata.json        # name, version, framework, opset, quantized flag
   ├── input_schema.json    # input tensor name / shape / dtype
   ├── checksums.sha256     # SHA256 over the model + metadata + schema
   └── README.md
   ```

   Copy the directory to the box, then verify integrity on the device:

   ```bash
   cd pump-anomaly-v1 && sha256sum -c checksums.sha256
   ```

## Tuning for edge performance

`modelctl` has two levers that affect how the model runs on the box:

- **Dynamic INT8 quantization** — `modelctl quantize --model model.onnx --output model.int8.onnx`
  (or `--quantize` on `convert`/`package`) produces a smaller model that is
  typically faster to load and run, at some accuracy cost. Requires the
  `[quantize]` extra. **Validate the quantized model** and confirm prediction
  quality before shipping it.
- **Opset targeting** — `--target-ort` validates the model's opset against the
  exact ONNX Runtime on the device, so an export that is too new fails on the
  workstation instead of on the floor.

**Window size and feature count still apply**

Quantizing or re-exporting does not change the model's **window size** and
**feature count**. These must match the input datasource when you pair the model
on the box — see [Deploying Models](/configure/models/).

**Reproducible bundles**

Pass `--timestamp` (or set `SOURCE_DATE_EPOCH`) on `package` so `metadata.json`
and `checksums.sha256` are byte-identical across rebuilds — useful for change
review and audit.

## Next steps

  - [modelctl Best Practices](/configure/modelctl-best-practices/) — Do/don't guidance: profiles, validation gating, reproducible bundles, CI.
  - [Deploy the model](/configure/models/) — Upload the ONNX, pair it with a datasource, activate.
  - [Offline bundle install](/install-deploy/offline-bundle-install/) — Get the product onto an air-gapped box.
