Phase 0-3 fixes from codebase audit: - Extract shared node logic into prompt_manager_base.py (DRY) - Add input validation guards to validators.py - Fix logging_config.py buffer management - Harden image_monitor.py against race conditions - Fix prompt_tracker.py cleanup edge cases - Fix diagnostics.py import path - Wire config.py server instance correctly
395 lines
17 KiB
Python
395 lines
17 KiB
Python
"""Configuration module for ComfyUI PromptManager.
|
|
|
|
This module provides centralized configuration management for the PromptManager
|
|
extension, including gallery monitoring settings, database configuration,
|
|
web interface options, and performance tuning parameters.
|
|
|
|
The configuration is organized into two main classes:
|
|
- GalleryConfig: Settings for image monitoring and gallery functionality
|
|
- PromptManagerConfig: General settings for the PromptManager core features
|
|
|
|
Configuration can be loaded from and saved to JSON files for persistence.
|
|
|
|
Example:
|
|
from config import PromptManagerConfig
|
|
config = PromptManagerConfig.get_config()
|
|
PromptManagerConfig.load_from_file('custom_config.json')
|
|
"""
|
|
|
|
# PromptManager/py/config.py
|
|
|
|
# Extension configuration
|
|
extension_name = "PromptManager"
|
|
|
|
# Get server instance and routes (same pattern as ComfyUI_Assets)
|
|
from server import PromptServer
|
|
server_instance = PromptServer.instance
|
|
routes = server_instance.routes
|
|
|
|
# Extension info
|
|
extension_uri = None # Will be set in __init__.py
|
|
|
|
import os
|
|
from typing import Dict, Any, List
|
|
|
|
# Import logging system
|
|
try:
|
|
from ..utils.logging_config import get_logger
|
|
except ImportError:
|
|
import sys
|
|
current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
sys.path.insert(0, current_dir)
|
|
from utils.logging_config import get_logger
|
|
|
|
# Initialize logger for config operations
|
|
config_logger = get_logger('prompt_manager.config')
|
|
|
|
|
|
class GalleryConfig:
|
|
"""Configuration class for the gallery monitoring and image processing system.
|
|
|
|
This class manages all settings related to automatic image monitoring,
|
|
prompt tracking, database cleanup, web interface display, and performance
|
|
optimization for the gallery functionality.
|
|
|
|
All configuration values are class attributes that can be modified at runtime
|
|
or loaded from external configuration files.
|
|
|
|
Attributes:
|
|
MONITORING_ENABLED (bool): Enable/disable automatic image monitoring
|
|
MONITORING_DIRECTORIES (List[str]): Directories to monitor for new images
|
|
SUPPORTED_EXTENSIONS (List[str]): Image file extensions to process
|
|
PROCESSING_DELAY (float): Delay in seconds before processing new files
|
|
PROMPT_TIMEOUT (int): Seconds to keep prompt context active
|
|
CLEANUP_INTERVAL (int): Seconds between cleanup of expired prompts
|
|
AUTO_CLEANUP_MISSING_FILES (bool): Automatically remove missing file records
|
|
MAX_IMAGE_AGE_DAYS (int): Maximum age in days before cleaning up images
|
|
IMAGES_PER_PAGE (int): Number of images to display per page in web UI
|
|
THUMBNAIL_SIZE (int): Size in pixels for generated thumbnails
|
|
ENABLE_SEARCH (bool): Enable search functionality in web interface
|
|
ENABLE_METADATA_VIEW (bool): Enable metadata viewing for images
|
|
MAX_CONCURRENT_PROCESSING (int): Maximum concurrent image processing tasks
|
|
METADATA_EXTRACTION_TIMEOUT (int): Timeout for metadata extraction operations
|
|
"""
|
|
|
|
# Image monitoring settings
|
|
MONITORING_ENABLED = True
|
|
MONITORING_DIRECTORIES = [] # Auto-detect if empty
|
|
SUPPORTED_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp', '.gif']
|
|
PROCESSING_DELAY = 2.0 # Seconds to wait before processing new files
|
|
|
|
# Prompt tracking settings
|
|
PROMPT_TIMEOUT = 600 # Seconds to keep prompt context active (10 min for long generations)
|
|
CLEANUP_INTERVAL = 300 # Seconds between cleanup of expired prompts
|
|
|
|
# Database settings
|
|
AUTO_CLEANUP_MISSING_FILES = True
|
|
MAX_IMAGE_AGE_DAYS = 365 # Clean up images older than this
|
|
|
|
# Web interface settings
|
|
IMAGES_PER_PAGE = 20
|
|
THUMBNAIL_SIZE = 256
|
|
ENABLE_SEARCH = True
|
|
ENABLE_METADATA_VIEW = True
|
|
|
|
# Performance settings
|
|
MAX_CONCURRENT_PROCESSING = 3
|
|
METADATA_EXTRACTION_TIMEOUT = 10 # Seconds
|
|
|
|
@classmethod
|
|
def get_config(cls) -> Dict[str, Any]:
|
|
"""Get the complete gallery configuration as a structured dictionary.
|
|
|
|
Returns:
|
|
Dict[str, Any]: A nested dictionary containing all gallery configuration
|
|
sections: monitoring, tracking, database, web_interface, and performance.
|
|
Each section contains the relevant configuration parameters as key-value pairs.
|
|
|
|
Example:
|
|
config = GalleryConfig.get_config()
|
|
monitoring_enabled = config['monitoring']['enabled']
|
|
images_per_page = config['web_interface']['images_per_page']
|
|
"""
|
|
return {
|
|
'monitoring': {
|
|
'enabled': cls.MONITORING_ENABLED,
|
|
'directories': cls.MONITORING_DIRECTORIES,
|
|
'extensions': cls.SUPPORTED_EXTENSIONS,
|
|
'processing_delay': cls.PROCESSING_DELAY
|
|
},
|
|
'tracking': {
|
|
'prompt_timeout': cls.PROMPT_TIMEOUT,
|
|
'cleanup_interval': cls.CLEANUP_INTERVAL
|
|
},
|
|
'database': {
|
|
'auto_cleanup': cls.AUTO_CLEANUP_MISSING_FILES,
|
|
'max_image_age_days': cls.MAX_IMAGE_AGE_DAYS
|
|
},
|
|
'web_interface': {
|
|
'images_per_page': cls.IMAGES_PER_PAGE,
|
|
'thumbnail_size': cls.THUMBNAIL_SIZE,
|
|
'enable_search': cls.ENABLE_SEARCH,
|
|
'enable_metadata_view': cls.ENABLE_METADATA_VIEW
|
|
},
|
|
'performance': {
|
|
'max_concurrent_processing': cls.MAX_CONCURRENT_PROCESSING,
|
|
'metadata_extraction_timeout': cls.METADATA_EXTRACTION_TIMEOUT
|
|
}
|
|
}
|
|
|
|
@classmethod
|
|
def update_config(cls, new_config: Dict[str, Any]):
|
|
"""Update gallery configuration attributes from a dictionary.
|
|
|
|
Takes a nested dictionary with gallery configuration sections and updates
|
|
the corresponding class attributes. Only updates attributes that are
|
|
present in the input dictionary, leaving others unchanged.
|
|
|
|
Args:
|
|
new_config (Dict[str, Any]): Nested dictionary containing gallery
|
|
configuration updates. Should follow the same structure as returned
|
|
by get_config(). Valid top-level keys are: 'monitoring', 'tracking',
|
|
'database', 'web_interface', 'performance'.
|
|
|
|
Example:
|
|
gallery_settings = {
|
|
'monitoring': {'enabled': False},
|
|
'web_interface': {'images_per_page': 50}
|
|
}
|
|
GalleryConfig.update_config(gallery_settings)
|
|
"""
|
|
monitoring = new_config.get('monitoring', {})
|
|
if 'enabled' in monitoring:
|
|
cls.MONITORING_ENABLED = monitoring['enabled']
|
|
if 'directories' in monitoring:
|
|
cls.MONITORING_DIRECTORIES = monitoring['directories']
|
|
if 'extensions' in monitoring:
|
|
cls.SUPPORTED_EXTENSIONS = monitoring['extensions']
|
|
if 'processing_delay' in monitoring:
|
|
cls.PROCESSING_DELAY = monitoring['processing_delay']
|
|
|
|
tracking = new_config.get('tracking', {})
|
|
if 'prompt_timeout' in tracking:
|
|
cls.PROMPT_TIMEOUT = tracking['prompt_timeout']
|
|
if 'cleanup_interval' in tracking:
|
|
cls.CLEANUP_INTERVAL = tracking['cleanup_interval']
|
|
|
|
database = new_config.get('database', {})
|
|
if 'auto_cleanup' in database:
|
|
cls.AUTO_CLEANUP_MISSING_FILES = database['auto_cleanup']
|
|
if 'max_image_age_days' in database:
|
|
cls.MAX_IMAGE_AGE_DAYS = database['max_image_age_days']
|
|
|
|
web_interface = new_config.get('web_interface', {})
|
|
if 'images_per_page' in web_interface:
|
|
cls.IMAGES_PER_PAGE = web_interface['images_per_page']
|
|
if 'thumbnail_size' in web_interface:
|
|
cls.THUMBNAIL_SIZE = web_interface['thumbnail_size']
|
|
if 'enable_search' in web_interface:
|
|
cls.ENABLE_SEARCH = web_interface['enable_search']
|
|
if 'enable_metadata_view' in web_interface:
|
|
cls.ENABLE_METADATA_VIEW = web_interface['enable_metadata_view']
|
|
|
|
performance = new_config.get('performance', {})
|
|
if 'max_concurrent_processing' in performance:
|
|
cls.MAX_CONCURRENT_PROCESSING = performance['max_concurrent_processing']
|
|
if 'metadata_extraction_timeout' in performance:
|
|
cls.METADATA_EXTRACTION_TIMEOUT = performance['metadata_extraction_timeout']
|
|
|
|
|
|
class PromptManagerConfig:
|
|
"""Main configuration class for PromptManager core functionality.
|
|
|
|
This class manages configuration for database operations, web UI behavior,
|
|
performance settings, and integrates gallery configuration. It provides
|
|
methods for loading and saving configuration from/to JSON files.
|
|
|
|
The configuration is organized into logical sections:
|
|
- Database: Settings for SQLite operations and data management
|
|
- Web UI: User interface behavior and display options
|
|
- Performance: Optimization and resource management settings
|
|
- Gallery: Embedded gallery configuration (via GalleryConfig)
|
|
|
|
Attributes:
|
|
DEFAULT_DB_PATH (str): Default path for the SQLite database file
|
|
ENABLE_DUPLICATE_DETECTION (bool): Enable automatic duplicate detection
|
|
ENABLE_AUTO_SAVE (bool): Enable automatic saving of prompts
|
|
RESULT_TIMEOUT (int): Auto-hide timeout for results in ComfyUI node
|
|
SHOW_TEST_BUTTON (bool): Show API test button in node interface
|
|
WEBUI_DISPLAY_MODE (str): Display mode for web UI ('popup' or 'newtab')
|
|
MAX_SEARCH_RESULTS (int): Maximum number of search results to return
|
|
ENABLE_FUZZY_SEARCH (bool): Enable fuzzy search capabilities
|
|
AUTO_BACKUP_INTERVAL (int): Hours between automatic database backups
|
|
"""
|
|
|
|
# Database settings
|
|
DEFAULT_DB_PATH = "prompts.db"
|
|
ENABLE_DUPLICATE_DETECTION = True
|
|
ENABLE_AUTO_SAVE = True
|
|
|
|
# Web UI settings
|
|
RESULT_TIMEOUT = 5 # Seconds to auto-hide results in ComfyUI node
|
|
SHOW_TEST_BUTTON = False # Show API test button in node UI
|
|
WEBUI_DISPLAY_MODE = 'newtab' # 'popup' or 'newtab'
|
|
|
|
# Performance settings
|
|
MAX_SEARCH_RESULTS = 100
|
|
ENABLE_FUZZY_SEARCH = False # Requires fuzzywuzzy
|
|
AUTO_BACKUP_INTERVAL = 24 # Hours
|
|
|
|
@classmethod
|
|
def get_config(cls) -> Dict[str, Any]:
|
|
"""Get the complete PromptManager configuration as a structured dictionary.
|
|
|
|
Returns:
|
|
Dict[str, Any]: A nested dictionary containing all configuration sections:
|
|
- database: Database-related settings
|
|
- web_ui: Web interface configuration
|
|
- performance: Performance and optimization settings
|
|
- gallery: Complete gallery configuration (from GalleryConfig)
|
|
|
|
Example:
|
|
config = PromptManagerConfig.get_config()
|
|
db_path = config['database']['default_path']
|
|
max_results = config['performance']['max_search_results']
|
|
"""
|
|
return {
|
|
'database': {
|
|
'default_path': cls.DEFAULT_DB_PATH,
|
|
'enable_duplicate_detection': cls.ENABLE_DUPLICATE_DETECTION,
|
|
'enable_auto_save': cls.ENABLE_AUTO_SAVE
|
|
},
|
|
'web_ui': {
|
|
'result_timeout': cls.RESULT_TIMEOUT,
|
|
'show_test_button': cls.SHOW_TEST_BUTTON,
|
|
'webui_display_mode': cls.WEBUI_DISPLAY_MODE
|
|
},
|
|
'performance': {
|
|
'max_search_results': cls.MAX_SEARCH_RESULTS,
|
|
'enable_fuzzy_search': cls.ENABLE_FUZZY_SEARCH,
|
|
'auto_backup_interval': cls.AUTO_BACKUP_INTERVAL
|
|
},
|
|
'gallery': GalleryConfig.get_config()
|
|
}
|
|
|
|
@classmethod
|
|
def load_from_file(cls, config_path: str):
|
|
"""Load configuration settings from a JSON file.
|
|
|
|
Reads configuration from the specified JSON file and updates the current
|
|
configuration attributes. If the file doesn't exist or contains invalid
|
|
JSON, logs an appropriate message and continues with default values.
|
|
|
|
Args:
|
|
config_path (str): Path to the JSON configuration file to load.
|
|
Can be relative or absolute path.
|
|
|
|
Raises:
|
|
The method handles all exceptions internally and logs errors rather
|
|
than propagating them, ensuring the system continues with defaults.
|
|
|
|
Example:
|
|
PromptManagerConfig.load_from_file('custom_config.json')
|
|
PromptManagerConfig.load_from_file('/path/to/config.json')
|
|
"""
|
|
import json
|
|
|
|
if os.path.exists(config_path):
|
|
try:
|
|
with open(config_path, 'r') as f:
|
|
config = json.load(f)
|
|
cls.update_config(config)
|
|
config_logger.info(f"Loaded configuration from {config_path}")
|
|
except Exception as e:
|
|
config_logger.error(f"Error loading config from {config_path}: {e}")
|
|
else:
|
|
config_logger.info(f"Config file not found: {config_path}, using defaults")
|
|
|
|
@classmethod
|
|
def save_to_file(cls, config_path: str):
|
|
"""Save the current configuration to a JSON file.
|
|
|
|
Serializes the complete configuration (including gallery settings) to
|
|
a JSON file. Creates the directory structure if it doesn't exist.
|
|
|
|
Args:
|
|
config_path (str): Path where the JSON configuration file should be saved.
|
|
Parent directories will be created if they don't exist.
|
|
|
|
Raises:
|
|
The method handles all exceptions internally and logs errors rather
|
|
than propagating them.
|
|
|
|
Example:
|
|
PromptManagerConfig.save_to_file('backup_config.json')
|
|
PromptManagerConfig.save_to_file('/etc/comfyui/prompt_manager.json')
|
|
"""
|
|
import json
|
|
|
|
try:
|
|
config = cls.get_config()
|
|
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
|
|
with open(config_path, 'w') as f:
|
|
json.dump(config, f, indent=2)
|
|
|
|
config_logger.info(f"Saved configuration to {config_path}")
|
|
except Exception as e:
|
|
config_logger.error(f"Error saving config to {config_path}: {e}")
|
|
|
|
@classmethod
|
|
def update_config(cls, new_config: Dict[str, Any]):
|
|
"""Update configuration attributes from a dictionary.
|
|
|
|
Takes a nested dictionary with configuration sections and updates
|
|
the corresponding class attributes. Only updates attributes that
|
|
are present in the input dictionary, leaving others unchanged.
|
|
|
|
Args:
|
|
new_config (Dict[str, Any]): Nested dictionary containing configuration
|
|
updates. Should follow the same structure as returned by get_config().
|
|
Valid top-level keys are: 'database', 'web_ui', 'performance', 'gallery'.
|
|
|
|
Example:
|
|
new_settings = {
|
|
'database': {'default_path': 'custom.db'},
|
|
'performance': {'max_search_results': 50}
|
|
}
|
|
PromptManagerConfig.update_config(new_settings)
|
|
"""
|
|
database = new_config.get('database', {})
|
|
if 'default_path' in database:
|
|
cls.DEFAULT_DB_PATH = database['default_path']
|
|
if 'enable_duplicate_detection' in database:
|
|
cls.ENABLE_DUPLICATE_DETECTION = database['enable_duplicate_detection']
|
|
if 'enable_auto_save' in database:
|
|
cls.ENABLE_AUTO_SAVE = database['enable_auto_save']
|
|
|
|
web_ui = new_config.get('web_ui', {})
|
|
if 'result_timeout' in web_ui:
|
|
cls.RESULT_TIMEOUT = web_ui['result_timeout']
|
|
if 'show_test_button' in web_ui:
|
|
cls.SHOW_TEST_BUTTON = web_ui['show_test_button']
|
|
if 'webui_display_mode' in web_ui:
|
|
cls.WEBUI_DISPLAY_MODE = web_ui['webui_display_mode']
|
|
|
|
performance = new_config.get('performance', {})
|
|
if 'max_search_results' in performance:
|
|
cls.MAX_SEARCH_RESULTS = performance['max_search_results']
|
|
if 'enable_fuzzy_search' in performance:
|
|
cls.ENABLE_FUZZY_SEARCH = performance['enable_fuzzy_search']
|
|
if 'auto_backup_interval' in performance:
|
|
cls.AUTO_BACKUP_INTERVAL = performance['auto_backup_interval']
|
|
|
|
# Update gallery config
|
|
if 'gallery' in new_config:
|
|
GalleryConfig.update_config(new_config['gallery'])
|
|
|
|
|
|
# Load configuration on import
|
|
try:
|
|
config_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
config_file = os.path.join(config_dir, 'config.json')
|
|
PromptManagerConfig.load_from_file(config_file)
|
|
except Exception as e:
|
|
config_logger.error(f"Error during config initialization: {e}") |