> This page is for Kumo Engine.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/sdgm/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/sdgm/_mcp/server.

# Register and Promote Models

> Register every Kumo Engine fit in your model registry, promote a version with an alias, and score with the promoted version

Register each trained model in your model registry and decide which version production uses. When you finish this page, every fit adds a new version under a `candidate` alias, a `champion` alias marks the version your scoring jobs use, and a scoring job loads the champion by name without knowing its version.

This page continues from [Track Runs](/sdgm/kumo-engine/user-code-guide/operate-models/track-runs). Kumo registers models in your tracker's registry, as listed in [Supported Platforms](/sdgm/kumo-engine/user-code-guide/operate-models/overview#supported-platforms). The Snowflake Model Registry (preview) is in the connector's database and schema.

## Name the Model

Choose a model name that your registry accepts:

| Registry                           | Rule                                                                                              | Example               |
| ---------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------- |
| Unity Catalog                      | The full three-part name `<catalog>.<schema>.<model>`, without spaces, `/`, or control characters | `main.ml.users_churn` |
| SageMaker managed MLflow           | Up to 57 letters and digits, optionally joined by hyphens                                         | `users-churn`         |
| Snowflake Model Registry (preview) | A Snowflake identifier                                                                            | `CHURN_GNN`           |
| Other MLflow servers               | The server's own rules                                                                            | `users-churn`         |

Creating `kumo.MLOps` checks SageMaker, Snowflake, and three-part Unity Catalog names, so an invalid name fails before training. For a three-part Unity Catalog name, it also checks that the identity can use the schema; the identity needs `USE CATALOG` and `USE SCHEMA`, and permission to create the model or add a version to it. For Snowflake, the role needs `CREATE MODEL` on the schema.

## Register Every Fit

Add `registry=` to your `kumo.MLOps` configuration. This example registers the model as `users-churn` on an MLflow server and moves the `candidate` alias to each new version:

```python
import kumo.engine as kumo
from kumo.engine.integrations.mlflow import MlflowTracking

cfg = kumo.MLOps(
    "s3://<bucket>/kumo",
    tracking=MlflowTracking("churn", target="http://<mlflow-host>:5000"),
    registry=kumo.ModelRegistryLocation("users-churn", alias="candidate"),
)

with kumo.mlops(cfg):
    snapshot = graph.snapshot()
    training_table = query.generate_training_table(snapshot)
    trainer = kumo.Trainer(plan)
    fit_result = trainer.fit(training_table, snapshot=snapshot)

print(fit_result.registered_model.name, fit_result.registered_model.version)
```

For other platforms, use the `tracking` value from [Track Runs](/sdgm/kumo-engine/user-code-guide/operate-models/track-runs) and a model name from the table above.

The command prints the model name and its new version. MLflow and Unity Catalog number versions `1`, `2`, and so on; Snowflake names them `V1`, `V2`, and so on. On MLflow platforms the fit run is renamed after the version, such as `fit-users-churn-v2`. Each registered version records the model id as `kumo_bundle_id`. In Unity Catalog, the version comment names the fit run and the model bundle, and the version is linked to the fit's logged model, which records the holdout metrics:

![MLflow registered model users-churn after a promotion, with Version 2 holding the candidate alias and Version 1 holding the champion alias, each with its kumo\_bundle\_id tag and holdout metrics](/sdgm/_fern-files/_img/ba877340d3781956765be825a8395c45b855ab1bf1d037f9c40ee3dcedc39e1e.webp)

If registration fails, `trainer.fit()` warns and returns with `fit_result.registered_model` set to `None`, or raises when `on_error="raise"`.

To register a model you trained or loaded earlier, call `export` with the configuration. `trainer` is a fitted or loaded `Trainer`:

```python
published = trainer.export(cfg)
print(published.version)
```

Registering the same model bundle again returns its existing version instead of adding one, and moves the alias to it. On Snowflake, a version holds at most one alias, so Kumo cannot move `candidate` to a version that already holds `champion`; it warns and leaves the aliases unchanged.

## Promote a Version

Register every fit under `candidate`, and let your scoring jobs load `champion`. A new version reaches production only when you move `champion` to it.

Before you promote, compare the candidate with the current champion on your model plan's tuned metric, `training_job.tune_metric`. `registered_model.metrics` holds a version's holdout metrics under their tracker names, such as `test/auroc`, both on the result of a fit and on a model loaded from the registry. They describe the model that ships, including when `training_job.refit_trainval` refits the best model on training and validation data. A loaded model has the trainer's metrics, such as `test/auroc`, `test/ap`, `test/auprc`, and `test/loss`, but not `test/logloss` or `test/brier`. With `training_job.refit_full`, the shipped model has no holdout, so `registered_model.metrics` has only `before_refit/<metric>`, the model before the refit; compare such models on your own evaluation data instead.

Each model's holdout comes from its own training table, so if a new fit uses newer data, also check both models on the same recent data when the difference matters.

#### MLflow, Databricks, and SageMaker

The following code moves `champion` to the new version when its holdout AUROC is higher than the champion's, or when no champion exists yet. `fit_result` and `cfg` come from the example above. Replace `test/auroc` with your tuned metric; for a metric where lower is better, such as `test/loss`, use `<` in the comparison. The code does not apply to models trained with `training_job.refit_full`:

```python
if fit_result.registered_model is None:
    raise RuntimeError("The fit was not registered.")
name = fit_result.registered_model.name
metric = "test/auroc"

with cfg.tracking.client() as client:
    has_champion = "champion" in client.get_registered_model(name).aliases

better = True
if has_champion:
    champion = kumo.Trainer.load_from_connector(
        cfg, location=kumo.ModelRegistryLocation(name, alias="champion")
    )
    better = (fit_result.registered_model.metrics[metric]
              > champion.result.registered_model.metrics[metric])

if better:
    with cfg.tracking.client() as client:
        client.set_registered_model_alias(name, "champion", fit_result.registered_model.version)
        print("champion:", client.get_model_version_by_alias(name, "champion").version)
```

`cfg.tracking.client()` is described in [Find Runs](/sdgm/kumo-engine/user-code-guide/operate-models/track-runs#find-runs). When the code moves the alias, it prints the version that now holds `champion`. You can also move an alias in the MLflow or Unity Catalog UI.

#### Snowflake (Preview)

Check which version holds `champion` with `SHOW VERSIONS IN MODEL ML.MODELS.CHURN_GNN`. If one does, load it with `kumo.Trainer.load_from_connector(cfg, location=kumo.ModelRegistryLocation("CHURN_GNN", alias="champion"))` and compare `registered_model.metrics` as in the other tab. To promote, remove `champion` from the old version and `candidate` from the new one, then set `champion`. Replace `<version>` with the new version, such as `V2`:

```sql
ALTER MODEL ML.MODELS.CHURN_GNN VERSION champion UNSET ALIAS;
ALTER MODEL ML.MODELS.CHURN_GNN VERSION <version> UNSET ALIAS;
ALTER MODEL ML.MODELS.CHURN_GNN VERSION <version> SET ALIAS = champion;
```

Skip the first statement on the first promotion, when no version holds `champion`. Check the result with `SHOW VERSIONS IN MODEL ML.MODELS.CHURN_GNN`.

## Load a Model by Alias

A scoring job loads the version an alias points to. Create a `kumo.MLOps` configuration with the same artifact root and tracker as the training job; scoring never registers models or moves aliases, so `registry=` is optional here. Then load the model:

```python
champion = kumo.Trainer.load_from_connector(
    cfg, location=kumo.ModelRegistryLocation("users-churn", alias="champion")
)
print(champion.result.id, champion.result.registered_model.version)
```

This works the same way on every platform; use your registered model name, such as `main.ml.users_churn` on Unity Catalog. The result is a fitted `Trainer`, and `champion.result` is its `TrainingResult`. The code prints the model id and the version `champion` points to. To load a specific version instead, pass `version="2"` (or `version="V2"` on Snowflake) in place of `alias=`.

Recreate `graph` and `query` as described in [Recreate the Prediction Context](/sdgm/kumo-engine/user-code-guide/train-a-model/publish-and-retrieve-models#recreate-the-prediction-context). Then score with the loaded model inside the block so the prediction run is stored and recorded:

```python
with kumo.mlops(cfg):
    snapshot = graph.snapshot()
    prediction = champion.predict(snapshot)

print(prediction.id, prediction.tracking.run_name)
```

Given only a snapshot, `predict()` scores the query's population as of the latest time in the tables the query's target reads, such as the newest order time for a query over orders; a static query has no prediction time. The scores are stored under `predictions/<prediction id>/` in the artifact root, and `prediction.files` lists the Parquet files this process wrote locally. To choose the population, prediction time, and an output table or path, see [Making Predictions](/sdgm/kumo-engine/user-code-guide/making-predictions). The prediction run is named after the version it scored with, such as `predict-users-churn-v1-2026-11-02`. Jobs that load `champion` pick up a newly promoted version on their next run.

If the snapshot's graph differs from the graph the model was trained on, `predict()` warns and names the changes. With `on_error="raise"`, it instead raises a `ValueError` before scoring when its tables, column types, or edges changed.

On MLflow platforms, you can also load a model by its MLflow model URI:

```python
from kumo.engine.integrations.mlflow import load_model

champion = load_model("models:/users-churn@champion", target="http://<mlflow-host>:5000")
```

`target` takes the same values as in `MlflowTracking`, such as a `DatabricksConnector` or `SageMakerMLflow`. This also returns a fitted `Trainer`, but it does not record which registered version it loaded, so its prediction runs are not named or tagged after the version. Use `Trainer.load_from_connector()` for scoring jobs.

Load only models from registries and publishers you trust.

## Next Steps

#### [Production Workflow](/sdgm/kumo-engine/user-code-guide/deploy-a-model/overview)

Score fresh data on a schedule with the model your jobs load.

#### [Monitor Predictions](/sdgm/kumo-engine/user-code-guide/operate-models/monitor)

Compare each prediction run's scores with the holdout and earlier runs, and measure realized accuracy.

#### [Score in Batch](/sdgm/kumo-engine/user-code-guide/operate-models/score)

Score the registered model with `trainer.predict()`, or through a platform's model API when it requires one.

#### [Publish and Retrieve Models](/sdgm/kumo-engine/user-code-guide/train-a-model/publish-and-retrieve-models)

Copy a model bundle to storage or a registry without MLOps.