概览
概览
Comfy服务器运行在aiohttp框架之上,后者又使用asyncio。
从服务器到客户端的消息由套接字消息通过服务器的send_sync
方法发送,该方法是PromptServer
(在server.py
中定义)的一个实例,它们由在api.js
中注册的套接字事件侦听器处理参见消息。
客户端到服务器的消息由api. js中定义的api.fetchApi()
方法api.js
,由服务器定义的http路由处理参见路由。
当您对请求进行排队时,客户端会提交整个工作流(小部件值和所有值)。在您向队列发送请求后,服务器不会收到您所做的任何更改。如果您想在执行期间修改服务器行为,您需要路由。
Messages
Messages
在执行期间(或队列状态发生变化时),PromptExecutor
通过PromptServer
的send_sync
方法将消息发送回客户端。
这些消息由api.js
中定义的套接字事件侦听器接收(在撰写第90行时,或搜索this.socket.addEventListener
),它为任何已知的消息类型创建一个CustomEvent
对象,并将其分派给任何注册的侦听器。
扩展可以按照标准Javascript习惯用法注册以接收事件(通常在setup()
函数中完成):
api.addEventListener(message_type, messageHandler);
如果message_type
不是内置的,它将自动添加到已知消息类型列表中。将使用CustomEvent
对象调用消息messageHandler
,该对象扩展套接字引发的事件以添加.detail
属性,这是服务器发送的数据的字典。因此,用法通常如下:
function messageHandler(event) {
if (event.detail.node == aNodeIdThatIsInteresting) {
// do something with event.detail.other_things
}
}
内置消息类型
在执行期间(或队列状态发生变化时),PromptExecutor
通过PromptServer
的send_sync
方法将以下消息发送回客户端。扩展可以注册为其中任何一个的侦听器。
事件 | 时 | 数据 |
---|---|---|
execution_start | 当提示即将运行时 | prompt_id |
execution_error | 当执行过程中发生错误时 | prompt_id ,加上附加信息 |
execution_interrupted | 当执行被节点停止时InterruptProcessingException | prompt_id 、node_id 、node_type 和executed (被执行节点的列表) |
execution_cached | 在执行开始时 | prompt_id 、nodes (因为可以使用缓存的输出而被跳过的节点列表) |
executing | 当一个新节点即将被执行时 | node (节点ID或None 表示完成),prompt_id |
executed | 当一个节点返回一个用户界面元素 | node``prompt_id , output |
progress | During execution of a node that implements the required hook | node (node id), prompt_id , value , max |
status | When the state of the queue changes | exec_info , a dictionary holding queue_remaining , the number of entries in the queue |
使用已执行
尽管有这个名称,但executed
消息不会在节点完成执行时发送(与executing
不同),而是仅在节点返回用户界面更新时发送。
为此,main函数需要返回字典而不是元组:
# at the end of my main method
return { "ui":a_new_dictionary, "result": the_tuple_of_output_values }
a_new_dictionary
然后将作为executed
消息中的output
值发送。如果节点没有输出,则可以省略result
键(例如,参见nodes.py
中SaveImage
的代码)
自定义消息类型
如上所述,在客户端,只需注册为唯一消息类型名称的侦听器即可添加自定义消息类型。
api.addEventListener("my.custom.message", messageHandler);
在服务器上,代码同样简单:
from server import PromptServer
# then, in your main execution function (normally)
PromptServer.instance.send_sync("my.custom.message", a_dictionary)
获取 node_id
大多数内置消息都在node的值中包含当前node
id。您可能也想这样做。
node_id通过隐藏输入在服务器端可用,该输入是通过INPUT_TYPES
字典中的hidden
键获得的:
@classmethod
def INPUT_TYPES(s):
return {"required" : { }, # whatever your required inputs are
"hidden": { "node_id": "UNIQUE_ID" } } # Add the hidden key
def my_main_function(self, required_inputs, node_id):
# do some things
PromptServer.instance.send_sync("my.custom.message", {"node": node_id, "other_things": etc})
路由
路由
get
和post
方法,可以通过在server.py
中搜索@routes
找到。当您在Web客户端中提交工作流时,它会被发布到/prompt
,该prompt_id
和number
(队列中的位置),或者如果验证失败则返回error
和node_errors
。提示队列在execution.py
中定义,它还定义了PromptExecutor
类。
内置路由
server.py
定义以下路由:
路径 | 获取/发布 | 目的 |
---|---|---|
/ | get | 加载舒适的网页 |
/embeddings | get | 检索可用嵌入名称的列表 |
/extensions | get | 检索注册WEB_DIRECTORY |
/upload/image | post | 上传图像的扩展列表 |
/upload/mask | post | 上传蒙版 |
/view | get | @routes.get("/view") in server.py |
/view_metadata / | get | retrieve metadata for a model |
/system_stats | get | retrieve information about the system (python version, devices, vram etc) |
/prompt | get | retrieve current status |
/prompt | post | submit a prompt to the queue |
/object_info | get | retrieve details of all node types |
/object_info/{node_class} | get | retrieve details of one node type |
/history | get | retrieve the queue history |
/history/{prompt_id} | get | retrieve the queue history for a specific prompt |
/history | post | clear history or delete history item |
/queue | get | retrieve the state of the queue |
/interrupt | post | stop the current workflow |
/free | post | free memory by unloading specified models |
自定义路由
如果您想在执行期间从客户端向服务器发送消息,您将需要添加到服务器的自定义路由。对于任何复杂的事情,您都需要深入了解aiohttp框架文档,但大多数情况下可以处理如下:
from server import PromptServer
from aiohttp import web
routes = PromptServer.instance.routes
@routes.post('/my_new_path')
async def my_function(request):
the_data = await request.post()
# the_data now holds a dictionary of the values sent
MyClass.handle_my_message(the_data)
return web.json_response({})
除非你知道你在做什么,否则不要试图在类中定义my_function
。@routes.post
装饰器做了很多工作!相反,定义上面的函数,然后调用类方法。
如果不更改任何内容,也可以定义@routes.get
。
客户端可以通过发送带有类似代码的FormData
对象来使用这个新路由,这将导致the_data
,在上面的代码中,包含message
和node_id
键:
import { api } from "../../scripts/api.js";
function send_message(node_id, message) {
const body = new FormData();
body.append('message',message);
body.append('node_id', node_id);
api.fetchApi("/my_new_path", { method: "POST", body, });
}