# Developing VIPR Plugins

## Start from the Example

The best way to create a plugin is to copy and adapt the example plugin in `example-plugin/`.

## Minimal Plugin - Step by Step

This follows the structure of `example-plugin/`.

### 1. Project Configuration (pyproject.toml)

```toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "vipr-log-file"  # PyPI package name
version = "0.1.0"
dependencies = ["vipr"]

[project.entry-points."vipr.plugins"]
log_file = "vipr_log_file:load"  # entry_point_name = "package:function"

[tool.setuptools.packages.find]
where = ["."]
include = ["vipr_log_file*"]
```

### 2. Package Structure

```
vipr_log_file/          # Package name (matches entry point target)
└── __init__.py         # Contains load(app) function
```

### 3. Implementation (__init__.py)

```python
def load(app):
    """Plugin initialization - called by VIPR"""
    app.log.info("Loading log_file plugin")
    
    # Register hook
    def write_logs_to_file(app):
        # Your plugin logic here
        pass
    
    # Register with weight to control execution order
    # Hooks are executed from lowest to highest weight value
    app.hook.register('INFERENCE_COMPLETE_HOOK', write_logs_to_file, weight=250)
```

**Note on weights:** The `weight` parameter controls execution order for hooks. Hooks are executed from lowest to highest weight value, regardless of registration order. This allows plugins to ensure their hooks run at the right time in relation to other plugins.

## Installation

```bash
cd example-plugin/
pip install -e .  # Development mode
```

Plugin is automatically discovered via entry points.

## Optional: Plugin Control

Create `vipr-plugins.yaml` in your working directory to disable a plugin:

```yaml
plugin.log_file:
  enabled: false
```

**Note:** `log_file` matches the entry point name in pyproject.toml, NOT the package name.

## Packaged Example Configs

Plugins can expose example configurations for the web frontend and Discovery API.

### Directory Convention

Example configs must be packaged YAML files located under:

```text
<plugin_package>/<group>/examples/configs/*.yaml
```

or

```text
<plugin_package>/<group>/examples/configs/*.yml
```

Examples:

```text
vipr_reflectometry/reflectorch/examples/configs/PTCDI-C3.yaml
vipr_reflectometry/reflectorch/examples/configs/Fe_Pt_DN.yaml
vipr_reflectometry/flow_models/examples/configs/Fe_Pt_DN_NSF.yaml
```

The path segment immediately before `examples/configs` becomes the example `group`.

### How Discovery Derives Metadata

VIPR scans installed plugin packages registered via the `vipr.plugins` entry point group.

For each matching YAML file:

- `plugin` is derived from the plugin entry point name
- `group` is derived from the path segment immediately before `examples/configs`
- `id` is built as `<plugin>.<group>.<file_stem>`

Example:

```text
vipr_reflectometry/reflectorch/examples/configs/Fe_Pt_DN.yaml
```

becomes:

```text
reflectometry.reflectorch.Fe_Pt_DN
```

### Requirements

For example configs to be discoverable:

- the plugin must be installed in the current Python environment
- the YAML files must be included as package data
- the config must reference handlers, hooks, and filters that are available in the current runtime
- callbacks with `enabled: false` are ignored during availability checks

### Packaging Note

Make sure the example files are included in the built package, for example via `MANIFEST.in` or package-data configuration.

## Complete Working Example

See `example-plugin/` for the full implementation that:
- Registers a hook
- Writes logs to a file after inference
- Shows proper project structure

Copy it and adapt for your needs.
