Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea45930102 | ||
|
|
b889f79baf | ||
|
|
1d8fed3534 | ||
|
|
a557788e70 | ||
|
|
05cccaffa2 | ||
|
|
85af9dd68f | ||
|
|
233615ea25 | ||
|
|
8b6aabbfaa | ||
|
|
c72539078c |
@@ -0,0 +1,116 @@
|
||||
import random
|
||||
|
||||
|
||||
class ComfyUIDeployExternalSeed:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"input_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "input_seed"},
|
||||
),
|
||||
"default_value": (
|
||||
"INT",
|
||||
{"default": -1},
|
||||
),
|
||||
"min_value": (
|
||||
"INT",
|
||||
{"default": 1, "min": 1, "max": 999999999999999},
|
||||
),
|
||||
"max_value": (
|
||||
"INT",
|
||||
{"default": 4294967295, "min": 1, "max": 999999999999999},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"display_name": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": ""},
|
||||
),
|
||||
"description": (
|
||||
"STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": 'For default value:\n"-1" (i.e. not in range): Randomize within the min and max value range. \nin range: Fixed, always the same value\n',
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("seed",)
|
||||
FUNCTION = "run"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
|
||||
# Limits
|
||||
_MAX_LIMIT = 999_999_999_999_999 # 15 digits
|
||||
|
||||
# Store cached seed when fixed flag is enabled
|
||||
_cached_seed = None
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(
|
||||
cls,
|
||||
input_id,
|
||||
min_value,
|
||||
max_value,
|
||||
default_value=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""Inform ComfyUI whether the node output should be considered changed.
|
||||
|
||||
If default_value is within range (Fixed mode), we return the inputs tuple
|
||||
so the cached result is reused until the user changes something.
|
||||
For Randomize mode, we force re-execution each queue.
|
||||
"""
|
||||
# Clamp values to allowed range for check
|
||||
min_value = max(1, min_value)
|
||||
max_value = min(cls._MAX_LIMIT, max_value)
|
||||
|
||||
# Fixed mode when default_value is within range
|
||||
if (
|
||||
default_value is not None
|
||||
and default_value >= min_value
|
||||
and default_value <= max_value
|
||||
):
|
||||
return (input_id, default_value)
|
||||
|
||||
# For Randomize (default_value is -1 or out of range) we force re-execution
|
||||
import random as _rnd
|
||||
|
||||
return _rnd.random()
|
||||
|
||||
def run(
|
||||
self,
|
||||
input_id,
|
||||
min_value: int,
|
||||
max_value: int,
|
||||
display_name=None,
|
||||
description=None,
|
||||
default_value: int = -1,
|
||||
):
|
||||
# Clamp values to allowed range
|
||||
min_value = max(1, min_value)
|
||||
max_value = min(self._MAX_LIMIT, max_value)
|
||||
|
||||
# Ensure limits are in correct order after clamping
|
||||
if min_value > max_value:
|
||||
min_value, max_value = max_value, min_value
|
||||
|
||||
# Fixed mode: default_value is within range
|
||||
if default_value >= min_value and default_value <= max_value:
|
||||
seed = int(default_value)
|
||||
self._cached_seed = seed
|
||||
return [seed]
|
||||
|
||||
# Randomize mode: default_value is -1 or out of range
|
||||
seed = random.randint(min_value, max_value)
|
||||
self._cached_seed = seed
|
||||
return [seed]
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalSeed": ComfyUIDeployExternalSeed}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ComfyUIDeployExternalSeed": "External Seed (ComfyUI Deploy)"
|
||||
}
|
||||
+53
-5
@@ -158,6 +158,8 @@ from logging import basicConfig, getLogger
|
||||
# Check for an environment variable to enable/disable Logfire
|
||||
use_logfire = os.environ.get("USE_LOGFIRE", "false").lower() == "true"
|
||||
|
||||
API_KEY_COMFY_ORG = os.environ.get("API_KEY_COMFY_ORG", None)
|
||||
|
||||
if use_logfire:
|
||||
try:
|
||||
import logfire
|
||||
@@ -283,6 +285,9 @@ def post_prompt(json_data):
|
||||
if "extra_data" in json_data:
|
||||
extra_data = json_data["extra_data"]
|
||||
|
||||
if API_KEY_COMFY_ORG is not None:
|
||||
extra_data["api_key_comfy_org"] = API_KEY_COMFY_ORG
|
||||
|
||||
if "client_id" in json_data:
|
||||
extra_data["client_id"] = json_data["client_id"]
|
||||
if valid[0]:
|
||||
@@ -316,7 +321,7 @@ def randomSeed(num_digits=15):
|
||||
return random.randint(range_start, range_end)
|
||||
|
||||
|
||||
def apply_random_seed_to_workflow(workflow_api):
|
||||
def apply_random_seed_to_workflow(workflow_api, workflow):
|
||||
"""
|
||||
Applies a random seed to each element in the workflow_api that has a 'seed' input.
|
||||
|
||||
@@ -329,6 +334,41 @@ def apply_random_seed_to_workflow(workflow_api):
|
||||
# If seed is a list, it's an input from another node (generally `external number int`)
|
||||
if isinstance(workflow_api[key]["inputs"]["seed"], list):
|
||||
continue
|
||||
|
||||
# Check node type in workflow to determine if we should randomize
|
||||
node_id = key
|
||||
should_skip = (
|
||||
False # Add a flag to track if we should skip randomization
|
||||
)
|
||||
|
||||
for node in workflow["nodes"]:
|
||||
if str(node["id"]) == node_id and node["type"] == "KSampler":
|
||||
# Check if this node has widgets_values and if seed setting is not "fixed"
|
||||
if "widgets_values" in node and len(node["widgets_values"]) > 1:
|
||||
seed_mode = node["widgets_values"][1]
|
||||
if seed_mode == "fixed":
|
||||
# Skip randomization for fixed seeds
|
||||
logger.info(
|
||||
f"Skipping random seed for KSampler (node {node_id}) as it's set to fixed"
|
||||
)
|
||||
should_skip = True # Set the flag to skip randomization
|
||||
break # Exit the inner loop
|
||||
|
||||
# Apply random seed for non-fixed seeds (randomize, iter, etc.)
|
||||
workflow_api[key]["inputs"]["seed"] = randomSeed()
|
||||
logger.info(
|
||||
f"Applied random seed {workflow_api[key]['inputs']['seed']} to KSampler (node {node_id})"
|
||||
)
|
||||
should_skip = (
|
||||
True # Set the flag to skip default randomization
|
||||
)
|
||||
break # Exit the inner loop
|
||||
break # This break will skip checking other nodes if widgets_values doesn't exist
|
||||
|
||||
# Skip the rest of the code for this key if we already handled it
|
||||
if should_skip:
|
||||
continue
|
||||
|
||||
# Special case for SONICSampler
|
||||
if workflow_api[key]["class_type"] == "SONICSampler":
|
||||
workflow_api[key]["inputs"]["seed"] = randomSeed("sonic")
|
||||
@@ -436,6 +476,12 @@ def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
||||
if value["class_type"] == "ComfyUIDeployExternalEXR":
|
||||
value["inputs"]["exr_file"] = new_value
|
||||
|
||||
if value["class_type"] == "ComfyUIDeployExternalSeed":
|
||||
logger.info(
|
||||
f"Applied random seed {new_value} to {value['class_type']}"
|
||||
)
|
||||
value["inputs"]["default_value"] = new_value
|
||||
|
||||
|
||||
def send_prompt(sid: str, inputs: StreamingPrompt):
|
||||
# workflow_api = inputs.workflow_api
|
||||
@@ -443,7 +489,7 @@ def send_prompt(sid: str, inputs: StreamingPrompt):
|
||||
workflow = copy.deepcopy(inputs.workflow)
|
||||
|
||||
# Random seed
|
||||
apply_random_seed_to_workflow(workflow_api)
|
||||
apply_random_seed_to_workflow(workflow_api, workflow)
|
||||
|
||||
logger.info("getting inputs", inputs.inputs)
|
||||
|
||||
@@ -529,7 +575,7 @@ async def comfy_deploy_run(request):
|
||||
workflow = data.get("workflow")
|
||||
|
||||
# Now it handles directly in here
|
||||
apply_random_seed_to_workflow(workflow_api)
|
||||
apply_random_seed_to_workflow(workflow_api, workflow)
|
||||
apply_inputs_to_workflow(workflow_api, inputs)
|
||||
|
||||
prompt = {
|
||||
@@ -596,7 +642,7 @@ async def stream_prompt(data, token):
|
||||
gpu_event_id = data.get("gpu_event_id", None)
|
||||
|
||||
# Now it handles directly in here
|
||||
apply_random_seed_to_workflow(workflow_api)
|
||||
apply_random_seed_to_workflow(workflow_api, workflow)
|
||||
apply_inputs_to_workflow(workflow_api, inputs)
|
||||
|
||||
prompt = {
|
||||
@@ -2010,13 +2056,14 @@ async def upload_in_background(
|
||||
("files", "content_type", "image/png"),
|
||||
("gifs", "format", "image/gif"),
|
||||
("model_file", "format", "application/octet-stream"),
|
||||
("result", "format", "application/octet-stream"),
|
||||
("text_file", "format", "text/plain"),
|
||||
]:
|
||||
items = data.get(file_type, [])
|
||||
|
||||
for item in items:
|
||||
# if is model_file, just add it to the data
|
||||
if file_type == "model_file":
|
||||
if file_type == "model_file" or file_type == "result":
|
||||
if isinstance(item, str):
|
||||
filename = os.path.basename(item)
|
||||
# Extract folder name from the path
|
||||
@@ -2096,6 +2143,7 @@ async def update_run_with_output(
|
||||
or "files" in data
|
||||
or "gifs" in data
|
||||
or "model_file" in data
|
||||
or "result" in data
|
||||
or "text_file" in data
|
||||
)
|
||||
if bypass_upload and have_upload_media:
|
||||
|
||||
+96
-78
@@ -783,15 +783,25 @@ const ext = {
|
||||
},
|
||||
|
||||
DYNAMIC_ENUM(node, inputName, inputData) {
|
||||
// console.log("DYNAMIC_ENUM", JSON.parse(JSON.stringify(node)), inputName, inputData);
|
||||
const enumWidget = node.addWidget(
|
||||
"combo",
|
||||
inputName,
|
||||
"",
|
||||
{ serialize: true, values: [] },
|
||||
);
|
||||
// Add a check to see if nativeEnum exists and provide fallback
|
||||
if (node.nativeEnum && typeof node.nativeEnum === "function") {
|
||||
// Original code using nativeEnum
|
||||
return node.nativeEnum(inputName, inputData);
|
||||
} else {
|
||||
// Fallback implementation
|
||||
console.log(
|
||||
"DYNAMIC_ENUM",
|
||||
JSON.parse(JSON.stringify(node)),
|
||||
inputName,
|
||||
inputData
|
||||
);
|
||||
const enumWidget = node.addWidget("combo", inputName, "", {
|
||||
serialize: true,
|
||||
values: [],
|
||||
});
|
||||
|
||||
return { widget: enumWidget };
|
||||
return { widget: enumWidget };
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
@@ -1966,80 +1976,88 @@ const client = new ComfyDeploy({
|
||||
serverURL: `${currentOrigin}/comfydeploy/api/`,
|
||||
});
|
||||
|
||||
app.extensionManager.registerSidebarTab({
|
||||
id: "search",
|
||||
icon: "pi pi-cloud-upload",
|
||||
title: "Deploy",
|
||||
tooltip: "Deploy and Configure",
|
||||
type: "custom",
|
||||
render: (el) => {
|
||||
el.innerHTML = `
|
||||
<div style="padding: 20px;">
|
||||
<h3>Comfy Deploy</h3>
|
||||
<div id="deploy-container" style="margin-bottom: 20px;"></div>
|
||||
<div id="workflows-container" style="display: none;">
|
||||
<h4>Your Workflows</h4>
|
||||
<div id="workflows-loading" style="display: flex; justify-content: center; align-items: center; height: 100px;">
|
||||
${loadingIcon}
|
||||
// Check if the current URL hostname starts with localhost or 127.0.0.1
|
||||
const isLocalhost =
|
||||
window.location.hostname === "localhost" ||
|
||||
window.location.hostname === "127.0.0.1";
|
||||
|
||||
// Only register the sidebar tab if we're on localhost
|
||||
if (isLocalhost) {
|
||||
app.extensionManager.registerSidebarTab({
|
||||
id: "search",
|
||||
icon: "pi pi-cloud-upload",
|
||||
title: "Deploy",
|
||||
tooltip: "Deploy and Configure",
|
||||
type: "custom",
|
||||
render: (el) => {
|
||||
el.innerHTML = `
|
||||
<div style="padding: 20px;">
|
||||
<h3>Comfy Deploy</h3>
|
||||
<div id="deploy-container" style="margin-bottom: 20px;"></div>
|
||||
<div id="workflows-container" style="display: none;">
|
||||
<h4>Your Workflows</h4>
|
||||
<div id="workflows-loading" style="display: flex; justify-content: center; align-items: center; height: 100px;">
|
||||
${loadingIcon}
|
||||
</div>
|
||||
<ul id="workflows-list" style="list-style-type: none; padding: 0; display: none;"></ul>
|
||||
</div>
|
||||
<ul id="workflows-list" style="list-style-type: none; padding: 0; display: none;"></ul>
|
||||
<div id="config-container"></div>
|
||||
</div>
|
||||
<div id="config-container"></div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
|
||||
// Add deploy button
|
||||
const deployContainer = el.querySelector("#deploy-container");
|
||||
const deployButton = document.createElement("button");
|
||||
deployButton.id = "sidebar-deploy-button";
|
||||
deployButton.style.display = "flex";
|
||||
deployButton.style.alignItems = "center";
|
||||
deployButton.style.justifyContent = "center";
|
||||
deployButton.style.width = "100%";
|
||||
deployButton.style.marginBottom = "10px";
|
||||
deployButton.style.padding = "10px";
|
||||
deployButton.style.fontSize = "16px";
|
||||
deployButton.style.fontWeight = "bold";
|
||||
deployButton.style.backgroundColor = "#4CAF50";
|
||||
deployButton.style.color = "white";
|
||||
deployButton.style.border = "none";
|
||||
deployButton.style.borderRadius = "5px";
|
||||
deployButton.style.cursor = "pointer";
|
||||
deployButton.innerHTML = `<i class="pi pi-cloud-upload" style="margin-right: 8px;"></i><div id='sidebar-button-title'>Deploy</div>`;
|
||||
deployButton.onclick = async () => {
|
||||
await deployWorkflow();
|
||||
// Refresh the workflows list after deployment
|
||||
refreshWorkflowsList(el);
|
||||
};
|
||||
deployContainer.appendChild(deployButton);
|
||||
|
||||
// Add config button
|
||||
const configContainer = el.querySelector("#config-container");
|
||||
const configButton = document.createElement("button");
|
||||
configButton.style.display = "flex";
|
||||
configButton.style.alignItems = "center";
|
||||
configButton.style.justifyContent = "center";
|
||||
configButton.style.width = "100%";
|
||||
configButton.style.padding = "8px";
|
||||
configButton.style.fontSize = "14px";
|
||||
configButton.style.backgroundColor = "#f0f0f0";
|
||||
configButton.style.color = "#333";
|
||||
configButton.style.border = "1px solid #ccc";
|
||||
configButton.style.borderRadius = "5px";
|
||||
configButton.style.cursor = "pointer";
|
||||
configButton.innerHTML = `<i class="pi pi-cog" style="margin-right: 8px;"></i>Configure`;
|
||||
configButton.onclick = () => {
|
||||
configDialog.show();
|
||||
};
|
||||
deployContainer.appendChild(configButton);
|
||||
|
||||
// Fetch and display workflows
|
||||
const workflowsList = el.querySelector("#workflows-list");
|
||||
const workflowsLoading = el.querySelector("#workflows-loading");
|
||||
|
||||
// Add deploy button
|
||||
const deployContainer = el.querySelector("#deploy-container");
|
||||
const deployButton = document.createElement("button");
|
||||
deployButton.id = "sidebar-deploy-button";
|
||||
deployButton.style.display = "flex";
|
||||
deployButton.style.alignItems = "center";
|
||||
deployButton.style.justifyContent = "center";
|
||||
deployButton.style.width = "100%";
|
||||
deployButton.style.marginBottom = "10px";
|
||||
deployButton.style.padding = "10px";
|
||||
deployButton.style.fontSize = "16px";
|
||||
deployButton.style.fontWeight = "bold";
|
||||
deployButton.style.backgroundColor = "#4CAF50";
|
||||
deployButton.style.color = "white";
|
||||
deployButton.style.border = "none";
|
||||
deployButton.style.borderRadius = "5px";
|
||||
deployButton.style.cursor = "pointer";
|
||||
deployButton.innerHTML = `<i class="pi pi-cloud-upload" style="margin-right: 8px;"></i><div id='sidebar-button-title'>Deploy</div>`;
|
||||
deployButton.onclick = async () => {
|
||||
await deployWorkflow();
|
||||
// Refresh the workflows list after deployment
|
||||
refreshWorkflowsList(el);
|
||||
};
|
||||
deployContainer.appendChild(deployButton);
|
||||
|
||||
// Add config button
|
||||
const configContainer = el.querySelector("#config-container");
|
||||
const configButton = document.createElement("button");
|
||||
configButton.style.display = "flex";
|
||||
configButton.style.alignItems = "center";
|
||||
configButton.style.justifyContent = "center";
|
||||
configButton.style.width = "100%";
|
||||
configButton.style.padding = "8px";
|
||||
configButton.style.fontSize = "14px";
|
||||
configButton.style.backgroundColor = "#f0f0f0";
|
||||
configButton.style.color = "#333";
|
||||
configButton.style.border = "1px solid #ccc";
|
||||
configButton.style.borderRadius = "5px";
|
||||
configButton.style.cursor = "pointer";
|
||||
configButton.innerHTML = `<i class="pi pi-cog" style="margin-right: 8px;"></i>Configure`;
|
||||
configButton.onclick = () => {
|
||||
configDialog.show();
|
||||
};
|
||||
deployContainer.appendChild(configButton);
|
||||
|
||||
// Fetch and display workflows
|
||||
const workflowsList = el.querySelector("#workflows-list");
|
||||
const workflowsLoading = el.querySelector("#workflows-loading");
|
||||
|
||||
refreshWorkflowsList(el);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getTimeAgo(date) {
|
||||
const seconds = Math.floor((new Date() - date) / 1000);
|
||||
|
||||
Reference in New Issue
Block a user