Integrate and Publish a Sleep Study

Distribute your study via HuggingFace, Docker, or PyPI

Once a study is trained and validated, SleePyPhases supports three complementary distribution channels depending on how consumers will use your work:

Channel Best for
HuggingFace Sharing model weights — downloaded automatically at runtime
Docker Reproducible execution — ship the full environment as a container
PyPI Reusable plugin — lets other SleePyPhases projects load your study as a dependency

These are not mutually exclusive. A typical study publishes model weights to HuggingFace, a Docker image to the GitLab container registry for batch inference, and a PyPI package so the broader ecosystem can extend it.


HuggingFace — publish model weights

Upload weights

Trained model weights are stored in a HuggingFace repository and pulled at runtime by ModelExporter.registerModel. To upload your weights, follow the HuggingFace model upload documentation.

The repository name (namespace/repo-name) is the identifier you reference in your plugin.

Register the model in your plugin

In your Plugin.py, call ModelExporter.registerModel with the HuggingFace repo and the data ID that corresponds to the saved model state:

from pyPhasesML.exporter.ModelExporter import ModelExporter

ModelExporter.registerModel(
    dataId='modelState<hash>-<ModelName>-<hash>--current',
    repository='your-namespace/your-repo'
)

At runtime, SleePyPhases checks whether the model file is present locally. If not, it downloads it from HuggingFace automatically before the first inference.

Note

The dataId is the filename (without path) under which the model state is saved. It is printed after training completes.


Docker — publish a container image

Dockerfile

A minimal Dockerfile installs dependencies and sets the phases CLI as the entrypoint:

FROM pytorch/pytorch:2.6.0-cuda12.6-cudnn9-runtime

ARG VERSION="0.0.0"
WORKDIR /app

COPY requirements.txt .
COPY src src
COPY configs configs
COPY project*.yaml ./

RUN echo $VERSION > ./VERSION && \
    apt-get -qq update && \
    apt-get -qq install -y build-essential libpq-dev && \
    apt-get clean && \
    pip install --upgrade pip setuptools wheel && \
    pip install --user -r requirements.txt && \
    mkdir data

ENTRYPOINT ["python", "-m", "phases"]

GitLab CI — push to container registry

The pipeline builds and pushes the image on every commit, and tags stable on a Git tag:

image: docker:20.10.21
services:
  - name: docker:20.10.21-dind
    alias: docker

variables:
  DOCKER_TLS_CERTDIR: "/certs"

stages:
  - release
  - publish

before_script:
  - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  - '[ "$CI_COMMIT_TAG" != "" ] && export VERSION=$CI_COMMIT_TAG || export VERSION=0.0.1-rc$CI_COMMIT_SHORT_SHA'

build-image:
  stage: release
  except:
    - tags
  script:
    - docker build --build-arg VERSION=$VERSION -t $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker push $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

publish-stable:
  stage: publish
  only:
    - tags
  script:
    - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:stable
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
    - docker push $CI_REGISTRY_IMAGE:stable
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

PyPI — publish as a plugin package

When packaged on PyPI, your study becomes a first-class SleePyPhases plugin that anyone can install and reference by name.

GitLab CI — publish to PyPI

image: python:3.13-alpine

stages:
  - test
  - release

test:
  stage: test
  before_script:
    - apk add --no-cache build-base
  script:
    - pip install -U -r requirements.txt coverage
    - python -m coverage run -m unittest discover -s tests -p "test_*.py"
    - python -m coverage report
    - python -m coverage xml -o cov/coverage.xml
  only:
    - pushes
  coverage: '/TOTAL.*\s+(\d+\%)/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: cov/coverage.xml

pypi:
  stage: release
  before_script:
    - apk add --no-cache build-base
  variables:
    APP_VERSION: $CI_COMMIT_TAG
  script:
    - sed -i "s/v0.0.0/${APP_VERSION}/g" setup.py
    - pip install -U twine setuptools
    - python setup.py sdist
    # for pyproject.toml-based projects, use:
    # - pip install -U twine build
    # - python -m build
    - twine upload dist/*
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/'
Tip

You need to create a PyPi project and add a trusted publisher. See the PyPI publishing guide.

Load the published plugin

Once on PyPI, any SleePyPhases project can use your study by name:

pip install YourStudyPackage

Config based:

plugins:
  - YourStudyPackage

Or flat workflow:

from SleePyPhases import SleePyPhases

project = SleePyPhases.create(plugins=["YourStudyPackage"])
prediction = project.predictFromFile("recording.edf")

For full usage example see the Use a Published Study guide.