ComfyUI的api接口文档:comfyui-api.md · YY/wailikeji-chatgpt - Gitee.com
1、调用API的使用流程
1)用户设计好工作流
2)将工作流保存为API形式的json内容
3)调用ComfyUI的API,输入json内容作为prompts参数传入,得到最终结果
2、绘图接口:POST/prompt
注意:该接口只做绘图任务的下发,然后返回任务ID信息。并不会直接返回最终的结果图!
与webui的api不同的是,comfyui的api并没有单独区分文生图、图生图的接口,而是所有的绘图任务的下发全部都使用POST /prompt。那具体是文生图、图生图、又或者是换脸、倒推关键词等,取决于你的参数!
需要上传的参数只有两个请求参数:
名称 | 类型 | 必选 | 说明 |
client_id | string | 是 | 任务ID,由客户端生成,用于标记任务是谁发起的 |
prompt | json | 是 | 任务参数 |
返回参数:
名称 | 类型 | 说明 |
prompt_id | string | 任务ID |
number | int | 当前任务序号,可用于后续获取需要等待任务数的计算 |
node_errors | json | 错误信息 |
返回示例:
{
"prompt_id": "bd2cfa2c-de87-4258-89cc-d8791bc13a61",
"number": 501,
"node_errors": {}
}
使用说明:
clientId:任务ID,由客户端生成,用于标记任务是谁发起的,相当于告诉comfyui,该绘图任务是由用户A发起的,后续comfyui就会通过websocket将属于用户A的绘图信息推送给你。
3、websocket:/ws?clientId=XXXXXXXX
client_id后面的参数即为上面/prompt接口中上传给comfyui的client_id,假如没有上传client_id,那comfyui就不知道连上该websocket的用户是谁,也就无法进行信息推送!comfyui拿到client_id后,即可知道当前是哪个用户,后续就会通过websocket将属于该用户的绘图信息精准推送给他。
注意:websocket只需做监听处理,无需通过websocket向comfyui发送任何消息。
websocket数据解析:
主要有两种数据格式:
1、文本数据,文本数据主要通知以下几个绘图信息:
通知任务变更、当前执行的步骤、进度
2、二进制数据,即图片预览信息
(1)任务变更通知
{
"type":"status",
"data":{
"status":{
"exec_info":{
"queue_remaining":7
}
}
}
}
当你收到type为status信息时,这是comfyui在告诉你,当前任务数发生变更,queue_remaining是指当前还有多少个任务需要处理。
注意,此处的queue_remaining并不是告诉你在你的任务之前还有多少个任务需要处理!而是总的!
所以,当还没轮到你的绘图任务时,显示还需等待多少个任务,你就需要借助comfyui的另一个接口:GET /queue:获取详细任务队列信息,正在运行的以及挂起的。该接口会返回挂起的任务信息,其中有prompt_id信息和number信息,你可以根据这number信息获取到当前任务排在第几位。
(2)当前任务开始执行
{
"type":"execution_start",
"data":{
"prompt_id":"3935f7c3-ec38-4d94-843f-86fe86c6d384"
}
}
当你收到type为execution_start信息时,这是comfyui在告诉你,你的任务id,prompt_id为“3935f7c3-ec38-4d94-843f-86fe86c6d384”的任务当前正在被执行
(3)当前任务执行的步骤信息
{
"type":"executing",
"data":{
"node":"5",
"prompt_id":"3935f7c3-ec38-4d94-843f-86fe86c6d384"
}
}
当你收到type为executing信息时,这是comfyui在告诉你,你的任务id,prompt_id为“3935f7c3-ec38-4d94-843f-86fe86c6d384”的任务当前正在执行节点5的步骤。
(4)当前进度信息
{
"type":"progress",
"data":{
"value":1,
"max":10
}
}
当你收到type为progress信息时,这是comfyui在告诉你,当前步骤执行的进度,value是当前的步数,max是总的步数。
(5)绘图结束
{
"type":"executing",
"data":{
"node":null,
"prompt_id":"37099310-a790-44f4-8d13-4f4d5f69c891"
}
}
绘图结束时,type类型仍然是executing,和前面的(3)是一样的,区别主要在于node为null,也就是当type=executing,且node=null的时候,说明流程已经跑完,此时需要通过接口GET /history/{prompt_id}获取输出的图片信息。底下是通过history获取到的图片信息。
{
"37099310-a790-44f4-8d13-4f4d5f69c891": {
略。。。。。。。。。。
"outputs": {
"18": {
"images": [
{
"filename": "ComfyUI_temp_slqio_00001_.png",
"subfolder": "",
"type": "temp"
},
{
"filename": "ComfyUI_temp_slqio_00002_.png",
"subfolder": "",
"type": "temp"
},
{
"filename": "ComfyUI_temp_slqio_00003_.png",
"subfolder": "",
"type": "temp"
},
{
"filename": "ComfyUI_temp_slqio_00004_.png",
"subfolder": "",
"type": "temp"
}
]
},
"22": {
"images": [
{
"filename": "ComfyUI_temp_rfvdr_00001_.png",
"subfolder": "",
"type": "temp"
},
{
"filename": "ComfyUI_temp_rfvdr_00002_.png",
"subfolder": "",
"type": "temp"
},
{
"filename": "ComfyUI_temp_rfvdr_00003_.png",
"subfolder": "",
"type": "temp"
},
{
"filename": "ComfyUI_temp_rfvdr_00004_.png",
"subfolder": "",
"type": "temp"
}
]
},
"24": {
"images": [
{
"filename": "ComfyUI_00702_.png",
"subfolder": "",
"type": "output"
},
{
"filename": "ComfyUI_00703_.png",
"subfolder": "",
"type": "output"
},
{
"filename": "ComfyUI_00704_.png",
"subfolder": "",
"type": "output"
},
{
"filename": "ComfyUI_00705_.png",
"subfolder": "",
"type": "output"
}
]
}
}
}
}
outputs中的内容就是最终生成的图片信息。
4、基于python的代码实战
按照websocket库:pip install websocket-client
import websocket
import uuid
import json
import urllib.request
import urllib.parse
from PIL import Image
import io
# 设置服务器地址和客户端
server_address = "127.0.0.1:18188"
client_id = str(uuid.uuid4())
# 定义向服务器发送提示的函数
def queue_prompt(prompt):
p = {"prompt": prompt, "client_id": client_id}
data = json.dumps(p).encode('utf-8')
req = urllib.request.Request("http://{}/prompt".format(server_address), data=data)
return json.loads(urllib.request.urlopen(req).read())
# 定义从服务器下载图像数据的函数
def get_image(filename, subfolder, folder_type):
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
url_values = urllib.parse.urlencode(data)
with urllib.request.urlopen("http://{}/view?{}".format(server_address, url_values)) as response:
return response.read()
# 定义获取历史记录的函数
def get_history(prompt_id):
with urllib.request.urlopen("http://{}/history/{}".format(server_address, prompt_id)) as response:
return json.loads(response.read())
# 定义通过WebSocket接收消息并下载图像的函数
def get_images(ws, prompt):
prompt_id = queue_prompt(prompt)['prompt_id']
output_images = {}
while True:
out = ws.recv()
if isinstance(out, str):
message = json.loads(out)
if message['type'] == 'executing':
data = message['data']
if data['node'] is None and data['prompt_id'] == prompt_id:
break # 执行完成
else:
continue # 预览是二进制数据
history = get_history(prompt_id)[prompt_id]
for o in history['outputs']:
for node_id in history['outputs']:
node_output = history['outputs'][node_id]
if 'images' in node_output:
images_output = []
for image in node_output['images']:
image_data = get_image(image['filename'], image['subfolder'], image['type'])
images_output.append(image_data)
output_images[node_id] = images_output
return output_images
# 示例JSON字符串,表示要使用的提示
prompt_text = """
{
"1": {
"inputs": {
"ckpt_name": "realcartoonXL_v5.safetensors"
},
"class_type": "CheckpointLoaderSimple",
"_meta": {
"title": "加载检查点"
}
},
"2": {
"inputs": {
"lora_name": "t_realcartoon.safetensors",
"strength_model": 0.7000000000000001,
"strength_clip": 0.7000000000000001,
"model": [
"1",
0
],
"clip": [
"1",
1
]
},
"class_type": "LoraLoader",
"_meta": {
"title": "加载LoRA"
}
},
"3": {
"inputs": {
"seed": 152656324223018,
"steps": 20,
"cfg": 8,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1,
"model": [
"2",
0
],
"positive": [
"8",
0
],
"negative": [
"9",
0
],
"latent_image": [
"6",
0
]
},
"class_type": "KSampler",
"_meta": {
"title": "K采样器"
}
},
"4": {
"inputs": {
"samples": [
"3",
0
],
"vae": [
"1",
2
]
},
"class_type": "VAEDecode",
"_meta": {
"title": "VAE解码"
}
},
"5": {
"inputs": {
"filename_prefix": "ComfyUI",
"images": [
"4",
0
]
},
"class_type": "SaveImage",
"_meta": {
"title": "保存图像"
}
},
"6": {
"inputs": {
"width": 512,
"height": 512,
"batch_size": 1
},
"class_type": "EmptyLatentImage",
"_meta": {
"title": "空潜空间图像"
}
},
"7": {
"inputs": {
"stop_at_clip_layer": -2,
"clip": [
"2",
1
]
},
"class_type": "CLIPSetLastLayer",
"_meta": {
"title": "设置CLIP最后一层"
}
},
"8": {
"inputs": {
"text": "t<lora:t_realcartoon:1> Running beside the beach",
"clip": [
"7",
0
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP文本编码(提示)"
}
},
"9": {
"inputs": {
"text": "",
"clip": [
"7",
0
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP文本编码(提示)"
}
}
}
"""
# 将示例JSON字符串解析为Python字典,并根据需要修改其中的文本提示和种子值
prompt = json.loads(prompt_text)
prompt["8"]["inputs"]["text"] = "t<lora:t_realcartoon:1> Running beside the beach"
prompt["3"]["inputs"]["seed"] = 1
# 创建一个WebSocket连接到服务器
ws = websocket.WebSocket()
ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id))
# 调用get_images()函数来获取图像
images = get_images(ws, prompt)
# 显示图片
for node_id in images:
for image_data in images[node_id]:
image = Image.open(io.BytesIO(image_data))
image.show()
input("")
本文参考: