model.yaml schema
model.yaml describes one packaged BioModule: its name and version, how to load it, its input and output ports, its dependencies, and optional ONNX and compatibility details.
Schema
schema_version: "2.0"
title: string
description: string
standard: sbml | neuroml | cellml | nmodl | onnx | other
package: namespace/name
version: 1.2.3
license_type: unknown | mit | apache_2 | cc_by | cc_by_sa | proprietary
authors: [string]
tags: [string]
species: [string]
biosim:
entrypoint: module.path:ClassName
init_kwargs: object
setup: object
communication_step: positive number
execution_policy: once_before_run | each_window | once_after_run
io:
inputs: [SignalSpec]
outputs: [SignalSpec]
runtime:
python_version: "3.12"
dependencies:
packages: [exact==pins]
requirements_file: relative/path.txt
lockfile: relative/lock.file
remote:
requirements:
accelerator: gpu
gpu_count: positive integer
onnx:
task: string
model_file: relative/path.onnx
class_labels: [string]
inputs: [TensorSpec]
outputs: [TensorSpec]Top-level fields
| Field | Type | Required | Meaning |
|---|---|---|---|
schema_version | string | Current packages | Manifest shape; current value is "2.0" |
title | string | Recommended | Human-facing model name |
description | string | Recommended | Scope, method, and intended-use summary |
standard | enum | Yes | Primary model/artifact family |
package | string | For releases | Stable namespace/name identity |
version | SemVer | For releases | Exact release version, such as 1.0.0 |
license_type or license | enum/string | Recommended | Distribution/use license; unknown values normalize to unknown |
authors | string[] | No | Author names or attributions |
tags | string[] | No | Search and discovery tags |
species | string[] | No | Species, for search and discovery. To check compatibility, set species in a port contract instead. |
biosim | object | Yes | How to load the model’s Python code |
io | object | Recommended | The model’s input and output ports |
runtime | object | No | Python, dependency, and remote resource requirements |
onnx | object | For ONNX | ONNX artifact and tensor metadata |
Package identity
package is the stable namespace/name identifier and version is the exact
SemVer package version, such as 1.0.0. Together they form the package ref
namespace/name@x.y.z.
When you build or publish a package, version must be exact SemVer. latest
and other non-SemVer values are rejected. This rule is for manifests. CLI
commands such as labs pull accept a ref without a version; see
Package and publish.
A lab does not reference a model by package. Its models[] entries use a
relative path. To add a model source tree to a lab:
biosimulant labs add-model ./models/hh-population --lab ./my-lab --alias hhadd-model writes the relative path and alias into the lab’s lab.yaml.
biosim block
biosim:
entrypoint: "src.hh_population:HHPopulation"
init_kwargs:
n: 100
setup:
seed: 1234
communication_step: 0.001| Field | Type | Required | Behavior |
|---|---|---|---|
entrypoint | string | Yes | Python symbol in module.path:ClassName form |
init_kwargs | object | No | Constructor kwargs merged with lab-level parameter overrides |
setup | object | No | Default setup() config for the module |
communication_step | number > 0 | Yes | Default coupling step for this packaged model |
execution_policy | enum | No | Checked copy of the Python class’s execution policy; see Execution policy |
Model-entry parameters in a lab are constructor overrides. They are merged into
biosim.init_kwargs before the entrypoint factory is called. Runtime inputs are
separate: declare them under io.inputs and pass values through run-time input
payloads.
Execution policy
biosim.execution_policy repeats the execution policy your Python class
declares, so tools that can’t import model code (Studio, Desktop and the Hub)
can tell when the model runs. It needs biosimulant>=0.0.33.
biosim:
entrypoint: "src.predictor:Predictor"
communication_step: 0.01
execution_policy: once_before_run| Value | Python declaration |
|---|---|
once_before_run | ExecutionPolicy.ONCE_BEFORE_RUN |
each_window | ExecutionPolicy.EACH_WINDOW |
once_after_run | ExecutionPolicy.ONCE_AFTER_RUN |
The Python execution_policy attribute is still the only thing that decides
when BioWorld runs the module. The manifest field is a copy that Biosimulant
checks against your code:
- When the model loads, the runtime compares the field with the policy of the constructed module. If they differ, the model doesn’t load, and the error names both values.
biosimulant labs validatereads the entrypoint source without importing it. It reports an error when the source clearly resolves to a different policy, and a warning when it can’t verify the value, for example when__init__sets the policy from a parameter.- For a model that doesn’t declare the field,
labs validatesuggests the value to add when it can read the policy from source.
Declare the policy your module resolves to:
| Your class | Declare |
|---|---|
Sets execution_policy as a class attribute | That value |
Sets self.execution_policy to a fixed value in __init__ | That value |
Implements execute() and sets no policy | each_window. BioWorld warns that the policy is implicit; declaring each_window in model.yaml clears the warning. |
Implements advance_window() | each_window |
Leave the field out when the policy depends on constructor parameters. Tools then treat the model as undeclared.
A lab can’t override a model’s policy. You can mix policies in one lab, as long as the wiring between them is valid. See Execution timing for the wiring rules and for how declared policies change the run settings a lab shows.
Ports (io)
Some tooling still accepts plain lists of port names, but new manifests should use typed ports like these:
io:
inputs:
- name: current
signal_type: scalar
dtype: float64
description: External current injection
- name: spikes
signal_type: event
schema:
ids: list[int]
outputs:
- name: membrane_potential
signal_type: scalar
dtype: float64
emitted_unit: mV
- name: state
signal_type: record
schema:
v: float64
u: float64Use the manifest to describe what the module publishes and accepts. The runtime signal classes are still emitted from Python code.
Run-time input payload values are checked and converted against these input
declarations before the module receives them. Raw scalar, array, record, and event payloads become
typed BioSignal instances when exactly one accepted input profile matches. If a
port accepts multiple profiles or units, provide an explicit typed input envelope
with value, signal_type, dtype, shape, schema, and/or emitted_unit.
SignalSpec: common port fields
| Field | Type | Default | Rules |
|---|---|---|---|
name | string | — | Required and unique within its direction |
signal_type | scalar | array | record | event | — | Structural signal category |
kind | state | event | Derived | Events require event; all other signals require state |
dtype | string | null | Numeric or implementation dtype; linear interpolation requires a numeric dtype |
shape | integer/wildcard list | null | Arrays require shape; scalars, records, and events cannot use an array shape in runtime declarations |
schema | object | null | Record field schema; records require it |
interpolation | zoh | linear | none | zoh | Events require none; linear is numeric scalar/array only |
max_age | number ≥ 0 | null | Maximum accepted state age before staleness policy |
stale_policy | ignore | warn | error | warn | Runtime response to an older-than-max_age value |
description | string | null | Human port description |
value_type or type | string | number | integer | boolean | record | array | file | null | Input-form and coercion metadata |
format | string | null | Format hint, such as URI or a domain-specific textual format |
required | boolean | null | Whether a user/run input must be supplied |
default | JSON value | null | Default run-input value |
advanced | boolean | null | Marks advanced authoring/UI controls |
examples | JSON[] | null | Example values |
allowed_values | JSON[] | null | Closed authoring choice list |
file | object | null | File-picker, media, extension, or size metadata |
ui | object | null | UI presentation hints; not runtime semantics |
contract | object | null | Compatibility declaration with one exact profile and, when relevant, species and identifier_namespace |
All defaults, examples, allowed values, file, ui, and contract values must be JSON-serializable.
Input-only fields
An input can set accepted_units directly when its other structural fields have
one representation. Use accepted_profiles only when it genuinely accepts
multiple structural representations.
| Field | Type | Meaning |
|---|---|---|
accepted_units | string[] | Units accepted without conversion |
accepted_profiles lists alternate structural representations accepted by one input. Each entry supports:
| Field | Type | Meaning |
|---|---|---|
signal_type | signal enum | Accepted signal category |
dtype | string | Accepted dtype |
shape | list | Accepted array shape |
schema | object | Accepted record schema |
accepted_units | string[] | Directly accepted units |
format | string | Accepted file or textual format |
description | string | Representation explanation |
contract | object | Optional semantic contract for this representation |
If accepted_profiles is omitted, the input’s own structural fields form its single accepted profile. An input cannot set emitted_unit.
Output-only fields
emitted_unit is the unit an output sends. Outputs cannot declare
accepted_units or accepted_profiles. An output’s contract describes what
the output sends.
Lifecycle and staleness example
io:
inputs:
- name: concentration
signal_type: scalar
kind: state
dtype: float64
interpolation: linear
max_age: 0.5
stale_policy: error
accepted_profiles:
- dtype: float64
accepted_units: [nM, uM]
outputs:
- name: spike
signal_type: event
kind: event
interpolation: none
schema:
neuron_id: string
time: float64Dependencies
runtime:
python_version: "3.12"
dependencies:
packages:
- numpy==1.26.4
- scipy==1.12.0
requirements_file: requirements.txt| Field | Rule |
|---|---|
python_version | Supported minor version, currently 3.10–3.14 in the open-source packager |
dependencies.packages | Every installable package uses an exact == pin |
dependencies.requirements_file | Relative path to a requirements declaration |
dependencies.lockfile | Relative path to a dependency lock |
Open-source biosimulant labs run only installs exact-pinned package specs into the
current Python environment. Runs on the Biosimulant platform use isolated
per-lock-hash environments and apply allow/deny dependency policy. Keep manifests
portable by pinning dependencies exactly when package installation is expected.
Remote execution requirements
Models that require accelerator-backed remote execution can declare the requirement in runtime.remote.requirements:
runtime:
remote:
requirements:
accelerator: gpu
gpu_count: 1accelerator: gpu marks the model as requiring a GPU-backed remote size. In the remote execution catalog, GPU-backed means the size has a non-empty gpu_type. gpu_count is the number of GPUs required. CPU-compatible models omit this block.
Lab authors should not duplicate model resource requirements in lab.yaml; Biosimulant aggregates model requirements into the resolved lab graph at run time.
ONNX metadata
When standard: onnx, include an onnx block describing the model artifact and tensor signatures:
onnx:
task: classification
model_file: data/assets/model.onnx
class_labels: [quiescent, subthreshold, spiking]
inputs:
- name: input
dtype: float32
shape: [-1, 10]
outputs:
- name: probabilities
dtype: float32
shape: [-1, 3]| Field | Meaning |
|---|---|
task | Computational task, such as classification or regression |
model_file | Relative path to the ONNX model file |
class_labels | Output labels, in order |
inputs, outputs | Tensor name, dtype, shape, and description metadata |
If io is absent, some ingestion tooling can derive ports from ONNX tensor metadata. New packages should declare io explicitly so every Biosimulant tool sees the same ports.
Compatibility
A contract directly on an input or output tells Biosimulant which versioned
profile describes the port. A model with any contract must declare the standard
once at the top level.
compatibility:
standard: biosimulant.model-compatibility
version: "0"
io:
inputs:
- name: protein_sequence
signal_type: scalar
dtype: str
format: sequence
contract:
profile: protein.sequence/v1
species: anyprofile is required when the contract is present. species and
identifier_namespace are optional and only allowed when the profile permits
them. Shape, format, value type and units remain normal port fields rather than
being repeated in the contract. The v0 compatibility block accepts only
standard and version; the model does not copy catalogue or profile digests.
Contracts are optional. If only one side of a wire declares a profile, a
structurally valid connection is allowed with a PROFILE_PARTIAL warning. The
declared profile still checks live values, but scientific compatibility is not
verified. When both ports declare profiles, they must reference the same exact
profile and agree on representation and required context. Values are checked
again during a run.
See Use compatibility in a model for a complete example and Propose a profile when no existing profile is accurate.
Directory layout
my-model/
model.yaml
src/
my_module.py
artifacts/
data/
assets/
model.onnx
tests/
requirements.txtValidate and build
Add the model to biosimulant-packages.yaml with type: model, then validate
and build:
biosimulant labs release validate biosimulant-packages.yaml
biosimulant labs release build biosimulant-packages.yaml --out dist/biosimulant-packagesPort contracts stay in payload/model.yaml. The package builder does not create
a separate compatibility lock file.