# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"7": {
"class_type": "OpenAIGPTImage1",
"inputs": {
"background": "auto",
"custom_height": 1024,
"custom_width": 1024,
"image": [
"31",
0
],
"model": "gpt-image-2",
"n": 1,
"prompt": "Remove the moss at the red circles. And add a strand of mint flowers around blue circle",
"quality": "high",
"seed": 1873743837,
"size": "auto"
},
"_meta": {
"title": "OpenAI GPT Image 2"
}
},
"8": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "Product_Edit",
"images": [
"7",
0
]
},
"_meta": {
"title": "Save Image"
}
},
"12": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_FILENAME"
},
"_meta": {
"title": "INPUT PRODUCT"
}
},
"13": {
"class_type": "ImageCompare",
"inputs": {
"compare_view": None,
"image_a": [
"31",
0
],
"image_b": [
"7",
0
]
},
"_meta": {
"title": "Compare Images"
}
},
"16": {
"class_type": "OpenAIGPTImage1",
"inputs": {
"background": "auto",
"custom_height": 1024,
"custom_width": 1024,
"image": [
"30",
0
],
"model": "gpt-image-2",
"n": 1,
"prompt": "Place the product in the first image to the scene in second image, and change the greenery to mint leaves.",
"quality": "high",
"seed": 515288000,
"size": "auto"
},
"_meta": {
"title": "OpenAI GPT Image 2"
}
},
"17": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "Product",
"images": [
"16",
0
]
},
"_meta": {
"title": "Save Image"
}
},
"18": {
"class_type": "ImageCompare",
"inputs": {
"compare_view": None,
"image_a": [
"12",
0
],
"image_b": [
"16",
0
]
},
"_meta": {
"title": "Compare Images"
}
},
"21": {
"class_type": "ByteDance2FirstLastFrameNode",
"inputs": {
"first_frame": [
"33",
0
],
"first_frame_asset_id": "",
"last_frame_asset_id": "",
"model": "Seedance 2.0",
"model.duration": 7,
"model.generate_audio": False,
"model.prompt": "Camera slowly orbits left to right around the subject, one continuous take, no cuts.",
"model.ratio": "adaptive",
"model.resolution": "720p",
"seed": 1401855032,
"watermark": False
},
"_meta": {
"title": "ByteDance Seedance 2.5 First-Last-Frame to Video"
}
},
"22": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/Product_Video",
"format": "auto",
"format.codec": "auto",
"video": [
"21",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"29": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_2_FILENAME"
},
"_meta": {
"title": "INPUT BACKGROUND (OPTIONAL)"
}
},
"30": {
"class_type": "ImageBatchMulti",
"inputs": {
"image_1": [
"12",
0
],
"image_2": [
"29",
0
],
"inputcount": 2
},
"_meta": {
"title": "Image Batch Multi"
}
},
"31": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_3_FILENAME"
},
"_meta": {
"title": "INPUT IMAGE"
}
},
"33": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_4_FILENAME"
},
"_meta": {
"title": "INPUT FRAME"
}
}
})
# Replace each example.com URL with a public URL of your file.
workflow.set_input("12", "image", client.assets.from_url("https://example.com/replace-with-a-url/image"))
workflow.set_input("29", "image", client.assets.from_url("https://example.com/replace-with-a-url/image_2"))
workflow.set_input("31", "image", client.assets.from_url("https://example.com/replace-with-a-url/image_3"))
workflow.set_input("33", "image", client.assets.from_url("https://example.com/replace-with-a-url/image_4"))
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)