Best practices for Wan video generation

更新时间:
复制 MD 格式

Wan is an open-source AI model for text-to-video (T2V) and image-to-video (I2V) generation. PAI provides built-in ComfyUI workflows and API calls to deploy and generate videos with Wan on EAS.

Deploy standard ComfyUI (single-user)

Deployment configuration

Deploy a standard ComfyUI image service with custom deployment:

  1. Log on to the PAI console. Select a region on the top of the page. Then, select the desired workspace and click Elastic Algorithm Service (EAS).

  2. Click Deploy Service. In the Custom Model Deployment section, click Custom Deployment.

  3. On the Custom Deployment page, configure the following parameters.

    1. In the Environment Information section, configure the following parameters.

      Parameter

      Description

      Image Configuration

      From the Alibaba Cloud Images list, select comfyui > comfyui:1.9.

      Note

      1.9 is used in this example. Select the latest version during deployment.

      Storage Mount

      Mount external storage such as OSS or NAS to the EAS service. Generated videos are automatically saved to the mounted storage. For example, click OSS and configure:

      • Uri: Select an OSS bucket directory. To create a bucket and directory, see Console quick start. The bucket must be in the same region as the EAS service.

      • Mount Path: The mount path in the service instance, such as /code/data-oss.

      Command

      Auto-populated after you select an image.

      Set the --data-dir parameter to your configured mount path.

      In version 1.9, --data-dir is pre-configured. Update its value to your mount path, for example, python main.py --listen --port 8000 --data-dir /code/data-oss.

    2. In the Resource Information section, select a resource specification.

      Parameter

      Description

      Resource Type

      Select Public Resources.

      Deployment

      Select a Resource Specification with at least 48 GB of GPU memory per card. GU60 is recommended, such as ml.gu8is.c16m128.1-gu60.

    3. In the Network Information section, configure a VPC with internet access. Allow an EAS service to access the internet or an internal network.

      Note

      EAS services cannot access the internet by default. The I2V feature requires internet access to download images, so a VPC with internet access is required.

  4. After you configure the parameters, click Deploy.

Web UI

After deployment, build a workflow in the WebUI:

  1. Click the target service name to open the Overview page. In the upper-right corner, click Web applications.

  2. In the upper-left corner of the web UI page, select Workflow > Open, select a JSON workflow file, and then click Open.

    Example ComfyUI JSON workflows:

    • Image to Video (direct image upload): wanvideo_720P_I2V.json

      After loading, click upload in the load image section to upload an image.image

    • Image to Video (load image by URL): wanvideo_720P_I2V_URL.json

      After loading, set the image URL in the load image by URL section.image

  3. Click run at the bottom of the page to generate the video.

    Processing takes about 20 minutes. Results appear in the video combine section.image

Synchronous API

The standard service supports synchronous calls only, sending requests directly to an inference instance without the EAS queue service:

  1. Export the workflow JSON file.

    The API request body depends on the workflow configuration. First, configure the workflow in the WebUI, then select Workflow > Export (API) to save it as a JSON file.

    image

  2. View the invocation method.

    1. In the service list, click the service name. In the Basic Information section, click View Endpoint Information.

    2. In the Invocation Information dialog box, get the endpoint and token.

      Note
      • To use the Internet Endpoint, your client must have public internet access.

      • To use the VPC Endpoint, your client must be in the same VPC as the service.

      image

  3. Call the service.

    The following code example shows a complete call. Extract the output file path with data[prompt_id]["outputs"]["fullpath"].

    Set the endpoint and token as environment variables:

    # Replace with your service endpoint and token. 
    
    export SERVICE_URL="http://test****.115770327099****.cn-beijing.pai-eas.aliyuncs.com/"
    export TOKEN="MzJlMDNjMmU3YzQ0ZDJ*****************TMxZA=="

    Complete I2V call code

    from time import sleep
    
    import os
    import json
    import requests
    
    service_url     = os.getenv("SERVICE_URL")
    token           = os.getenv("TOKEN")
    image_url       = "https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/wan_fun/asset/3.png"
    prompt          = "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, showing natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, and the color of her clothes coordinates with the background, with an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm."
    negative_prompt = "Vibrant color tones, overexposure, static, blurry details, captions, style, artwork, painting, image, still, overall grayness, worst quality, low quality, JPEG compression artifacts, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, deformed limbs, finger fusion, static image, messy background, three legs, many people in the background, walking backwards"
    height          = 720
    width           = 1280
    steps           = 40
    num_frames      = 81
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    prompt_url = f"{service_url}/prompt"
    
    # Set the value of the `prompt` key in the payload to the content of the JSON file exported from the workflow.
    payload = """
    {
        "prompt":
        {
            "11": {
                "inputs": {
                "model_name": "umt5-xxl-enc-bf16.safetensors",
                "precision": "bf16",
                "load_device": "offload_device",
                "quantization": "disabled"
                },
                "class_type": "LoadWanVideoT5TextEncoder",
                "_meta": {
                "title": "Load WanVideo T5 TextEncoder"
                }
            },
            "16": {
                "inputs": {
                "positive_prompt": "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, showing natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, and the color of her clothes coordinates with the background, with an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm.",
                "negative_prompt": "Vibrant color tones, overexposure, static, blurry details, captions, style, artwork, painting, image, still, overall grayness, worst quality, low quality, JPEG compression artifacts, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, deformed limbs, finger fusion, static image, messy background, three legs, many people in the background, walking backwards",
                "force_offload": true,
                "speak_and_recognation": {
                    "__value__": [
                    false,
                    true
                    ]
                },
                "t5": [
                    "11",
                    0
                ],
                "model_to_offload": [
                    "22",
                    0
                ]
                },
                "class_type": "WanVideoTextEncode",
                "_meta": {
                "title": "WanVideo TextEncode"
                }
            },
            "22": {
                "inputs": {
                "model": "WanVideo/wan2.1_i2v_720p_14B_bf16.safetensors",
                "base_precision": "fp16",
                "quantization": "fp8_e4m3fn",
                "load_device": "offload_device",
                "attention_mode": "sageattn",
                "compile_args": [
                    "35",
                    0
                ]
                },
                "class_type": "WanVideoModelLoader",
                "_meta": {
                "title": "WanVideo Model Loader"
                }
            },
            "27": {
                "inputs": {
                "steps": 40,
                "cfg": 6,
                "shift": 5,
                "seed": 1057359483639287,
                "force_offload": true,
                "scheduler": "unipc",
                "riflex_freq_index": 0,
                "denoise_strength": 1,
                "batched_cfg": "",
                "rope_function": "comfy",
                "nocfg_begin": 0.7500000000000001,
                "nocfg_end": 1,
                "model": [
                    "22",
                    0
                ],
                "text_embeds": [
                    "16",
                    0
                ],
                "image_embeds": [
                    "63",
                    0
                ],
                "teacache_args": [
                    "52",
                    0
                ]
                },
                "class_type": "WanVideoSampler",
                "_meta": {
                "title": "WanVideo Sampler"
                }
            },
            "28": {
                "inputs": {
                "enable_vae_tiling": false,
                "tile_x": 272,
                "tile_y": 272,
                "tile_stride_x": 144,
                "tile_stride_y": 128,
                "vae": [
                    "38",
                    0
                ],
                "samples": [
                    "27",
                    0
                ]
                },
                "class_type": "WanVideoDecode",
                "_meta": {
                "title": "WanVideo Decode"
                }
            },
            "30": {
                "inputs": {
                "frame_rate": 16,
                "loop_count": 0,
                "filename_prefix": "WanVideoWrapper_I2V",
                "format": "video/h264-mp4",
                "pix_fmt": "yuv420p",
                "crf": 19,
                "save_metadata": true,
                "trim_to_audio": false,
                "pingpong": false,
                "save_output": true,
                "images": [
                    "28",
                    0
                ]
                },
                "class_type": "VHS_VideoCombine",
                "_meta": {
                "title": "Merge to video"
                }
            },
            "35": {
                "inputs": {
                "backend": "inductor",
                "fullgraph": false,
                "mode": "default",
                "dynamic": false,
                "dynamo_cache_size_limit": 64,
                "compile_transformer_blocks_only": true
                },
                "class_type": "WanVideoTorchCompileSettings",
                "_meta": {
                "title": "WanVideo Torch Compile Settings"
                }
            },
            "38": {
                "inputs": {
                "model_name": "WanVideo/Wan2_1_VAE_bf16.safetensors",
                "precision": "bf16"
                },
                "class_type": "WanVideoVAELoader",
                "_meta": {
                "title": "WanVideo VAE Loader"
                }
            },
            "52": {
                "inputs": {
                "rel_l1_thresh": 0.25,
                "start_step": 1,
                "end_step": -1,
                "cache_device": "offload_device",
                "use_coefficients": "true"
                },
                "class_type": "WanVideoTeaCache",
                "_meta": {
                "title": "WanVideo TeaCache"
                }
            },
            "59": {
                "inputs": {
                "clip_name": "wanx_clip_vision_h.safetensors"
                },
                "class_type": "CLIPVisionLoader",
                "_meta": {
                "title": "CLIP Vision Loader"
                }
            },
            "63": {
                "inputs": {
                "width": [
                    "66",
                    1
                ],
                "height": [
                    "66",
                    2
                ],
                "num_frames": 81,
                "noise_aug_strength": 0.030000000000000006,
                "start_latent_strength": 1,
                "end_latent_strength": 1,
                "force_offload": true,
                "start_image": [
                    "66",
                    0
                ],
                "vae": [
                    "38",
                    0
                ],
                "clip_embeds": [
                    "65",
                    0
                ]
                },
                "class_type": "WanVideoImageToVideoEncode",
                "_meta": {
                "title": "WanVideo ImageToVideo Encode"
                }
            },
            "65": {
                "inputs": {
                "strength_1": 1,
                "strength_2": 1,
                "crop": "center",
                "combine_embeds": "average",
                "force_offload": true,
                "tiles": 4,
                "ratio": 0.20000000000000004,
                "clip_vision": [
                    "59",
                    0
                ],
                "image_1": [
                    "66",
                    0
                ]
                },
                "class_type": "WanVideoClipVisionEncode",
                "_meta": {
                "title": "WanVideo ClipVision Encode"
                }
            },
            "66": {
                "inputs": {
                "width": 832,
                "height": 480,
                "upscale_method": "lanczos",
                "keep_proportion": false,
                "divisible_by": 16,
                "crop": "disabled",
                "image": [
                    "68",
                    0
                ]
                },
                "class_type": "ImageResizeKJ",
                "_meta": {
                "title": "Image Resize (KJ)"
                }
            },
            "68": {
                "inputs": {
                "url": "https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/wan_fun/asset/3.png",
                "cache": true
                },
                "class_type": "LoadImageByUrl //Browser",
                "_meta": {
                "title": "Load Image By URL"
                }
            }
        }
    }
    """
    
    session = requests.session()
    session.headers.update({"Authorization":token})
    
    payload = json.loads(payload)
    payload["prompt"]["16"]["inputs"]["positive_prompt"] = prompt
    payload["prompt"]["16"]["inputs"]["negative_prompt"] = negative_prompt
    payload["prompt"]["27"]["inputs"]["steps"] = steps
    payload["prompt"]["66"]["inputs"]["height"] = height
    payload["prompt"]["66"]["inputs"]["width"] = width
    payload["prompt"]["63"]["inputs"]["num_frames"] = num_frames
    payload["prompt"]["68"]["inputs"]["url"] = image_url
    
    response = session.post(url=f'{prompt_url}', json=payload)
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    prompt_id = data["prompt_id"]
    print(data)
    
    while 1:
        url = f"{service_url}/history/{prompt_id}"
    
        response = session.get(url=f'{url}')
    
        if response.status_code != 200:
            raise Exception(response.content)
       
        data = response.json()
        if len(data) != 0:
            print(data[prompt_id]["outputs"])
            if len(data[prompt_id]["outputs"]) == 0:
                print("Could not find the 'outputs' key in the output JSON. The process may have failed. Please check the logs.")
            break
        else:
            sleep(1)

    Code breakdown

    • Get the prompt ID.

      import requests
      import os
      
      service_url = os.getenv("SERVICE_URL")
      token = os.getenv("TOKEN")
      url = f"{service_url}/prompt"
      
      payload = {
          "prompt":
          # Request body omitted for brevity.
      }
      
      session = requests.session()
      session.headers.update({"Authorization":token})
      
      
      response = session.post(url=f'{url}', json=payload)
      if response.status_code != 200:
          raise Exception(response.content)
      
      data = response.json()
      print(data)

      Set the prompt key to the JSON content exported with Export (API).

      The initial request returns a response similar to this example:

      {
          "prompt_id": "021ebc5b-e245-4e37-8bd3-00f7b949****",
          "number": 5,
          "node_errors": {}
      }
    • Retrieve the final prediction result.

      import requests
      import os
      # Construct the request URL.
      service_url = os.getenv("SERVICE_URL")
      token = os.getenv("TOKEN")
      url = f"{service_url}history/<prompt_id>"
      
      session = requests.session()
      session.headers.update({"Authorization":f'{token}'})
      
      response = session.get(url=f'{url}')
      
      if response.status_code != 200:
          raise Exception(response.content)
      
      data = response.json()
      print(data)

      Replace <prompt_id> with the prompt ID from the previous step.

      The endpoint returns a response similar to this example:

      {
          "130bcd6b-5bb5-496c-9c8c-3a1359a0****": {
              "prompt": ...omitted for brevity,
              "outputs": {
                  "30": {
                    'gifs': [
                      {
                        'filename': 'WanVideo2_1_T2V_00002.mp4', 
                        'subfolder': '', 
                        'type': 'output', 
                        'format': 'video/h264-mp4', 
                        'frame_rate': 16.0, 'workflow': 
                        'WanVideo2_1_T2V_00002.png', 'fullpath': 
                        '/code/data-oss/output/WanVideo2_1_T2V_00002.mp4'
                      }
                    ]
                  }
              },
              "status": {
                  "status_str": "success",
                  "completed": true,
                  "messages": ...omitted for brevity,
              }
          }
      }

ComfyUI API for high-concurrency scenarios

Configuration

Note

To switch from standard to API mode, delete the existing service and create a new one.

Deploy a ComfyUI API image service with custom deployment:

  1. Log on to the PAI console. Select a region on the top of the page. Then, select the desired workspace and click Elastic Algorithm Service (EAS).

  2. Click Deploy Service. In the Custom Model Deployment section, click Custom Deployment.

  3. On the Custom Deployment page, configure the following parameters.

    1. In the Environment Information section, configure the following parameters.

      Parameter

      Description

      Image Configuration

      From the Alibaba Cloud image list, select comfyui > comfyui:1.9-api.

      Note

      1.9 is used in this example. Select the latest version during deployment.

      Storage Mount

      Mount external storage such as OSS or NAS for the EAS service. Generated videos are automatically saved to this storage. For example, click OSS and configure:

      • Uri: Select an OSS bucket directory. To create a bucket and directory, see Console quick start. The bucket must be in the same region as the EAS service.

      • Mount Path: The mount path in the service instance, such as /code/data-oss.

      Command to Run

      Auto-populated after you select an image.

      Set the --data-dir parameter to your configured mount path.

      In version 1.9, --data-dir is pre-configured. Update its value to your mount path, for example, python main.py --listen --port 8000 --api --data-dir /code/data-oss.

    2. In the Resource Information section, select a resource specification.

      Parameter

      Description

      Resource Type

      Select Public Resources.

      Deployment

      Select a Resource Specification with at least 48 GB of GPU memory per card. GU60 is recommended, such as ml.gu8is.c16m128.1-gu60.

    3. In the Asynchronous Queue section, configure the Maximum Data for A Single Input Request and Maximum Data for A Single Output parameters. A common setting for these parameters is 1024 KB.

      Note

      Set appropriate queue sizes to prevent rejected requests, dropped samples, failed responses, or queue blocking.

    4. In the Network Information section, select a VPC with public internet access. Allow an EAS service to access public or private network resources.

      Note

      EAS services cannot access the public internet by default. The I2V feature requires internet access to download images, so a VPC with public internet access is required.

  4. After configuring the parameters, click Deploy.

Asynchronous API call

The API supports asynchronous calls only through the /api_prompt endpoint via the EAS queue service. Send a request to an input queue and subscribe to an output queue for results:

  1. In the Invocation Information dialog box, get the endpoint and token.

    Note
    • To use the internet endpoint, your client must have public internet access.

    • To use the VPC endpoint, your client must be in the same VPC as the service.

  2. Run the following command in your terminal to install the eas_prediction SDK.

    pip install eas_prediction  --user
  3. Call the service.

    The following code example shows a complete call. Extract the output file path with json.loads(x.data.decode('utf-8'))[1]["data"]["output"]["gifs"][0]["fullpath"].

    Set the endpoint and token as environment variables:

    # Replace the values with your service endpoint and token. 
    
    export SERVICE_URL="http://test****.115770327099****.cn-beijing.pai-eas.aliyuncs.com/"
    export TOKEN="MzJlMDNjMmU3YzQ0ZDJ*****************TMxZA=="

    Complete I2V calling code

    import json
    import os
    import requests
    from urllib.parse import urlparse, urlunparse
    from eas_prediction import QueueClient
    
    service_url     = os.getenv("SERVICE_URL")
    token           = os.getenv("TOKEN")
    
    image_url       = "https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/wan_fun/asset/3.png"
    prompt          = "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, showing natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, and the color of her clothes coordinates with the background, with an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm."
    negative_prompt = "Vibrant color tones, overexposure, static, blurry details, captions, style, artwork, painting, image, still, overall grayness, worst quality, low quality, JPEG compression artifacts, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, deformed limbs, finger fusion, static image, messy background, three legs, many people in the background, walking backwards"
    height          = 720
    width           = 1280
    steps           = 40
    num_frames      = 81
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    
    def parse_service_url(service_url):
        parsed = urlparse(service_url)
        service_domain = f"{parsed.scheme}://{parsed.netloc}"
        path_parts = [p for p in parsed.path.strip('/').split('/') if p]
        service_name = path_parts[-1]
        return service_domain, service_name
    
    
    service_domain, service_name = parse_service_url(service_url)
    print(f"service_domain: {service_domain}, service_name: {service_name}.")
    
    # Set the payload to the content of the JSON file for the corresponding workflow.
    payload = """
    {
        "11": {
            "inputs": {
            "model_name": "umt5-xxl-enc-bf16.safetensors",
            "precision": "bf16",
            "load_device": "offload_device",
            "quantization": "disabled"
            },
            "class_type": "LoadWanVideoT5TextEncoder",
            "_meta": {
            "title": "Load WanVideo T5 TextEncoder"
            }
        },
        "16": {
            "inputs": {
            "positive_prompt": "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, showing natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, and the color of her clothes coordinates with the background, with an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm.",
            "negative_prompt": "Vibrant color tones, overexposure, static, blurry details, captions, style, artwork, painting, image, still, overall grayness, worst quality, low quality, JPEG compression artifacts, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, deformed limbs, finger fusion, static image, messy background, three legs, many people in the background, walking backwards",
            "force_offload": true,
            "speak_and_recognation": {
                "__value__": [
                false,
                true
                ]
            },
            "t5": [
                "11",
                0
            ],
            "model_to_offload": [
                "22",
                0
            ]
            },
            "class_type": "WanVideoTextEncode",
            "_meta": {
            "title": "WanVideo TextEncode"
            }
        },
        "22": {
            "inputs": {
            "model": "WanVideo/wan2.1_i2v_720p_14B_bf16.safetensors",
            "base_precision": "fp16",
            "quantization": "fp8_e4m3fn",
            "load_device": "offload_device",
            "attention_mode": "sageattn",
            "compile_args": [
                "35",
                0
            ]
            },
            "class_type": "WanVideoModelLoader",
            "_meta": {
            "title": "WanVideo Model Loader"
            }
        },
        "27": {
            "inputs": {
            "steps": 40,
            "cfg": 6,
            "shift": 5,
            "seed": 1057359483639287,
            "force_offload": true,
            "scheduler": "unipc",
            "riflex_freq_index": 0,
            "denoise_strength": 1,
            "batched_cfg": "",
            "rope_function": "comfy",
            "nocfg_begin": 0.7500000000000001,
            "nocfg_end": 1,
            "model": [
                "22",
                0
            ],
            "text_embeds": [
                "16",
                0
            ],
            "image_embeds": [
                "63",
                0
            ],
            "teacache_args": [
                "52",
                0
            ]
            },
            "class_type": "WanVideoSampler",
            "_meta": {
            "title": "WanVideo Sampler"
            }
        },
        "28": {
            "inputs": {
            "enable_vae_tiling": false,
            "tile_x": 272,
            "tile_y": 272,
            "tile_stride_x": 144,
            "tile_stride_y": 128,
            "vae": [
                "38",
                0
            ],
            "samples": [
                "27",
                0
            ]
            },
            "class_type": "WanVideoDecode",
            "_meta": {
            "title": "WanVideo Decode"
            }
        },
        "30": {
            "inputs": {
            "frame_rate": 16,
            "loop_count": 0,
            "filename_prefix": "WanVideoWrapper_I2V",
            "format": "video/h264-mp4",
            "pix_fmt": "yuv420p",
            "crf": 19,
            "save_metadata": true,
            "trim_to_audio": false,
            "pingpong": false,
            "save_output": true,
            "images": [
                "28",
                0
            ]
            },
            "class_type": "VHS_VideoCombine",
            "_meta": {
            "title": "Combine into video"
            }
        },
        "35": {
            "inputs": {
            "backend": "inductor",
            "fullgraph": false,
            "mode": "default",
            "dynamic": false,
            "dynamo_cache_size_limit": 64,
            "compile_transformer_blocks_only": true
            },
            "class_type": "WanVideoTorchCompileSettings",
            "_meta": {
            "title": "WanVideo Torch Compile Settings"
            }
        },
        "38": {
            "inputs": {
            "model_name": "WanVideo/Wan2_1_VAE_bf16.safetensors",
            "precision": "bf16"
            },
            "class_type": "WanVideoVAELoader",
            "_meta": {
            "title": "WanVideo VAE Loader"
            }
        },
        "52": {
            "inputs": {
            "rel_l1_thresh": 0.25,
            "start_step": 1,
            "end_step": -1,
            "cache_device": "offload_device",
            "use_coefficients": "true"
            },
            "class_type": "WanVideoTeaCache",
            "_meta": {
            "title": "WanVideo TeaCache"
            }
        },
        "59": {
            "inputs": {
            "clip_name": "wanx_clip_vision_h.safetensors"
            },
            "class_type": "CLIPVisionLoader",
            "_meta": {
            "title": "CLIP vision loader"
            }
        },
        "63": {
            "inputs": {
            "width": [
                "66",
                1
            ],
            "height": [
                "66",
                2
            ],
            "num_frames": 81,
            "noise_aug_strength": 0.030000000000000006,
            "start_latent_strength": 1,
            "end_latent_strength": 1,
            "force_offload": true,
            "start_image": [
                "66",
                0
            ],
            "vae": [
                "38",
                0
            ],
            "clip_embeds": [
                "65",
                0
            ]
            },
            "class_type": "WanVideoImageToVideoEncode",
            "_meta": {
            "title": "WanVideo ImageToVideo Encode"
            }
        },
        "65": {
            "inputs": {
            "strength_1": 1,
            "strength_2": 1,
            "crop": "center",
            "combine_embeds": "average",
            "force_offload": true,
            "tiles": 4,
            "ratio": 0.20000000000000004,
            "clip_vision": [
                "59",
                0
            ],
            "image_1": [
                "66",
                0
            ]
            },
            "class_type": "WanVideoClipVisionEncode",
            "_meta": {
            "title": "WanVideo ClipVision Encode"
            }
        },
        "66": {
            "inputs": {
            "width": 832,
            "height": 480,
            "upscale_method": "lanczos",
            "keep_proportion": false,
            "divisible_by": 16,
            "crop": "disabled",
            "image": [
                "68",
                0
            ]
            },
            "class_type": "ImageResizeKJ",
            "_meta": {
            "title": "image resize (KJ)"
            }
        },
        "68": {
            "inputs": {
            "url": "https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/wan_fun/asset/3.png",
            "cache": true
            },
            "class_type": "LoadImageByUrl //Browser",
            "_meta": {
            "title": "Load Image By URL"
            }
        }
    }
    """
    
    
    session = requests.session()
    session.headers.update({"Authorization":token})
    
    payload = json.loads(payload)
    payload["16"]["inputs"]["positive_prompt"] = prompt
    payload["16"]["inputs"]["negative_prompt"] = negative_prompt
    payload["27"]["inputs"]["steps"] = steps
    payload["66"]["inputs"]["height"] = height
    payload["66"]["inputs"]["width"] = width
    payload["63"]["inputs"]["num_frames"] = num_frames
    payload["68"]["inputs"]["url"] = image_url
    
    response = session.post(url=f'{service_url}/api_prompt?task_id=txt2img', json=payload)
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    sink_queue = QueueClient(service_domain, f'{service_name}/sink')
    sink_queue.set_token(token)
    sink_queue.init()
    
    watcher = sink_queue.watch(0, 1, auto_commit=False)
    for x in watcher.run():
        if 'task_id' in x.tags:
            print('index {} task_id is {}'.format(x.index, x.tags['task_id']))
        print(f'index {x.index} data is {x.data}')
        print(json.loads(x.data.decode('utf-8'))[1]["data"]["output"]["gifs"][0]["fullpath"])
        sink_queue.commit(x.index)

    Step-by-step code explanation

    • Send a request:

      import requests, io, base64, os
      from PIL import Image, PngImagePlugin
      
      url = os.getenv("SERVICE_URL")
      token = os.getenv("TOKEN")
      session = requests.session()
      session.headers.update({"Authorization": token})
      
      work_flow = {
          '3':
          ...omitted
      }  # Note: Unlike the standard version, the request body does not have a "prompt" key.
      
      for i in range(1):
          payload = work_flow
          response = session.post(url=f'{url}/api_prompt?task_id=txt2img_{i}', json=payload)
          if response.status_code != 200:
            exit(f"send request error:{response.content}")
          else:
            print(f"send {i} success, index is {response.content}")

      The work_flow variable is the request body containing the JSON content exported in API format. In Python, boolean values (True and False) must be capitalized.

    • Subscribe to the results:

      from eas_prediction import QueueClient
      import os
      
      token = os.getenv("TOKEN")
      sink_queue = QueueClient('<service_domain>', '<service_name>/sink')
      sink_queue.set_token(token)
      sink_queue.init()
      
      watcher = sink_queue.watch(0, 1, auto_commit=False)
      for x in watcher.run():
          if 'task_id' in x.tags:
              print('index {} task_id is {}'.format(x.index, x.tags['task_id']))
          print(f'index {x.index} data is {x.data}')
          sink_queue.commit(x.index)

      The following table describes the key parameters.

      Parameter

      Description

      <service_domain>

      The service domain from the endpoint, for example, 139699392458****.cn-hangzhou.pai-eas.aliyuncs.com.

      <service_name>

      The name of your EAS service.

      The following is an example response:

      index 2 task_id is txt2img
      index 2 data is b'[{"status_code": 200}, {"type": "executed", "data": {"node": "30", "display_node": "30", "output": {"gifs": [{"filename": "WanVideoWrapper_I2V_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4", "frame_rate": 16.0, "workflow": "WanVideoWrapper_I2V_00001.png", "fullpath": "/code/data-oss/output/WanVideoWrapper_I2V_00001.mp4"}]}, "prompt_id": "e20b1cb0-fb48-4ddd-92e5-3c783b064a2c"}}, {"e20b1cb0-fb48-4ddd-92e5-3c783b064a2c": {"prompt": [1, "e20b1cb0-fb48-4ddd-92e5-3c783b064a2c", {"11": {"inputs": {"model_name": "umt5-xxl-enc-bf16.safetensors", "precision": "bf16", "load_device": "offload_device", "quantization": "disabled"}, "class_type": "LoadWanVideoT5TextEncoder", "_meta": {"title": "Load WanVideo T5 TextEncoder"}}, "16": {"inputs": {"positive_prompt": "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, showing natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, and the color of her clothes coordinates with the background, with an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm.", "negative_prompt": "Vibrant color tones, overexposure, static, blurry details, captions, style, artwork, painting, image, still, overall grayness, worst quality, low quality, JPEG compression artifacts, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, deformed limbs, finger fusion, static image, messy background, three legs, many people in the background, walking backwards", "force_offload": true, "speak_and_recognation": {"__value__": [false, true]}, "t5": ["11", 0], "model_to_offload": ["22", 0]}, "class_type": "WanVideoTextEncode", "_meta": {"title": "WanVideo TextEncode"}}, "22": {"inputs": {"model": "WanVideo/wan2.1_i2v_720p_14B_bf16.safetensors", "base_precision": "fp16", "quantization": "fp8_e4m3fn", "load_device": "offload_device", "attention_mode": "sageattn", "compile_args": ["35", 0]}, "class_type": "WanVideoModelLoader", "_meta": {"title": "WanVideo Model Loader"}}, "27": {"inputs": {"steps": 40, "cfg": 6.0, "shift": 5.0, "seed": 1057359483639287, "force_offload": true, "scheduler": "unipc", "riflex_freq_index": 0, "denoise_strength": 1.0, "batched_cfg": false, "rope_function": "comfy", "nocfg_begin": 0.7500000000000001, "nocfg_end": 1.0, "model": ["22", 0], "text_embeds": ["16", 0], "image_embeds": ["63", 0], "teacache_args": ["52", 0]}, "class_type": "WanVideoSampler", "_meta": {"title": "WanVideo Sampler"}}, "28": {"inputs": {"enable_vae_tiling": false, "tile_x": 272, "tile_y": 272, "tile_stride_x": 144, "tile_stride_y": 128, "vae": ["38", 0], "samples": ["27", 0]}, "class_type": "WanVideoDecode", "_meta": {"title": "WanVideo Decode"}}, "30": {"inputs": {"frame_rate": 16.0, "loop_count": 0, "filename_prefix": "WanVideoWrapper_I2V", "format": "video/h264-mp4", "pix_fmt": "yuv420p", "crf": 19, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true, "images": ["28", 0]}, "class_type": "VHS_VideoCombine", "_meta": {"title": "merge to video"}}, "35": {"inputs": {"backend": "inductor", "fullgraph": false, "mode": "default", "dynamic": false, "dynamo_cache_size_limit": 64, "compile_transformer_blocks_only": true}, "class_type": "WanVideoTorchCompileSettings", "_meta": {"title": "WanVideo Torch Compile Settings"}}, "38": {"inputs": {"model_name": "WanVideo/Wan2_1_VAE_bf16.safetensors", "precision": "bf16"}, "class_type": "WanVideoVAELoader", "_meta": {"title": "WanVideo VAE Loader"}}, "52": {"inputs": {"rel_l1_thresh": 0.25, "start_step": 1, "end_step": -1, "cache_device": "offload_device", "use_coefficients": true}, "class_type": "WanVideoTeaCache", "_meta": {"title": "WanVideo TeaCache"}}, "59": {"inputs": {"clip_name": "wanx_clip_vision_h.safetensors"}, "class_type": "CLIPVisionLoader", "_meta": {"title": "CLIP vision loader"}}, "63": {"inputs": {"width": ["66", 1], "height": ["66", 2], "num_frames": 81, "noise_aug_strength": 0.030000000000000006, "start_latent_strength": 1.0, "end_latent_strength": 1.0, "force_offload": true, "start_image": ["66", 0], "vae": ["38", 0], "clip_embeds": ["65", 0]}, "class_type": "WanVideoImageToVideoEncode", "_meta": {"title": "WanVideo ImageToVideo Encode"}}, "65": {"inputs": {"strength_1": 1.0, "strength_2": 1.0, "crop": "center", "combine_embeds": "average", "force_offload": true, "tiles": 4, "ratio": 0.20000000000000004, "clip_vision": ["59", 0], "image_1": ["66", 0]}, "class_type": "WanVideoClipVisionEncode", "_meta": {"title": "WanVideo ClipVision Encode"}}, "66": {"inputs": {"width": 1280, "height": 720, "upscale_method": "lanczos", "keep_proportion": false, "divisible_by": 16, "crop": "disabled", "image": ["68", 0]}, "class_type": "ImageResizeKJ", "_meta": {"title": "image resize (KJ)"}}, "68": {"inputs": {"url": "https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/wan_fun/asset/3.png", "cache": true}, "class_type": "LoadImageByUrl //Browser", "_meta": {"title": "Load Image By URL"}}}, {"client_id": "unknown"}, ["30"]], "outputs": {"30": {"gifs": [{"filename": "WanVideoWrapper_I2V_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4", "frame_rate": 16.0, "workflow": "WanVideoWrapper_I2V_00001.png", "fullpath": "/code/data-oss/output/WanVideoWrapper_I2V_00001.mp4"}]}}, "status": {"status_str": "success", "completed": true, "messages": [["execution_start", {"prompt_id": "e20b1cb0-fb48-4ddd-92e5-3c783b064a2c", "timestamp": 1746512702895}], ["execution_cached", {"nodes": ["11", "16", "22", "27", "28", "30", "35", "38", "52", "59", "63", "65", "66", "68"], "prompt_id": "e20b1cb0-fb48-4ddd-92e5-3c783b064a2c", "timestamp": 1746512702899}], ["execution_success", {"prompt_id": "e20b1cb0-fb48-4ddd-92e5-3c783b064a2c", "timestamp": 1746512702900}]]}, "meta": {"30": {"node_id": "30", "display_node": "30", "parent_node": null, "real_node_id": "30"}}}}, {"30": {"gifs": [{"filename": "WanVideoWrapper_I2V_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4", "frame_rate": 16.0, "workflow": "WanVideoWrapper_I2V_00001.png", "fullpath": "/code/data-oss/output/WanVideoWrapper_I2V_00001.mp4"}]}}]'

Examples

T2V and I2V share the same deployment and invocation workflow. T2V does not require internet access, so VPC configuration is not needed.

Load the example workflow file (wanvideo_720P_T2V.json) in the WebUI as described in Using the WebUI. Enter a text prompt in the WanVideo TextEncode input box and click Run.imageTo make an API call, see the code example below.

Set the endpoint and token as environment variables:

# Replace with your endpoint and token.

export SERVICE_URL="http://test****.115770327099****.cn-beijing.pai-eas.aliyuncs.com/"
export TOKEN="MzJlMDNjMmU3YzQ0ZDJ*****************TMxZA=="

Synchronous API call

Complete code for a T2V call

from time import sleep

import os
import json
import requests

service_url     = os.getenv("SERVICE_URL")
token           = os.getenv("TOKEN")
prompt          = "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, with natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, the color of which coordinates with the background, contributing to an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm."
negative_prompt = "Vibrant colors, overexposed, static, blurry details, subtitles, style, artwork, painting, picture, motionless, overall grayness, worst quality, low quality, JPEG artifacts, ugly, mutilated, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, malformed limbs, fused fingers, static image, cluttered background, three legs, crowded background, walking backwards"
height          = 720
width           = 1280
steps           = 40
num_frames      = 81

if service_url[-1] == "/":
    service_url = service_url[:-1]

prompt_url = f"{service_url}/prompt"

# Define the workflow payload as a JSON string.
payload = """
{
    "prompt":
    {
        "11": {
            "inputs": {
            "model_name": "umt5-xxl-enc-bf16.safetensors",
            "precision": "bf16",
            "load_device": "offload_device",
            "quantization": "disabled"
            },
            "class_type": "LoadWanVideoT5TextEncoder",
            "_meta": {
            "title": "Load WanVideo T5 TextEncoder"
            }
        },
        "16": {
            "inputs": {
            "positive_prompt": "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, with natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, the color of which coordinates with the background, contributing to an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm.",
            "negative_prompt": "Vibrant colors, overexposed, static, blurry details, subtitles, style, artwork, painting, picture, motionless, overall grayness, worst quality, low quality, JPEG artifacts, ugly, mutilated, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, malformed limbs, fused fingers, static image, cluttered background, three legs, crowded background, walking backwards",
            "force_offload": true,
            "speak_and_recognation": {
                "__value__": [
                false,
                true
                ]
            },
            "t5": [
                "11",
                0
            ]
            },
            "class_type": "WanVideoTextEncode",
            "_meta": {
            "title": "WanVideo TextEncode"
            }
        },
        "22": {
            "inputs": {
            "model": "WanVideo/wan2.1_t2v_14B_bf16.safetensors",
            "base_precision": "fp16",
            "quantization": "fp8_e4m3fn",
            "load_device": "offload_device",
            "attention_mode": "sageattn",
            "compile_args": [
                "35",
                0
            ]
            },
            "class_type": "WanVideoModelLoader",
            "_meta": {
            "title": "WanVideo Model Loader"
            }
        },
        "27": {
            "inputs": {
            "steps": 40,
            "cfg": 6.000000000000002,
            "shift": 5.000000000000001,
            "seed": 1057359483639287,
            "force_offload": true,
            "scheduler": "unipc",
            "riflex_freq_index": 0,
            "denoise_strength": 1,
            "batched_cfg": false,
            "rope_function": "default",
            "nocfg_begin": 0.7500000000000001,
            "nocfg_end": 1,
            "model": [
                "22",
                0
            ],
            "text_embeds": [
                "16",
                0
            ],
            "image_embeds": [
                "37",
                0
            ],
            "teacache_args": [
                "52",
                0
            ]
            },
            "class_type": "WanVideoSampler",
            "_meta": {
            "title": "WanVideo Sampler"
            }
        },
        "28": {
            "inputs": {
            "enable_vae_tiling": true,
            "tile_x": 272,
            "tile_y": 272,
            "tile_stride_x": 144,
            "tile_stride_y": 128,
            "vae": [
                "38",
                0
            ],
            "samples": [
                "27",
                0
            ]
            },
            "class_type": "WanVideoDecode",
            "_meta": {
            "title": "WanVideo Decode"
            }
        },
        "30": {
            "inputs": {
            "frame_rate": 16,
            "loop_count": 0,
            "filename_prefix": "WanVideo2_1_T2V",
            "format": "video/h264-mp4",
            "pix_fmt": "yuv420p",
            "crf": 19,
            "save_metadata": true,
            "trim_to_audio": false,
            "pingpong": false,
            "save_output": true,
            "images": [
                "28",
                0
            ]
            },
            "class_type": "VHS_VideoCombine",
            "_meta": {
            "title": "combine into video"
            }
        },
        "35": {
            "inputs": {
            "backend": "inductor",
            "fullgraph": false,
            "mode": "default",
            "dynamic": false,
            "dynamo_cache_size_limit": 64,
            "compile_transformer_blocks_only": true
            },
            "class_type": "WanVideoTorchCompileSettings",
            "_meta": {
            "title": "WanVideo Torch Compile Settings"
            }
        },
        "37": {
            "inputs": {
            "width": 832,
            "height": 480,
            "num_frames": 81
            },
            "class_type": "WanVideoEmptyEmbeds",
            "_meta": {
            "title": "WanVideo Empty Embeds"
            }
        },
        "38": {
            "inputs": {
            "model_name": "WanVideo/Wan2_1_VAE_bf16.safetensors",
            "precision": "bf16"
            },
            "class_type": "WanVideoVAELoader",
            "_meta": {
            "title": "WanVideo VAE Loader"
            }
        },
        "52": {
            "inputs": {
            "rel_l1_thresh": 0.25000000000000006,
            "start_step": 1,
            "end_step": -1,
            "cache_device": "offload_device",
            "use_coefficients": "true"
            },
            "class_type": "WanVideoTeaCache",
            "_meta": {
            "title": "WanVideo TeaCache"
            }
        }
    }
}
"""

session = requests.session()
session.headers.update({"Authorization":token})

payload = json.loads(payload)
payload["prompt"]["16"]["inputs"]["positive_prompt"] = prompt
payload["prompt"]["16"]["inputs"]["negative_prompt"] = negative_prompt
payload["prompt"]["27"]["inputs"]["steps"] = steps
payload["prompt"]["37"]["inputs"]["height"] = height
payload["prompt"]["37"]["inputs"]["width"] = width
payload["prompt"]["37"]["inputs"]["num_frames"] = num_frames

response = session.post(url=f'{prompt_url}', json=payload)
if response.status_code != 200:
    raise Exception(response.content)

data = response.json()
prompt_id = data["prompt_id"]
print(data)

while 1:
    url = f"{service_url}/history/{prompt_id}"

    response = session.get(url=f'{url}')

    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    if len(data) != 0:
        print(data[prompt_id]["outputs"])
        if len(data[prompt_id]["outputs"]) == 0:
            print("Could not find the 'outputs' key in the output JSON. The process may have failed. Please check the logs.")
        break
    else:
        sleep(1)

Asynchronous API call

Complete code for a T2V call

import json
import os
import requests
from urllib.parse import urlparse, urlunparse
from eas_prediction import QueueClient

service_url     = os.getenv("SERVICE_URL")
token           = os.getenv("TOKEN")

prompt          = "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, with natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, the color of which coordinates with the background, contributing to an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm."
negative_prompt = "Vibrant colors, overexposed, static, blurry details, subtitles, style, artwork, painting, picture, motionless, overall grayness, worst quality, low quality, JPEG artifacts, ugly, mutilated, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, malformed limbs, fused fingers, static image, cluttered background, three legs, crowded background, walking backwards"
height          = 720
width           = 1280
steps           = 40
num_frames      = 81

if service_url[-1] == "/":
    service_url = service_url[:-1]


def parse_service_url(service_url):
    parsed = urlparse(service_url)
    service_domain = f"{parsed.scheme}://{parsed.netloc}"
    path_parts = [p for p in parsed.path.strip('/').split('/') if p]
    service_name = path_parts[-1]
    return service_domain, service_name


service_domain, service_name = parse_service_url(service_url)
print(f"service_domain: {service_domain}, service_name: {service_name}.")

# Define the workflow payload as a JSON string.
payload = """
{
    "11": {
        "inputs": {
        "model_name": "umt5-xxl-enc-bf16.safetensors",
        "precision": "bf16",
        "load_device": "offload_device",
        "quantization": "disabled"
        },
        "class_type": "LoadWanVideoT5TextEncoder",
        "_meta": {
        "title": "Load WanVideo T5 TextEncoder"
        }
    },
    "16": {
        "inputs": {
        "positive_prompt": "A blonde woman with her head tilted back and eyes closed, her expression serene and dreamy. Her hair is very long and fluffy, with natural waves as if blown by the wind. In the background, some blurred flowers are falling, creating a romantic and dreamy atmosphere. She is wearing a top with lace decorations, the color of which coordinates with the background, contributing to an overall soft tone. Light shines down from above, illuminating her face and hair, making the entire image appear very soft and warm.",
        "negative_prompt": "Vibrant colors, overexposed, static, blurry details, subtitles, style, artwork, painting, picture, motionless, overall grayness, worst quality, low quality, JPEG artifacts, ugly, mutilated, extra fingers, poorly drawn hands, poorly drawn face, deformed, disfigured, malformed limbs, fused fingers, static image, cluttered background, three legs, crowded background, walking backwards",
        "force_offload": true,
        "speak_and_recognation": {
            "__value__": [
            false,
            true
            ]
        },
        "t5": [
            "11",
            0
        ]
        },
        "class_type": "WanVideoTextEncode",
        "_meta": {
        "title": "WanVideo TextEncode"
        }
    },
    "22": {
        "inputs": {
        "model": "WanVideo/wan2.1_t2v_14B_bf16.safetensors",
        "base_precision": "fp16",
        "quantization": "fp8_e4m3fn",
        "load_device": "offload_device",
        "attention_mode": "sageattn",
        "compile_args": [
            "35",
            0
        ]
        },
        "class_type": "WanVideoModelLoader",
        "_meta": {
        "title": "WanVideo Model Loader"
        }
    },
    "27": {
        "inputs": {
        "steps": 40,
        "cfg": 6.000000000000002,
        "shift": 5.000000000000001,
        "seed": 1057359483639287,
        "force_offload": true,
        "scheduler": "unipc",
        "riflex_freq_index": 0,
        "denoise_strength": 1,
        "batched_cfg": false,
        "rope_function": "default",
        "nocfg_begin": 0.7500000000000001,
        "nocfg_end": 1,
        "model": [
            "22",
            0
        ],
        "text_embeds": [
            "16",
            0
        ],
        "image_embeds": [
            "37",
            0
        ],
        "teacache_args": [
            "52",
            0
        ]
        },
        "class_type": "WanVideoSampler",
        "_meta": {
        "title": "WanVideo Sampler"
        }
    },
    "28": {
        "inputs": {
        "enable_vae_tiling": true,
        "tile_x": 272,
        "tile_y": 272,
        "tile_stride_x": 144,
        "tile_stride_y": 128,
        "vae": [
            "38",
            0
        ],
        "samples": [
            "27",
            0
        ]
        },
        "class_type": "WanVideoDecode",
        "_meta": {
        "title": "WanVideo Decode"
        }
    },
    "30": {
        "inputs": {
        "frame_rate": 16,
        "loop_count": 0,
        "filename_prefix": "WanVideo2_1_T2V",
        "format": "video/h264-mp4",
        "pix_fmt": "yuv420p",
        "crf": 19,
        "save_metadata": true,
        "trim_to_audio": false,
        "pingpong": false,
        "save_output": true,
        "images": [
            "28",
            0
        ]
        },
        "class_type": "VHS_VideoCombine",
        "_meta": {
        "title": "combine into video"
        }
    },
    "35": {
        "inputs": {
        "backend": "inductor",
        "fullgraph": false,
        "mode": "default",
        "dynamic": false,
        "dynamo_cache_size_limit": 64,
        "compile_transformer_blocks_only": true
        },
        "class_type": "WanVideoTorchCompileSettings",
        "_meta": {
        "title": "WanVideo Torch Compile Settings"
        }
    },
    "37": {
        "inputs": {
        "width": 832,
        "height": 480,
        "num_frames": 81
        },
        "class_type": "WanVideoEmptyEmbeds",
        "_meta": {
        "title": "WanVideo Empty Embeds"
        }
    },
    "38": {
        "inputs": {
        "model_name": "WanVideo/Wan2_1_VAE_bf16.safetensors",
        "precision": "bf16"
        },
        "class_type": "WanVideoVAELoader",
        "_meta": {
        "title": "WanVideo VAE Loader"
        }
    },
    "52": {
        "inputs": {
        "rel_l1_thresh": 0.25000000000000006,
        "start_step": 1,
        "end_step": -1,
        "cache_device": "offload_device",
        "use_coefficients": "true"
        },
        "class_type": "WanVideoTeaCache",
        "_meta": {
        "title": "WanVideo TeaCache"
        }
    }
}
"""

session = requests.session()
session.headers.update({"Authorization":token})

payload = json.loads(payload)
payload["16"]["inputs"]["positive_prompt"] = prompt
payload["16"]["inputs"]["negative_prompt"] = negative_prompt
payload["27"]["inputs"]["steps"] = steps
payload["37"]["inputs"]["height"] = height
payload["37"]["inputs"]["width"] = width
payload["37"]["inputs"]["num_frames"] = num_frames

response = session.post(url=f'{service_url}/api_prompt?task_id=txt2img', json=payload)
if response.status_code != 200:
    raise Exception(response.content)

data = response.json()
sink_queue = QueueClient(service_domain, f'{service_name}/sink')
sink_queue.set_token(token)
sink_queue.init()

watcher = sink_queue.watch(0, 1, auto_commit=False)
for x in watcher.run():
    if 'task_id' in x.tags:
        print('index {} task_id is {}'.format(x.index, x.tags['task_id']))
    print(f'index {x.index} data is {x.data}')
    print(json.loads(x.data.decode('utf-8'))[1]["data"]["output"]["gifs"][0]["fullpath"])
    sink_queue.commit(x.index)

References

For custom models, ComfyUI plug-ins, and troubleshooting, see AI video generation - ComfyUI deployment.