mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-08-02 23:14:49 +08:00
Compare commits
4 Commits
v0.3.39
...
required_f
Author | SHA1 | Date | |
---|---|---|---|
|
74a17e9460 | ||
|
4a4c546276 | ||
|
92de68aabd | ||
|
e73c78e292 |
@@ -31,12 +31,12 @@ def frontend_install_warning_message():
|
|||||||
return f"Please install the updated requirements.txt file by running:\n{sys.executable} {extra}-m pip install -r {req_path}\n\nThis error is happening because the ComfyUI frontend is no longer shipped as part of the main repo but as a pip package instead.\n\nIf you are on the portable package you can run: update\\update_comfyui.bat to solve this problem"
|
return f"Please install the updated requirements.txt file by running:\n{sys.executable} {extra}-m pip install -r {req_path}\n\nThis error is happening because the ComfyUI frontend is no longer shipped as part of the main repo but as a pip package instead.\n\nIf you are on the portable package you can run: update\\update_comfyui.bat to solve this problem"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version(version: str) -> tuple[int, int, int]:
|
||||||
|
return tuple(map(int, version.split(".")))
|
||||||
|
|
||||||
|
|
||||||
def check_frontend_version():
|
def check_frontend_version():
|
||||||
"""Check if the frontend version is up to date."""
|
"""Check if the frontend version is up to date."""
|
||||||
|
|
||||||
def parse_version(version: str) -> tuple[int, int, int]:
|
|
||||||
return tuple(map(int, version.split(".")))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
frontend_version_str = version("comfyui-frontend-package")
|
frontend_version_str = version("comfyui-frontend-package")
|
||||||
frontend_version = parse_version(frontend_version_str)
|
frontend_version = parse_version(frontend_version_str)
|
||||||
@@ -73,6 +73,11 @@ class FrontEndProvider:
|
|||||||
owner: str
|
owner: str
|
||||||
repo: str
|
repo: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_official(self) -> bool:
|
||||||
|
"""Check if the provider is the default official one."""
|
||||||
|
return self.owner == "Comfy-Org" and self.repo == "ComfyUI_frontend"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def folder_name(self) -> str:
|
def folder_name(self) -> str:
|
||||||
return f"{self.owner}_{self.repo}"
|
return f"{self.owner}_{self.repo}"
|
||||||
@@ -143,14 +148,24 @@ def download_release_asset_zip(release: Release, destination_path: str) -> None:
|
|||||||
zip_ref.extractall(destination_path)
|
zip_ref.extractall(destination_path)
|
||||||
|
|
||||||
|
|
||||||
|
class FrontendInit(TypedDict):
|
||||||
|
web_root: str
|
||||||
|
""" The path to the initialized frontend. """
|
||||||
|
version: tuple[int, int, int] | None
|
||||||
|
""" The version of the initialized frontend. None for unrecognized version."""
|
||||||
|
|
||||||
class FrontendManager:
|
class FrontendManager:
|
||||||
CUSTOM_FRONTENDS_ROOT = str(Path(__file__).parents[1] / "web_custom_versions")
|
CUSTOM_FRONTENDS_ROOT = str(Path(__file__).parents[1] / "web_custom_versions")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_frontend_path(cls) -> str:
|
def init_default_frontend(cls) -> FrontendInit:
|
||||||
|
check_frontend_version()
|
||||||
try:
|
try:
|
||||||
import comfyui_frontend_package
|
import comfyui_frontend_package
|
||||||
return str(importlib.resources.files(comfyui_frontend_package) / "static")
|
return FrontendInit(
|
||||||
|
web_root=str(importlib.resources.files(comfyui_frontend_package) / "static"),
|
||||||
|
version=parse_version(version("comfyui-frontend-package")),
|
||||||
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logging.error(f"\n\n********** ERROR ***********\n\ncomfyui-frontend-package is not installed. {frontend_install_warning_message()}\n********** ERROR **********\n")
|
logging.error(f"\n\n********** ERROR ***********\n\ncomfyui-frontend-package is not installed. {frontend_install_warning_message()}\n********** ERROR **********\n")
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
@@ -175,7 +190,7 @@ class FrontendManager:
|
|||||||
return match_result.group(1), match_result.group(2), match_result.group(3)
|
return match_result.group(1), match_result.group(2), match_result.group(3)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def init_frontend_unsafe(cls, version_string: str, provider: Optional[FrontEndProvider] = None) -> str:
|
def init_frontend_unsafe(cls, version_string: str, provider: Optional[FrontEndProvider] = None) -> FrontendInit:
|
||||||
"""
|
"""
|
||||||
Initializes the frontend for the specified version.
|
Initializes the frontend for the specified version.
|
||||||
|
|
||||||
@@ -191,8 +206,7 @@ class FrontendManager:
|
|||||||
main error source might be request timeout or invalid URL.
|
main error source might be request timeout or invalid URL.
|
||||||
"""
|
"""
|
||||||
if version_string == DEFAULT_VERSION_STRING:
|
if version_string == DEFAULT_VERSION_STRING:
|
||||||
check_frontend_version()
|
return cls.init_default_frontend()
|
||||||
return cls.default_frontend_path()
|
|
||||||
|
|
||||||
repo_owner, repo_name, version = cls.parse_version_string(version_string)
|
repo_owner, repo_name, version = cls.parse_version_string(version_string)
|
||||||
|
|
||||||
@@ -227,10 +241,13 @@ class FrontendManager:
|
|||||||
if not os.listdir(web_root):
|
if not os.listdir(web_root):
|
||||||
os.rmdir(web_root)
|
os.rmdir(web_root)
|
||||||
|
|
||||||
return web_root
|
return FrontendInit(
|
||||||
|
web_root=web_root,
|
||||||
|
version=parse_version(semantic_version) if provider.is_official else None,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def init_frontend(cls, version_string: str) -> str:
|
def init_frontend(cls, version_string: str) -> FrontendInit:
|
||||||
"""
|
"""
|
||||||
Initializes the frontend with the specified version string.
|
Initializes the frontend with the specified version string.
|
||||||
|
|
||||||
@@ -245,5 +262,4 @@ class FrontendManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Failed to initialize frontend: %s", e)
|
logging.error("Failed to initialize frontend: %s", e)
|
||||||
logging.info("Falling back to the default frontend.")
|
logging.info("Falling back to the default frontend.")
|
||||||
check_frontend_version()
|
return cls.init_default_frontend()
|
||||||
return cls.default_frontend_path()
|
|
||||||
|
@@ -220,6 +220,13 @@ class ComfyNodeABC(ABC):
|
|||||||
"""Flags a node as experimental, informing users that it may change or not work as expected."""
|
"""Flags a node as experimental, informing users that it may change or not work as expected."""
|
||||||
DEPRECATED: bool
|
DEPRECATED: bool
|
||||||
"""Flags a node as deprecated, indicating to users that they should find alternatives to this node."""
|
"""Flags a node as deprecated, indicating to users that they should find alternatives to this node."""
|
||||||
|
REQUIRED_FRONTEND_VERSION: str | None
|
||||||
|
"""The minimum version of the ComfyUI frontend required to load this node.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
REQUIRED_FRONTEND_VERSION = "1.9.7"
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
47
server.py
47
server.py
@@ -1,3 +1,4 @@
|
|||||||
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -24,11 +25,12 @@ import logging
|
|||||||
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
|
from comfy.comfy_types.node_typing import ComfyNodeABC
|
||||||
import comfy.utils
|
import comfy.utils
|
||||||
import comfy.model_management
|
import comfy.model_management
|
||||||
import node_helpers
|
import node_helpers
|
||||||
from comfyui_version import __version__
|
from comfyui_version import __version__
|
||||||
from app.frontend_management import FrontendManager
|
from app.frontend_management import FrontendInit, FrontendManager, parse_version
|
||||||
from app.user_manager import UserManager
|
from app.user_manager import UserManager
|
||||||
from app.model_manager import ModelFileManager
|
from app.model_manager import ModelFileManager
|
||||||
from app.custom_node_manager import CustomNodeManager
|
from app.custom_node_manager import CustomNodeManager
|
||||||
@@ -146,6 +148,11 @@ def create_origin_only_middleware():
|
|||||||
return origin_only_middleware
|
return origin_only_middleware
|
||||||
|
|
||||||
class PromptServer():
|
class PromptServer():
|
||||||
|
web_root: str
|
||||||
|
"""The path to the initialized frontend assets."""
|
||||||
|
frontend_version: tuple[int, int, int] | None = None
|
||||||
|
"""The version of the initialized frontend. None for unrecognized version."""
|
||||||
|
|
||||||
def __init__(self, loop):
|
def __init__(self, loop):
|
||||||
PromptServer.instance = self
|
PromptServer.instance = self
|
||||||
|
|
||||||
@@ -176,12 +183,19 @@ class PromptServer():
|
|||||||
max_upload_size = round(args.max_upload_size * 1024 * 1024)
|
max_upload_size = round(args.max_upload_size * 1024 * 1024)
|
||||||
self.app = web.Application(client_max_size=max_upload_size, middlewares=middlewares)
|
self.app = web.Application(client_max_size=max_upload_size, middlewares=middlewares)
|
||||||
self.sockets = dict()
|
self.sockets = dict()
|
||||||
self.web_root = (
|
|
||||||
FrontendManager.init_frontend(args.front_end_version)
|
if args.front_end_root:
|
||||||
if args.front_end_root is None
|
frontend_init = FrontendInit(
|
||||||
else args.front_end_root
|
web_root=args.front_end_root,
|
||||||
)
|
version=None,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
frontend_init = FrontendManager.init_frontend(args.front_end_version)
|
||||||
|
|
||||||
|
self.frontend_version = frontend_init["version"]
|
||||||
|
self.web_root = frontend_init["web_root"]
|
||||||
logging.info(f"[Prompt Server] web root: {self.web_root}")
|
logging.info(f"[Prompt Server] web root: {self.web_root}")
|
||||||
|
|
||||||
routes = web.RouteTableDef()
|
routes = web.RouteTableDef()
|
||||||
self.routes = routes
|
self.routes = routes
|
||||||
self.last_node_id = None
|
self.last_node_id = None
|
||||||
@@ -587,6 +601,9 @@ class PromptServer():
|
|||||||
with folder_paths.cache_helper:
|
with folder_paths.cache_helper:
|
||||||
out = {}
|
out = {}
|
||||||
for x in nodes.NODE_CLASS_MAPPINGS:
|
for x in nodes.NODE_CLASS_MAPPINGS:
|
||||||
|
if not self.node_is_supported(x):
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
out[x] = node_info(x)
|
out[x] = node_info(x)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -598,7 +615,11 @@ class PromptServer():
|
|||||||
async def get_object_info_node(request):
|
async def get_object_info_node(request):
|
||||||
node_class = request.match_info.get("node_class", None)
|
node_class = request.match_info.get("node_class", None)
|
||||||
out = {}
|
out = {}
|
||||||
if (node_class is not None) and (node_class in nodes.NODE_CLASS_MAPPINGS):
|
if (
|
||||||
|
node_class is not None
|
||||||
|
and node_class in nodes.NODE_CLASS_MAPPINGS
|
||||||
|
and self.node_is_supported(node_class)
|
||||||
|
):
|
||||||
out[node_class] = node_info(node_class)
|
out[node_class] = node_info(node_class)
|
||||||
return web.json_response(out)
|
return web.json_response(out)
|
||||||
|
|
||||||
@@ -863,3 +884,15 @@ class PromptServer():
|
|||||||
logging.warning(traceback.format_exc())
|
logging.warning(traceback.format_exc())
|
||||||
|
|
||||||
return json_data
|
return json_data
|
||||||
|
|
||||||
|
def node_is_supported(self, node_class: ComfyNodeABC) -> bool:
|
||||||
|
"""Check if the node is supported by the frontend."""
|
||||||
|
# For unrecognized frontend version, we assume the node is supported.
|
||||||
|
if self.frontend_version is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check if the node is supported by the frontend.
|
||||||
|
if node_class.REQUIRED_FRONTEND_VERSION is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return parse_version(node_class.REQUIRED_FRONTEND_VERSION) <= self.frontend_version
|
||||||
|
@@ -69,8 +69,10 @@ def test_get_release_invalid_version(mock_provider):
|
|||||||
|
|
||||||
def test_init_frontend_default():
|
def test_init_frontend_default():
|
||||||
version_string = DEFAULT_VERSION_STRING
|
version_string = DEFAULT_VERSION_STRING
|
||||||
frontend_path = FrontendManager.init_frontend(version_string)
|
frontend_init = FrontendManager.init_frontend(version_string)
|
||||||
assert frontend_path == FrontendManager.default_frontend_path()
|
assert isinstance(frontend_init, dict)
|
||||||
|
assert "web_root" in frontend_init
|
||||||
|
assert "version" in frontend_init
|
||||||
|
|
||||||
|
|
||||||
def test_init_frontend_invalid_version():
|
def test_init_frontend_invalid_version():
|
||||||
@@ -138,37 +140,47 @@ def test_parse_version_string_invalid():
|
|||||||
def test_init_frontend_default_with_mocks():
|
def test_init_frontend_default_with_mocks():
|
||||||
# Arrange
|
# Arrange
|
||||||
version_string = DEFAULT_VERSION_STRING
|
version_string = DEFAULT_VERSION_STRING
|
||||||
|
mock_path = "/mocked/path"
|
||||||
|
mock_version = (1, 0, 0)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
with (
|
with (
|
||||||
patch("app.frontend_management.check_frontend_version") as mock_check,
|
patch("app.frontend_management.check_frontend_version") as mock_check,
|
||||||
patch.object(
|
patch.object(
|
||||||
FrontendManager, "default_frontend_path", return_value="/mocked/path"
|
FrontendManager,
|
||||||
|
"init_default_frontend",
|
||||||
|
return_value={"web_root": mock_path, "version": mock_version},
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
frontend_path = FrontendManager.init_frontend(version_string)
|
frontend_init = FrontendManager.init_frontend(version_string)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert frontend_path == "/mocked/path"
|
assert frontend_init["web_root"] == mock_path
|
||||||
mock_check.assert_called_once()
|
assert frontend_init["version"] == mock_version
|
||||||
|
mock_check.assert_not_called() # check_frontend_version is now called inside init_default_frontend
|
||||||
|
|
||||||
|
|
||||||
def test_init_frontend_fallback_on_error():
|
def test_init_frontend_fallback_on_error():
|
||||||
# Arrange
|
# Arrange
|
||||||
version_string = "test-owner/test-repo@1.0.0"
|
version_string = "test-owner/test-repo@1.0.0"
|
||||||
|
mock_path = "/default/path"
|
||||||
|
mock_version = (1, 0, 0)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
with (
|
with (
|
||||||
patch.object(
|
patch.object(
|
||||||
FrontendManager, "init_frontend_unsafe", side_effect=Exception("Test error")
|
FrontendManager,
|
||||||
|
"init_frontend_unsafe",
|
||||||
|
side_effect=Exception("Test error")
|
||||||
),
|
),
|
||||||
patch("app.frontend_management.check_frontend_version") as mock_check,
|
|
||||||
patch.object(
|
patch.object(
|
||||||
FrontendManager, "default_frontend_path", return_value="/default/path"
|
FrontendManager,
|
||||||
|
"init_default_frontend",
|
||||||
|
return_value={"web_root": mock_path, "version": mock_version},
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
frontend_path = FrontendManager.init_frontend(version_string)
|
frontend_init = FrontendManager.init_frontend(version_string)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert frontend_path == "/default/path"
|
assert frontend_init["web_root"] == mock_path
|
||||||
mock_check.assert_called_once()
|
assert frontend_init["version"] == mock_version
|
||||||
|
Reference in New Issue
Block a user