# How the Generator Registry Initializes and Manages AI Model Adapters in Modly

> Discover how the Modly Generator Registry initializes and manages AI model adapters by discovering extensions, instantiating generators, and providing runtime APIs for model switching and hot-reloading.

- Repository: [lightningpixel/modly](https://github.com/lightningpixel/modly)
- Tags: internals
- Published: 2026-08-15

---

**The GeneratorRegistry discovers extensions from the filesystem, instantiates generators in direct or subprocess mode, and provides runtime APIs for model switching and hot-reloading.**

The Modly platform uses a flexible **GeneratorRegistry** system to handle AI model adapters (called *extensions*). This registry eliminates tight coupling between the core application and individual model implementations, allowing dynamic discovery of new generators without code changes. Understanding how the generator registry initializes and manages AI model adapters is essential for extending Modly with custom models or troubleshooting adapter loading issues.

## Extension Discovery on Startup

When the application launches, the registry begins its initialization by scanning for available extensions. This discovery phase happens in `_discover_extensions()`, located in [`api/services/generator_registry.py`](https://github.com/lightningpixel/modly/blob/main/api/services/generator_registry.py) at lines 45-53.

The discovery process follows these steps:

1. **Locate the extensions directory** – The registry checks for an `EXTENSIONS_DIR` environment variable. If unset, discovery is skipped entirely.
2. **Validate extension structure** – Each subdirectory must contain both [`manifest.json`](https://github.com/lightningpixel/modly/blob/main/manifest.json) and [`generator.py`](https://github.com/lightningpixel/modly/blob/main/generator.py) to qualify.
3. **Parse and validate manifests** – The manifest is read and verified to be of *model* type (as opposed to other extension types).
4. **Build the extension map** – Valid entries are stored as tuples mapping a *full ID* (`"<ext_id>/<node_id>"`) to `(GeneratorClass, node manifest, extension path)`.

This approach allows Modly to support an arbitrary number of model extensions without hardcoding their locations or configurations.

## Generator Instantiation: Direct vs. Subprocess Mode

After discovery completes, `GeneratorRegistry.initialize()` (lines 67-81 of [`generator_registry.py`](https://github.com/lightningpixel/modly/blob/main/generator_registry.py)) instantiates each generator. The registry supports **two loading strategies** depending on what the extension provides:

**Direct mode (legacy)** – When `cls` is not `None`, the registry instantiates the concrete generator class directly. The constructor receives the model-specific directory and workspace folder. Additional manifest fields—`hf_repo`, `hf_skip_prefixes`, and `params_schema`—are attached as instance attributes for later use.

**Subprocess mode** – When no class is provided (`cls` is `None`), the registry verifies that a virtual environment (`venv`) exists in the extension directory. If valid, it wraps the model in an `ExtensionProcess` instance that isolates execution in a subprocess. This mode prevents dependency conflicts between models with incompatible requirements.

All successfully created generators populate the `_generators` dictionary, with their manifests stored in `_manifests`. Loading failures are captured in `_errors` for diagnostic access via `load_errors()`.

## Runtime Management API

The registry exposes a concise API for the rest of the application to interact with generators without managing their lifecycle directly:

- **`get_active()`** – Returns the currently selected generator (defaulting to `SELECTED_MODEL_ID`), automatically downloading and loading it if necessary.
- **`get_generator(model_id)`** – Retrieves a specific generator by its full ID.
- **`switch_model(model_id)`** – Unloads the previous generator and activates the requested one.
- **`reload()`** – Re-scans `EXTENSIONS_DIR`, clears existing state, and re-runs `initialize()` for hot-reloading without server restart.
- **`all_status()` / `active_status()`** – Provide metadata (download status, load state, description, VRAM requirements) for UI rendering.

For subprocess-based generators, the registry ensures proper cleanup through `ExtensionProcess.stop()` when models are switched or shut down.

## Code Examples

### Obtaining and Using the Active Generator

```python
from api.services.generator_registry import generator_registry

# Initialize the registry at application startup

generator_registry.initialize()

# Retrieve the active model (downloads and loads automatically if needed)

active_gen = generator_registry.get_active()

# Generate using the BaseGenerator API

result = active_gen.generate(prompt="A futuristic cityscape", seed=42)
print("Generated output saved to:", result.output_path)

```

### Switching Models at Runtime

```python
from api.services.generator_registry import generator_registry

# List available models

print("Available models:", list(generator_registry._generators.keys()))

# Switch to the 'sdxl' model

generator_registry.switch_model("sdxl")

# Verify the switch

new_gen = generator_registry.get_active()
print("Now using:", new_gen.__class__.__name__)

```

### Hot-Reloading After Adding Extensions

```python
from api.services.generator_registry import generator_registry

# Assume a new extension was added to EXTENSION_DIR

generator_registry.reload()

# Confirm new models are available

print("Models after reload:", list(generator_registry._generators.keys()))

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`api/services/generator_registry.py`](https://github.com/lightningpixel/modly/blob/main/api/services/generator_registry.py) | Core registry implementation with discovery, instantiation, and management logic |
| [`api/services/generators/base.py`](https://github.com/lightningpixel/modly/blob/main/api/services/generators/base.py) | `BaseGenerator` abstract class defining the interface all adapters must implement |
| [`api/services/extension_process.py`](https://github.com/lightningpixel/modly/blob/main/api/services/extension_process.py) | `ExtensionProcess` wrapper for isolated subprocess execution |
| [`extensions/example/manifest.json`](https://github.com/lightningpixel/modly/blob/main/extensions/example/manifest.json) | Reference manifest showing required metadata fields |
| [`extensions/example/generator.py`](https://github.com/lightningpixel/modly/blob/main/extensions/example/generator.py) | Example concrete generator for direct-mode loading |

## Summary

- **Discovery is filesystem-driven**: Extensions are found by scanning `EXTENSIONS_DIR` for valid [`manifest.json`](https://github.com/lightningpixel/modly/blob/main/manifest.json) + [`generator.py`](https://github.com/lightningpixel/modly/blob/main/generator.py) pairs.
- **Two instantiation modes exist**: Direct mode instantiates Python classes directly; subprocess mode isolates models via `ExtensionProcess` when no class is provided.
- **Runtime flexibility is built-in**: Hot-reloading, model switching, and status introspection require no application restarts.
- **Errors are non-fatal**: Loading failures are captured and exposed through `load_errors()` without crashing the registry.

## Frequently Asked Questions

### How does Modly find new AI model adapters without restarting?

The `reload()` method re-scans `EXTENSIONS_DIR`, clears the existing registry state, and re-runs `initialize()`. This allows administrators to drop new extension folders into the configured directory and activate them immediately via API call or UI action.

### What happens if an extension's virtual environment is missing?

For subprocess-mode extensions, the registry verifies `venv` existence during initialization. If missing, the extension is skipped and an error is recorded in `_errors`. The registry continues loading other extensions; no single failure prevents the system from starting.

### Can the same model run in both direct and subprocess modes?

No—the loading mode is determined per-extension by whether [`generator.py`](https://github.com/lightningpixel/modly/blob/main/generator.py) exports a concrete class. If a class is present, direct mode is used. If `cls` is `None` after import, subprocess mode is selected. Extension authors choose this behavior by their implementation structure.

### How does `get_active()` handle models that aren't downloaded yet?

`get_active()` checks the download status of the target model and triggers the download mechanism if needed. Only after the model assets are present does it proceed to load the generator into memory, ensuring the caller receives a ready-to-use instance.