feat(standalone): add --verbose flag for DEBUG logging

Add --verbose command line argument that enables DEBUG level logging, equivalent to --log-level DEBUG
This commit is contained in:
Will Miao
2026-01-21 09:35:28 +08:00
parent 6f74186498
commit 7b4607bed7

View File

@@ -7,26 +7,31 @@ from py.utils.settings_paths import ensure_settings_file
# Set environment variable to indicate standalone mode # Set environment variable to indicate standalone mode
os.environ["LORA_MANAGER_STANDALONE"] = "1" os.environ["LORA_MANAGER_STANDALONE"] = "1"
# Create mock modules for py/nodes directory - add this before any other imports # Create mock modules for py/nodes directory - add this before any other imports
def mock_nodes_directory(): def mock_nodes_directory():
"""Create mock modules for all Python files in the py/nodes directory""" """Create mock modules for all Python files in the py/nodes directory"""
nodes_dir = os.path.join(os.path.dirname(__file__), 'py', 'nodes') nodes_dir = os.path.join(os.path.dirname(__file__), "py", "nodes")
if os.path.exists(nodes_dir): if os.path.exists(nodes_dir):
# Create a mock module for the nodes package itself # Create a mock module for the nodes package itself
sys.modules['py.nodes'] = type('MockNodesModule', (), {}) sys.modules["py.nodes"] = type("MockNodesModule", (), {})
# Create mock modules for all Python files in the nodes directory # Create mock modules for all Python files in the nodes directory
for file in os.listdir(nodes_dir): for file in os.listdir(nodes_dir):
if file.endswith('.py') and file != '__init__.py': if file.endswith(".py") and file != "__init__.py":
module_name = file[:-3] # Remove .py extension module_name = file[:-3] # Remove .py extension
full_module_name = f'py.nodes.{module_name}' full_module_name = f"py.nodes.{module_name}"
# Create empty module object # Create empty module object
sys.modules[full_module_name] = type(f'Mock{module_name.capitalize()}Module', (), {}) sys.modules[full_module_name] = type(
f"Mock{module_name.capitalize()}Module", (), {}
)
print(f"Created mock module for: {full_module_name}") print(f"Created mock module for: {full_module_name}")
# Run the mocking function before any other imports # Run the mocking function before any other imports
mock_nodes_directory() mock_nodes_directory()
# Create mock folder_paths module BEFORE any other imports # Create mock folder_paths module BEFORE any other imports
class MockFolderPaths: class MockFolderPaths:
@staticmethod @staticmethod
@@ -35,17 +40,17 @@ class MockFolderPaths:
settings_path = ensure_settings_file() settings_path = ensure_settings_file()
try: try:
if os.path.exists(settings_path): if os.path.exists(settings_path):
with open(settings_path, 'r', encoding='utf-8') as f: with open(settings_path, "r", encoding="utf-8") as f:
settings = json.load(f) settings = json.load(f)
# For diffusion_models, combine unet and diffusers paths # For diffusion_models, combine unet and diffusers paths
if folder_name == "diffusion_models": if folder_name == "diffusion_models":
paths = [] paths = []
if 'folder_paths' in settings: if "folder_paths" in settings:
if 'unet' in settings['folder_paths']: if "unet" in settings["folder_paths"]:
paths.extend(settings['folder_paths']['unet']) paths.extend(settings["folder_paths"]["unet"])
if 'diffusers' in settings['folder_paths']: if "diffusers" in settings["folder_paths"]:
paths.extend(settings['folder_paths']['diffusers']) paths.extend(settings["folder_paths"]["diffusers"])
# Filter out paths that don't exist # Filter out paths that don't exist
valid_paths = [p for p in paths if os.path.exists(p)] valid_paths = [p for p in paths if os.path.exists(p)]
if valid_paths: if valid_paths:
@@ -53,8 +58,11 @@ class MockFolderPaths:
else: else:
print(f"Warning: No valid paths found for {folder_name}") print(f"Warning: No valid paths found for {folder_name}")
# For other folder names, return their paths directly # For other folder names, return their paths directly
elif 'folder_paths' in settings and folder_name in settings['folder_paths']: elif (
paths = settings['folder_paths'][folder_name] "folder_paths" in settings
and folder_name in settings["folder_paths"]
):
paths = settings["folder_paths"][folder_name]
valid_paths = [p for p in paths if os.path.exists(p)] valid_paths = [p for p in paths if os.path.exists(p)]
if valid_paths: if valid_paths:
return valid_paths return valid_paths
@@ -68,13 +76,14 @@ class MockFolderPaths:
@staticmethod @staticmethod
def get_temp_directory(): def get_temp_directory():
return os.path.join(os.path.dirname(__file__), 'temp') return os.path.join(os.path.dirname(__file__), "temp")
@staticmethod @staticmethod
def set_temp_directory(path): def set_temp_directory(path):
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
return path return path
# Create mock server module with PromptServer # Create mock server module with PromptServer
class MockPromptServer: class MockPromptServer:
def __init__(self): def __init__(self):
@@ -83,6 +92,7 @@ class MockPromptServer:
def send_sync(self, *args, **kwargs): def send_sync(self, *args, **kwargs):
pass pass
# Create mock metadata_collector module # Create mock metadata_collector module
class MockMetadataCollector: class MockMetadataCollector:
def init(self): def init(self):
@@ -91,10 +101,11 @@ class MockMetadataCollector:
def get_metadata(self, prompt_id=None): def get_metadata(self, prompt_id=None):
return {} return {}
# Initialize basic mocks before any imports # Initialize basic mocks before any imports
sys.modules['folder_paths'] = MockFolderPaths() sys.modules["folder_paths"] = MockFolderPaths()
sys.modules['server'] = type('server', (), {'PromptServer': MockPromptServer()}) sys.modules["server"] = type("server", (), {"PromptServer": MockPromptServer()})
sys.modules['py.metadata_collector'] = MockMetadataCollector() sys.modules["py.metadata_collector"] = MockMetadataCollector()
# Now we can safely import modules that depend on folder_paths and server # Now we can safely import modules that depend on folder_paths and server
import argparse import argparse
@@ -106,12 +117,14 @@ from aiohttp import web
HEADER_SIZE_LIMIT = 16384 HEADER_SIZE_LIMIT = 16384
# Setup logging # Setup logging
logging.basicConfig(level=logging.INFO, logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger("lora-manager-standalone") logger = logging.getLogger("lora-manager-standalone")
# Configure aiohttp access logger to be less verbose # Configure aiohttp access logger to be less verbose
logging.getLogger('aiohttp.access').setLevel(logging.WARNING) logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
# Add specific suppression for connection reset errors # Add specific suppression for connection reset errors
class ConnectionResetFilter(logging.Filter): class ConnectionResetFilter(logging.Filter):
@@ -125,6 +138,7 @@ class ConnectionResetFilter(logging.Filter):
return False return False
return True return True
# Apply the filter to asyncio logger # Apply the filter to asyncio logger
asyncio_logger = logging.getLogger("asyncio") asyncio_logger = logging.getLogger("asyncio")
asyncio_logger.addFilter(ConnectionResetFilter()) asyncio_logger.addFilter(ConnectionResetFilter())
@@ -132,6 +146,7 @@ asyncio_logger.addFilter(ConnectionResetFilter())
# Now we can import the global config from our local modules # Now we can import the global config from our local modules
from py.config import config from py.config import config
class StandaloneServer: class StandaloneServer:
"""Server implementation for standalone mode""" """Server implementation for standalone mode"""
@@ -166,23 +181,27 @@ class StandaloneServer:
def setup_routes(self): def setup_routes(self):
"""Set up basic routes""" """Set up basic routes"""
# Add a simple status endpoint # Add a simple status endpoint
self.app.router.add_get('/', self.handle_status) self.app.router.add_get("/", self.handle_status)
# Add static route for example images if the path exists in settings # Add static route for example images if the path exists in settings
settings_path = ensure_settings_file(logger) settings_path = ensure_settings_file(logger)
if os.path.exists(settings_path): if os.path.exists(settings_path):
with open(settings_path, 'r', encoding='utf-8') as f: with open(settings_path, "r", encoding="utf-8") as f:
settings = json.load(f) settings = json.load(f)
example_images_path = settings.get('example_images_path') example_images_path = settings.get("example_images_path")
logger.info(f"Example images path: {example_images_path}") logger.info(f"Example images path: {example_images_path}")
if example_images_path and os.path.exists(example_images_path): if example_images_path and os.path.exists(example_images_path):
self.app.router.add_static('/example_images_static', example_images_path) self.app.router.add_static(
logger.info(f"Added static route for example images: /example_images_static -> {example_images_path}") "/example_images_static", example_images_path
)
logger.info(
f"Added static route for example images: /example_images_static -> {example_images_path}"
)
async def handle_status(self, request): async def handle_status(self, request):
"""Handle status request by redirecting to loras page""" """Handle status request by redirecting to loras page"""
# Redirect to loras page instead of showing status # Redirect to loras page instead of showing status
raise web.HTTPFound('/loras') raise web.HTTPFound("/loras")
# Original JSON response (commented out) # Original JSON response (commented out)
# return web.json_response({ # return web.json_response({
@@ -205,7 +224,7 @@ class StandaloneServer:
# In standalone mode, we don't have the same websocket system # In standalone mode, we don't have the same websocket system
pass pass
async def start(self, host='127.0.0.1', port=8188): async def start(self, host="127.0.0.1", port=8188):
"""Start the server""" """Start the server"""
runner = web.AppRunner(self.app) runner = web.AppRunner(self.app)
await runner.setup() await runner.setup()
@@ -224,9 +243,11 @@ class StandaloneServer:
# This method exists in ComfyUI's server but we don't need it # This method exists in ComfyUI's server but we don't need it
pass pass
# After all mocks are in place, import LoraManager # After all mocks are in place, import LoraManager
from py.lora_manager import LoraManager from py.lora_manager import LoraManager
def validate_settings(): def validate_settings():
"""Initialize settings and log any startup warnings.""" """Initialize settings and log any startup warnings."""
try: try:
@@ -267,6 +288,7 @@ def validate_settings():
return True return True
class StandaloneLoraManager(LoraManager): class StandaloneLoraManager(LoraManager):
"""Extended LoraManager for standalone mode""" """Extended LoraManager for standalone mode"""
@@ -276,19 +298,23 @@ class StandaloneLoraManager(LoraManager):
app = server_instance.app app = server_instance.app
# Store app in a global-like location for compatibility # Store app in a global-like location for compatibility
sys.modules['server'].PromptServer.instance = server_instance sys.modules["server"].PromptServer.instance = server_instance
# Add static route for locales JSON files # Add static route for locales JSON files
if os.path.exists(config.i18n_path): if os.path.exists(config.i18n_path):
app.router.add_static('/locales', config.i18n_path) app.router.add_static("/locales", config.i18n_path)
logger.info(f"Added static route for locales: /locales -> {config.i18n_path}") logger.info(
f"Added static route for locales: /locales -> {config.i18n_path}"
)
# Add static route for plugin assets # Add static route for plugin assets
app.router.add_static('/loras_static', config.static_path) app.router.add_static("/loras_static", config.static_path)
# Setup feature routes # Setup feature routes
from py.services.model_service_factory import ModelServiceFactory, register_default_model_types from py.services.model_service_factory import (
ModelServiceFactory,
register_default_model_types,
)
from py.routes.recipe_routes import RecipeRoutes from py.routes.recipe_routes import RecipeRoutes
from py.routes.update_routes import UpdateRoutes from py.routes.update_routes import UpdateRoutes
from py.routes.misc_routes import MiscRoutes from py.routes.misc_routes import MiscRoutes
@@ -297,7 +323,6 @@ class StandaloneLoraManager(LoraManager):
from py.routes.stats_routes import StatsRoutes from py.routes.stats_routes import StatsRoutes
from py.services.websocket_manager import ws_manager from py.services.websocket_manager import ws_manager
register_default_model_types() register_default_model_types()
# Setup all model routes using the factory # Setup all model routes using the factory
@@ -314,9 +339,11 @@ class StandaloneLoraManager(LoraManager):
PreviewRoutes.setup_routes(app) PreviewRoutes.setup_routes(app)
# Setup WebSocket routes that are shared across all model types # Setup WebSocket routes that are shared across all model types
app.router.add_get('/ws/fetch-progress', ws_manager.handle_connection) app.router.add_get("/ws/fetch-progress", ws_manager.handle_connection)
app.router.add_get('/ws/download-progress', ws_manager.handle_download_connection) app.router.add_get(
app.router.add_get('/ws/init-progress', ws_manager.handle_init_connection) "/ws/download-progress", ws_manager.handle_download_connection
)
app.router.add_get("/ws/init-progress", ws_manager.handle_init_connection)
# Schedule service initialization # Schedule service initialization
app.on_startup.append(lambda app: cls._initialize_services()) app.on_startup.append(lambda app: cls._initialize_services())
@@ -324,28 +351,48 @@ class StandaloneLoraManager(LoraManager):
# Add cleanup # Add cleanup
app.on_shutdown.append(cls._cleanup) app.on_shutdown.append(cls._cleanup)
def parse_args(): def parse_args():
"""Parse command line arguments""" """Parse command line arguments"""
parser = argparse.ArgumentParser(description="LoRA Manager Standalone Server") parser = argparse.ArgumentParser(description="LoRA Manager Standalone Server")
parser.add_argument("--host", type=str, default="0.0.0.0", parser.add_argument(
help="Host address to bind the server to (default: 0.0.0.0)") "--host",
parser.add_argument("--port", type=int, default=8188, type=str,
help="Port to bind the server to (default: 8188, access via http://localhost:8188/loras)") default="0.0.0.0",
help="Host address to bind the server to (default: 0.0.0.0)",
)
parser.add_argument(
"--port",
type=int,
default=8188,
help="Port to bind the server to (default: 8188, access via http://localhost:8188/loras)",
)
# parser.add_argument("--loras", type=str, nargs="+", # parser.add_argument("--loras", type=str, nargs="+",
# help="Additional paths to LoRA model directories (optional if settings.json has paths)") # help="Additional paths to LoRA model directories (optional if settings.json has paths)")
# parser.add_argument("--checkpoints", type=str, nargs="+", # parser.add_argument("--checkpoints", type=str, nargs="+",
# help="Additional paths to checkpoint model directories (optional if settings.json has paths)") # help="Additional paths to checkpoint model directories (optional if settings.json has paths)")
parser.add_argument("--log-level", type=str, default="INFO", parser.add_argument(
"--log-level",
type=str,
default="INFO",
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
help="Logging level") help="Logging level",
)
parser.add_argument(
"--verbose",
action="store_true",
help="Enable verbose logging (equivalent to --log-level DEBUG)",
)
return parser.parse_args() return parser.parse_args()
async def main(): async def main():
"""Main entry point for standalone mode""" """Main entry point for standalone mode"""
args = parse_args() args = parse_args()
# Set log level # Set log level (verbose flag overrides to DEBUG)
logging.getLogger().setLevel(getattr(logging, args.log_level)) log_level = "DEBUG" if args.verbose else args.log_level
logging.getLogger().setLevel(getattr(logging, log_level))
# Validate settings before proceeding # Validate settings before proceeding
if not validate_settings(): if not validate_settings():
@@ -363,6 +410,7 @@ async def main():
await server.setup() await server.setup()
await server.start(host=args.host, port=args.port) await server.start(host=args.host, port=args.port)
if __name__ == "__main__": if __name__ == "__main__":
try: try:
# Run the main function # Run the main function