# 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({
"97": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_FILENAME"
},
"_meta": {
"title": "Start Frame Image"
}
},
"108": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/Wan2.2_image_to_video",
"format": "auto",
"format.codec": "auto",
"video": [
"116:94",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"116:101": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "wan2.2_i2v_lightx2v_4steps_lora_v1_high_noise.safetensors",
"model": [
"116:95",
0
],
"strength_model": 1.0000000000000002
},
"_meta": {
"title": "Load LoRA"
}
},
"116:102": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "wan2.2_i2v_lightx2v_4steps_lora_v1_low_noise.safetensors",
"model": [
"116:96",
0
],
"strength_model": 1.0000000000000002
},
"_meta": {
"title": "Load LoRA"
}
},
"116:103": {
"class_type": "ModelSamplingSD3",
"inputs": {
"model": [
"116:102",
0
],
"shift": 5.000000000000001
},
"_meta": {
"title": "ModelSamplingSD3"
}
},
"116:104": {
"class_type": "ModelSamplingSD3",
"inputs": {
"model": [
"116:101",
0
],
"shift": 5.000000000000001
},
"_meta": {
"title": "ModelSamplingSD3"
}
},
"116:84": {
"class_type": "CLIPLoader",
"inputs": {
"clip_name": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"device": "default",
"type": "wan"
},
"_meta": {
"title": "Load CLIP"
}
},
"116:85": {
"class_type": "KSamplerAdvanced",
"inputs": {
"add_noise": "disable",
"cfg": 1,
"end_at_step": 4,
"latent_image": [
"116:86",
0
],
"model": [
"116:103",
0
],
"negative": [
"116:98",
1
],
"noise_seed": 0,
"positive": [
"116:98",
0
],
"return_with_leftover_noise": "disable",
"sampler_name": "euler",
"scheduler": "simple",
"start_at_step": 2,
"steps": 4
},
"_meta": {
"title": "KSampler (Advanced)"
}
},
"116:86": {
"class_type": "KSamplerAdvanced",
"inputs": {
"add_noise": "enable",
"cfg": 1,
"end_at_step": 2,
"latent_image": [
"116:98",
2
],
"model": [
"116:104",
0
],
"negative": [
"116:98",
1
],
"noise_seed": 950349635748642,
"positive": [
"116:98",
0
],
"return_with_leftover_noise": "enable",
"sampler_name": "euler",
"scheduler": "simple",
"start_at_step": 0,
"steps": 4
},
"_meta": {
"title": "KSampler (Advanced)"
}
},
"116:87": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"116:85",
0
],
"vae": [
"116:90",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"116:89": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"116:84",
0
],
"text": "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
},
"_meta": {
"title": "CLIP Text Encode (Negative Prompt)"
}
},
"116:90": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "wan_2.1_vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"116:93": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"116:84",
0
],
"text": "The charcoal-furred dog stands in the black void and slowly turns its head from the right toward the camera, amber eyes catching the light, ears lifting alert. Its nostrils flare as it sniffs the air, then it lowers its chin slightly and lets out a calm breath, chest rising and falling. The chrome spiked collar shifts and glints with every movement, throwing small sparks of reflected light. The electric blue rim light traces the ridge of its back and brow as the camera slowly orbits a quarter turn around the dog from its side toward its face while gently pushing in. Fine short hairs ripple along the shoulders; the darkness behind stays deep and still. Cinematic, moody, high-contrast studio lighting, smooth natural motion, shallow depth of field, crisp detail."
},
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
},
"116:94": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 16,
"images": [
"116:87",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"116:95": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"116:96": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"116:98": {
"class_type": "WanImageToVideo",
"inputs": {
"batch_size": 1,
"height": 720,
"length": 81,
"negative": [
"116:89",
0
],
"positive": [
"116:93",
0
],
"start_image": [
"97",
0
],
"vae": [
"116:90",
0
],
"width": 1280
},
"_meta": {
"title": "WanImageToVideo"
}
}
})
workflow.set_input("97", "image", client.assets.from_url("https://comfy.org/catalog-media/62c24908-ceff-4862-a07b-875ca5b4dc6b/64d6b10e-3b97-452c-ba6e-6327610d9247"))
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)
for output in job.get_outputs("108"):
output.to_file(output.name)