mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
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:
138
standalone.py
138
standalone.py
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user