All Products
Search
Document Center

Platform For AI:Best practices for Wan video generation

Last Updated:Feb 02, 2026

Wan is an open-source video generation model that supports T2V (text-to-video) and I2V (image-to-video) generations. PAI provides customized JSON workflows and API calling methods to help you use the Wan model in ComfyUI to generate high-quality videos. This topic uses I2V as an example to show how to deploy the ComfyUI service and use Wan to generate videos.

Deploy a standard ComfyUI service (for single-user use)

Deployment configuration

Follow these steps to deploy a standard ComfyUI service using 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

      Select comfyui > comfyui:1.9 from the Alibaba Cloud Images list.

      Note

      In this example, 1.9 is the image version. Due to fast iterations, select the latest image version during deployment.

      Mount storage

      Mount an external storage data source, such as Object Storage Service (OSS) or Network Attached Storage (NAS), to the EAS service. The service automatically saves generated videos to the mounted data source. To use OSS as an example, click OSS and configure the following parameters:

      • Uri: Select the source directory of an OSS bucket. For information about how to create a bucket and a directory, see Get started with the OSS console. The bucket must be in the same region as the EAS service.

      • Mount Path: The target path to mount in the service instance. For example, /code/data-oss.

      Command

      The system automatically populates the command after you select an image.
      Set the --data-dir mount directory in the Command section. This directory must match the Mount Path you configured.
      For the 1.9 version of the image, --data-dir is pre-configured. You only need to update it to the Mount Path you configured. 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 Resources

      Choose a Resource Type. Video generation requires more GPU memory than image generation. Select a resource specification with a single-card memory of at least 48 GB. We recommend the GU60 instance type, such as ml.gu8is.c16m128.1-gu60.

    3. In the Network information section, set a virtual private cloud (VPC) with Internet access. For more information, see Configure Internet access for VPC.

      Note

      By default, an EAS service cannot access the internet. The I2V feature requires an internet connection to download images. Therefore, you must configure a VPC with public network access for the EAS service.

  4. After you configure the parameters, click Deploy.

Use WebUI

After the service is deployed, you can build a workflow and call the service on the WebUI page. Follow these steps:

  1. Click View Web App in the Service Type column.

  2. In the upper-left corner of the WebUI page, select WorkflowOpen, select a JSON workflow file, and then click Open.

    PAI has integrated various acceleration algorithms into ComfyUI. The following ComfyUI JSON workflows are optimized for speed and quality:

    • Image-to-Video (load an image from a URL): wanvideo_720P_I2V.json

      After the workflow is loaded, you can click upload in the Load Image section to upload or update image files.image

    • I2V (load image URL): wanvideo_720P_I2V_URL.json

      After the workflow is loaded, set the image URL in the Load Image By URL section to update images.image

  3. Click the Run button at the bottom of the page to generate the video.

    The process takes about 20 minutes. The result appears in the Video Combine area on the right.image

Make a synchronous call through the API

The standard service only supports synchronous calls. These calls request the inference instance directly, bypassing the EAS queue service. Follow these steps:

  1. Export the workflow JSON file.

    The API request body for ComfyUI depends on the workflow configuration. First, configure your workflow in the WebUI. Then, export it as a JSON file by selecting Workflow > Export (API).

    image

  2. View endpoint information.

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

    2. In the Invocation Method dialog box, obtain the endpoint and token.

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

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

      image

  3. Call the service.

    The following code provides a complete example of how to call the service and retrieve the result. The outputs object of the final response includes the full OSS path of the output video. For details about the response structure, see the code breakdown section.

    The example code retrieves the EAS service endpoint and token from environment variables. You can run the following commands in your terminal to add temporary environment variables that are valid only for the current session:

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

    Complete I2V calling 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"
    
    # Please configure the value of prompt in the payload as the content of the JSON file corresponding to 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("Find no outputs key in output json, the process may be failed, please check the log")
            break
        else:
            sleep(1)
    

    Step-by-step explanation of the above code

    • Send a POST request and get the prompt ID from the response.

      The payload is the request body. The value of the prompt key is the JSON content exported from the workflow.

      import requests
      import os
      
      service_url = os.getenv("SERVICE_URL")
      token = os.getenv("TOKEN")
      url = f"{service_url}/prompt"
      
      payload = {
          "prompt":
          Request body... omitted
      }
      
      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)

      The following is an example of the response from the initial request:

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

      Use the prompt_id obtained in the previous step to poll the /history/<prompt_id> endpoint until a result is available.

      import requests
      import os
      # Create 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)

      The following is a truncated example of the final response. You can find the full path to the generated video at data[<prompt_id>]["outputs"][<node_id>]["gifs"][0]["fullpath"].

      {
          "130bcd6b-5bb5-496c-9c8c-3a1359a0****": {
              "prompt": ... omitted,
              "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,
              }
          }
      }

Deploy an API version of ComfyUI (for high-concurrency scenarios)

Deployment configuration

Note

If you have an existing standard ComfyUI service and want to switch to the API version, we recommend deleting the standard service and creating a new one.

Use the custom deployment method to deploy the ComfyUI API service. Perform the following steps:

  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:

      Parameter

      Description

      Image Configuration

      Select comfyui > comfyui:1.9-api from the Aibaba Cloud Images list.

      Note

      In this example, 1.9 is the image version. Due to fast iterations, select the latest image version during deployment.

      Storage mount

      Mount an external storage data source, such as OSS or NAS, to the EAS service. The videos generated by calling the service are automatically saved to the corresponding data source. To use OSS as an example, click OSS and configure the parameters as described in the standard deployment section.

      Command

      The system automatically populates the command after you select an image.

      Set the --data-dir mount directory and include the --api flag in the Command section. This directory must match the Mount Path you configured.

      For example, python main.py --listen --port 8000 --api --data-dir /code/data-oss.

    2. In the Resource Information section, select the resource specifications.

      Parameter

      Description

      Resource Type

      Select Public Resources.

      Deployment Resources

      Select Resource Type. Because video generation requires more GPU video memory than image generation, we recommend a type with GPU memory of no less than 48 GB per card, such as the GU60 types (for example, ml.gu8is.c16m128.1-gu60).

    3. In the Asynchronous Queue section, set Maximum Data for A Single Input Request and Maximum Data for A Single Output. The standard value is 1024 KB.

      Note

      We recommend setting a reasonable queue data size to prevent issues like request rejections, response failures, or queue blockages.

    4. In the Network information section, set a VPC with Internet access, including the VPC, vSwitch, and Security Group parameters. For more information, see Configure Internet access for VPC.

      Note

      The EAS service does not have Internet access by default. However, because the I2V function needs to download images from the internet, a VPC with Internet access is required.

  4. After you configure the parameters, click Deploy.

Asynchronous API call

The API version only supports asynchronous calls, which are made to the /api_prompt endpoint. In an asynchronous call, you send a request to an input queue and subscribe to an output queue to receive the results. Follow these steps:

  1. View the endpoint information.

    Click Invocation Information in the Service Type column of the service. In the Invocation Method panel, view the endpoint and token on the Asynchronous Invocation tab.

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

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

    image

  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 provides a complete example of a call. You can get the full OSS path of the output video from json.loads(x.data.decode('utf-8'))[1]["data"]["output"]["gifs"][0]["fullpath"] in the final result.

    The example code retrieves the EAS service endpoint and token from environment variables. You can run the following commands in your terminal to add temporary environment variables that are valid only for the current session:

    # Set your 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}.")
    
    # Please configure the payload as the content of the JSON file corresponding to the 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": "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["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 explanation of the above code

    • Send a request:

      The request body is the JSON exported from the workflow. Unlike the standard version, the API version does not wrap the JSON in a prompt key.

      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
      }  # Different from the Standard Edition, there is no 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}")
      
    • Subscribe to results:

      Use the eas_prediction SDK to create a QueueClient that watches the output (sink) queue for 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 configuration items.

      Item

      Description

      <service_domain>

      The domain part of your service endpoint. For example, 139699392458****.cn-hangzhou.pai-eas.aliyuncs.com.

      <service_name>

      The name of your EAS service.

      A command output similar to the following one is returned:

      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": "\\u4e00\\u4f4d\\u91d1\\u53d1\\u5973\\u5b50\\uff0c\\u5979\\u4ef0\\u5934\\u95ed\\u773c\\uff0c\\u8868\\u60c5\\u5b81\\u9759\\u800c\\u68a6\\u5e7b\\u3002\\u5979\\u7684\\u5934\\u53d1\\u975e\\u5e38\\u957f\\u4e14\\u84ec\\u677e\\uff0c\\u5448\\u73b0\\u51fa\\u81ea\\u7136\\u7684\\u6ce2\\u6d6a\\u72b6\\uff0c\\u4eff\\u4f5b\\u88ab\\u98ce\\u5439\\u62c2\\u3002\\u80cc\\u666f\\u4e2d\\u6709\\u4e00\\u4e9b\\u6a21\\u7cca\\u7684\\u82b1\\u6735\\u98d8\\u843d\\uff0c\\u8425\\u9020\\u51fa\\u4e00\\u79cd\\u6d6a\\u6f2b\\u548c\\u68a6\\u5e7b\\u7684\\u6c1b\\u56f4\\u3002\\u5979\\u7a7f\\u7740\\u4e00\\u4ef6\\u5e26\\u6709\\u857e\\u4e1d\\u88c5\\u9970\\u7684\\u4e0a\\u8863\\uff0c\\u8863\\u670d\\u7684\\u989c\\u8272\\u4e0e\\u80cc\\u666f\\u76f8\\u534f\\u8c03\\uff0c\\u6574\\u4f53\\u8272\\u8c03\\u67d4\\u548c\\u3002\\u5149\\u7ebf\\u4ece\\u4e0a\\u65b9\\u7167\\u5c04\\u4e0b\\u6765\\uff0c\\u7167\\u4eae\\u4e86\\u5979\\u7684\\u8138\\u5e9e\\u548c\\u5934\\u53d1\\uff0c\\u4f7f\\u6574\\u4e2a\\u753b\\u9762\\u663e\\u5f97\\u975e\\u5e38\\u67d4\\u548c\\u548c\\u6e29\\u6696\\u3002", "negative_prompt": "\\u8272\\u8c03\\u8273\\u4e3d\\uff0c\\u8fc7\\u66dd\\uff0c\\u9759\\u6001\\uff0c\\u7ec6\\u8282\\u6a21\\u7cca\\u4e0d\\u6e05\\uff0c\\u5b57\\u5e55\\uff0c\\u98ce\\u683c\\uff0c\\u4f5c\\u54c1\\uff0c\\u753b\\u4f5c\\uff0c\\u753b\\u9762\\uff0c\\u9759\\u6b62\\uff0c\\u6574\\u4f53\\u53d1\\u7070\\uff0c\\u6700\\u5dee\\u8d28\\u91cf\\uff0c\\u4f4e\\u8d28\\u91cf\\uff0cJPEG\\u538b\\u7f29\\u6b8b\\u7559\\uff0c\\u4e11\\u964b\\u7684\\uff0c\\u6b8b\\u7f3a\\u7684\\uff0c\\u591a\\u4f59\\u7684\\u624b\\u6307\\uff0c\\u753b\\u5f97\\u4e0d\\u597d\\u7684\\u624b\\u90e8\\uff0c\\u753b\\u5f97\\u4e0d\\u597d\\u7684\\u8138\\u90e8\\uff0c\\u7578\\u5f62\\u7684\\uff0c\\u6bc1\\u5bb9\\u7684\\uff0c\\u5f62\\u6001\\u7578\\u5f62\\u7684\\u80a2\\u4f53\\uff0c\\u624b\\u6307\\u878d\\u5408\\uff0c\\u9759\\u6b62\\u4e0d\\u52a8\\u7684\\u753b\\u9762\\uff0c\\u6742\\u4e71\\u7684\\u80cc\\u666f\\uff0c\\u4e09\\u6761\\u817f\\uff0c\\u80cc\\u666f\\u4eba\\u5f88\\u591a\\uff0c\\u5012\\u7740\\u8d70", "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": "\\u5408\\u5e76\\u4e3a\\u89c6\\u9891"}}, "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\\u89c6\\u89c9\\u52a0\\u8f7d\\u5668"}}, "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": "\\u56fe\\u50cf\\u7f29\\u653e\\uff08KJ\\uff09"}}, "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"}]}}]'

Appendix: More examples

The procedures for using T2V and I2V are similar. You can deploy and call the service by following the steps described in this article. However, T2V does not require a public network connection, so you do not need to configure a VPC when deploying the EAS service.

You can use the example workflow file (wanvideo_720P_T2V.json) to test the WebUI call process. Refer to Load the workflow on the WebUI page to load the workflow on the WebUI page. Then, enter a text prompt in the WanVideo TextEncode input box and click Run.imageIf you need to make an API call, see the complete code examples below.

The example code retrieves the EAS service endpoint and token from environment variables. You can run the following commands in your terminal to add temporary environment variables that are valid only for the current session:

# Set your endpoint and token. 

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

Synchronous API call

Complete T2V calling code

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, 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"

# Please configure the value of prompt in the payload as the content of the JSON file corresponding to 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
            ]
            },
            "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": "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"
            }
        },
        "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("Find no outputs key in output json, the process may be failed, please check the log")
        break
    else:
        sleep(1)

Asynchronous API call

Complete T2V 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")

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}.")

# Please configure the payload as the content of the JSON file corresponding to the 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
        ]
        },
        "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": "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"
        }
    },
    "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

To learn more about ComfyUI deployment and features, including loading custom models, integrating ComfyUI plug-ins, and common issues, see AI video generation - ComfyUI deployment.