How OpenMontage Auto-Discovers Tools in the Tool Registry at Runtime
OpenMontage uses Python's importlib and pkgutil libraries to scan the tools/ package at startup, dynamically importing each module and registering any class inheriting from BaseTool that implements the required name, description, and run() contract.
OpenMontage is an open-source media processing framework designed for extensible video workflows. The heart of its plugin architecture is a tool registry that enables OpenMontage auto-discover tools at runtime without hardcoded imports. When the application initializes, the registry automatically populates itself by inspecting the filesystem and loading compliant tool classes from the tools/ directory.
How the Tool Registry Scans for Modules
The discovery logic resides in [tools/tool_registry.py](https://github.com/calesthio/OpenMontage/blob/main/tools/tool_registry.py). This module orchestrates the entire auto-discovery pipeline through three distinct phases: filesystem enumeration, dynamic importing, and contract validation.
Scanning the tools Package with pkgutil
The registry begins by treating the tools/ directory as a Python package. It utilizes pkgutil.iter_modules (or importlib.util.find_spec in some implementations) to walk the package structure at runtime. This approach enumerates every Python file in the directory, including newly added tools that were not present during the previous execution.
# Conceptual implementation based on tools/tool_registry.py
import pkgutil
import importlib
from tools import base
def scan_tools_package():
modules = []
for importer, modname, ispkg in pkgutil.iter_modules(tools.__path__):
modules.append(modname)
return modules
By walking the filesystem dynamically, the registry ensures that developers can add new tools to the tools/ directory without updating a central manifest or configuration file.
Dynamic Module Import and Error Isolation
For each module discovered during the scan, the registry calls importlib.import_module to load it into the Python interpreter. The implementation includes error handling to prevent a single faulty tool from crashing the entire discovery process. If a module fails to import due to missing dependencies or syntax errors, the registry logs the failure and continues processing the remaining tools.
def import_tool_module(modname):
try:
module = importlib.import_module(f"tools.{modname}")
return module
except ImportError as e:
print(f"Failed to import {modname}: {e}")
return None
This defensive programming approach allows the OpenMontage system to remain stable even when individual tools contain errors or require optional dependencies that are not installed in the current environment.
Validating the BaseTool Contract
After successfully importing a module, the registry inspects its global namespace for classes that inherit from the abstract BaseTool class (typically defined in tools/base.py or imported via tools/__init__.py). A valid tool must implement three essential attributes:
name: A unique string identifier used for registry lookupsdescription: A human-readable summary of the tool's functionalityrun(): A method accepting keyword arguments that executes the tool's primary logic
The registry verifies inheritance and attribute presence before proceeding with registration.
Registering Tools in the Runtime Dictionary
Once a class passes validation, the registry stores it in a central dictionary mapping tool names to their corresponding classes. This registry dictionary serves as the authoritative lookup mechanism for the rest of the application, including the pipeline loader in lib/pipeline_loader.py.
# tools/tool_registry.py
registry = {}
def register_tool(tool_class):
if hasattr(tool_class, 'name') and hasattr(tool_class, 'run'):
registry[tool_class.name] = tool_class
The registry stores class references rather than instances, enabling lazy instantiation and preventing heavy initialization from impacting application startup time.
Example: Creating an Auto-Discovered Tool
To add a new tool to OpenMontage, create a Python file in the tools/ directory and define a class inheriting from BaseTool. The following example demonstrates a simple video processing utility:
# tools/my_custom_tool.py
from .base import BaseTool
class MyCustomTool(BaseTool):
name = "my_custom"
description = "Demo tool that prints a greeting."
def run(self, **kwargs):
print("Hello from MyCustomTool!")
return {"status": "success"}
When OpenMontage restarts, tools/tool_registry.py automatically discovers my_custom_tool.py, imports it, detects the MyCustomTool class, and registers it under the name "my_custom". No modifications to the registry source code are required.
Retrieving and Executing Registered Tools
Other components interact with the registry through helper functions that abstract the underlying dictionary operations.
Looking Up a Specific Tool
The get_tool() function retrieves a tool class by its registered name, allowing the pipeline loader to instantiate and execute tools dynamically:
from tools.tool_registry import get_tool
# Retrieve the tool class by its registered name
ToolClass = get_tool("my_custom") # Returns MyCustomTool class
tool = ToolClass()
result = tool.run(input_data="sample")
Listing Available Capabilities
The list_tools() function returns all registered tool names, useful for generating documentation or user interfaces:
from tools.tool_registry import list_tools
print("Registered tools:")
for name in list_tools():
print(f" - {name}")
Example output:
Registered tools:
- wan_video
- video_trimmer
- my_custom
Optimizing Performance with Lazy Loading
Tools that depend on large machine learning models or external APIs can slow down application startup if initialized immediately. The OpenMontage registry supports lazy loading patterns where the class is registered immediately but heavy resources are instantiated only upon first use.
# tools/heavy_ai_tool.py
from .base import BaseTool
class HeavyAITool(BaseTool):
name = "heavy_ai"
description = "Runs a large AI model."
_model = None
@property
def model(self):
if self._model is None:
from some_heavy_lib import Model
self._model = Model.load() # Expensive operation
return self._model
def run(self, **kwargs):
result = self.model.predict(kwargs["input"])
return result
In this implementation, tools/tool_registry.py registers HeavyAITool during startup, but the Model class remains unloaded until the first call to run().
Summary
The OpenMontage tool registry implements a robust auto-discovery mechanism that eliminates manual plugin registration. Key implementation details include:
- Filesystem Scanning: Uses
pkgutil.iter_modulesto enumerate modules intools/dynamically - Dynamic Importing: Leverages
importlib.import_moduleto load code at runtime with error isolation - Contract Validation: Enforces the
BaseToolinterface requiringname,description, andrun()attributes - Central Dictionary: Stores classes in
registry[name]for O(1) lookup by the pipeline loader - Lazy Loading: Supports deferred initialization to maintain fast startup times for heavy tools
Frequently Asked Questions
Where is the tool registry logic implemented in OpenMontage?
The core discovery and registration logic resides in [tools/tool_registry.py](https://github.com/calesthio/OpenMontage/blob/main/tools/tool_registry.py). This module handles the package scanning, dynamic imports, and dictionary population. The BaseTool abstract class is typically defined in tools/base.py, while lib/pipeline_loader.py consumes the registry to build execution pipelines.
What interface must a class implement to be auto-discovered?
To be automatically registered, a class must inherit from BaseTool and implement three required elements: a name string attribute for registry lookups, a description string for documentation, and a run(**kwargs) method that executes the tool's logic. The registry inspects these attributes using hasattr() checks after importing the module.
How does OpenMontage handle import errors during tool scanning?
The registry wraps each importlib.import_module call in a try-except block that catches ImportError exceptions. When a tool fails to import—due to missing dependencies or syntax errors—the error is logged and the scanner continues with the next module. This ensures that a single broken tool does not prevent the rest of the system from loading.
Can tools be added without restarting the OpenMontage application?
The standard implementation scans for tools once during application startup when tools/tool_registry is first imported. To add tools without restarting, you would need to trigger a manual rescan of pkgutil.iter_modules and re-run the registration logic, though this is not the typical workflow. For production deployments, placing new tool files in the tools/ directory and restarting the application is the recommended approach.
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 →