232 lines
7.8 KiB
Python
232 lines
7.8 KiB
Python
# =============================================================================
|
|
# Standard Library Imports
|
|
# =============================================================================
|
|
|
|
import logging
|
|
import os
|
|
|
|
"""
|
|
ComfyUI-Prompt-Companion Custom Node Package
|
|
|
|
A comprehensive prompt management system for ComfyUI that provides hierarchical organization,
|
|
template-based subprompts, and advanced prompt manipulation capabilities.
|
|
|
|
This package includes:
|
|
- Core prompt manipulation nodes
|
|
- Checkpoint loading integration
|
|
- Persistent JSON storage for prompt templates
|
|
- Web-based editing interface with tree view
|
|
- Circular reference detection and validation
|
|
|
|
Author: ComfyUI-Prompt-Companion
|
|
License: See LICENSE file
|
|
"""
|
|
|
|
# =============================================================================
|
|
# Package Metadata
|
|
# =============================================================================
|
|
|
|
__version__ = "0.1.0"
|
|
__author__ = "ComfyUI-Prompt-Companion"
|
|
__email__ = "jfcantu@gmail.com"
|
|
__description__ = "A node that lets you save and reuse parts of prompts (embeddings, quality keywords, and so on.)"
|
|
|
|
# =============================================================================
|
|
# Constants & Configuration
|
|
# =============================================================================
|
|
|
|
# Web directory for frontend extensions
|
|
WEB_DIRECTORY = os.path.join(os.path.dirname(__file__), "web")
|
|
|
|
# Required web files for ComfyUI integration
|
|
_REQUIRED_WEB_FILES = [
|
|
"edit_dialog.js",
|
|
"tree_view.js",
|
|
"prompt_companion.css",
|
|
"extensions.js",
|
|
]
|
|
|
|
# NOTE: ComfyUI handles custom data types automatically through node validation
|
|
# No explicit registration is required - SUBPROMPT will be treated as a regular type
|
|
|
|
# =============================================================================
|
|
# Third-Party Imports (ComfyUI)
|
|
# =============================================================================
|
|
|
|
# ComfyUI server integration (imported in initialization section)
|
|
|
|
# =============================================================================
|
|
# Local/Project Imports
|
|
# =============================================================================
|
|
|
|
# Core functionality imports
|
|
try:
|
|
from .core import (
|
|
Subprompt,
|
|
SubpromptCollection,
|
|
ResolvedPrompts,
|
|
SubpromptError,
|
|
CircularReferenceError,
|
|
ValidationError,
|
|
get_global_storage,
|
|
)
|
|
except Exception as e:
|
|
logging.getLogger(__name__).error(f"Failed to import core functionality: {e}")
|
|
raise
|
|
|
|
# Node classes imports
|
|
try:
|
|
from .nodes import (
|
|
PromptCompanionAddSubpromptNode,
|
|
PromptCompanionSubpromptToStringsNode,
|
|
PromptCompanionStringsToSubpromptNode,
|
|
PromptCompanionLoadCheckpointWithSubpromptNode,
|
|
NODE_CLASS_MAPPINGS as NODES_MAPPINGS,
|
|
NODE_DISPLAY_NAME_MAPPINGS as NODES_DISPLAY_MAPPINGS,
|
|
)
|
|
except Exception as e:
|
|
logging.getLogger(__name__).error(f"Failed to import node classes: {e}")
|
|
raise
|
|
|
|
# =============================================================================
|
|
# Module-Level Variables
|
|
# =============================================================================
|
|
|
|
# Set up logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# ComfyUI node registration mappings
|
|
NODE_CLASS_MAPPINGS = {
|
|
"PromptCompanion_AddSubprompt": PromptCompanionAddSubpromptNode,
|
|
"PromptCompanion_SubpromptToStrings": PromptCompanionSubpromptToStringsNode,
|
|
"PromptCompanion_StringsToSubprompt": PromptCompanionStringsToSubpromptNode,
|
|
"PromptCompanion_LoadCheckpointWithSubprompt": PromptCompanionLoadCheckpointWithSubpromptNode,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"PromptCompanion_AddSubprompt": "Prompt Companion: Add Subprompt",
|
|
"PromptCompanion_SubpromptToStrings": "Prompt Companion: Subprompt to Strings",
|
|
"PromptCompanion_StringsToSubprompt": "Prompt Companion: Strings to Subprompt",
|
|
"PromptCompanion_LoadCheckpointWithSubprompt": "Prompt Companion: Load Checkpoint with Subprompt",
|
|
}
|
|
|
|
# Package exports
|
|
__all__ = [
|
|
"NODE_CLASS_MAPPINGS",
|
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
|
"WEB_DIRECTORY",
|
|
"Subprompt",
|
|
"SubpromptCollection",
|
|
"ResolvedPrompts",
|
|
"get_global_storage",
|
|
]
|
|
|
|
# =============================================================================
|
|
# Utility Functions
|
|
# =============================================================================
|
|
|
|
def _ensure_web_files():
|
|
"""Ensure required web files exist and are accessible for ComfyUI integration."""
|
|
missing_files = []
|
|
for file in _REQUIRED_WEB_FILES:
|
|
if not os.path.exists(os.path.join(WEB_DIRECTORY, file)):
|
|
missing_files.append(file)
|
|
|
|
if missing_files:
|
|
logger.warning(f"Missing web files: {missing_files}")
|
|
|
|
|
|
def _print_node_info():
|
|
"""Print information about loaded nodes for debugging purposes."""
|
|
try:
|
|
# Reserved for future debugging functionality
|
|
pass
|
|
except Exception as e:
|
|
logger.error(f"Error loading nodes: {e}")
|
|
|
|
|
|
def _register_api_routes():
|
|
"""Register API routes with ComfyUI's server instance."""
|
|
try:
|
|
from server import PromptServer
|
|
from .api_routes import (
|
|
get_subprompts,
|
|
get_subprompt,
|
|
create_subprompt,
|
|
update_subprompt,
|
|
delete_subprompt,
|
|
get_folders,
|
|
create_folder,
|
|
delete_folder,
|
|
rename_folder_by_path,
|
|
get_subprompt_dropdown_options,
|
|
)
|
|
|
|
# Access ComfyUI's route system (standard pattern used by ComfyUI-Manager)
|
|
routes = PromptServer.instance.routes
|
|
|
|
# Register API routes using ComfyUI's standard decorator pattern
|
|
@routes.get("/prompt_companion/subprompts")
|
|
async def api_get_subprompts(request):
|
|
return await get_subprompts(request)
|
|
|
|
@routes.get("/prompt_companion/subprompts/dropdown_options")
|
|
async def api_get_subprompt_dropdown_options(request):
|
|
return await get_subprompt_dropdown_options(request)
|
|
|
|
@routes.post("/prompt_companion/subprompts")
|
|
async def api_create_subprompt(request):
|
|
return await create_subprompt(request)
|
|
|
|
@routes.get("/prompt_companion/subprompts/{id}")
|
|
async def api_get_subprompt(request):
|
|
return await get_subprompt(request)
|
|
|
|
@routes.put("/prompt_companion/subprompts/{id}")
|
|
async def api_update_subprompt(request):
|
|
return await update_subprompt(request)
|
|
|
|
@routes.delete("/prompt_companion/subprompts/{id}")
|
|
async def api_delete_subprompt(request):
|
|
return await delete_subprompt(request)
|
|
|
|
@routes.get("/prompt_companion/folders")
|
|
async def api_get_folders(request):
|
|
return await get_folders(request)
|
|
|
|
@routes.post("/prompt_companion/folders")
|
|
async def api_create_folder(request):
|
|
return await create_folder(request)
|
|
|
|
@routes.delete("/prompt_companion/folders/{folder_path}")
|
|
async def api_delete_folder(request):
|
|
return await delete_folder(request)
|
|
|
|
@routes.put("/prompt_companion/folders/{folder_path}")
|
|
async def api_rename_folder(request):
|
|
return await rename_folder_by_path(request)
|
|
|
|
except ImportError as e:
|
|
logger.error(f"Failed to import ComfyUI server: {e}")
|
|
except Exception as e:
|
|
logger.error(f"Failed to register API routes: {e}")
|
|
|
|
# =============================================================================
|
|
# Module Initialization
|
|
# =============================================================================
|
|
|
|
# Initialize global storage system
|
|
try:
|
|
_storage = get_global_storage()
|
|
except Exception as e:
|
|
logger.warning(f"Storage system initialization warning: {e}")
|
|
|
|
# Verify web files are available
|
|
_ensure_web_files()
|
|
|
|
# Print node information for debugging
|
|
_print_node_info()
|
|
|
|
# Register API routes with ComfyUI server
|
|
_register_api_routes()
|