Skip to main content

Overview

This guide walks you through adding a new model to boileroom. You will create a set of files that follow the same three-layer architecture used by every existing model (ESMFold, ESM-2, Chai-1, Boltz-2): a core algorithm, a typed output, a Modal image, a Modal wrapper, and a high-level user-facing class.
This page is written for boileroom v0.3. The contributor workflow is currently being reshaped around the new registry and contract-test architecture, so older branches may not match this guide exactly.
By the end, your model will work with every backend (Modal, Apptainer) and be accessible through the same interface as the built-in models.

File structure

Create a new directory under boileroom/models/{name}/ with the following files:

Step 1: Define the output type

Create types.py with a dataclass that structurally conforms to the appropriate protocol. For structure prediction models, follow StructurePrediction; for embedding models, follow EmbeddingPrediction. These are Protocol classes — your dataclass should satisfy them structurally rather than inheriting from them. Structure prediction output:
Embedding output:
The metadata field must always be a PredictionMetadata instance. Keep additional fields optional and default to None so that _filter_include_fields() can zero them out when users request a subset.

Step 2: Create the core algorithm

Create core.py with a class that inherits from FoldingAlgorithm (structure prediction) or EmbeddingAlgorithm (embeddings). Both live in boileroom.base.
Key points:
  • _resolve_device() returns a torch.device based on the config or CUDA availability.
  • _merge_options(options) merges per-call overrides into the config but raises ValueError if the caller tries to override a key in STATIC_CONFIG_KEYS.
  • _validate_sequences() normalizes input to a list and checks for invalid amino acids.
  • _filter_include_fields() lets users request only specific output fields, reducing data transfer.

Step 3: Create the Modal image

Create image.py that extends the shared base image with your model’s dependencies.
The base image is a Debian slim container with Python 3.12, wget, git, and biotite pre-installed. Chain additional .pip_install(), .apt_install(), or .env() calls as needed. If your model needs environment variables (e.g., memory allocation settings), add them:

Step 4: Create the Modal wrapper

In {name}.py, define a Modal class that wraps your core algorithm for serverless GPU execution.
The core is imported lazily inside @modal.enter() so that heavy dependencies (PyTorch, model libraries) are only loaded inside the container, not on your local machine. The config parameter is declared as bytes and deserialized from JSON because Modal serializes parameters — this is the established pattern across all models.

Step 5: Create the high-level wrapper

In the same {name}.py file, add the user-facing class that delegates to a backend.
_call_backend_method handles the dispatch: for Modal it calls .remote(), for Apptainer it calls the method through an HTTP microservice. You do not need to handle this yourself.

Step 6: Register the model

Export from the model package — create __init__.py with lazy imports:
Export from the models index — add the lazy import to boileroom/models/__init__.py. Export from the top-level package — add the lazy import to boileroom/__init__.py:
And add "MyModel" to the __all__ list. After this, users can write:

Step 7: Add tests

Create tests/{name}/test_{name}.py. Follow the existing test patterns:
Key testing conventions:
  • Use scope="module" fixtures for model instances so the container is reused across tests.
  • Store reference outputs in tests/data/{name}/ for regression testing.
  • Compare structures using RMSD with a tolerance (typically < 1.0 angstrom for self-consistency tests).
  • For confidence scores, use relative error tolerances.
  • Wrap the model instantiation with enable_output() to see Modal container logs during CI.

Step 8: Docker image for Apptainer

To support the Apptainer backend, you need a Docker image that can run your model locally or on HPC clusters. Create a Dockerfile at boileroom/models/{name}/Dockerfile:
Create environment.yml listing conda/pip dependencies for your model. Create config.yaml specifying supported CUDA versions:
The build script at scripts/images/build_model_images.py reads these files and pushes images to docker.io/jakublala/boileroom-{name}:{tag}.