---
title: "Model Metadata Library"
description: "Annotate an ONNX model with the bindings, normalization, and training metadata Koios reads on upload"
source_url: https://ai-ops.com/docs/python-packages/model-utils
---

# Model Metadata Library

`koios-model-utils` writes the metadata Koios reads when you upload an ONNX
model: what each input and output means, how values are normalized, and how
often the model expects to run.

Bring your own ONNX file from any framework. The library only touches
metadata, so it never re-encodes or re-optimizes the graph.

Without it you can still upload a model and configure everything by hand.
With it, the model arrives already describing itself, and Koios fills in the
bindings, normalization rules, sample rate, and action map for you.

## Installation

```bash
pip install koios-model-utils
```

The only runtime dependency is `onnx`. No training framework, no command-line
tool.

## Annotating a model

```python
import onnx
from koios_model_utils import (
    Algorithm,
    InputBinding,
    NormalizationSource,
    NormalizationType,
    OutputBinding,
    TrainingMeta,
    embed_koios_metadata,
)

model = onnx.load("my_model.onnx")

embed_koios_metadata(
    model,
    inputs=[
        InputBinding(name="tank_temperature", description="Tank temperature (C)"),
        InputBinding(name="pressure", description="Vessel pressure (kPa)"),
    ],
    outputs=[
        OutputBinding(
            name="valve_position",
            range_min=0.0,
            range_max=100.0,
            normalization_type=NormalizationType.SYMMETRIC,
            normalization_source=NormalizationSource.CUSTOM,
            custom_minimum=0.0,
            custom_maximum=100.0,
            clamp_output=True,
        ),
    ],
    training=TrainingMeta(
        scenario_name="tank_temperature",
        algorithm=Algorithm.PPO,
        obs_depth=5,
        sample_rate=1.0,
    ),
)

onnx.save(model, "my_model_koios.onnx")
```

Upload the saved file as you would any other model. See
[Creating a Model](https://ai-ops.com/docs/models/creating-a-model.md) and
[Assigning Bindings](https://ai-ops.com/docs/models/assigning-bindings.md) for what happens next.

## Reading metadata back

```python
from koios_model_utils import parse_koios_metadata

parsed = parse_koios_metadata(onnx.load("my_model_koios.onnx"))
parsed.training      # TrainingMeta, or None
parsed.inputs        # list[InputBinding]
parsed.outputs       # list[OutputBinding]
parsed.has_metadata  # True if any Koios metadata was present
```

If the metadata has already been extracted from the file and stored elsewhere,
`parse_koios_metadata_from_dict` takes the decoded dictionary directly.

## What you can declare

| Class | Describes |
|---|---|
| `InputBinding` | One observation feature: name, normalization rules, failure bounds |
| `OutputBinding` | One action or output: name, range, normalization, clamping |
| `TrainingMeta` | The model itself: algorithm, sample and scan rate, model type, action map |
| `ActionMapEntry` | One row of a discrete action map, a value and a label |
| `ParsedKoiosMetadata` | What `parse_koios_metadata` returns |

Every class validates on construction, so a combination that cannot work —
Z-score normalization together with a custom minimum, for example — raises
immediately rather than at upload.

`NormalizationType`, `NormalizationSource`, `ModelType`, `OutputMode`,
`Algorithm` and `FailureRangeMode` are string enums. The classes accept plain
strings too, but passing enum members catches typos before you ever build the
file.

| Function | Does |
|---|---|
| `embed_koios_metadata(model, *, inputs, outputs, training=None, output_denormalized=False)` | Writes the metadata into an ONNX model, in place |
| `parse_koios_metadata(model)` | Reads it back as typed objects |
| `parse_koios_metadata_from_dict(raw)` | The same, from an already-decoded dictionary |

Parsing failures raise `KoiosMetadataError`, `UnsupportedSchemaVersionError`,
or `KoiosMetadataDecodeError`.

## Sample rate and scan rate

These are different settings and the distinction matters.

**Sample rate** is the interval the model was trained at. Koios resamples
historical inputs to this rate before running the model.

**Scan rate** is how often Koios *executes* the model. Leave it unset and it
follows the sample rate, which is what you want for a forecasting model
trained and run at the same cadence. Set it explicitly when a controller needs
to act faster than the simulator step it was trained against — a one-second
history lookback driving a control loop that runs ten times a second.

See [Configuring a Model](https://ai-ops.com/docs/models/configuring-a-model.md).

## Models that emit an action index

For a model that returns a choice rather than a continuous value, set the
output mode and supply an action map:

```python
from koios_model_utils import ActionMapEntry, OutputMode, TrainingMeta

training = TrainingMeta(
    algorithm="DQN",
    output_mode=OutputMode.DISCRETE,
    action_map=[
        ActionMapEntry(value=-1.0, label="Decrease setpoint"),
        ActionMapEntry(value=0.0, label="Hold"),
        ActionMapEntry(value=1.0, label="Increase setpoint"),
    ],
)
```

The labels are what an operator sees when the model fires, so write them for
that reader. `action_map` also accepts plain dictionaries with `value` and
`label` keys.

## Versioning

The metadata format is at schema version 1, and the library moves in step with
the platform. A model annotated by a newer library than your Koios version may
carry fields that version does not read; it still uploads, and the unknown
fields are ignored.

> [!TIP] Match the library to your platform line
> Install the release that matches the Koios you run, the same way you would
> pin any other dependency.
