How to Create Self-Modifying Patches Using Nallely's Introspective API
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 (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, 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 (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, 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 (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.
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, 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.
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.
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.
{
"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), it calls MetaTrevorAPI.compile_inject under the hood, instantly updating the target device.
Summary
- Dynamic linking via
TrevorAPI.associate_parameterscreates persistent signal pathways between devices, storing connections inlinks_registryon eachVirtualDevice. - Method injection via
MetaTrevorAPI.compile_injectcompiles and binds new functions to running classes, storing source code in__source__for reference. - Class replacement via
MetaTrevorAPI.compile_save_new_classrebuilds device definitions from strings and migrates all live instances while preserving links. - Remote modification via the
WebsocketBusallows 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 (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, 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, 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →