mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 14:42:11 -03:00
Update prompt configuration and enhance Lora management functionality
- Expanded the prompt.json file with new configurations for KSampler, CheckpointLoaderSimple, and various CLIPTextEncode nodes. - Introduced additional Lora management features, including a new Lora Stacker and improved trigger word handling. - Enhanced the loras_widget.js to log the generated prompt when saving recipes directly, aiding in debugging and user feedback. - Improved overall structure and organization of the prompt configurations for better maintainability.
This commit is contained in:
163
py/workflow_params/extension_manager.py
Normal file
163
py/workflow_params/extension_manager.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""Module for dynamically loading node processor extensions"""
|
||||
|
||||
import os
|
||||
import importlib
|
||||
import importlib.util
|
||||
import logging
|
||||
import inspect
|
||||
from typing import Dict, Any, List, Set, Type
|
||||
from pathlib import Path
|
||||
|
||||
from .node_processors import NodeProcessor, NODE_PROCESSORS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ExtensionManager:
|
||||
"""Manager for dynamically loading node processor extensions"""
|
||||
|
||||
def __init__(self, extensions_dir: str = None):
|
||||
"""
|
||||
Initialize the extension manager
|
||||
|
||||
Args:
|
||||
extensions_dir: Optional path to a directory containing extensions
|
||||
If None, uses the default extensions directory
|
||||
"""
|
||||
if extensions_dir is None:
|
||||
# Use the default extensions directory
|
||||
module_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
self.extensions_dir = os.path.join(module_dir, "extensions")
|
||||
else:
|
||||
self.extensions_dir = extensions_dir
|
||||
|
||||
self.loaded_extensions: Dict[str, Any] = {}
|
||||
|
||||
def discover_extensions(self) -> List[str]:
|
||||
"""
|
||||
Discover available extensions in the extensions directory
|
||||
|
||||
Returns:
|
||||
List of extension file paths that can be loaded
|
||||
"""
|
||||
if not os.path.exists(self.extensions_dir):
|
||||
logger.warning(f"Extensions directory not found: {self.extensions_dir}")
|
||||
return []
|
||||
|
||||
extension_files = []
|
||||
|
||||
# Walk through the extensions directory
|
||||
for root, _, files in os.walk(self.extensions_dir):
|
||||
for filename in files:
|
||||
# Only consider Python files
|
||||
if filename.endswith('.py') and not filename.startswith('__'):
|
||||
filepath = os.path.join(root, filename)
|
||||
extension_files.append(filepath)
|
||||
|
||||
return extension_files
|
||||
|
||||
def load_extension(self, extension_path: str) -> bool:
|
||||
"""
|
||||
Load a single extension from a file path
|
||||
|
||||
Args:
|
||||
extension_path: Path to the extension file
|
||||
|
||||
Returns:
|
||||
True if loaded successfully, False otherwise
|
||||
"""
|
||||
if extension_path in self.loaded_extensions:
|
||||
logger.debug(f"Extension already loaded: {extension_path}")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Get module name from file path
|
||||
module_name = os.path.basename(extension_path).replace(".py", "")
|
||||
|
||||
# Load the module
|
||||
spec = importlib.util.spec_from_file_location(module_name, extension_path)
|
||||
if spec is None or spec.loader is None:
|
||||
logger.error(f"Failed to load extension spec: {extension_path}")
|
||||
return False
|
||||
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# Find NodeProcessor subclasses in the module
|
||||
processor_classes = []
|
||||
for _, obj in inspect.getmembers(module):
|
||||
if (inspect.isclass(obj) and
|
||||
issubclass(obj, NodeProcessor) and
|
||||
obj is not NodeProcessor):
|
||||
processor_classes.append(obj)
|
||||
|
||||
if not processor_classes:
|
||||
logger.warning(f"No NodeProcessor subclasses found in {extension_path}")
|
||||
return False
|
||||
|
||||
# Register each processor class
|
||||
for cls in processor_classes:
|
||||
cls.register()
|
||||
|
||||
# Store the loaded module
|
||||
self.loaded_extensions[extension_path] = module
|
||||
logger.info(f"Loaded extension: {extension_path} with {len(processor_classes)} processors")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load extension {extension_path}: {e}")
|
||||
return False
|
||||
|
||||
def load_all_extensions(self) -> Dict[str, bool]:
|
||||
"""
|
||||
Load all available extensions
|
||||
|
||||
Returns:
|
||||
Dict mapping extension paths to success/failure status
|
||||
"""
|
||||
extension_files = self.discover_extensions()
|
||||
results = {}
|
||||
|
||||
for extension_path in extension_files:
|
||||
results[extension_path] = self.load_extension(extension_path)
|
||||
|
||||
return results
|
||||
|
||||
def get_loaded_processor_types(self) -> Set[str]:
|
||||
"""
|
||||
Get the set of all loaded processor types
|
||||
|
||||
Returns:
|
||||
Set of class_type names for all loaded processors
|
||||
"""
|
||||
return set(NODE_PROCESSORS.keys())
|
||||
|
||||
def get_loaded_extension_count(self) -> int:
|
||||
"""
|
||||
Get the number of loaded extensions
|
||||
|
||||
Returns:
|
||||
Number of loaded extensions
|
||||
"""
|
||||
return len(self.loaded_extensions)
|
||||
|
||||
|
||||
# Create a singleton instance
|
||||
_extension_manager = None
|
||||
|
||||
def get_extension_manager(extensions_dir: str = None) -> ExtensionManager:
|
||||
"""
|
||||
Get the singleton ExtensionManager instance
|
||||
|
||||
Args:
|
||||
extensions_dir: Optional path to extensions directory
|
||||
|
||||
Returns:
|
||||
ExtensionManager instance
|
||||
"""
|
||||
global _extension_manager
|
||||
|
||||
if _extension_manager is None:
|
||||
_extension_manager = ExtensionManager(extensions_dir)
|
||||
|
||||
return _extension_manager
|
||||
Reference in New Issue
Block a user