Fix exporter metadata kwarg and seed sync handling

This commit is contained in:
Peyton DeNiro
2026-03-28 17:27:03 -05:00
parent 494f7cf79a
commit 4370a78728
2 changed files with 141 additions and 10 deletions
+41 -10
View File
@@ -276,17 +276,19 @@ class CodeGenerator:
if no_params or key in class_def_params
}
# Deal with hidden variables
hidden_inputs = input_types.get("hidden", {})
if (
"hidden" in input_types.keys()
and "unique_id" in input_types["hidden"].keys()
"unique_id" in hidden_inputs
and (no_params or "unique_id" in class_def_params)
):
inputs["unique_id"] = random.randint(1, 2**64)
if "hidden" in input_types.keys():
if "prompt" in input_types["hidden"].keys():
inputs["prompt"] = {"variable_name": "prompt"}
if "extra_pnginfo" in input_types["hidden"].keys():
inputs["extra_pnginfo"] = {"variable_name": "extra_pnginfo"}
elif class_def_params is not None:
if "prompt" in hidden_inputs and (no_params or "prompt" in class_def_params):
inputs["prompt"] = {"variable_name": "prompt"}
if "extra_pnginfo" in hidden_inputs and (
no_params or "extra_pnginfo" in class_def_params
):
inputs["extra_pnginfo"] = {"variable_name": "extra_pnginfo"}
if "hidden" not in input_types and class_def_params is not None:
if "unique_id" in class_def_params:
inputs["unique_id"] = random.randint(1, 2**64)
@@ -296,8 +298,13 @@ class CodeGenerator:
f"{self.sanitize_node_id(str(idx))}"
)
inputs = self.update_inputs(inputs, executed_variables)
seed_sync_code = self.create_prompt_seed_sync_code(
idx, inputs, is_special_function
)
if is_special_function:
if seed_sync_code:
special_functions_code.extend(seed_sync_code)
special_functions_code.append(
self.create_function_call_code(
initialized_objects[class_type],
@@ -308,6 +315,8 @@ class CodeGenerator:
)
)
else:
if seed_sync_code:
code.extend(seed_sync_code)
code.append(
self.create_function_call_code(
initialized_objects[class_type],
@@ -363,6 +372,28 @@ class CodeGenerator:
return code
def create_prompt_seed_sync_code(
self, node_id: str, inputs: Dict, is_special_function: bool
) -> List[str]:
"""Generate code that keeps prompt metadata aligned with randomized seeds."""
seed_sync_lines = []
for key in ("seed", "noise_seed"):
if key not in inputs:
continue
randomized_seed_variable = (
f"node_{self.sanitize_node_id(str(node_id))}_{self.clean_variable_name(key)}"
)
seed_sync_lines.append(
f'{randomized_seed_variable} = prompt["{node_id}"]["inputs"]["{key}"] = random.randint(1, 2**64)'
)
inputs[key] = {"variable_name": randomized_seed_variable}
if not seed_sync_lines:
return []
indentation = "" if is_special_function else "\t"
return [f"{indentation}{line}\n" for line in seed_sync_lines]
def format_arg(self, key: str, value: any) -> str:
"""Formats arguments based on key and value.
@@ -381,12 +412,12 @@ class CodeGenerator:
@staticmethod
def format_arg_value(key: str, value: any) -> str:
"""Formats an argument value as Python source."""
if isinstance(value, dict) and "variable_name" in value:
return value["variable_name"]
if key == "noise_seed" or key == "seed":
return "random.randint(1, 2**64)"
if isinstance(value, str):
return json.dumps(value)
if isinstance(value, dict) and "variable_name" in value:
return value["variable_name"]
return repr(value)
def assemble_python_code(
+100
View File
@@ -74,6 +74,46 @@ class MergeImages:
return (left, right)
class HiddenMetadataFilteredNode:
CATEGORY = "image"
FUNCTION = "save"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
},
"hidden": {
"prompt": "PROMPT",
"extra_pnginfo": "EXTRA_PNGINFO",
},
}
def save(self, image):
return (image,)
class HiddenPromptSeedNode:
CATEGORY = "sampling"
FUNCTION = "sample"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"seed": ("INT",),
"noise_seed": ("INT",),
},
"hidden": {
"prompt": "PROMPT",
},
}
def sample(self, seed, noise_seed, prompt):
return (seed, noise_seed, prompt)
class UpscaleModelLoaderExportTest(unittest.TestCase):
def test_upscale_workflow_uses_direct_upscale_model_loader_init(self):
workflow = {
@@ -259,6 +299,66 @@ class UpscaleModelLoaderExportTest(unittest.TestCase):
self.assertIn("extra_pnginfo = None", generated)
self.assertNotIn('"workflow": json.loads(', generated)
def test_hidden_metadata_kwargs_follow_function_signature(self):
workflow = {
"1": {
"class_type": "HiddenMetadataFilteredNode",
"inputs": {
"image": "example.png",
},
}
}
output = StringIO()
ComfyUItoPython(
workflow=json.dumps(workflow),
output_file=output,
node_class_mappings={
"HiddenMetadataFilteredNode": HiddenMetadataFilteredNode,
},
)
generated = output.getvalue()
self.assertIn("hiddenmetadatafilterednode_1 =", generated)
self.assertNotIn("prompt=prompt", generated)
self.assertNotIn("extra_pnginfo=extra_pnginfo", generated)
def test_randomized_seed_inputs_update_prompt_metadata_before_execution(self):
workflow = {
"1": {
"class_type": "HiddenPromptSeedNode",
"inputs": {
"seed": 1,
"noise_seed": 2,
},
}
}
output = StringIO()
ComfyUItoPython(
workflow=json.dumps(workflow),
output_file=output,
node_class_mappings={
"HiddenPromptSeedNode": HiddenPromptSeedNode,
},
)
generated = output.getvalue()
self.assertIn(
'node_1_seed = prompt["1"]["inputs"]["seed"] = random.randint(1, 2**64)',
generated,
)
self.assertIn(
'node_1_noise_seed = prompt["1"]["inputs"]["noise_seed"] = random.randint(',
generated,
)
self.assertIn("1, 2**64", generated)
self.assertIn("seed=node_1_seed", generated)
self.assertIn("noise_seed=node_1_noise_seed", generated)
self.assertIn("prompt=prompt", generated)
if __name__ == "__main__":
unittest.main()