From 8fabb2d56ff678d4dcdab82ca7ad654dc3736764 Mon Sep 17 00:00:00 2001 From: Inoland Date: Tue, 14 Apr 2026 23:56:51 +0400 Subject: [PATCH] removed invalidate cache and added async --- .../class_helpers/file_helper.py | 3 +- .../class_helpers/openai_helper.py | 12 +++--- .../node_helpers/image_helper.py | 32 ++++++++------- .../node_helpers/string_helper.py | 13 ++++-- .../node_helpers/video_helper.py | 6 ++- .../s3_helper/s3_upload_file_node.py | 10 ++--- .../s3_helper/s3_upload_folder_node.py | 10 ++--- .../workflow_helpers/download_model_helper.py | 41 ++++++------------- 8 files changed, 61 insertions(+), 66 deletions(-) diff --git a/src/comfyui_ino_nodes/class_helpers/file_helper.py b/src/comfyui_ino_nodes/class_helpers/file_helper.py index 6b53466..96eaf95 100644 --- a/src/comfyui_ino_nodes/class_helpers/file_helper.py +++ b/src/comfyui_ino_nodes/class_helpers/file_helper.py @@ -1,3 +1,4 @@ +import asyncio from pathlib import Path from inopyutils import InoFileHelper @@ -374,7 +375,7 @@ class InoGetLastFile(io.ComfyNode): return io.NodeOutput(False, "Node is disabled", "", "") _, abs_path = resolve_comfy_path(parent_folder, folder) - res = InoFileHelper.get_last_file(path=Path(abs_path)) + res = await asyncio.to_thread(InoFileHelper.get_last_file, path=Path(abs_path)) if not res["success"]: return io.NodeOutput(False, res["msg"], "", "") return io.NodeOutput(True, res["msg"], res.get("file_name", ""), res.get("file_path", "")) diff --git a/src/comfyui_ino_nodes/class_helpers/openai_helper.py b/src/comfyui_ino_nodes/class_helpers/openai_helper.py index 0bcbcd9..90f8e0b 100644 --- a/src/comfyui_ino_nodes/class_helpers/openai_helper.py +++ b/src/comfyui_ino_nodes/class_helpers/openai_helper.py @@ -1,3 +1,4 @@ +import asyncio import os from inopyutils import InoJsonHelper, InoOpenAIHelper @@ -5,10 +6,10 @@ from openai import OpenAI from comfy_api.latest import io -from ..node_helper import ino_print_log, FailureInvalidatesCacheMixin +from ..node_helper import ino_print_log -class InoOpenaiResponses(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoOpenaiResponses(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -60,7 +61,7 @@ class InoOpenaiResponses(FailureInvalidatesCacheMixin, io.ComfyNode): } ] - response = client.responses.create(model=model, input=response_input) + response = await asyncio.to_thread(client.responses.create, model=model, input=response_input) error_message = response.error.message if response.error else "none" response_output = InoJsonHelper.dict_to_string(response.output)["data"] if response.output else "empty" @@ -69,11 +70,10 @@ class InoOpenaiResponses(FailureInvalidatesCacheMixin, io.ComfyNode): return io.NodeOutput(True, response.id, response.status, error_message, response_text, response_output) except Exception as e: ino_print_log("InoOpenaiResponses", "", e) - cls._bump_failure() return io.NodeOutput(False, "", "Openai response failed", str(e), "", "") -class InoOpenaiChatCompletions(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoOpenaiChatCompletions(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -124,11 +124,9 @@ class InoOpenaiChatCompletions(FailureInvalidatesCacheMixin, io.ComfyNode): else: error_msg = result.get("message", "unknown error") ino_print_log("InoOpenaiChatCompletions", "", error_msg) - cls._bump_failure() return io.NodeOutput(False, "", "", error_msg) except Exception as e: ino_print_log("InoOpenaiChatCompletions", "", e) - cls._bump_failure() return io.NodeOutput(False, "", "", str(e)) diff --git a/src/comfyui_ino_nodes/node_helpers/image_helper.py b/src/comfyui_ino_nodes/node_helpers/image_helper.py index 006ee02..1353f91 100644 --- a/src/comfyui_ino_nodes/node_helpers/image_helper.py +++ b/src/comfyui_ino_nodes/node_helpers/image_helper.py @@ -1,3 +1,4 @@ +import asyncio import base64 import io as std_io import torch @@ -79,7 +80,7 @@ class InoSaveImages(io.ComfyNode): ) @classmethod - def execute(cls, images, parent_folder, folder, filename_prefix) -> io.NodeOutput: + async def execute(cls, images, parent_folder, folder, filename_prefix) -> io.NodeOutput: time_now = datetime.now(timezone.utc).isoformat() rel_path, abs_path = resolve_comfy_path(parent_folder, folder) @@ -96,20 +97,23 @@ class InoSaveImages(io.ComfyNode): from comfy.cli_args import args from PIL.PngImagePlugin import PngInfo - results = [] - for (batch_number, image) in enumerate(images): - i = 255. * image.cpu().numpy() - img = PILImage.fromarray(np.clip(i, 0, 255).astype(np.uint8)) - metadata = None - if not args.disable_metadata: - metadata = PngInfo() + images_cpu = [img.cpu().numpy() for img in images] + include_metadata = not args.disable_metadata - filename_with_batch_num = filename.replace("%batch_num%", str(batch_number)) - file = f"{filename_with_batch_num}_{counter:05}_.png" - full_path = os.path.join(full_output_folder, file) - img.save(full_path, pnginfo=metadata, compress_level=4) - results.append({"filename": file, "subfolder": subfolder, "type": parent_folder}) - counter += 1 + def _save_all(): + saved = [] + local_counter = counter + for batch_number, arr in enumerate(images_cpu): + pil_img = PILImage.fromarray(np.clip(255. * arr, 0, 255).astype(np.uint8)) + metadata = PngInfo() if include_metadata else None + filename_with_batch_num = filename.replace("%batch_num%", str(batch_number)) + file = f"{filename_with_batch_num}_{local_counter:05}_.png" + pil_img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4) + saved.append({"filename": file, "subfolder": subfolder, "type": parent_folder}) + local_counter += 1 + return saved + + results = await asyncio.to_thread(_save_all) if len(results) == 0: return io.NodeOutput(False, "No images saved", rel_path, abs_path, 0, time_now) diff --git a/src/comfyui_ino_nodes/node_helpers/string_helper.py b/src/comfyui_ino_nodes/node_helpers/string_helper.py index 0e8aef6..d6fccc3 100644 --- a/src/comfyui_ino_nodes/node_helpers/string_helper.py +++ b/src/comfyui_ino_nodes/node_helpers/string_helper.py @@ -1,3 +1,4 @@ +import asyncio import re import hashlib import base64 @@ -8,6 +9,12 @@ from comfy_api.latest import io from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path +def _write_text_sync(abs_path, text): + Path(abs_path).parent.mkdir(parents=True, exist_ok=True) + with open(abs_path, "w", encoding="utf-8") as f: + f.write(text) + + class InoStringToggleCase(io.ComfyNode): @classmethod def define_schema(cls): @@ -168,15 +175,13 @@ class InoSaveText(io.ComfyNode): ) @classmethod - def execute(cls, enabled, text, parent_folder, folder, filename) -> io.NodeOutput: + async def execute(cls, enabled, text, parent_folder, folder, filename) -> io.NodeOutput: rel_path, abs_path = resolve_comfy_path(parent_folder, folder, filename) if not enabled: return io.NodeOutput(False, "not enabled", rel_path, abs_path) try: - Path(abs_path).parent.mkdir(parents=True, exist_ok=True) - with open(abs_path, "w", encoding="utf-8") as f: - f.write(text) + await asyncio.to_thread(_write_text_sync, abs_path, text) return io.NodeOutput(True, "saved", rel_path, abs_path) except Exception as e: return io.NodeOutput(False, str(e), rel_path, abs_path) diff --git a/src/comfyui_ino_nodes/node_helpers/video_helper.py b/src/comfyui_ino_nodes/node_helpers/video_helper.py index 285bc4f..6cc5b1d 100644 --- a/src/comfyui_ino_nodes/node_helpers/video_helper.py +++ b/src/comfyui_ino_nodes/node_helpers/video_helper.py @@ -1,3 +1,4 @@ +import asyncio import os import random import string @@ -27,7 +28,7 @@ class InoPreviewVideo(io.ComfyNode): ) @classmethod - def execute(cls, enabled, video: Input.Video, format="mp4", codec="h264") -> io.NodeOutput: + async def execute(cls, enabled, video: Input.Video, format="mp4", codec="h264") -> io.NodeOutput: if not enabled: return io.NodeOutput(ui=ui.PreviewVideo([]).as_dict()) @@ -37,7 +38,8 @@ class InoPreviewVideo(io.ComfyNode): ) file = f"{filename}_{counter:05}_.{Types.VideoContainer.get_extension(format)}" - video.save_to( + await asyncio.to_thread( + video.save_to, os.path.join(full_output_folder, file), format=Types.VideoContainer(format), codec=Types.VideoCodec(codec), diff --git a/src/comfyui_ino_nodes/s3_helper/s3_upload_file_node.py b/src/comfyui_ino_nodes/s3_helper/s3_upload_file_node.py index 3b96030..9290f21 100644 --- a/src/comfyui_ino_nodes/s3_helper/s3_upload_file_node.py +++ b/src/comfyui_ino_nodes/s3_helper/s3_upload_file_node.py @@ -1,3 +1,4 @@ +import asyncio import os from pathlib import Path @@ -6,10 +7,10 @@ from inopyutils import ino_is_err from comfy_api.latest import io from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING -from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path, FailureInvalidatesCacheMixin +from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path -class InoS3UploadFile(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoS3UploadFile(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -64,12 +65,11 @@ class InoS3UploadFile(FailureInvalidatesCacheMixin, io.ComfyNode): s3_instance = S3Helper.get_instance(s3_config) if ino_is_err(s3_instance): - cls._bump_failure() return io.NodeOutput(False, s3_instance["msg"], "") s3_instance = s3_instance["instance"] s3_result = await s3_instance.upload_file(s3_key=s3_full_key, local_file_path=abs_path) if s3_result["success"] and delete_local: - os.remove(abs_path) + await asyncio.to_thread(os.remove, abs_path) - return io.NodeOutput(cls._track(s3_result["success"]), s3_result["msg"], s3_full_key) + return io.NodeOutput(s3_result["success"], s3_result["msg"], s3_full_key) diff --git a/src/comfyui_ino_nodes/s3_helper/s3_upload_folder_node.py b/src/comfyui_ino_nodes/s3_helper/s3_upload_folder_node.py index 2bf61a3..6ceb6a7 100644 --- a/src/comfyui_ino_nodes/s3_helper/s3_upload_folder_node.py +++ b/src/comfyui_ino_nodes/s3_helper/s3_upload_folder_node.py @@ -1,3 +1,4 @@ +import asyncio import shutil from pathlib import Path @@ -6,10 +7,10 @@ from inopyutils import ino_is_err from comfy_api.latest import io from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING -from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path, FailureInvalidatesCacheMixin +from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path -class InoS3UploadFolder(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoS3UploadFolder(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -60,12 +61,11 @@ class InoS3UploadFolder(FailureInvalidatesCacheMixin, io.ComfyNode): s3_instance = S3Helper.get_instance(s3_config) if ino_is_err(s3_instance): - cls._bump_failure() return io.NodeOutput(False, s3_instance["msg"], rel_path, abs_path, 0, 0, 0, "") s3_instance = s3_instance["instance"] s3_result = await s3_instance.upload_folder(s3_folder_key=s3_key, local_folder_path=abs_path, max_concurrent=max_concurrent, verify=verify_with_s3) if s3_result["success"] and delete_local: - shutil.rmtree(Path(abs_path)) + await asyncio.to_thread(shutil.rmtree, Path(abs_path)) - return io.NodeOutput(cls._track(s3_result["success"]), s3_result["msg"], rel_path, abs_path, s3_result["total_files"], s3_result["uploaded_successfully"], s3_result["failed_uploads"], str(s3_result["errors"])) + return io.NodeOutput(s3_result["success"], s3_result["msg"], rel_path, abs_path, s3_result["total_files"], s3_result["uploaded_successfully"], s3_result["failed_uploads"], str(s3_result["errors"])) diff --git a/src/comfyui_ino_nodes/workflow_helpers/download_model_helper.py b/src/comfyui_ino_nodes/workflow_helpers/download_model_helper.py index 6742671..9874600 100644 --- a/src/comfyui_ino_nodes/workflow_helpers/download_model_helper.py +++ b/src/comfyui_ino_nodes/workflow_helpers/download_model_helper.py @@ -1,3 +1,4 @@ +import asyncio import os import shutil @@ -10,7 +11,7 @@ import folder_paths from comfy_api.latest import io from ..s3_helper.s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING -from ..node_helper import ino_print_log, MODEL_TYPES, FailureInvalidatesCacheMixin +from ..node_helper import ino_print_log, MODEL_TYPES from ..node_helper import get_list_from_csv, get_model_from_csv @@ -227,7 +228,7 @@ class InoGetLoraDownloadConfig(io.ComfyNode): return io.NodeOutput(data) -class InoHttpDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoHttpDownloadModel(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -286,18 +287,16 @@ class InoHttpDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): allow_redirects=True, mkdirs=True, connection=6 ) if ino_is_err(http_result): - cls._bump_failure() return io.NodeOutput(False, "failed to download", model_type, str(model_path), str(rel_path)) ino_print_log("InoHttpDownloadModel", "file downloaded successfully") - return io.NodeOutput(cls._track(http_result["success"]), http_result["msg"], model_type, str(model_path), str(rel_path)) + return io.NodeOutput(http_result["success"], http_result["msg"], model_type, str(model_path), str(rel_path)) except Exception as e: ino_print_log("InoHttpDownloadModel", "", e) - cls._bump_failure() return io.NodeOutput(False, f"Error: {e}", "", "", "") -class InoS3DownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoS3DownloadModel(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -359,21 +358,19 @@ class InoS3DownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): s3_instance = S3Helper.get_instance(s3_config) if ino_is_err(s3_instance): - cls._bump_failure() return io.NodeOutput(False, s3_instance["msg"], "", "", "") s3_instance = s3_instance["instance"] s3_result = await s3_instance.download_file(s3_key=s3_key, local_file_path=model_path) ino_print_log("InoS3DownloadModel", "file downloaded successfully") - return io.NodeOutput(cls._track(s3_result["success"]), s3_result["msg"], model_type, str(model_path), str(rel_path)) + return io.NodeOutput(s3_result["success"], s3_result["msg"], model_type, str(model_path), str(rel_path)) except Exception as e: ino_print_log("InoS3DownloadModel", "", e) - cls._bump_failure() return io.NodeOutput(False, f"Error: {e}", "", "", "") -class InoHuggingFaceDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoHuggingFaceDownloadModel(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -442,24 +439,22 @@ class InoHuggingFaceDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): args["local_dir"] = model_path except Exception as e: ino_print_log("InoHuggingFaceDownloadModel", "", e) - cls._bump_failure() return io.NodeOutput(False, f"Error: {e}", "", "", "") try: - result = hf_hub_download(repo_id, filename, **args) + result = await asyncio.to_thread(hf_hub_download, repo_id, filename, **args) if ignore_repo_dir: - shutil.move(result, model_path) + await asyncio.to_thread(shutil.move, result, model_path) result = f"{model_path}/{Path(result).name}" rel_path = Path(result).relative_to(model_path_base) ino_print_log("InoHuggingFaceDownloadModel", "file downloaded successfully") return io.NodeOutput(True, "Successful", model_type, str(result), str(rel_path)) except Exception as e: ino_print_log("InoHuggingFaceDownloadModel", "", e) - cls._bump_failure() return io.NodeOutput(False, f"Error: {e}", "", "", "") -class InoHuggingFaceDownloadRepo(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoHuggingFaceDownloadRepo(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -521,21 +516,19 @@ class InoHuggingFaceDownloadRepo(FailureInvalidatesCacheMixin, io.ComfyNode): args["local_dir"] = model_path except Exception as e: ino_print_log("InoHuggingFaceDownloadRepo", "", e) - cls._bump_failure() return io.NodeOutput(False, f"Error: {e}", "", "", "") try: - result = snapshot_download(repo_id, **args) + result = await asyncio.to_thread(snapshot_download, repo_id, **args) rel_path = Path(result).relative_to(model_path_base) ino_print_log("InoHuggingFaceDownloadRepo", "file downloaded successfully") return io.NodeOutput(True, "Successful", model_type, str(result), str(rel_path)) except Exception as e: ino_print_log("InoHuggingFaceDownloadRepo", "", e) - cls._bump_failure() return io.NodeOutput(False, f"Error: {e}", "", "", "") -class InoCivitaiDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoCivitaiDownloadModel(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -599,7 +592,6 @@ class InoCivitaiDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): file_id=file_id, ) if ino_is_err(download_model): - cls._bump_failure() return io.NodeOutput(False, download_model["msg"], model_type, "", "") await civit_client.close() @@ -610,11 +602,10 @@ class InoCivitaiDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): return io.NodeOutput(True, download_model["msg"], model_type, str(abs_path), str(rel_path)) except Exception as e: ino_print_log("InoCivitaiDownloadModel", "", e) - cls._bump_failure() return io.NodeOutput(False, str(e), "", "", "") -class InoHandleDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): +class InoHandleDownloadModel(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( @@ -661,12 +652,6 @@ class InoHandleDownloadModel(FailureInvalidatesCacheMixin, io.ComfyNode): result = await loader.execute(enabled=True, model_config=config) ino_print_log("InoHandleDownloadModel", "delegated to loader completed") - # Propagate failure to the outer class's cache-invalidation nonce - try: - inner_success = result.args[0] - except (AttributeError, IndexError): - inner_success = False - cls._track(inner_success) return result