Compare commits

...
Author SHA1 Message Date
nick 778e6fefe6 Merge branch 'main' into nickkao/gpu_event 2024-10-12 12:56:11 -07:00
nick fd310e8478 globals 2024-10-11 21:46:48 -07:00
karrix 3a3b93d564 tweak: modify the local storage of the dock 2024-10-11 17:15:28 +08:00
nick 82c564228d None gpu event 2024-10-10 17:58:11 -07:00
nick ad0a23434b merge 2024-10-10 17:23:19 -07:00
karrix 292f77f06b fix: default queue button position to dock 2024-10-11 02:21:32 +08:00
bennykok a139424b91 fix: output node status 2024-10-10 11:20:12 -07:00
bennykok 44a91d2093 fix: default new ui for comfyui 2024-10-09 17:20:08 -07:00
bennykok 7cff930861 fix: token will be fetched everytime to make sure it is the latest 2024-10-09 16:54:34 -07:00
nick ce464c6ce4 Merge branch 'main' into nickkao/gpu_event 2024-10-07 15:50:47 -07:00
bennykok 1c7998c554 feat: attach gpu event 2024-10-07 15:48:14 -07:00
nick 66d1e42409 lopgs 2024-10-07 14:16:55 -07:00
nick 8882f4983c fix: pydantic type simpleprompt 2024-10-04 19:14:37 -07:00
nick 492b81c340 print 2024-10-04 19:02:25 -07:00
nick 8b05ed26c9 merge 2024-10-04 18:49:30 -07:00
nick ce67604926 stuff 2024-10-04 17:34:50 -07:00
bennykok c115c22a91 fix: send ws after cd logic 2024-10-04 16:16:19 -07:00
bennykok 2f33bcf497 chore: return item on upload 2024-10-04 15:27:31 -07:00
nick bcf466c472 merge 2024-10-04 12:10:01 -07:00
bennykok f812d9d698 Merge branch 'workspace-v3' into public-main 2024-10-02 16:38:55 -07:00
nick 101b6cca57 merge 2024-09-29 12:02:46 -07:00
EdwinWong ae68aae011 fix: add workflow data to extra data 2024-09-27 18:48:51 -07:00
EmmanuelMr18 07926158f0 feat: model_list node to display all the models available 2024-09-27 18:19:56 -07:00
EmmanuelMr18 ce92dd0570 refactor: remove ExternalTextList node, was for lora traning 2024-09-27 15:13:27 -07:00
nick 79650f48d0 merge 2024-09-24 23:16:49 -07:00
nick 0e70db4013 merge 2024-09-17 16:37:39 -07:00
nick 06805e310d merge 2024-09-17 14:32:52 -07:00
nick 4cd7d7a8f9 gpu event 2024-09-08 09:55:47 -07:00
5 changed files with 809 additions and 490 deletions
-52
View File
@@ -1,52 +0,0 @@
import folder_paths
from PIL import Image, ImageOps
import numpy as np
import torch
import json
class ComfyUIDeployExternalTextList:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_id": (
"STRING",
{"multiline": False, "default": 'input_text_list'},
),
"text": (
"STRING",
{"multiline": True, "default": "[]"},
),
},
"optional": {
"display_name": (
"STRING",
{"multiline": False, "default": ""},
),
"description": (
"STRING",
{"multiline": True, "default": ""},
),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
OUTPUT_IS_LIST = (True,)
FUNCTION = "run"
CATEGORY = "text"
def run(self, input_id, text=None, display_name=None, description=None):
text_list = []
try:
text_list = json.loads(text) # Assuming text is a JSON array string
except Exception as e:
print(f"Error processing images: {e}")
pass
return ([text_list],)
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalTextList": ComfyUIDeployExternalTextList}
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployExternalTextList": "External Text List (ComfyUI Deploy)"}
+60
View File
@@ -0,0 +1,60 @@
import folder_paths
class AnyType(str):
def __ne__(self, __value: object) -> bool:
return False
from os import walk
WILDCARD = AnyType("*")
MODEL_EXTENSIONS = {
"safetensors": "SafeTensors file format",
"ckpt": "Checkpoint file",
"pth": "PyTorch serialized file",
"pkl": "Pickle file",
"onnx": "ONNX file",
}
def fetch_files(path):
for (dirpath, dirnames, filenames) in walk(path):
fs = []
if len(dirnames) > 0:
for dirname in dirnames:
fs.extend(fetch_files(f"{dirpath}/{dirname}"))
for filename in filenames:
# Remove "./models/" from the beginning of dirpath
relative_dirpath = dirpath.replace("./models/", "", 1)
file_path = f"{relative_dirpath}/{filename}"
# Only add files that are known model extensions
file_extension = filename.split('.')[-1].lower()
if file_extension in MODEL_EXTENSIONS:
fs.append(file_path)
return fs
allModels = fetch_files("./models")
class ComfyUIDeployModalList:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": (allModels, ),
}
}
RETURN_TYPES = (WILDCARD,)
RETURN_NAMES = ("model",)
FUNCTION = "run"
CATEGORY = "model"
def run(self, model=""):
# Split the model path by '/' and select the last item
model_name = model.split('/')[-1]
return [model_name]
NODE_CLASS_MAPPINGS = {"ComfyUIDeployModelList": ComfyUIDeployModalList}
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployModelList": "Model List (ComfyUI Deploy)"}
+608 -365
View File
File diff suppressed because it is too large Load Diff
+25 -8
View File
@@ -6,10 +6,12 @@ from PIL import Image, ImageOps
from io import BytesIO from io import BytesIO
from pydantic import BaseModel as PydanticBaseModel from pydantic import BaseModel as PydanticBaseModel
class BaseModel(PydanticBaseModel): class BaseModel(PydanticBaseModel):
class Config: class Config:
arbitrary_types_allowed = True arbitrary_types_allowed = True
class Status(Enum): class Status(Enum):
NOT_STARTED = "not-started" NOT_STARTED = "not-started"
RUNNING = "running" RUNNING = "running"
@@ -17,6 +19,7 @@ class Status(Enum):
FAILED = "failed" FAILED = "failed"
UPLOADING = "uploading" UPLOADING = "uploading"
class StreamingPrompt(BaseModel): class StreamingPrompt(BaseModel):
workflow_api: Any workflow_api: Any
auth_token: str auth_token: str
@@ -25,6 +28,8 @@ class StreamingPrompt(BaseModel):
status_endpoint: Optional[str] status_endpoint: Optional[str]
file_upload_endpoint: Optional[str] file_upload_endpoint: Optional[str]
workflow: Any workflow: Any
gpu_event_id: Optional[str] = None
class SimplePrompt(BaseModel): class SimplePrompt(BaseModel):
status_endpoint: Optional[str] status_endpoint: Optional[str]
@@ -35,34 +40,39 @@ class SimplePrompt(BaseModel):
workflow_api: dict workflow_api: dict
status: Status = Status.NOT_STARTED status: Status = Status.NOT_STARTED
progress: set = set() progress: set = set()
last_updated_node: Optional[str] = None, last_updated_node: Optional[str] = None
uploading_nodes: set = set() uploading_nodes: set = set()
done: bool = False done: bool = False
is_realtime: bool = False, is_realtime: bool = False
start_time: Optional[float] = None, start_time: Optional[float] = None
gpu_event_id: Optional[str] = None
sockets = dict() sockets = dict()
prompt_metadata: dict[str, SimplePrompt] = {} prompt_metadata: dict[str, SimplePrompt] = {}
streaming_prompt_metadata: dict[str, StreamingPrompt] = {} streaming_prompt_metadata: dict[str, StreamingPrompt] = {}
class BinaryEventTypes: class BinaryEventTypes:
PREVIEW_IMAGE = 1 PREVIEW_IMAGE = 1
UNENCODED_PREVIEW_IMAGE = 2 UNENCODED_PREVIEW_IMAGE = 2
max_output_id_length = 24 max_output_id_length = 24
async def send_image(image_data, sid=None, output_id:str = None):
async def send_image(image_data, sid=None, output_id: str = None):
max_length = max_output_id_length max_length = max_output_id_length
output_id = output_id[:max_length] output_id = output_id[:max_length]
padded_output_id = output_id.ljust(max_length, '\x00') padded_output_id = output_id.ljust(max_length, "\x00")
encoded_output_id = padded_output_id.encode('ascii', 'replace') encoded_output_id = padded_output_id.encode("ascii", "replace")
image_type = image_data[0] image_type = image_data[0]
image = image_data[1] image = image_data[1]
max_size = image_data[2] max_size = image_data[2]
quality = image_data[3] quality = image_data[3]
if max_size is not None: if max_size is not None:
if hasattr(Image, 'Resampling'): if hasattr(Image, "Resampling"):
resampling = Image.Resampling.BILINEAR resampling = Image.Resampling.BILINEAR
else: else:
resampling = Image.ANTIALIAS resampling = Image.ANTIALIAS
@@ -91,12 +101,18 @@ async def send_image(image_data, sid=None, output_id:str = None):
preview_bytes = bytesIO.getvalue() preview_bytes = bytesIO.getvalue()
await send_bytes(BinaryEventTypes.PREVIEW_IMAGE, preview_bytes, sid=sid) await send_bytes(BinaryEventTypes.PREVIEW_IMAGE, preview_bytes, sid=sid)
async def send_socket_catch_exception(function, message): async def send_socket_catch_exception(function, message):
try: try:
await function(message) await function(message)
except (aiohttp.ClientError, aiohttp.ClientPayloadError, ConnectionResetError) as err: except (
aiohttp.ClientError,
aiohttp.ClientPayloadError,
ConnectionResetError,
) as err:
print("send error:", err) print("send error:", err)
def encode_bytes(event, data): def encode_bytes(event, data):
if not isinstance(event, int): if not isinstance(event, int):
raise RuntimeError(f"Binary event types must be integers, got {event}") raise RuntimeError(f"Binary event types must be integers, got {event}")
@@ -106,6 +122,7 @@ def encode_bytes(event, data):
message.extend(data) message.extend(data)
return message return message
async def send_bytes(event, data, sid=None): async def send_bytes(event, data, sid=None):
message = encode_bytes(event, data) message = encode_bytes(event, data)
+75 -24
View File
@@ -92,8 +92,30 @@ const context = {
// native_run_api_endpoint: "http://localhost:3011/api/run", // native_run_api_endpoint: "http://localhost:3011/api/run",
// }; // };
function getSelectedWorkflowInfo() { async function getSelectedWorkflowInfo() {
return context.selectedWorkflowInfo; const workflow_info_promise = new Promise((resolve) => {
try {
const handleMessage = (event) => {
try {
const message = JSON.parse(event.data);
if (message.type === "workflow_info") {
resolve(message.data);
window.removeEventListener("message", handleMessage);
}
} catch (error) {
console.error(error);
resolve(undefined);
}
};
window.addEventListener("message", handleMessage);
sendEventToCD("workflow_info");
} catch (error) {
console.error(error);
resolve(undefined);
}
});
return workflow_info_promise;
} }
function setSelectedWorkflowInfo(info) { function setSelectedWorkflowInfo(info) {
@@ -105,6 +127,8 @@ function setSelectedWorkflowInfo(info) {
const ext = { const ext = {
name: "BennyKok.ComfyUIDeploy", name: "BennyKok.ComfyUIDeploy",
native_mode: false,
init(app) { init(app) {
addButton(); addButton();
@@ -114,6 +138,7 @@ const ext = {
const org_display = queryParams.get("org_display"); const org_display = queryParams.get("org_display");
const origin = queryParams.get("origin"); const origin = queryParams.get("origin");
const workspace_mode = queryParams.get("workspace_mode"); const workspace_mode = queryParams.get("workspace_mode");
this.native_mode = queryParams.get("native_mode") === "true";
if (workspace_mode) { if (workspace_mode) {
document.querySelector(".comfy-menu").style.display = "none"; document.querySelector(".comfy-menu").style.display = "none";
@@ -319,7 +344,10 @@ const ext = {
false, false,
); );
} catch (error) { } catch (error) {
console.warning("Error setting validation to false, is fine to ignore this", error); console.warning(
"Error setting validation to false, is fine to ignore this",
error,
);
} }
console.log("loadGraphData"); console.log("loadGraphData");
app.loadGraphData(comfyUIWorkflow); app.loadGraphData(comfyUIWorkflow);
@@ -429,6 +457,25 @@ const ext = {
// } // }
}); });
if (this.native_mode) {
// console.log("native mode", window, window.app);
try {
await app.ui.settings.setSettingValueAsync("Comfy.UseNewMenu", "Top");
await app.ui.settings.setSettingValueAsync(
"Comfy.Sidebar.Size",
"small"
);
await app.ui.settings.setSettingValueAsync(
"Comfy.Sidebar.Location",
"right"
);
localStorage.setItem("Comfy.MenuPosition.Docked", "true");
console.log("native mode manmanman");
} catch (error) {
console.error("Error setting validation to false", error);
}
}
app.graph.onAfterChange = ((originalFunction) => app.graph.onAfterChange = ((originalFunction) =>
async function () { async function () {
const prompt = await app.graphToPrompt(); const prompt = await app.graphToPrompt();
@@ -1527,30 +1574,34 @@ async function loadWorkflowApi(versionId) {
const orginal_fetch_api = api.fetchApi; const orginal_fetch_api = api.fetchApi;
api.fetchApi = async (route, options) => { api.fetchApi = async (route, options) => {
console.log("Fetch API called with args:", route, options); console.log("Fetch API called with args:", route, options, ext.native_mode);
const info = getSelectedWorkflowInfo(); if (route.startsWith("/prompt") && ext.native_mode) {
if (info && route.startsWith("/prompt")) { const info = await getSelectedWorkflowInfo();
const body = JSON.parse(options.body); console.log("info", info);
if (info) {
const body = JSON.parse(options.body);
const data = { const data = {
client_id: body.client_id, client_id: body.client_id,
workflow_api_json: body.prompt, workflow_api_json: body.prompt,
workflow: body?.extra_data?.extra_pnginfo?.workflow, workflow: body?.extra_data?.extra_pnginfo?.workflow,
is_native_run: true, is_native_run: true,
machine_id: info.machine_id, machine_id: info.machine_id,
workflow_id: info.workflow_id, workflow_id: info.workflow_id,
native_run_api_endpoint: info.native_run_api_endpoint, native_run_api_endpoint: info.native_run_api_endpoint,
}; gpu_event_id: info.gpu_event_id,
};
return await fetch("/comfyui-deploy/run", { return await fetch("/comfyui-deploy/run", {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${info.cd_token}`, Authorization: `Bearer ${info.cd_token}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}); });
}
} }
return await orginal_fetch_api.call(api, route, options); return await orginal_fetch_api.call(api, route, options);