Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

on:
push:
branches: [main]
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint (ruff)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
python-version: "3.11"
- run: uvx ruff check .
- run: uvx ruff format --check .

test:
name: Test (py${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
- name: Run tests
run: uv run --group dev pytest -v
64 changes: 64 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Publish to PyPI

# Publishes the package to PyPI whenever a GitHub Release is published.
# Uses PyPI Trusted Publishing (OIDC) — no API tokens or secrets required.
# See README "Releasing" for the one-time PyPI setup and the release steps.

on:
release:
types: [published]

jobs:
build:
name: Build distributions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
python-version: "3.11"

- name: Verify tag matches package version
run: |
tag="${GITHUB_REF_NAME#v}"
pkg="$(uv version --short)"
if [ "$tag" != "$pkg" ]; then
echo "::error::Release tag ($tag) does not match pyproject version ($pkg)."
echo "Bump the version in pyproject.toml to match the release tag, or retag."
exit 1
fi
echo "Tag and package version agree: $pkg"

- name: Build sdist and wheel
run: uv build

- name: Check distribution metadata
run: uvx twine check dist/*

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
name: Publish to PyPI
needs: build
runs-on: ubuntu-latest
# The GitHub Environment that the PyPI trusted publisher is scoped to.
environment:
name: pypi
url: https://pypi.org/p/hypnos
permissions:
id-token: write # required for OIDC trusted publishing
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ __pycache__/
*.pyc
.pytest_cache/
.ruff_cache/
dist/
# Seeded checkpoints / manifests (large binaries; fetched or seeded, not committed)
checkpoints/
*.ckpt
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<p align="center">
<a href="https://arxiv.org/abs/2606.09605"><img src="https://img.shields.io/badge/arXiv-2606.09605-b31b1b.svg" alt="arXiv"></a>
<a href="https://huggingface.co/joncarter/hypnos"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Model-yellow" alt="Hugging Face"></a>
<a href="https://pypi.org/project/hypnos/"><img src="https://img.shields.io/pypi/v/hypnos.svg" alt="PyPI"></a>
</p>

<p align="center">
Expand All @@ -21,7 +22,13 @@
## Installation

```bash
uv sync # or: pip install -e .
pip install hypnos # or: uv add hypnos
```

To work on the library itself, clone the repo and install from source:

```bash
uv sync # or: pip install -e .
```


Expand Down
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ license-files = ["LICENSE"]
authors = [
{name = "Jonathan Carter"}
]
keywords = ["sleep", "eeg", "ecg", "physiology", "embeddings", "foundation-model",
"time-series", "polysomnography", "edf", "deep-learning"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Scientific/Engineering :: Medical Science Apps.",
"Programming Language :: Python :: 3",
]
dependencies = [
"torch>=2.4", # torch.nn.attention.flex_attention (hypnos.models.attention)
"numpy",
Expand All @@ -19,6 +28,12 @@ dependencies = [
"fsspec", # SignalTokenizer.from_checkpoint hf:// / s3:// support
]

[project.urls]
Homepage = "https://github.com/joncarter1/hypnos"
Repository = "https://github.com/joncarter1/hypnos"
Paper = "https://arxiv.org/abs/2606.09605"
"Hugging Face" = "https://huggingface.co/joncarter/hypnos"

[dependency-groups]
# Dev-only: resolving checkpoints/configs from the research stack when seeding manifests.
seed = [
Expand Down
Loading
Loading