Skip to content
Open
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
11 changes: 11 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ reviews:
Examine code for logical error or inconsistencies, and suggest what may be changed to addressed these. Suggest
any enhancements for code improving efficiency, maintainability, comprehensibility, and correctness. Ensure new
or modified definitions will be covered by existing or new unit tests.
- path: '**/AGENTS.md'
instructions: >-
Check that the guidance matches the current repository conventions (pull requests target the dev branch,
commits need a DCO sign-off, formatting and tests run through runtests.sh) and that every file, command,
and path it mentions still exists and behaves as described.
- path: 'monai/**'
instructions: >-
This repository keeps AGENTS.md files at the root and in the subpackages. If this change alters behavior,
conventions, commands, or public APIs described in the AGENTS.md file closest to the changed code, or in
the root AGENTS.md, verify that the relevant AGENTS.md was updated in the same change to stay in sync,
and flag it if it was not.

auto_review:
# Automatic Review | Automatic code review
Expand Down
101 changes: 101 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# AGENTS.md

Guidance for AI coding assistants (and the humans reviewing their work) in this
repository. The file closest to the code being edited takes precedence: start
here, then read the `AGENTS.md` in the subpackage you are changing.

## Project overview

MONAI (Medical Open Network for AI) is a PyTorch-based, open-source framework
for deep learning in healthcare imaging, part of the PyTorch Ecosystem. It
provides domain-optimized data transforms, networks, losses, metrics, training
engines, and a bundle configuration system, with research and clinical users.
Supported Python and PyTorch versions live in `pyproject.toml`; do not copy
version numbers into other files, link there instead.

## Repository map

| Path | Purpose |
|------|---------|
| `monai/transforms` | array and dictionary transforms, lazy resampling, invertibility |
| `monai/networks` | networks, building blocks, layers, schedulers |
| `monai/data` | datasets, data loaders, image readers and writers, `MetaTensor` |
| `monai/losses` | loss functions |
| `monai/metrics` | evaluation metrics, mostly cumulative across batches |
| `monai/engines` | training and evaluation workflows, built on PyTorch Ignite |
| `monai/handlers` | Ignite handlers: checkpointing, logging, metrics output |
| `monai/inferers` | sliding window and other inference strategies |
| `monai/bundle` | JSON/YAML configuration system and bundle scripts |
| `monai/apps` | application packages: detection, DeepEdit, pathology, and more |
| `monai/auto3dseg` | Auto3DSeg algorithms; the runner lives in `monai/apps/auto3dseg` |
| `monai/fl` | federated learning client and utilities |
| `monai/visualize` | visualization and interpretability tools |
| `monai/optimizers` | optimizers and learning rate utilities |
| `monai/csrc` | optional C++/CUDA extensions |
| `monai/utils` | shared utilities; check here before writing a new helper |
| `tests` | unit and integration tests, mirroring the `monai` tree |
| `docs` | Sphinx documentation sources |

## Where the detail lives

| File | Covers |
|------|--------|
| [`monai/transforms/AGENTS.md`](monai/transforms/AGENTS.md) | array/dict transform pairs, randomness, lazy and inverse transforms |
| [`monai/networks/AGENTS.md`](monai/networks/AGENTS.md) | network, block, and layer conventions |
| [`monai/data/AGENTS.md`](monai/data/AGENTS.md) | datasets, caching, readers, test data |
| [`monai/losses/AGENTS.md`](monai/losses/AGENTS.md) | loss conventions, shared with metrics |
| [`monai/metrics/AGENTS.md`](monai/metrics/AGENTS.md) | cumulative metrics lifecycle |
| [`monai/bundle/AGENTS.md`](monai/bundle/AGENTS.md) | config syntax, references, bundle scripts |
| [`monai/apps/AGENTS.md`](monai/apps/AGENTS.md) | application packages and downloaded data |
| [`monai/auto3dseg/AGENTS.md`](monai/auto3dseg/AGENTS.md) | Auto3DSeg pipeline and generated bundles |
| [`monai/engines/AGENTS.md`](monai/engines/AGENTS.md) | engines and handlers |
| [`monai/csrc/AGENTS.md`](monai/csrc/AGENTS.md) | C++/CUDA extension build |
| [`tests/AGENTS.md`](tests/AGENTS.md) | test layout, minimal tests, skip helpers |
| [`docs/AGENTS.md`](docs/AGENTS.md) | documentation sources and API lists |

## Setup and commands

Verified in a fresh checkout of `dev` (Python 3.12, CPU-only PyTorch):

```bash
./runtests.sh --help # list every option of the test runner
./runtests.sh --setup # install git pre-commit hooks (black, isort, ruff, DCO check)
./runtests.sh --autofix # auto-format code with isort, black, and Ruff
./runtests.sh --ruff # ruff checks only, a fast pre-push check
python -m pytest tests/utils/test_look_up_option.py -q # run a single test module
```

For the full pre-PR test suite, see the "Unit testing" section of
`CONTRIBUTING.md`. Do not add a command to the list above unless you have
actually run it in this repository.

## Repo-wide conventions

- Pull requests target the `dev` branch. Name branches `[ticket_id]-[task_name]`,
for example `9199-introduce-agents-md`, based on the latest `dev`.
- Every commit needs a DCO sign-off: `git commit -s`. The sign-off identity is
the human author who takes responsibility for the change.
- Line length is 120. Docstrings are Google style. Public APIs carry type hints.
- American English spelling (`normalize`, `visualize`), as in PyTorch.
- New source files start with the Apache 2.0 license header used across the
repo (copy it from a neighboring file).
- Optional dependencies are only reached through `monai.utils.optional_import`;
never hard-import an optional package at module top level.
- A new public API is not finished until it is exported (the module's `__all__`
and the package `__init__.py`) and added to the matching page in
`docs/source/*.rst`.
- Deprecations go through `monai.utils.deprecated` / `deprecated_arg`, with
`since` and `removed` versions, following the process in `CONTRIBUTING.md`.
- Tests mirror the source tree under `tests/`; a change without a test is
treated as broken.
- Before writing a utility, search `monai/utils` and the subpackage's own
`utils.py`; MONAI already has many shared helpers.

## AI-assisted contributions

MONAI accepts tool-assisted contributions under the Tool-Assisted Contribution
Policy in `CONTRIBUTING.md`: the submitting author must review and understand
everything submitted, sign the DCO for it, and label AI-generated content in
issues and pull requests. These `AGENTS.md` files exist so that assisted work
arrives closer to mergeable. When a change alters a convention written down in
one of these files, update that file in the same pull request.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Attribution of AI-generated content should be included, for example with trailer

Code review and analysis can be assisted through the use of tools, for example linting or formatting tools. Diagnostic output from such tools does not form part of the submission but should be clearly labelled. The key emphasis is that these tools assist and do not replace human review, so contributors and reviewers must consider what might be missed by these tools, or what might be hallucinated. It might be helpful to review code with a different tool from one used for generation.

For assisted coding work, read the root `AGENTS.md` and the nearest package-specific `AGENTS.md`. When a change alters a documented convention, update the applicable `AGENTS.md` file in the same pull request.

## The contribution process

*Pull request early*
Expand Down
27 changes: 27 additions & 0 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Documentation

Sources live in `docs/source`: one page per area (`transforms.rst`,
`networks.rst`, `data.rst`, `losses.rst`, `metrics.rst`, `engines.rst`,
`bundle.rst`, and the rest), long-form guides in Markdown, and the Sphinx
configuration in `conf.py`.

## Things that are easy to get wrong

- The per-area `.rst` pages are the public API lists. A new public class or
function that is not added to its area page does not appear in the
published documentation, even if it is exported in `__init__.py`.
- Build before submitting documentation changes:

```bash
pip install -r docs/requirements.txt
cd docs && make html
```

and check the page you changed in `docs/build/html`. Do not commit the
build output.
- Python docstrings are Google style and written in reStructuredText; they
are pulled into these pages, so a docstring error is a documentation
build error.
- Release notes and feature summaries go in the `whatsnew` page for the
upcoming release, not into older `whatsnew_*.md` files.
- Spelling is American English, matching the code (see `CONTRIBUTING.md`).
24 changes: 24 additions & 0 deletions monai/apps/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Apps

Application-level packages built on the core library: `detection` (3D
detection with RetinaNet), `deepedit` and `deepgrow` (interactive
segmentation), `pathology`, `nuclick`, `nnunet`, `generation`,
`reconstruction`, `tcia`, `mmars` (the MONAI model zoo metadata), and
`auto3dseg` (the Auto3DSeg runner; its algorithms live in
`monai/auto3dseg`, see that package's `AGENTS.md`).

## Things that are easy to get wrong

- Sample datasets in `datasets.py` are downloaded from the web at runtime.
Tests that use them need network access and are excluded from offline
runs; do not make a unit test depend on a download when a small synthetic
array covers the same path.
- Apps pin down end-to-end workflows, so they compose core APIs (transforms,
networks, engines) rather than reimplementing them. If an app needs a new
option, add it to the core component and use it here.
- Several apps wrap external toolkits or model hubs. Those dependencies stay
optional and are reached through `monai.utils.optional_import`, so that
`import monai.apps` works without them.
- Version-sensitive metadata (model zoo entries in `mmars`, TCIA collection
info) is data, not code: update the JSON or metadata file and its tests,
not the loader.
23 changes: 23 additions & 0 deletions monai/auto3dseg/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Auto3DSeg

This package holds the Auto3DSeg algorithms: data analysis (`analyzer.py`),
algorithm generation (`algo_gen.py`), operations on the generated bundles
(`operations.py`), and result summaries (`seg_summarizer.py`). The user-facing
runner, bundle generator, and ensemble builder live one level up in
`monai/apps/auto3dseg` (`AutoRunner`, `BundleGen`, `EnsembleBuilder`); most
changes need to consider both places.

## Things that are easy to get wrong

- Auto3DSeg works by generating bundle folders on disk and then running
them. Paths inside the generated configs are relative to the generated
work directory; do not bake in absolute paths from the machine that ran
the generation.
- The data analyzer's statistics drive algorithm selection and the generated
configs. Changing an analyzer output key silently changes every generated
bundle, so update the summarizer and the tests together.
- End-to-end Auto3DSeg runs are slow and usually GPU-based. Keep unit tests
on tiny synthetic datasets and leave full runs to the integration tests.
- Templates for the generated algorithms live with the runner in
`monai/apps/auto3dseg`; editing generated output by hand is never the fix,
edit the template or the generator.
31 changes: 31 additions & 0 deletions monai/bundle/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Bundle and the configuration system

## How configs work

`ConfigParser` (`config_parser.py`) reads JSON and YAML configs. Inside a
config:

- `@name` references another config item (nested keys are joined with `::`,
as in `@preprocessing::transforms::keys`).
- `$...` is a Python expression, evaluated when the config is parsed.
- A dict with `_target_` is instantiated as that class or function.
`_disabled_` skips instantiation, `_requires_` lists references that must
be resolved first, `_mode_` changes how the callable is invoked. These
keys are defined in `config_item.py`; do not add new underscore keys
without updating `ConfigComponent` and `docs/source/config_syntax.md`.

## Things that are easy to get wrong

- Reference resolution order matters. `ReferenceResolver` resolves items on
demand and caches them; changing resolution code can quietly alter every
bundle in the model zoo, so run the bundle tests even for small changes.
- Configs are user-facing files, often edited by hand. Error messages should
name the config ID that failed; a bare `KeyError` from deep in the parser
is a bug report waiting to happen.
- Command line entry points live in `scripts.py` and run through
`python -m monai.bundle` (`run`, `download`, `ckpt_export`, and others).
Their arguments map onto config overrides; keep the CLI and the config
syntax in sync.
- Bundle metadata follows the specification in
`docs/source/mb_specification.rst` and `mb_properties.rst`; `properties.py`
holds the key definitions.
21 changes: 21 additions & 0 deletions monai/csrc/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# C++/CUDA extensions (csrc)

Optional native extensions: resampling (`resample/`), filtering
(`filtering/`), the LLTM cell (`lltm/`), shared helpers (`utils/`), and the
pybind entry point (`ext.cpp`).

## Things that are easy to get wrong

- The extensions are not built by default. `setup.py` builds them only when
`BUILD_MONAI=1` is set, and CUDA sources only when a CUDA toolkit is
available or `FORCE_CUDA=1` is set. Most contributors and CI jobs run
without them.
- Every extension has, or shares, a pure PyTorch path in the Python
packages, and MONAI must import and pass its quick tests with the
extensions absent. If you add an accelerated path, the fallback stays.
- C++ style is governed by the repo-root `.clang-format`; format native
code with `./runtests.sh --clangformat` instead of formatting by hand.
Note that it applies clang-format in place and modifies the files, it is
not a read-only check.
- Python and native implementations of the same operation must agree on
dtype and device behavior; test both paths when you touch one.
27 changes: 27 additions & 0 deletions monai/data/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Data

## Key types

- `MetaTensor` (`meta_tensor.py`) is a `torch.Tensor` subclass carrying image
metadata, affine information, and the record of applied transforms used by
inverse transforms and lazy resampling. Code that silently converts it to a
plain tensor (for example through `np.asarray` round trips) loses that
information; preserve the type unless dropping it is the point.
- Datasets live in `dataset.py`: `Dataset`, `CacheDataset`,
`SmartCacheDataset`, `PersistentDataset`, and friends. Readers and writers
are registered in `image_reader.py` and `image_writer.py`.

## Things that are easy to get wrong

- `CacheDataset` caches the deterministic part of the pipeline. Its
`copy_cache=True` default deep-copies cached items before the random
transforms run; turning it off for speed reuses the cached tensors and can
corrupt the cache for the next epoch.
- Optional formats (nibabel, pydicom, PIL, and others) are loaded through
`monai.utils.optional_import` inside the readers. A reader must still
import cleanly when its backend package is missing.
- `monai.data.DataLoader` extends the PyTorch loader with MONAI defaults;
check it before reaching for `torch.utils.data.DataLoader` in MONAI code.
- Test images and other binary data do not belong in this repository. Tests
fetch data through `tests.utils` helpers from the MONAI-extra-test-data
releases; see `tests/AGENTS.md`.
23 changes: 23 additions & 0 deletions monai/engines/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Engines and handlers

`Trainer` and `Evaluator` (in `trainer.py` and `evaluator.py`) build on the
`Workflow` base class and on PyTorch Ignite. Handlers live in
`monai/handlers` and are attached to an engine to add checkpointing,
logging, early stopping, and metrics output.

## Things that are easy to get wrong

- Ignite is an optional dependency here: it is imported through
`monai.utils.optional_import` with the minimum version pinned in
`IgniteInfo.OPT_IMPORT_VERSION` (`monai/utils/enums.py`). Never import
`ignite` directly at module top level in this package.
- Handlers react to Ignite events (`Events.EPOCH_COMPLETED`, and so on) and
read from `state.output`. Changing what a trainer puts into `state.output`
breaks handlers in another package; keep the documented keys stable.
- Metrics handlers expect the metric lifecycle from
`monai/metrics/AGENTS.md`: accumulate per iteration, `aggregate()` and
`reset()` at event time. Do not call `aggregate()` per batch in a handler.
- `monai/engines` decides when things happen; `monai/handlers` decides what
happens. A new capability usually means a small handler plus, at most, a
new event or state entry in the engine, not logic inside the training
loop.
24 changes: 24 additions & 0 deletions monai/losses/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Losses

Shared conventions with `monai/metrics` (see `monai/metrics/AGENTS.md`):
most losses and metrics take an `include_background` flag (default `True`)
and a `reduction` from the enums in `monai.utils` (`LossReduction` here,
`MetricReduction` in metrics). Keep the flag names, defaults, and argument
order consistent with the neighboring classes when adding a new one.

## Things that are easy to get wrong

- Several losses (Dice and its variants in `dice.py`, focal loss) do not apply
sigmoid or softmax internally unless `sigmoid=True` or `softmax=True` is
passed. Check those flags before assuming the loss expects probabilities
rather than logits, and do not change a default: it silently changes
training results for existing users.
- `to_onehot_y` and `include_background` interact: with one-hot targets the
background channel is channel 0, and excluding it slices after the one-hot
conversion. Test both settings.
- Losses should support the batch and channel shapes their docstrings
promise and return a scalar under the default reduction. Reductions other
than the mean must not silently change the output shape contract.
- Numerical stability matters here (log of probabilities, division by sums
over empty classes). Follow the epsilon handling of the closest existing
loss instead of inventing a new one.
28 changes: 28 additions & 0 deletions monai/metrics/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Metrics

Shared conventions with `monai/losses` (see `monai/losses/AGENTS.md`):
`include_background` defaults to `True`, and reductions come from
`MetricReduction` in `monai.utils`.

## How metrics accumulate

Most metrics derive from `Cumulative` or `CumulativeIterationMetric` in
`metric.py`. Calls during an epoch append to internal buffers through
`__call__`; `aggregate()` computes the result from the buffers; `reset()`
clears them.

## Things that are easy to get wrong

- Forgetting `reset()` between epochs or evaluations is the classic metrics
bug: the second result silently includes the first run's data. Any new
metric must reset every buffer it owns, and handlers in `monai/handlers`
call `reset()` and `aggregate()` at event time, not per batch.
- `aggregate()` must not destroy the buffers unless documented; some
workflows aggregate intermediate results and keep accumulating.
- Handle the not-applicable case (for example, a class missing from both
prediction and target) the way the neighboring metric does, usually by
producing `nan` and letting `get_not_nans` or the handler filter it,
instead of returning 0 and flattering the score.
- Distance-based metrics (Hausdorff, surface distance) are computed per
class and can be the slow part of a test suite; keep test inputs small
and mark slow tests as described in `tests/AGENTS.md`.
Loading