"""
Plugin manager for the Solana Agent system.
This module implements the concrete PluginManager that discovers,
loads, and manages plugins.
"""
import importlib
import logging
from typing import Dict, List, Any, Optional
import importlib.metadata
from solana_agent.interfaces.plugins.plugins import (
PluginManager as PluginManagerInterface,
)
from solana_agent.interfaces.plugins.plugins import Plugin
from solana_agent.plugins.registry import ToolRegistry
# Setup logger for this module
logger = logging.getLogger(__name__)
[docs]
class PluginManager(PluginManagerInterface):
"""Manager for discovering and loading plugins."""
# Class variable to track loaded entry points
_loaded_entry_points = set()
def __init__(
self,
config: Optional[Dict[str, Any]] = None,
tool_registry: Optional[ToolRegistry] = None,
):
"""Initialize with optional configuration and tool registry."""
self.config = config or {}
self.tool_registry = tool_registry or ToolRegistry()
self._plugins = {} # Changed to instance variable
[docs]
def register_plugin(self, plugin: Plugin) -> bool:
"""Register a plugin in the manager.
Args:
plugin: The plugin to register
Returns:
True if registration succeeded, False otherwise
"""
try:
# Initialize the plugin with the tool registry first
plugin.initialize(self.tool_registry)
# Then configure the plugin
plugin.configure(self.config)
# Only store plugin if both initialize and configure succeed
self._plugins[plugin.name] = plugin
logger.info(
f"Successfully registered plugin {plugin.name}"
) # Use logger.info
return True
except Exception as e:
logger.error(
f"Error registering plugin {plugin.name}: {e}"
) # Use logger.error
# Remove plugin from registry if it was added
self._plugins.pop(plugin.name, None)
return False
[docs]
def load_plugins(self) -> List[str]:
"""Load all plugins using entry points and apply configuration.
Returns:
List of loaded plugin names
"""
loaded_plugins = []
# Discover plugins through entry points
for entry_point in importlib.metadata.entry_points(
group="solana_agent.plugins"
):
# Skip if this entry point has already been loaded
entry_point_id = f"{entry_point.name}:{entry_point.value}"
if entry_point_id in PluginManager._loaded_entry_points:
logger.info(
f"Skipping already loaded plugin: {entry_point.name}"
) # Use logger.info
continue
try:
logger.info(
f"Found plugin entry point: {entry_point.name}"
) # Use logger.info
PluginManager._loaded_entry_points.add(entry_point_id)
plugin_factory = entry_point.load()
plugin = plugin_factory()
# Register the plugin
if self.register_plugin(plugin):
# Use entry_point.name instead of plugin.name
loaded_plugins.append(entry_point.name)
except Exception as e:
logger.error(
f"Error loading plugin {entry_point.name}: {e}"
) # Use logger.error
return loaded_plugins
[docs]
def get_plugin(self, name: str) -> Optional[Plugin]:
"""Get a plugin by name.
Args:
name: Name of the plugin to retrieve
Returns:
Plugin instance or None if not found
"""
return self._plugins.get(name)
[docs]
def list_plugins(self) -> List[Dict[str, Any]]:
"""List all registered plugins with their details.
Returns:
List of plugin details dictionaries
"""
return [
{"name": plugin.name, "description": plugin.description}
for plugin in self._plugins.values()
]