# How to Create Self-Modifying Patches Using Nallely's Introspective API

> Learn to create self-modifying patches by compiling and injecting Python code into running virtual devices using Nallely's introspective API for real-time signal graph changes without restarts.

- Repository: [dr-schlange/nallely-midi](https://github.com/dr-schlange/nallely-midi)
- Tags: how-to-guide
- Published: 2026-02-28

---

**You create self-modifying patches by calling MetaTrevorAPI methods to compile and inject new Python code into running virtual devices, enabling real-time structural changes to the signal graph without session restarts.**

The dr-schlange/nallely-midi framework treats every device as an autonomous neuron in a signal graph. Through the introspective API, running systems can inspect, re-wire, and rewrite the Python classes implementing virtual devices while the session remains active, enabling true self-modifying patches that evolve their own behavior dynamically.

## Architecture of the Introspective API

### Dynamic Parameter Linking

The foundation of any patch is the connection between devices. In [`nallely/trevor/trevor_api.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/trevor/trevor_api.py) (lines 133-150), `TrevorAPI.associate_parameters` creates or removes links between two parameters, optionally inserting a **Scaler** to map value ranges. This method builds `Link` objects stored in each device's `links_registry` (defined in [`nallely/core/virtual_device.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/core/virtual_device.py), lines 17-18), establishing persistent signal pathways that survive code modifications.

### Hot-Patching Methods at Runtime

For surgical changes, `MetaTrevorAPI.compile_inject` receives a method name and raw source code, compiles it, and swaps it into the device's class definition. According to [`nallely/trevor/meta_trevor_api.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/trevor/meta_trevor_api.py) (lines 39-56), the new method is bound immediately to all instances and stored as `__source__` for later retrieval. This allows you to add behavior to live objects without restarting the interpreter.

### Full-Class Replacement

When structural changes require new attributes or altered initialization, `MetaTrevorAPI.object_centric_compile_inject` and its helper `compile_save_new_class` (implemented in [`nallely/trevor/meta_trevor_api.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/trevor/meta_trevor_api.py), lines 57-88 and 17-27) construct entirely new classes from code strings. The session migrates every existing instance to the new class definition while preserving state and maintaining all established links in the `links_registry`.

### Remote Configuration via WebSocket

External clients can trigger modifications through the `WebsocketBus`. As implemented in [`nallely/websocket_bus.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/websocket_bus.py) (lines 188-207), JSON *autoconfig* messages containing method definitions are parsed and forwarded to the MetaTrevorAPI, allowing remote self-modification of the patch.

## Implementing Self-Modifying Patches

### Linking Parameters for Dynamic Signal Flow

Before modifying behavior, establish the signal graph using `associate_parameters`. This creates the conduits through which modified logic will flow.

```python
from nallely.trevor.trevor_api import TrevorAPI

# Connect an LFO's output to a synthesizer's filter cutoff

trevor.associate_parameters(
    from_parameter="lfo1::output_cv",
    to_parameter="synth1::filter::cutoff",
    unbind=False,           # Create the link (True would remove it)

    with_scaler=True        # Automatically map value ranges

)

```

`TrevorAPI.associate_parameters` instantiates `Link` objects registered in both devices' `links_registry` (defined in [`nallely/core/virtual_device.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/core/virtual_device.py), lines 70-78). Once established, these links persist through subsequent code injections.

### Injecting Methods into Running Devices

Use `MetaTrevorAPI.compile_inject` to add functionality to a live device. The new method becomes available immediately on the instance.

```python
from nallely.trevor.meta_trevor_api import MetaTrevorAPI
from nallely.session import Session
import time

session = Session.current()
mt = MetaTrevorAPI(session)
device = session.trevor.get_device_instance("lfo1")

method_code = """
def burst(self, intensity: float = 127):
    original = self.amplitude
    self.amplitude = intensity
    self.start()
    time.sleep(0.1)
    self.amplitude = original
"""

mt.compile_inject(device, "burst", method_code)

# Execute the newly injected method immediately

device.burst(100)

```

The source code is compiled and bound to the class, while the original text is preserved in `device.__source__['burst']` for introspection.

### Replacing Entire Device Classes

For comprehensive behavioral changes, replace the entire class definition. This preserves existing links but changes how the device processes signals.

```python
from nallely.trevor.meta_trevor_api import MetaTrevorAPI
from nallely.session import Session

session = Session.current()
mt = MetaTrevorAPI(session)
synth = session.trevor.get_device_instance("synth1")

new_class_code = """
class Synth1:
    def __init__(self, *, channel=0):
        self.channel = channel
        self.harmonic_mode = False

    def note_on(self, note, velocity):
        if self.harmonic_mode:
            self.send_note(note, velocity)
            self.send_note(note + 4, velocity // 2)
        else:
            self.send_note(note, velocity)

    def toggle_harmonic(self):
        self.harmonic_mode = not self.harmonic_mode
"""

mt.compile_save_new_class(synth, new_class_code, force_name="Synth1", commit=True)

# The instance now uses the new class definition

synth.toggle_harmonic()
synth.note_on(60, 100)  # Plays a C-E chord instead of single note

```

`compile_save_new_class` writes the class to a module file, registers it with the current `Session`, and migrates all live instances to the new definition, preserving their state where possible.

### Triggering Modifications Remotely

Send JSON payloads to the device's `/autoconfig` endpoint to modify behavior from external applications. The `WebsocketBus` handles these messages and invokes the MetaTrevorAPI automatically.

```json
{
  "type": "add_parameters",
  "parameters": {
    "methods": {
      "burst": "def burst(self, intensity=127):\n    original = self.amplitude\n    self.amplitude = intensity\n    self.start()\n    time.sleep(0.1)\n    self.amplitude = original"
    }
  }
}

```

When the `WebsocketBus` receives this payload (as processed in [`nallely/websocket_bus.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/websocket_bus.py)), it calls `MetaTrevorAPI.compile_inject` under the hood, instantly updating the target device.

## Summary

- **Dynamic linking** via `TrevorAPI.associate_parameters` creates persistent signal pathways between devices, storing connections in `links_registry` on each `VirtualDevice`.
- **Method injection** via `MetaTrevorAPI.compile_inject` compiles and binds new functions to running classes, storing source code in `__source__` for reference.
- **Class replacement** via `MetaTrevorAPI.compile_save_new_class` rebuilds device definitions from strings and migrates all live instances while preserving links.
- **Remote modification** via the `WebsocketBus` allows external clients to send Python code through JSON autoconfig messages, triggering the same compilation pipeline.

## Frequently Asked Questions

### What is the introspective API in Nallely?

The introspective API is a meta-programming layer that allows a running Nallely session to examine and modify its own structure. It consists of `TrevorAPI` for graph topology changes and `MetaTrevorAPI` for code-level modifications, enabling devices to rewrite their own Python classes while processing MIDI signals.

### How does `MetaTrevorAPI.compile_inject` work?

`compile_inject` takes a device instance, method name, and source code string, compiles the code into a function object using Python's `compile()` builtin, and assigns it to the device's class. As implemented in [`nallely/trevor/meta_trevor_api.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/trevor/meta_trevor_api.py) (lines 39-56), the method stores the original source in `__source__` and binds the compiled function immediately to all instances of that class.

### Can I modify devices remotely without restarting the session?

Yes. By sending JSON autoconfig messages to the `WebsocketBus` (handled in [`nallely/websocket_bus.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/websocket_bus.py), lines 188-207), you can trigger `compile_inject` or `compile_save_new_class` remotely. The changes take effect immediately without interrupting the audio signal flow or requiring a session restart.

### What happens to existing parameter links when I replace a device class?

Existing links remain intact. The `VirtualDevice` base class maintains a `links_registry` (defined in [`nallely/core/virtual_device.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/core/virtual_device.py), lines 17-18) that maps source and destination paths to `Link` objects. When `compile_save_new_class` migrates instances to the new class definition, the `links_registry` is preserved, ensuring signal flow continues uninterrupted through the modified logic.