Compare commits

...
3 changed files with 65 additions and 40 deletions
+9 -6
View File
@@ -4,6 +4,7 @@ import numpy as np
from PIL import Image from PIL import Image
from PIL.PngImagePlugin import PngInfo from PIL.PngImagePlugin import PngInfo
import folder_paths import folder_paths
from comfy.cli_args import args
class ComfyDeployOutputImage: class ComfyDeployOutputImage:
@@ -64,12 +65,14 @@ class ComfyDeployOutputImage:
for batch_number, image in enumerate(images): for batch_number, image in enumerate(images):
i = 255.0 * image.cpu().numpy() i = 255.0 * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
metadata = PngInfo() metadata = None
if prompt is not None: if not args.disable_metadata:
metadata.add_text("prompt", json.dumps(prompt)) metadata = PngInfo()
if extra_pnginfo is not None: if prompt is not None:
for x in extra_pnginfo: metadata.add_text("prompt", json.dumps(prompt))
metadata.add_text(x, json.dumps(extra_pnginfo[x])) if extra_pnginfo is not None:
for x in extra_pnginfo:
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
filename_with_batch_num = filename.replace("%batch_num%", str(batch_number)) filename_with_batch_num = filename.replace("%batch_num%", str(batch_number))
file = f"{filename_with_batch_num}_{counter:05}_.{file_type}" file = f"{filename_with_batch_num}_{counter:05}_.{file_type}"
+55 -33
View File
@@ -1,8 +1,5 @@
from io import BytesIO from io import BytesIO
from pprint import pprint
from aiohttp import web
import os import os
import requests
import folder_paths import folder_paths
import json import json
import server import server
@@ -13,34 +10,24 @@ import random
import traceback import traceback
import uuid import uuid
import asyncio import asyncio
import logging import inspect
from urllib.parse import quote from urllib.parse import quote, urlencode
import threading import threading
import hashlib import hashlib
import aiohttp import aiohttp
from aiohttp import ClientSession, web from aiohttp import web, ClientSession, ClientError, ClientTimeout
import aiofiles import aiofiles
from typing import Dict, List, Union, Any, Optional from typing import Dict, Any
from PIL import Image
import copy import copy
import struct import struct
from aiohttp import web, ClientSession, ClientError, ClientTimeout, ClientResponseError
import atexit import atexit
from model_management import get_torch_device from model_management import get_torch_device
import torch import torch
import psutil import psutil
from collections import OrderedDict
import io
from urllib.parse import urlencode
# Global session # Global session
client_session = None client_session = None
# def create_client_session():
# global client_session
# if client_session is None:
# client_session = aiohttp.ClientSession()
async def ensure_client_session(): async def ensure_client_session():
global client_session global client_session
@@ -82,8 +69,6 @@ retry_delay_multiplier = float(os.environ.get("RETRY_DELAY_MULTIPLIER", "2"))
print(f"max_retries: {max_retries}, retry_delay_multiplier: {retry_delay_multiplier}") print(f"max_retries: {max_retries}, retry_delay_multiplier: {retry_delay_multiplier}")
import time
async def async_request_with_retry( async def async_request_with_retry(
method, url, disable_timeout=False, token=None, **kwargs method, url, disable_timeout=False, token=None, **kwargs
@@ -334,9 +319,28 @@ async def post_prompt(json_data):
extra_data["client_id"] = json_data["client_id"] extra_data["client_id"] = json_data["client_id"]
if valid[0]: if valid[0]:
outputs_to_execute = valid[2] outputs_to_execute = valid[2]
prompt_server.prompt_queue.put( # Backward compatibility: sensitive data handling added in newer ComfyUI
(number, prompt_id, prompt, extra_data, outputs_to_execute) sensitive_keys = getattr(execution, "SENSITIVE_EXTRA_DATA_KEYS", None)
) if sensitive_keys:
sensitive = {}
for sensitive_val in sensitive_keys:
if sensitive_val in extra_data:
sensitive[sensitive_val] = extra_data.pop(sensitive_val)
prompt_server.prompt_queue.put(
(
number,
prompt_id,
prompt,
extra_data,
outputs_to_execute,
sensitive,
)
)
else:
# Old ComfyUI version without sensitive data support
prompt_server.prompt_queue.put(
(number, prompt_id, prompt, extra_data, outputs_to_execute)
)
response = { response = {
"prompt_id": prompt_id, "prompt_id": prompt_id,
"number": number, "number": number,
@@ -1323,6 +1327,9 @@ try:
is_async = asyncio.iscoroutinefunction(origin_execute) is_async = asyncio.iscoroutinefunction(origin_execute)
if is_async: if is_async:
# Check signature for backward compatibility (v0.3.67 has 10 params, v0.3.68+ has 11)
sig = inspect.signature(origin_execute)
has_ui_outputs = len(sig.parameters) >= 11
async def swizzle_execute( async def swizzle_execute(
server, server,
@@ -1335,12 +1342,14 @@ try:
execution_list, execution_list,
pending_subgraph_results, pending_subgraph_results,
pending_async_nodes, pending_async_nodes,
ui_outputs=None,
): ):
unique_id = current_item unique_id = current_item
class_type = dynprompt.get_node(unique_id)["class_type"] class_type = dynprompt.get_node(unique_id)["class_type"]
last_node_id = server.last_node_id last_node_id = server.last_node_id
result = await origin_execute( # Build args list - add ui_outputs only for v0.3.68+
args = [
server, server,
dynprompt, dynprompt,
caches, caches,
@@ -1351,12 +1360,16 @@ try:
execution_list, execution_list,
pending_subgraph_results, pending_subgraph_results,
pending_async_nodes, pending_async_nodes,
) ]
if has_ui_outputs:
args.append(ui_outputs)
result = await origin_execute(*args)
handle_execute(class_type, last_node_id, prompt_id, server, unique_id) handle_execute(class_type, last_node_id, prompt_id, server, unique_id)
return result return result
else: else:
# Sync version for very old ComfyUI versions
def swizzle_execute( def swizzle_execute(
server, server,
dynprompt, dynprompt,
@@ -1554,18 +1567,26 @@ async def send_json_override(self, event, data, sid=None):
) )
if event == "executing" and data.get("node") is not None: if event == "executing" and data.get("node") is not None:
node = data.get("node") raw_node = data.get("node")
node = str(raw_node)
if prompt_id in prompt_metadata: if prompt_id in prompt_metadata:
# if 'progress' not in prompt_metadata[prompt_id]: wf_api = prompt_metadata[prompt_id].workflow_api
# prompt_metadata[prompt_id]["progress"] = set()
# Normalize dotted display ids like "23.0.0.1" to base "23"
if node not in wf_api and "." in node:
base = node.split(".")[0]
if base in wf_api:
node = base
# If still unknown, skip safely
if node not in wf_api:
logger.info(f"Skipping unknown node id in 'executing': {raw_node}")
return
prompt_metadata[prompt_id].progress.add(node) prompt_metadata[prompt_id].progress.add(node)
calculated_progress = len(prompt_metadata[prompt_id].progress) / len( calculated_progress = len(prompt_metadata[prompt_id].progress) / len(wf_api)
prompt_metadata[prompt_id].workflow_api
)
calculated_progress = round(calculated_progress, 2) calculated_progress = round(calculated_progress, 2)
# logger.info("calculated_progress", calculated_progress)
if ( if (
prompt_metadata[prompt_id].last_updated_node is not None prompt_metadata[prompt_id].last_updated_node is not None
@@ -1573,7 +1594,8 @@ async def send_json_override(self, event, data, sid=None):
): ):
return return
prompt_metadata[prompt_id].last_updated_node = node prompt_metadata[prompt_id].last_updated_node = node
class_type = prompt_metadata[prompt_id].workflow_api[node]["class_type"]
class_type = wf_api[node]["class_type"]
logger.info(f"At: {round(calculated_progress * 100)}% - {class_type}") logger.info(f"At: {round(calculated_progress * 100)}% - {class_type}")
asyncio.create_task( asyncio.create_task(
send( send(
+1 -1
View File
@@ -1,7 +1,7 @@
[project] [project]
name = "comfyui-deploy" name = "comfyui-deploy"
description = "Open source comfyui deployment platform, a vercel for generative workflow infra." description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
version = "2.3.7" version = "2.3.10"
license = { file = "LICENSE" } license = { file = "LICENSE" }
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"] dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]