<!-- Source: https://docs.biosimulant.com/how-to/library/write-model-manifest -->

# How to Write a model.yaml

`model.yaml` defines a packaged module: what Python class to load, what communication step the module targets, what ports it exposes, and what dependencies it needs.

## Annotated example

```yaml
schema_version: "2.0"
title: "Hodgkin-Huxley Population"
description: "Conductance-based neuron population"
standard: other
package: biosimulant/hh-population
version: 1.0.0

biosim:
  entrypoint: "src.hh_population:HHPopulation"
  init_kwargs:
    n: 100
    v_init: -65.0
  setup:
    seed: 1234
  communication_step: 0.001

io:
  inputs:
    - name: current
      signal_type: scalar
      dtype: float64
      description: Injected current
  outputs:
    - name: spikes
      signal_type: event
      schema:
        ids: list[int]
    - name: membrane_potential
      signal_type: scalar
      dtype: float64
      emitted_unit: mV

runtime:
  python_version: "3.12"
  dependencies:
    packages:
      - numpy>=1.26
      - scipy>=1.12

metadata:
  authors: ["Your Name"]
  tags: ["neuroscience", "hh"]
  species: ["Homo sapiens"]
```

## Package identity

For model source trees vendored into published labs, `package` and `version` can record package identity. `package` uses the `namespace/name` form, and `version` must be an exact SemVer value such as `1.0.0`.

Consumers refer to the published model as `namespace/name@x.y.z`:

```bash
biosimulant labs add-model ./my-lab ./models/hh-population --as hh
```

Labs that depend on a published model store the same exact reference as separate `package` and `version` fields in `lab.yaml`. Biosimulant does not fall back to `latest` when a version is missing or invalid.

## Required fields

- `schema_version`
- `title`
- `description`
- `standard`
- `biosim.entrypoint`
- `biosim.communication_step`

## `biosim` block

| Field | Meaning |
|-------|---------|
| `entrypoint` | Python symbol in `module.path:ClassName` form |
| `init_kwargs` | Constructor kwargs merged with lab-level parameter overrides |
| `setup` | Default `setup()` config for the module |
| `communication_step` | Positive default coupling boundary for this packaged model |

## I/O contract

Prefer structured typed ports over bare strings:

```yaml
io:
  inputs:
    - name: state_vector
      signal_type: array
      dtype: float32
      shape: [10]
  outputs:
    - name: probabilities
      signal_type: array
      dtype: float32
      shape: [3]
```

## Remote execution requirements

If a packaged model requires GPU-backed remote execution, declare it in `runtime.remote.requirements`:

```yaml
runtime:
  remote:
    requirements:
      accelerator: gpu
      gpu_count: 1
```

CPU-compatible models should omit `runtime.remote.requirements`. During remote runs, Biosimulant backend, desktop, and web surfaces use model requirements to select GPU-backed sizes, disable CPU sizes, and reject incompatible submissions before a run starts.

## Validation

Add the model to `biosimulant-packages.yaml`, then validate and build with the canonical CLI:

```bash
biosimulant labs release validate biosimulant-packages.yaml
biosimulant labs release build biosimulant-packages.yaml --out dist/biosimulant-packages
```

## Notes

- `biosim.communication_step` is required.
- Structured `io` entries are the recommended way to document typed ports.
- Keep dependency pins realistic. They are used by packaging and execution layers.
- Declare `runtime.remote.requirements` only for models that need a specific remote resource class.

## Next steps

- [model.yaml Schema Reference](/references/library/model-manifest)
- [Write a lab.yaml](/how-to/library/write-lab-manifest)
- [Add an ONNX Model](/how-to/library/add-onnx-model)
