diff --git a/Makefile b/Makefile index 50d2a3f..60b0785 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,9 @@ REGISTRY = cap-demo-public-registry.cn-hangzhou.cr.aliyuncs.com/cap-app AGENT_IMAGE ?= $(REGISTRY)/image-generation-comfyui-agent-dev:$(VERSION) COMFYUI_VERSION ?= v0.3.77 ifeq ($(COMFYUI_VERSION),v0.16.4) -CAP_SYSTEM_VERSION ?= 2.0.3 -# 指纹只用裸系统版本号(不带 -vp 后缀),与存量实例 NAS 上记录的版本一致, -# 避免内置依赖指纹变化触发存量 ComfyUI 冷启动时重跑 install_all +CAP_SYSTEM_VERSION ?= 2.0.5 +# 指纹只用裸系统版本号(不带 -vp 后缀),与存量实例 NAS 记录保持同一口径。 +# 内置插件代码或依赖实质变更时必须 bump,以触发存量项目更新。 BUILTIN_DEPENDENCY_VERSION ?= $(CAP_SYSTEM_VERSION) else CAP_SYSTEM_VERSION ?= 1.6.7 diff --git a/src/code/comfyui/custom_nodes/funart_prompt_id_compat/__init__.py b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/__init__.py new file mode 100644 index 0000000..acf4396 --- /dev/null +++ b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/__init__.py @@ -0,0 +1,14 @@ +"""Keep Function Compute task IDs compatible with newer ComfyUI releases.""" + +from .prompt_id_compat import install_prompt_id_compat + + +# Install before ComfyUI starts accepting requests. This lives in a dedicated +# built-in plugin so an older FunArt-ComfyUI-APIs copy on a user's NAS cannot +# shadow the compatibility fix during an in-place ComfyUI upgrade. +install_prompt_id_compat() + +NODE_CLASS_MAPPINGS = {} +NODE_DISPLAY_NAME_MAPPINGS = {} + +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] diff --git a/src/code/comfyui/custom_nodes/funart_prompt_id_compat/prompt_id_compat.py b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/prompt_id_compat.py new file mode 100644 index 0000000..b520df6 --- /dev/null +++ b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/prompt_id_compat.py @@ -0,0 +1,62 @@ +"""Compatibility for Function Compute task IDs on newer ComfyUI releases. + +ComfyUI v0.27.0 only accepts canonical UUIDs when callers provide a +``prompt_id``. Function Compute task and request IDs are opaque strings, and +the Agent intentionally uses the same value end-to-end for queue, history, +status, and cancellation lookups. Keep that established contract while still +rejecting values that cannot safely be used as dictionary keys. +""" + +from typing import Any + + +_PROBE_ID = "funart-fc-task-id" + + +def _validate_fc_task_id(value: Any) -> str: + """Accept non-empty string IDs, matching the pre-v0.27 ComfyUI contract.""" + if not isinstance(value, str): + raise ValueError(f"job id must be a string, got {type(value).__name__}") + if not value: + raise ValueError("job id must not be empty") + return value + + +def install_prompt_id_compat(server_module=None, jobs_module=None) -> bool: + """Relax UUID-only validation when the running ComfyUI requires it. + + ``server.py`` imports ``validate_job_id`` into its own module namespace, so + both aliases are replaced. Older ComfyUI versions do not expose the + validator (or already accept opaque IDs) and are left unchanged. + + Returns ``True`` when the compatibility validator was installed. + """ + if server_module is None: + try: + import server as server_module + except ImportError: + return False + + validator = getattr(server_module, "validate_job_id", None) + if not callable(validator): + return False + + try: + validator(_PROBE_ID) + except (AttributeError, TypeError, ValueError): + pass + else: + return False + + if jobs_module is None: + try: + from comfy_execution import jobs as jobs_module + except ImportError: + jobs_module = None + + server_module.validate_job_id = _validate_fc_task_id + if jobs_module is not None: + jobs_module.validate_job_id = _validate_fc_task_id + + print("[FunArt-Prompt-ID-Compat] Function Compute prompt_id compatibility enabled") + return True diff --git a/src/code/comfyui/custom_nodes/funart_prompt_id_compat/tests/__init__.py b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/code/comfyui/custom_nodes/funart_prompt_id_compat/tests/unit/__init__.py b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/code/comfyui/custom_nodes/funart_prompt_id_compat/tests/unit/test_prompt_id_compat.py b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/tests/unit/test_prompt_id_compat.py new file mode 100644 index 0000000..3e38d7d --- /dev/null +++ b/src/code/comfyui/custom_nodes/funart_prompt_id_compat/tests/unit/test_prompt_id_compat.py @@ -0,0 +1,70 @@ +from types import SimpleNamespace +import uuid + +import pytest + +from funart_prompt_id_compat.prompt_id_compat import install_prompt_id_compat + + +CANONICAL_UUID = "123e4567-e89b-12d3-a456-426614174000" +FC_REQUEST_ID = "1-6a603c38-1525c9f1-9b1542799d80" + + +def _uuid_only(value): + """Match ComfyUI v0.27.0's canonical UUID validator.""" + if not isinstance(value, str): + raise ValueError("job id must be a string") + if str(uuid.UUID(value)) != value: + raise ValueError("job id must be a canonical UUID") + return value + + +@pytest.mark.parametrize("task_id", [FC_REQUEST_ID, "my-task-001", CANONICAL_UUID]) +def test_preserves_task_id_across_server_and_jobs_aliases(task_id): + server = SimpleNamespace(validate_job_id=_uuid_only) + jobs = SimpleNamespace(validate_job_id=_uuid_only) + + assert install_prompt_id_compat(server, jobs) is True + assert server.validate_job_id(task_id) == task_id + assert jobs.validate_job_id(task_id) == task_id + + # Queue/history/status/cancel all use the prompt id as an exact-match key. + # Both aliases must therefore produce the same original FC identifier. + stored = {server.validate_job_id(task_id): "queued"} + assert stored[jobs.validate_job_id(task_id)] == "queued" + + +def test_rejects_values_that_cannot_be_task_ids(): + server = SimpleNamespace(validate_job_id=_uuid_only) + jobs = SimpleNamespace(validate_job_id=_uuid_only) + install_prompt_id_compat(server, jobs) + + with pytest.raises(ValueError): + server.validate_job_id(123) + with pytest.raises(ValueError): + server.validate_job_id("") + + +def test_install_is_idempotent(): + server = SimpleNamespace(validate_job_id=_uuid_only) + jobs = SimpleNamespace(validate_job_id=_uuid_only) + + assert install_prompt_id_compat(server, jobs) is True + installed_validator = server.validate_job_id + assert install_prompt_id_compat(server, jobs) is False + assert server.validate_job_id is installed_validator + assert jobs.validate_job_id is installed_validator + + +def test_leaves_older_permissive_validator_unchanged(): + def permissive(value): + return value + + server = SimpleNamespace(validate_job_id=permissive) + + assert install_prompt_id_compat(server) is False + assert server.validate_job_id is permissive + + +def test_skips_versions_without_job_id_validator(): + assert install_prompt_id_compat(SimpleNamespace()) is False diff --git a/src/code/comfyui/shared/Dockerfile.template b/src/code/comfyui/shared/Dockerfile.template index c1008bc..d186289 100644 --- a/src/code/comfyui/shared/Dockerfile.template +++ b/src/code/comfyui/shared/Dockerfile.template @@ -220,6 +220,7 @@ RUN rm -rf ${COMFYUI_DIR}/custom_nodes/Comfy-VisionPlaid && \ RUN mkdir -p /root/built-in/custom_nodes && \ cp -r ${COMFYUI_DIR}/custom_nodes/FunArt-ComfyUI-Multi-User /root/built-in/custom_nodes/ && \ cp -r ${COMFYUI_DIR}/custom_nodes/FunArt-ComfyUI-APIs /root/built-in/custom_nodes/ && \ + cp -r ${COMFYUI_DIR}/custom_nodes/funart_prompt_id_compat /root/built-in/custom_nodes/ && \ cp -r ${COMFYUI_DIR}/custom_nodes/Comfy-VisionPlaid /root/built-in/custom_nodes/ && \ echo "${BUILTIN_DEPENDENCY_VERSION}" > /root/built-in/version.txt diff --git a/src/code/comfyui/v0.16.4/Makefile b/src/code/comfyui/v0.16.4/Makefile index ffbd34e..43452eb 100644 --- a/src/code/comfyui/v0.16.4/Makefile +++ b/src/code/comfyui/v0.16.4/Makefile @@ -2,12 +2,12 @@ COMFYUI_VERSION := v0.16.4 CUSTOM_NODES_JSON := comfyui/v0.16.4/custom_nodes.json OSS_COMFYUI_BASE_DIR := base/comfyui/$(COMFYUI_VERSION)-gamma OSS_COMFYUI_DEEPGPU_BASE_DIR := base/comfyui/$(COMFYUI_VERSION)-gamma-deepgpu -CAP_SYSTEM_VERSION := 2.0.3 -VISION_PLAID_VERSION := 1.2.2 +CAP_SYSTEM_VERSION := 2.0.5 +VISION_PLAID_VERSION := 1.3.0 BITFORGE_VERSION := 0.2.4 VISION_PLAID_EXTRA_PACKAGES := flashinfer-python flashinfer-cubin -# 指纹回归旧方式:只用 CAP_SYSTEM_VERSION,不编码 VP 版本, -# 防止存量实例因指纹变化在冷启动时触发 install_all +# 指纹只用裸 CAP_SYSTEM_VERSION,不编码 VP 后缀。 +# 内置插件代码或依赖实质变更时必须 bump,以触发存量项目更新。 BUILTIN_DEPENDENCY_VERSION := $(CAP_SYSTEM_VERSION) include ../shared/Makefile.common