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
16 changes: 14 additions & 2 deletions .claude/skills/konfai-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,35 @@ There are two command-line surfaces:

## The canonical loop (`konfai`)

Three workflows map to three files, each with one mandatory root key:
Each workflow maps to one file with one mandatory root key:

| Command | File | Root key |
|---|---|---|
| `TRAIN` / `RESUME` | `Config.yml` | `Trainer:` |
| `PREDICTION` | `Prediction.yml` | `Predictor:` |
| `EVALUATION` | `Evaluation.yml` | `Evaluator:` |
| `TRANSFORM` | `Transform.yml` | `Transformer:` |

`TRANSFORM` sits outside the loop: it runs no model. It reads a dataset, applies a chain, and writes
a dataset — resampling a cohort onto one grid, folding it into a template (`Reduce`, N→1), expanding
each case into drawn copies (`Expand`, 1→N). A chain may still embed a `KonfAIInference` stage, so
"no model" means no top-level one. It takes no `-tb`, and `--plan` prints what a run would do and
stops without writing the deliverable — it does probe each destination with a real region-write it
then removes, so the output store may be created.

**Don't write configs from scratch — copy a runnable template from `examples/`** (Segmentation,
Synthesis or Registration) and adapt it. Then:
Synthesis, Registration or Transform) and adapt it. Then:

```bash
cd examples/Segmentation # always run from the dir holding the configs + Dataset/

konfai TRAIN -y --gpu 0 --config Config.yml
konfai PREDICTION -y --gpu 0 --config Prediction.yml --models Checkpoints/<train_name>/<checkpoint>.pt
konfai EVALUATION -y --config Evaluation.yml

cd ../Transform # no model, no GPU
konfai TRANSFORM --config Transform.yml --plan # what it would do; writes no deliverable
konfai TRANSFORM --config Transform.yml
```

Outputs are namespaced by the `train_name` in the config: `Checkpoints/<train_name>/`,
Expand Down
80 changes: 80 additions & 0 deletions .github/scripts/release_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (c) 2025 Valentin Boussot
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0

"""The body of a GitHub Release: the committed CHANGELOG section for a tag, verbatim.

Commitizen drafts that section from the commits, but the file is edited afterwards -- a squash merge
collapses to one line, a subject with no conventional prefix is dropped entirely, and a subject
written for a reviewer says nothing to a user. Rendering the commits again at release time would
publish text nobody reviewed, and the file and the release page would then describe one version
differently.

A file rather than a heredoc in the workflow because this decides what gets published, and a thing
that decides that should be testable. See ``tests/unit/test_release_notes.py``.
"""

from __future__ import annotations

import re
import sys
from pathlib import Path


def section_for(changelog: str, tag: str) -> str:
"""The body under ``## <tag>``, stripped, without its heading.

The heading has to END where the tag does. A ``\\b`` would match at the dot too, so a ``v1.8``
tag would take ``v1.8.0``'s notes and publish them under its own release -- silently, since both
are real versions and the text reads fine.
"""
found = re.search(rf"^## {re.escape(tag)}(?=[ \t]|$)[^\n]*\n(.*?)(?=^## |\Z)", changelog, re.M | re.S)
if found is None or not found.group(1).strip():
raise SystemExit(f"CHANGELOG.md carries no section for {tag}. Write it before tagging.")
return found.group(1).strip() + "\n"


#: PEP 440, as the spec itself writes it. Only the groups this module decides on are named.
_VERSION = re.compile(
r"^v?(?:\d+!)?\d+(?:\.\d+)*"
r"(?P<pre>[-_.]?(?:a|b|c|rc|alpha|beta|pre|preview)[-_.]?\d*)?"
r"(?:[-_.]?(?:post|rev|r)[-_.]?\d*|-\d+)?"
r"(?P<dev>[-_.]?dev[-_.]?\d*)?"
r"(?:\+[a-z0-9]+(?:[-_.][a-z0-9]+)*)?$",
re.IGNORECASE,
)


def is_prerelease(tag: str) -> bool:
"""Whether ``tag`` names a pre-release — the versions ``latest`` must not follow.

PEP 440 and not a numeric shape: a POST-release (``v1.8.0.post1``) is stable and must take
``latest``, while ``v1.8.0rc1`` and ``v1.8.0.dev1`` must not. A tag that does not parse counts as
a pre-release, because the alternative is handing ``latest`` to something nobody can classify.
"""
matched = _VERSION.match(tag.strip())
return matched is None or bool(matched.group("pre") or matched.group("dev"))


def main(argv: list[str]) -> None:
if argv[1] == "--prerelease":
print("true" if is_prerelease(argv[2]) else "false")
return
tag, source, destination = argv[1], Path(argv[2]), Path(argv[3])
destination.write_text(section_for(source.read_text(encoding="utf-8"), tag), encoding="utf-8")


if __name__ == "__main__":
main(sys.argv)
7 changes: 5 additions & 2 deletions .github/workflows/commit-hygiene.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out full history
uses: actions/checkout@v5
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
fetch-depth: 0
# This job runs code the pull request controls; the token has no business staying
# in .git/config while it does.
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.13"

Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/konfai_apps_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,14 @@ jobs:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# This job runs code the pull request controls; the token has no business staying
# in .git/config while it does.
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -57,7 +61,7 @@ jobs:
pip install -e ".[dev]"

- name: Restore Hugging Face cache
uses: actions/cache@v5
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: .cache/huggingface
key: hf-apps-${{ runner.os }}
Expand Down
39 changes: 30 additions & 9 deletions .github/workflows/konfai_ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
name: konfai_ci

# config_guide and examples/Transform are sources here: the integration tests extract the YAML blocks
# from those pages and run the example's configs as shipped, so editing either is editing a fixture.
on:
push:
branches: [main]
Expand All @@ -8,6 +10,8 @@ on:
- "konfai/**"
- "konfai-apps/**"
- "tests/**"
- "docs/source/config_guide/**"
- "examples/Transform/**"
- "pyproject.toml"
- "README.md"
pull_request:
Expand All @@ -16,6 +20,8 @@ on:
- "konfai/**"
- "konfai-apps/**"
- "tests/**"
- "docs/source/config_guide/**"
- "examples/Transform/**"
- "pyproject.toml"
- "README.md"

Expand All @@ -29,10 +35,14 @@ jobs:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# This job runs code the pull request controls; the token has no business staying
# in .git/config while it does.
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -51,10 +61,14 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# This job runs code the pull request controls; the token has no business staying
# in .git/config while it does.
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"

Expand All @@ -67,10 +81,14 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# This job runs code the pull request controls; the token has no business staying
# in .git/config while it does.
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"

Expand All @@ -83,12 +101,15 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# This job runs code the pull request controls; the token has no business staying
# in .git/config while it does.
persist-credentials: false
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"

Expand All @@ -108,7 +129,7 @@ jobs:
run: pytest tests/unit/test_packaging.py -m slow

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: konfai-wheel
path: dist/*.whl
9 changes: 7 additions & 2 deletions .github/workflows/konfai_mcp_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,15 @@ jobs:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# konfai-mcp bounds konfai, so the tag has to be reachable: without it setuptools_scm
# builds the core package as 0.1.dev1 and no bound on it can hold.
fetch-depth: 0
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: ${{ matrix.python-version }}

Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/konfai_studio_ci.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
name: konfai_studio_ci

# konfai/** is here for the same reason konfai_mcp_ci.yml lists it: Studio's tests guard the wiring
# between Studio and the workflows, so a change confined to konfai/ is exactly what breaks them.
on:
push:
branches: [main]
paths:
- ".github/workflows/konfai_studio_ci.yml"
- "studio/**"
- "konfai-mcp/**"
- "konfai/**"
- "pyproject.toml"
pull_request:
paths:
- ".github/workflows/konfai_studio_ci.yml"
- "studio/**"
- "konfai-mcp/**"
- "konfai/**"
- "pyproject.toml"

jobs:
Expand All @@ -24,20 +28,20 @@ jobs:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# studio/setup.py pins konfai-mcp to the setuptools_scm version -- tags must be reachable.
fetch-depth: 0
# PR CI runs untrusted build/test steps and never needs git auth.
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: ${{ matrix.python-version }}

- name: Set up Node
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "20"

Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
# This job runs code the pull request controls; the token has no business staying
# in .git/config while it does.
persist-credentials: false

- uses: actions/setup-python@v6
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.13"

- uses: pre-commit/action@v3.0.1
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
Loading
Loading