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
15 changes: 7 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -456,16 +456,15 @@ flip-utils/dist/
# version control (covered by .claude/* above, listed explicitly for clarity).
.claude/worktrees/

# fl-tutorials generated / uncommitted artifacts (data/, the spleen-seg uv.lock), scoped under the
# per-backend tutorial trees (fl-tutorials/<backend>/) so a bare `data/` doesn't over-match
# other data/ dirs in the monorepo:
# - generated tutorial datasets (spleen/xray images + accession CSVs)
# - tutorial uv.lock (tutorials intentionally don't pin a strict lockfile)
fl-tutorials/nvflare/data/
fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/uv.lock
# fl-tutorials generated / uncommitted artifacts, scoped (not a bare `data/`, which would
# over-match other data/ dirs in the monorepo):
# - the shared tutorial-dataset root both backends' harnesses read (fl-tutorials/datasets/
# downloads land here: spleen/xray/arkplus images + accession CSVs + the HF cache)
# - the dataset tooling's uv.lock (intentionally not pinned to a strict lockfile)
fl-tutorials/data/
fl-tutorials/datasets/spleen/uv.lock
fl-services/flower/provision/creds/
fl-services/flower/runs/
fl-tutorials/flower/data/

# Round-timing extraction output (scripts/fl_round_metrics/ extractors and the
# tutorials' `make round-metrics` write
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ FLIP/
├── flip-utils/ # FLIP Python library (pip-installable flip-utils)
├── fl-services/ # FL Docker services + network provisioning, per backend (Makefile owns build/provision/up/down/submit; flower also up-secure): fl-services/nvflare/{fl-base,fl-server,fl-client,fl-api-base, provision/{net-*_project_*.yml, scripts/, workspace-{dev,stag,prod}/ gitignored}}, fl-services/flower/{fl-base,superlink,supernode,fl-api-flower, provision/{scripts/, creds/ gitignored}} (#622)
├── fl-apps/ # FL app templates per backend: fl-apps/nvflare/{standard,evaluation,diffusion_model,fed_opt} (all Client-API), fl-apps/flower/{standard,evaluation} + check_required_files.sh (cross-backend CI validator at root)
├── fl-tutorials/ # FL tutorials per backend (all NVFLARE ones are Client-API apps): fl-tutorials/nvflare/{image_*}, fl-tutorials/flower/{xray_classification,3d_spleen_segmentation*} (root Makefile forwards by FL_BACKEND); xray classification, spleen seg/eval, diffusion. Plus fl-tutorials/tests/ — CPU-only pytest over the tutorial transform chains (#871) plus a static `min_clients` wiring guard covering fl-apps/flower too, run by `make -C fl-tutorials test`
├── fl-tutorials/ # FL tutorials per backend (all NVFLARE ones are Client-API apps): fl-tutorials/nvflare/{image_*}, fl-tutorials/flower/{xray_classification,3d_spleen_segmentation*} (root Makefile forwards by FL_BACKEND); xray classification, spleen seg/eval, diffusion. Shared dataset tooling in fl-tutorials/datasets/ (download/derive/enrich, single copy for both backends — the download-*-data + upload-spleen-labels targets), outputs in the shared gitignored fl-tutorials/data/. Plus fl-tutorials/tests/ — CPU-only pytest over the tutorial transform chains (#871) plus a static `min_clients` wiring guard covering fl-apps/flower too, run by `make -C fl-tutorials test`
├── map-apps/ # MONAI Application Package (MAP) templates for packaging FLIP-trained models for clinical deployment
├── trust/
│ ├── trust-api/ # Trust API gateway (Python/FastAPI)
Expand Down Expand Up @@ -159,7 +159,7 @@ cause. (Older app copies die with torch's opaque `num_samples=0` instead.)

`e2e_smoke` has a hook for exactly this: `--data-enrichment-cwd` + `--data-enrichment-cmd` run a shell command
**between the image pull and training**, with `FLIP_PROJECT_ID` exported. Since FLIP#776 the spleen uploader is
in-tree at `fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/utils/upload_spleen_labels_to_xnat.py`
in-tree at `fl-tutorials/datasets/spleen/upload_spleen_labels_to_xnat.py` (the shared dataset-tooling tree)
— **no private repo required**. It resolves each trust's XNAT project by `secondary_ID == <FLIP project_id>`,
fetches the accession→MSD-case mapping at run time from the public `aicentreflip/trust-data` dataset
(`omop-csv/<version>/spleen_project/image_occurrence.csv`, which also carries `source_trust`), and writes each
Expand Down Expand Up @@ -198,8 +198,8 @@ instead:
cd flip-api && uv run python -m tests.e2e_smoke \
--model-files-dir ../fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/app_files \
--query-file ../fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/query.sql \
--data-enrichment-cwd ../fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation \
--data-enrichment-cmd 'uv run --no-project --with ../../../../flip-utils python utils/upload_spleen_labels_to_xnat.py --flip-project-id "$FLIP_PROJECT_ID" --labels-dir ../../data/spleen/images --xnat-url http://127.0.0.1:8104 --xnat-url http://127.0.0.1:8106'
--data-enrichment-cwd ../fl-tutorials/datasets/spleen \
--data-enrichment-cmd 'uv run --no-project --with ../../../flip-utils python upload_spleen_labels_to_xnat.py --flip-project-id "$FLIP_PROJECT_ID" --labels-dir ../../data/spleen/images --xnat-url http://127.0.0.1:8104 --xnat-url http://127.0.0.1:8106'
```

(The `$`-escaping trap still applies to any hand-written `EXTRA_ARGS`: `make -C flip-api …` expands once, so
Expand All @@ -209,7 +209,7 @@ there. The in-Makefile targets handle this for you. Note that `e2e_smoke_spleen`
tutorial (`../fl-tutorials/flower/3d_spleen_segmentation/{app,query.sql}`), not the NVFLARE paths shown in the
direct-invocation example above — pair it with `FL_BACKEND=flower`, or invoke `tests.e2e_smoke` directly with
the NVFLARE paths for the NVFLARE tutorial. The enrichment step itself is backend-agnostic and runs out of the
NVFLARE tree either way.)
shared `fl-tutorials/datasets/spleen/` tree either way.)

Enrichment must land **after** the pull and after DICOM→NIfTI conversion; the hook's position guarantees that.
The uploader derives each target filename from the converted `input_*.nii.gz`, so with no `NIFTI` resource it
Expand Down
10 changes: 5 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ FLIP/
├── flip-utils/ # FLIP Python library (pip-installable flip-utils)
├── fl-services/ # FL Docker services + network provisioning, per backend (Makefile owns build/provision/up/down/submit; flower also up-secure): fl-services/nvflare/{fl-base,fl-server,fl-client,fl-api-base, provision/{net-*_project_*.yml, scripts/, workspace-{dev,stag,prod}/ gitignored}}, fl-services/flower/{fl-base,superlink,supernode,fl-api-flower, provision/{scripts/, creds/ gitignored}} (#622)
├── fl-apps/ # FL app templates per backend: fl-apps/nvflare/{standard,evaluation,diffusion_model,fed_opt} (all Client-API), fl-apps/flower/{standard,evaluation} + check_required_files.sh (cross-backend CI validator at root)
├── fl-tutorials/ # FL tutorials per backend (all NVFLARE ones are Client-API apps): fl-tutorials/nvflare/{image_*}, fl-tutorials/flower/{xray_classification,3d_spleen_segmentation*} (root Makefile forwards by FL_BACKEND); xray classification, spleen seg/eval, diffusion. Plus fl-tutorials/tests/ — CPU-only pytest over the tutorial transform chains (#871) plus a static `min_clients` wiring guard covering fl-apps/flower too, run by `make -C fl-tutorials test`
├── fl-tutorials/ # FL tutorials per backend (all NVFLARE ones are Client-API apps): fl-tutorials/nvflare/{image_*}, fl-tutorials/flower/{xray_classification,3d_spleen_segmentation*} (root Makefile forwards by FL_BACKEND); xray classification, spleen seg/eval, diffusion. Shared dataset tooling in fl-tutorials/datasets/ (download/derive/enrich, single copy for both backends — the download-*-data + upload-spleen-labels targets), outputs in the shared gitignored fl-tutorials/data/. Plus fl-tutorials/tests/ — CPU-only pytest over the tutorial transform chains (#871) plus a static `min_clients` wiring guard covering fl-apps/flower too, run by `make -C fl-tutorials test`
├── map-apps/ # MONAI Application Package (MAP) templates for packaging FLIP-trained models for clinical deployment
├── trust/
│ ├── trust-api/ # Trust API gateway (Python/FastAPI)
Expand Down Expand Up @@ -159,7 +159,7 @@ cause. (Older app copies die with torch's opaque `num_samples=0` instead.)

`e2e_smoke` has a hook for exactly this: `--data-enrichment-cwd` + `--data-enrichment-cmd` run a shell command
**between the image pull and training**, with `FLIP_PROJECT_ID` exported. Since FLIP#776 the spleen uploader is
in-tree at `fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/utils/upload_spleen_labels_to_xnat.py`
in-tree at `fl-tutorials/datasets/spleen/upload_spleen_labels_to_xnat.py` (the shared dataset-tooling tree)
— **no private repo required**. It resolves each trust's XNAT project by `secondary_ID == <FLIP project_id>`,
fetches the accession→MSD-case mapping at run time from the public `aicentreflip/trust-data` dataset
(`omop-csv/<version>/spleen_project/image_occurrence.csv`, which also carries `source_trust`), and writes each
Expand Down Expand Up @@ -198,8 +198,8 @@ instead:
cd flip-api && uv run python -m tests.e2e_smoke \
--model-files-dir ../fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/app_files \
--query-file ../fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/query.sql \
--data-enrichment-cwd ../fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation \
--data-enrichment-cmd 'uv run --no-project --with ../../../../flip-utils python utils/upload_spleen_labels_to_xnat.py --flip-project-id "$FLIP_PROJECT_ID" --labels-dir ../../data/spleen/images --xnat-url http://127.0.0.1:8104 --xnat-url http://127.0.0.1:8106'
--data-enrichment-cwd ../fl-tutorials/datasets/spleen \
--data-enrichment-cmd 'uv run --no-project --with ../../../flip-utils python upload_spleen_labels_to_xnat.py --flip-project-id "$FLIP_PROJECT_ID" --labels-dir ../../data/spleen/images --xnat-url http://127.0.0.1:8104 --xnat-url http://127.0.0.1:8106'
```

(The `$`-escaping trap still applies to any hand-written `EXTRA_ARGS`: `make -C flip-api …` expands once, so
Expand All @@ -209,7 +209,7 @@ there. The in-Makefile targets handle this for you. Note that `e2e_smoke_spleen`
tutorial (`../fl-tutorials/flower/3d_spleen_segmentation/{app,query.sql}`), not the NVFLARE paths shown in the
direct-invocation example above — pair it with `FL_BACKEND=flower`, or invoke `tests.e2e_smoke` directly with
the NVFLARE paths for the NVFLARE tutorial. The enrichment step itself is backend-agnostic and runs out of the
NVFLARE tree either way.)
shared `fl-tutorials/datasets/spleen/` tree either way.)

Enrichment must land **after** the pull and after DICOM→NIfTI conversion; the hook's position guarantees that.
The uploader derives each target filename from the converted `input_*.nii.gz`, so with no `NIFTI` resource it
Expand Down
2 changes: 1 addition & 1 deletion docs/source/user-guides/arkplus-fine-tuning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ published reference dataset on Hugging Face:

This pulls the site-1 / site-2 training splits from
`aicentreflip/tutorials-arkplus-cxr-classification <https://huggingface.co/datasets/aicentreflip/tutorials-arkplus-cxr-classification>`_
and lays them out under ``fl-tutorials/nvflare/data/arkplus/`` (gitignored).
and lays them out under ``fl-tutorials/data/arkplus/`` (gitignored).

To use your own data instead, point the per-site ``.env.app`` values
(``SITE{1,2}_IMAGES_DIR`` / ``SITE{1,2}_DATAFRAME``) at your directories. The trainer selects a
Expand Down
6 changes: 3 additions & 3 deletions fl-services/flower/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,10 @@ trains on an empty dataset with no signal that the data is missing. So download
point both variables at it when bringing the net up:

```bash
make -C fl-tutorials download-xray-data FL_BACKEND=flower # → fl-tutorials/flower/data/xrays_mini_300/
make -C fl-tutorials download-xray-data FL_BACKEND=flower # → fl-tutorials/data/xrays_mini_300/
make -C fl-services/flower up \
DEV_IMAGES_DIR=$(pwd)/fl-tutorials/flower/data/xrays_mini_300/accession-resources \
DEV_DATAFRAME=$(pwd)/fl-tutorials/flower/data/xrays_mini_300/sample_get_dataframe_response.csv
DEV_IMAGES_DIR=$(pwd)/fl-tutorials/data/xrays_mini_300/accession-resources \
DEV_DATAFRAME=$(pwd)/fl-tutorials/data/xrays_mini_300/dataframe.csv
```

(Run from the repo root; `data/` is gitignored. `up-secure` takes the same two variables.)
Expand Down
19 changes: 13 additions & 6 deletions fl-tutorials/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,29 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# Backend-aware entrypoint for the FL tutorial runners. The actual runner + harness live
# per-backend under fl-tutorials/<backend>/ (fl-tutorials/nvflare/ and fl-tutorials/flower/). This thin
# Makefile forwards every target to fl-tutorials/$(FL_BACKEND)/ so the documented entrypoint
# Backend-aware entrypoint for the FL tutorial runners. The runner + harness live per-backend
# under fl-tutorials/<backend>/ (fl-tutorials/nvflare/ and fl-tutorials/flower/); the shared
# dataset tooling lives in fl-tutorials/datasets/ (outputs in the gitignored fl-tutorials/data/,
# one download serving both backends). This thin Makefile forwards each target to the right
# sub-make so the documented entrypoint
# make -C fl-tutorials <target> [TUTORIAL=... NUM_CASES=...]
# keeps working and selects the backend via FL_BACKEND (default nvflare). Command-line
# variables are forwarded to the sub-make automatically.
FL_BACKEND ?= nvflare

TUTORIAL_TARGETS := list-tutorials run-tutorial run-all-tutorials \
download-xray-data download-spleen-data upload-spleen-labels \
TUTORIAL_TARGETS := list-tutorials run-tutorial run-all-tutorials
# Dataset targets are backend-agnostic (shared data root, single copy in datasets/);
# FL_BACKEND still selects a per-harness variant where one exists (the two spleen downloads).
DATASET_TARGETS := download-xray-data download-spleen-data upload-spleen-labels \
download-arkplus-finetuning-data download-arkplus-eval-data

.PHONY: $(TUTORIAL_TARGETS)
.PHONY: $(TUTORIAL_TARGETS) $(DATASET_TARGETS)
$(TUTORIAL_TARGETS):
@$(MAKE) --no-print-directory -C $(FL_BACKEND) $@

$(DATASET_TARGETS):
@$(MAKE) --no-print-directory -C datasets $@ FL_BACKEND=$(FL_BACKEND)

# ---------------------------------------------------------------------------
# Lint / autoformat the tutorial sources. Runs ruff over the whole tree; ruff
# auto-excludes the per-tutorial .venv/ trees and resolves config per-file
Expand Down
7 changes: 4 additions & 3 deletions fl-tutorials/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ make -C fl-tutorials run-tutorial TUTORIAL=xray_classification
built `flare-fl-base` image and fail without it. The spleen examples take `download-spleen-data` in place of
`download-xray-data`.

The simulator requires Docker, and GPU-backed examples require the NVIDIA Container Toolkit. Dataset downloads and
generated runs are kept in gitignored backend data/output directories. Run `make -C fl-tutorials run-all-tutorials`
only when you intentionally want the full, heavyweight suite.
The simulator requires Docker, and GPU-backed examples require the NVIDIA Container Toolkit. Dataset tooling is
shared across backends in [`datasets/`](datasets/) and downloads land in the shared gitignored `data/` root, so
one download serves both backends; generated runs stay in gitignored per-backend output directories. Run
`make -C fl-tutorials run-all-tutorials` only when you intentionally want the full, heavyweight suite.

## Check the sources without a GPU

Expand Down
Loading
Loading