ComfyUI API调用

部署ComfyUI项目后,您不仅可以通过WebUI页面生成图像,还可以通过API调用的方式实现图像生成。

背景信息

原生的ComfyUI API并不适用Serverless场景,因此我们提供了ComfyUI Serverless API,以帮助您更好地进行文生图API调用。

ComfyUI Serverless API将原生ComfyUI的出图、上传图片、获取状态及获取图片合并成了同一个接口,使得可以借助多实例并发出图的能力,加速您的出图任务。

开启API调用模式

导出工作流APIJSON文件

在进行API调用之前,请先在ComfyUI WebUI中搭建并调试您所需的工作流,并确认该工作流可以成功生成图像。 调试完毕后,在ComfyUI界面点击菜单项工作流 > 导出(API),您的浏览器将会下载一个包含工作流定义的JSON文件。

image

JSON文件描述了当前工作流是如何工作的。

{
  "3": {
    "inputs": {
      "seed": 740511732689542,
      "steps": 20,
      "cfg": 8,
      "sampler_name": "euler",
      "scheduler": "normal",
      "denoise": 1,
      "model": [
        "4",
        0
      ],
      "positive": [
        "6",
        0
      ],
      "negative": [
        "7",
        0
      ],
      "latent_image": [
        "5",
        0
      ]
    },
    "class_type": "KSampler",
    "_meta": {
      "title": "K采样器"
    }
  },
  "4": {
    "inputs": {
      "ckpt_name": "sd-v1-5-inpainting.ckpt"
    },
    "class_type": "CheckpointLoaderSimple",
    "_meta": {
      "title": "加载检查点"
    }
  },
  "5": {
    "inputs": {
      "width": 512,
      "height": 512,
      "batch_size": 1
    },
    "class_type": "EmptyLatentImage",
    "_meta": {
      "title": "空潜空间图像"
    }
  },
  "6": {
    "inputs": {
      "text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
      "clip": [
        "4",
        1
      ]
    },
    "class_type": "CLIPTextEncode",
    "_meta": {
      "title": "CLIP文本编码(提示)"
    }
  },
  "7": {
    "inputs": {
      "text": "text, watermark",
      "clip": [
        "4",
        1
      ]
    },
    "class_type": "CLIPTextEncode",
    "_meta": {
      "title": "CLIP文本编码(提示)"
    }
  },
  "8": {
    "inputs": {
      "samples": [
        "3",
        0
      ],
      "vae": [
        "4",
        2
      ]
    },
    "class_type": "VAEDecode",
    "_meta": {
      "title": "VAE解码"
    }
  },
  "9": {
    "inputs": {
      "filename_prefix": "ComfyUI",
      "images": [
        "8",
        0
      ]
    },
    "class_type": "SaveImage",
    "_meta": {
      "title": "保存图像"
    }
  }
}

发布API

工作流JSON文件准备就绪后,接下来需要为您的ComfyUI函数配置API访问入口。切换到API调用页签,单击发布API

image

修改GPU规格和弹性策略

在配置ComfyUI API接口时,您需要根据预期的调用负载和性能要求,合理设置函数的资源规格。主要考量以下参数:

  • GPU规格:选择不同卡型和配置的GPU实例来处理API调用请求。不同的ComfyUI工作流,其计算复杂度和资源消耗差异巨大。我们建议您根据实际工作流进行测试,评估不同GPU规格在任务处理时间、成本上的表现,以选择最优配置。

  • 弹性策略:

    • 按量模式:完全按量计费,没有请求的阶段不会产生计算费用,稀疏调用场景下会有冷启动,首个请求响应较慢。

    • 极速模式:将ComfyUI缓存至预置快照中,此时仅会产生极低的快照存储费用。通过这种方式,可以有效避免冷启动问题,显著提升低频调用的响应速度。当所需要的实例数超过预置快照的数量时,仍然可能会弹出按量实例,此时可能部分请求会经历冷启动的阶段。

image

完成API的发布配置后,平台会自动生成一个可用于外部调用的API地址。参照页面内的API调用文档,并结合前面导出的JSON文件,即可构造API请求,完成AI生图业务调用。

image

配置图片的读取与转存

在发布完成之后,在API 调用 > 配置管理页签,配置图片的读取与转存。

  • 角色权限:为ComfyUI授权,选择一个角色,角色拥有从OSS读入图片或将图片输出至OSS的权限。

  • 生成结果转存至OSS:允许在配置相关参数后,自动将图片转存至OSS。

    • Bucket存储桶:用于存储图片的OSS,如果与当前ComfyUI处于不同的地域,可能会存在网络延迟,并产生额外的流量费用,建议您使用和ComfyUI相同的地域。

    • 路径:图片在OSS Bucket存储桶中的路径。

    • 链接有效期:转存成功后,为您生成可访问资源的临时链接。未配置时,则仅转存图片,不会生成链接。

image

重要

控制台开启OSS图片转存之后,请确保在调用的请求query中配置了output_oss=true,才会将生成的图片转存至OSS Bucket中。

自动转存图片的调用示例如下:

curl "{您的公网请求地址}/api/serverless/run?output_oss=true" -X POST -H "Content-Type: application/json" -d '{ 您的文生图工作流JSON}'

API定义

ComfyUI导出的工作流JSON文件可以直接用于ComfyUI Serverless API调用,同时额外增加如下能力。

  • KSampler节点,seed为-1时,自动替换为随机数。

  • LoadImage节点,支持如下形式的图片。

    • 文件名:从input目录读入图片,如example.png。

    • URL:从URL读入图片,如https://example.com/example.png

    • OSS:从OSS读入图片(需要配置具有OSS权限的角色),如oss://bucket.oss-cn-hangzhou.aliyuncs.com/example.png。

    • base64:从base64读入图片,如iVBORw0KGgoAAAANSUhE...。注意,base64会导致请求体大小远超过常规请求,这可能会突破函数计算对于请求体长度的限制(同步请求最大允许32MB,异步请求最大允许128KB)。

HTTP出图

Method

POST

Path

/api/serverless/run

Request

Header

Key

Description

Content-Type

application/json

Query

Key

Type

Description

output_oss

bool

是否将结果输出至OSS

output_base64

bool

是否将结果输出为base64

stream

bool

是否以流式输出进度

Body

参考ComfyUI原生API,key为节点id,value为节点信息

Response

Stream

参考ComfyUI原生API WebSocket信息,每次流式的输出为WebSocket 的一条message。每次出图结束,会增加类型为serverless_api的一行message,与非流式结果相同。

Non-Stream Body

Key

Type

Description

.type

string

固定为 serverless_api

.data

object

出图结果

.data.prompt_id

string

本次出图的 prompt_id,由 ComfyUI 生成,可用于后续查询历史记录(数据在内存中,实例轮转后销毁)

.data.results

array

出图结果图片列表

.data.results[*].node_id

string

输出图片的节点 id

.data.results[*].batch_id

number

当前图片在节点中输出的序号

.data.results[*].output

object

输出的图片

.data.results[*].output.raw

object

ComfyUI 原生输出包含信息

.data.results[*].output.raw.filename

string

文件名

.data.results[*].output.raw.type

string

输出图片的类型(输出的文件夹)

.data.results[*].output.raw.subfolder

string

输出的子文件夹

.data.results[*].output.raw.filepath

string

输出的目录

.data.results[*].output.base64

object

输出的base64信息

.data.results[*].output.base64.content

string

当前图片的base64(需要开启output_base64

.data.results[*].output.oss

object

输出的OSS信息

.data.results[*].output.oss.region

string

OSS region

.data.results[*].output.oss.bucket

string

OSS bucket

.data.results[*].output.oss.object

string

当前图片输出到OSS的名称(需要开启 output_oss

.data.results[*].output.oss.url

string

当前图片在OSS中的过期时间(需要开启 output_oss

WebSocket出图

该接口逻辑与HTTP模式一致,输入参数通过websocket.send()传入。

Protocol

WebSocket

Path

/api/serverless/ws

Request

Query

Key

Type

Description

output_oss

bool

是否将结果输出至 OSS

output_base64

bool

是否将结果输出为 base64

stream

bool

是否以流式输出进度

Send Message

参考ComfyUI原生API,key为节点id,value为节点信息。

Response

Receive Message

参考ComfyUI原生API WebSocket信息,每次流式的输出为WebSocket 的一条message。 每次出图结束,会增加类型为serverless_api的一行message,与非流式结果相同。

状态获取

获取出图任务状态,由于FaaS无状态的特性,为确保可以正确持久化状态信息,需要进行存储配置。

存储至文件系统:挂载NAS、OSS到函数实例,文件将会被存储至{挂载目录}/output/serverless_api

Method

GET

Path

/api/serverless/status

Request

Query

Key

Type

Description

task_id

string

任务ID(可从AP调用 > 运行日志 > 调用请求 > 请求ID获取)

Response

Body

Key

Type

Description

.

array

流式与WebSocket模式输出的内容数组

.[*].type

string

事件类型

.[*].data

object

对应事件内容

调用方式

同步调用出图

仅关心出图结果,不关心出图进度,可以通过同步HTTP调用。

import requests
import json

# 填入您的公网请求地址
endpoint = "https://xxx.xxx.fcapp.run"


def txt2img(prompt):
    resp = requests.post(f"{endpoint}/api/serverless/run?output_oss=true", json=prompt)

    return resp.json()


with open("./api.json", "r", encoding="utf-8") as f:
    result = txt2img(json.loads(f.read()))
    images = result["data"]["results"]
    for image in images:
        # 会输出 serverless-api-output/b74f33c0-69ba-4e4b-b584-6700890b7f51.png
        print(image["output"]["oss"]["object"])

流式出图

如果需要实时获取出图进度,可以考虑通过同步HTTP调用,同时读取流式输出。

import requests
import json

# 填入您的公网请求地址
endpoint = "https://xxx.xxx.fcapp.run"


def txt2img(prompt):

    with requests.post(
        f"{endpoint}/api/serverless/run?output_oss=true&stream=true",
        json=prompt,
        stream=True,
    ) as response:
        for line in response.iter_lines():
            # 只有 data: 开头的是数据行
            if line.startswith(b"data: "):
                data = json.loads(line[6:])
                # 随着出图进度的变化,会输出当前的节点状态
                # 部分可能出现的行如下
                # {'type': 'status', 'data': {'status': {'exec_info': {'queue_remaining': 1}}, 'sid': '200c62b4-111f-4994-a4a4-20a28c944408'}}
                # {'type': 'executing', 'data': {'node': '4'}}
                # {'type': 'status', 'data': {'status': {'exec_info': {'queue_remaining': 1}}}}
                # {'type': 'execution_start', 'data': {'prompt_id': '453484bb-1749-4fe3-9f2c-31664b8c3aec', 'timestamp': 1747821408300}}
                # {'type': 'execution_cached', 'data': {'nodes': [], 'prompt_id': '453484bb-1749-4fe3-9f2c-31664b8c3aec', 'timestamp': 1747821408304}}
                # {'type': 'executing', 'data': {'node': '4', 'display_node': '4', 'prompt_id': '453484bb-1749-4fe3-9f2c-31664b8c3aec'}}
                # {'type': 'progress', 'data': {'value': 1, 'max': 20, 'prompt_id': '453484bb-1749-4fe3-9f2c-31664b8c3aec', 'node': '3'}}
                # {'type': 'executing', 'data': {'node': None, 'prompt_id': '453484bb-1749-4fe3-9f2c-31664b8c3aec'}}
                # 'type': 'serverless_api', 'data': ... }
                print(data)

                # 最终结果的类型的是 serverless_api
                if data["type"] == "serverless_api":
                    return data


with open("./api.json", "r", encoding="utf-8") as f:
    result = txt2img(json.loads(f.read()))
    images = result["data"]["results"]
    for image in images:
        # 会输出 serverless-api-output/b74f33c0-69ba-4e4b-b584-6700890b7f51.png
        print(image["output"]["oss"]["object"])

异步调用出图

如果不希望使用时被阻塞住,可以通过函数计算自带的异步调用能力,并通过获取状态接口轮询获取结果。异步调用能力由函数计算提供,请参考 InvokeFunction - 调用函数

from time import sleep
import requests
import json
import uuid

# 填入您的公网请求地址
endpoint = "https://xxx.xxx.fcapp.run"


def txt2img(prompt):
    task_id = str(uuid.uuid4())

    requests.post(
        f"{endpoint}/api/serverless/run?output_oss=true",
        json=prompt,
        headers={"X-Fc-Invocation-Type": "Async", "X-Fc-Trace-Id": task_id},
    )

    while True:
        resp = requests.get(
            f"{endpoint}/api/serverless/status?task_id={task_id}",
        )
        statusList = resp.json()
        # 会以数组的方式输出当前出图任务的所有中间状态
        print(statusList)

        if len(statusList) > 0:
            if statusList[-1]["type"] == "serverless_api":
                return statusList[-1]

        sleep(1)


with open("./api.json", "r", encoding="utf-8") as f:
    result = txt2img(json.loads(f.read()))
    images = result["data"]["results"]
    for image in images:
        # 会输出 serverless-api-output/b74f33c0-69ba-4e4b-b584-6700890b7f51.png
        print(image["output"]["oss"]["object"])

WebSocket出图

如果已经接入了WebSocket原生API,并通过WebSocket获取出图进度,可以使用WebSocket生图接口,在最小改动的情况下,使用ComfyUI Serverless API。

import json
import websocket  # pip install websocket-client

# 填入您的公网请求地址
# 注意要用 ws 或 wss 协议
endpoint = "wss://xxx.xxx.fcapp.run"


def txt2img(prompt):
    result = {}

    def on_open(ws):
        ws.send(json.dumps(prompt))

    def on_message(ws, msg):
        # 随着出图进度的变化,会输出当前的节点状态
        data = json.loads(msg)
        print(data)

        if data["type"] == "serverless_api":
            nonlocal result
            result = data
            ws.close()

    ws = websocket.WebSocketApp(
        f"{endpoint}/api/serverless/ws?output_oss=true",
        on_open=on_open,
        on_message=on_message,
    )

    ws.run_forever()

    return result


with open("./api.json", "r", encoding="utf-8") as f:
    result = txt2img(json.loads(f.read()))
    images = result["data"]["results"]
    for image in images:
        # 会输出 serverless-api-output/b74f33c0-69ba-4e4b-b584-6700890b7f51.png
        print(image["output"]["oss"]["object"])

查看调用日志及监控

API 调用 > 运行日志页面,您可以查看历史的请求信息、对应的运行日志。方便您明确出图请求的具体耗时,以及排查出图失败时的错误原因。

image

image

实例列表

API 调用 > 实例列表页面,您可以查看当前正在运行的实例列表,查看每一个实例的日志和监控指标,并且在必要时,可以登录实例进行Shell操作。

image

常见问题

工作流找不到了怎么办?

如果您已经出图成功,可以在左侧菜单找到出图历史记录,在队列中选择图片,单击右键,选择加载工作流进行恢复。

image