mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-19 05:27:24 +08:00
Merge 6817d1303d into 97f58baaaf
This commit is contained in:
commit
7c57590aa9
382
blueprints/Text to Image (WaveSpeed Flux).json
Normal file
382
blueprints/Text to Image (WaveSpeed Flux).json
Normal file
@ -0,0 +1,382 @@
|
||||
{
|
||||
"revision": 0,
|
||||
"last_node_id": 1,
|
||||
"last_link_id": 5,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "d7434b88-8db4-4db4-833f-1f5f729bc8c3",
|
||||
"pos": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
310
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "prompt"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "model",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "model"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "width"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "height"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "IMAGE",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"proxyWidgets": [
|
||||
[
|
||||
"-1",
|
||||
"prompt"
|
||||
],
|
||||
[
|
||||
"-1",
|
||||
"model"
|
||||
],
|
||||
[
|
||||
"-1",
|
||||
"width"
|
||||
],
|
||||
[
|
||||
"-1",
|
||||
"height"
|
||||
],
|
||||
[
|
||||
"1",
|
||||
"seed"
|
||||
]
|
||||
],
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.18.1",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {
|
||||
"prompt": true,
|
||||
"model": true
|
||||
},
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"",
|
||||
"wavespeed-ai/flux-dev",
|
||||
1024,
|
||||
1024
|
||||
],
|
||||
"title": "Text to Image (WaveSpeed Flux)"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"version": 0.4,
|
||||
"definitions": {
|
||||
"subgraphs": [
|
||||
{
|
||||
"id": "d7434b88-8db4-4db4-833f-1f5f729bc8c3",
|
||||
"version": 1,
|
||||
"state": {
|
||||
"lastGroupId": 0,
|
||||
"lastNodeId": 1,
|
||||
"lastLinkId": 5,
|
||||
"lastRerouteId": 0
|
||||
},
|
||||
"revision": 0,
|
||||
"config": {},
|
||||
"name": "Text to Image (WaveSpeed Flux)",
|
||||
"inputNode": {
|
||||
"id": -10,
|
||||
"bounding": [
|
||||
-350,
|
||||
100,
|
||||
120,
|
||||
160
|
||||
]
|
||||
},
|
||||
"outputNode": {
|
||||
"id": -20,
|
||||
"bounding": [
|
||||
550,
|
||||
200,
|
||||
120,
|
||||
60
|
||||
]
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"id": "5a5065bf-290c-4592-a3e1-5582603295d3",
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"linkIds": [
|
||||
1
|
||||
],
|
||||
"pos": [
|
||||
-230,
|
||||
120
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "14adcda4-e0b1-4cfb-95e2-b468774548a2",
|
||||
"name": "model",
|
||||
"type": "COMBO",
|
||||
"linkIds": [
|
||||
2
|
||||
],
|
||||
"pos": [
|
||||
-230,
|
||||
140
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "aed38934-ba2f-4a5e-8829-60ce4ce7b80b",
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"linkIds": [
|
||||
3
|
||||
],
|
||||
"pos": [
|
||||
-230,
|
||||
160
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a5c7d360-174d-4ab8-b646-311fd5544e5f",
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"linkIds": [
|
||||
4
|
||||
],
|
||||
"pos": [
|
||||
-230,
|
||||
180
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"id": "8654951d-aeb3-4ca1-82b1-0eebffc506ae",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"linkIds": [
|
||||
5
|
||||
],
|
||||
"localized_name": "IMAGE",
|
||||
"pos": [
|
||||
530,
|
||||
220
|
||||
]
|
||||
}
|
||||
],
|
||||
"widgets": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "WavespeedTextToImageNode",
|
||||
"pos": [
|
||||
100,
|
||||
100
|
||||
],
|
||||
"size": [
|
||||
380,
|
||||
390
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"localized_name": "model",
|
||||
"name": "model",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "model"
|
||||
},
|
||||
"link": 2
|
||||
},
|
||||
{
|
||||
"localized_name": "prompt",
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "prompt"
|
||||
},
|
||||
"link": 1
|
||||
},
|
||||
{
|
||||
"localized_name": "width",
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "width"
|
||||
},
|
||||
"link": 3
|
||||
},
|
||||
{
|
||||
"localized_name": "height",
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "height"
|
||||
},
|
||||
"link": 4
|
||||
},
|
||||
{
|
||||
"localized_name": "steps",
|
||||
"name": "steps",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "steps"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "guidance_scale",
|
||||
"name": "guidance_scale",
|
||||
"type": "FLOAT",
|
||||
"widget": {
|
||||
"name": "guidance_scale"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "seed",
|
||||
"name": "seed",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "seed"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"localized_name": "safety_checker",
|
||||
"name": "safety_checker",
|
||||
"type": "BOOLEAN",
|
||||
"widget": {
|
||||
"name": "safety_checker"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"localized_name": "IMAGE",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
5
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.18.1",
|
||||
"Node name for S&R": "WavespeedTextToImageNode",
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
"wavespeed-ai/flux-dev",
|
||||
"",
|
||||
1024,
|
||||
1024,
|
||||
28,
|
||||
3.5,
|
||||
0,
|
||||
"randomize",
|
||||
true
|
||||
]
|
||||
}
|
||||
],
|
||||
"groups": [],
|
||||
"links": [
|
||||
{
|
||||
"id": 1,
|
||||
"origin_id": -10,
|
||||
"origin_slot": 0,
|
||||
"target_id": 1,
|
||||
"target_slot": 1,
|
||||
"type": "STRING"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"origin_id": -10,
|
||||
"origin_slot": 1,
|
||||
"target_id": 1,
|
||||
"target_slot": 0,
|
||||
"type": "COMBO"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"origin_id": -10,
|
||||
"origin_slot": 2,
|
||||
"target_id": 1,
|
||||
"target_slot": 2,
|
||||
"type": "INT"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"origin_id": -10,
|
||||
"origin_slot": 3,
|
||||
"target_id": 1,
|
||||
"target_slot": 3,
|
||||
"type": "INT"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"origin_id": 1,
|
||||
"origin_slot": 0,
|
||||
"target_id": -20,
|
||||
"target_slot": 0,
|
||||
"type": "IMAGE"
|
||||
}
|
||||
],
|
||||
"extra": {
|
||||
"workflowRendererVersion": "LG"
|
||||
},
|
||||
"category": "Image generation and editing/Text to image"
|
||||
}
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.0,
|
||||
"offset": [0, 0]
|
||||
},
|
||||
"ue_links": []
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,22 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class WavespeedTextToImageRequest(BaseModel):
|
||||
prompt: str = Field(...)
|
||||
width: int = Field(1024)
|
||||
height: int = Field(1024)
|
||||
num_inference_steps: int = Field(28)
|
||||
guidance_scale: float = Field(3.5)
|
||||
seed: int = Field(-1)
|
||||
enable_safety_checker: bool = Field(True)
|
||||
enable_sync_mode: bool = Field(False)
|
||||
num_images: int = Field(1)
|
||||
output_format: str = Field("jpeg")
|
||||
negative_prompt: Optional[str] = Field(None)
|
||||
|
||||
|
||||
class SeedVR2ImageRequest(BaseModel):
|
||||
image: str = Field(...)
|
||||
target_resolution: str = Field(...)
|
||||
|
||||
@ -6,6 +6,7 @@ from comfy_api_nodes.apis.wavespeed import (
|
||||
TaskCreatedResponse,
|
||||
TaskResultResponse,
|
||||
SeedVR2ImageRequest,
|
||||
WavespeedTextToImageRequest,
|
||||
)
|
||||
from comfy_api_nodes.util import (
|
||||
ApiEndpoint,
|
||||
@ -165,10 +166,162 @@ class WavespeedImageUpscaleNode(IO.ComfyNode):
|
||||
return IO.NodeOutput(await download_url_to_image_tensor(final_response.data.outputs[0]))
|
||||
|
||||
|
||||
_TEXT_TO_IMAGE_MODELS = [
|
||||
"wavespeed-ai/flux-dev",
|
||||
"wavespeed-ai/flux-dev-fp8",
|
||||
"wavespeed-ai/flux-schnell",
|
||||
"wavespeed-ai/flux-schnell-fp8",
|
||||
]
|
||||
|
||||
_MODEL_ENDPOINT = {
|
||||
"wavespeed-ai/flux-dev": "flux-dev",
|
||||
"wavespeed-ai/flux-dev-fp8": "flux-dev-fp8",
|
||||
"wavespeed-ai/flux-schnell": "flux-schnell",
|
||||
"wavespeed-ai/flux-schnell-fp8": "flux-schnell-fp8",
|
||||
}
|
||||
|
||||
_SCHNELL_MODELS = {"wavespeed-ai/flux-schnell", "wavespeed-ai/flux-schnell-fp8"}
|
||||
|
||||
|
||||
class WavespeedTextToImageNode(IO.ComfyNode):
|
||||
@classmethod
|
||||
def define_schema(cls):
|
||||
return IO.Schema(
|
||||
node_id="WavespeedTextToImageNode",
|
||||
display_name="WaveSpeed Text to Image",
|
||||
category="api node/image/WaveSpeed",
|
||||
description="Generate images from text prompts using WaveSpeed's fast Flux inference.",
|
||||
inputs=[
|
||||
IO.Combo.Input("model", options=_TEXT_TO_IMAGE_MODELS),
|
||||
IO.String.Input(
|
||||
"prompt",
|
||||
multiline=True,
|
||||
default="",
|
||||
tooltip="Text prompt describing the image to generate.",
|
||||
),
|
||||
IO.Int.Input(
|
||||
"width",
|
||||
default=1024,
|
||||
min=256,
|
||||
max=2048,
|
||||
step=64,
|
||||
tooltip="Image width in pixels.",
|
||||
),
|
||||
IO.Int.Input(
|
||||
"height",
|
||||
default=1024,
|
||||
min=256,
|
||||
max=2048,
|
||||
step=64,
|
||||
tooltip="Image height in pixels.",
|
||||
),
|
||||
IO.Int.Input(
|
||||
"steps",
|
||||
default=28,
|
||||
min=1,
|
||||
max=50,
|
||||
tooltip="Number of inference steps. Schnell models work well with 4 steps.",
|
||||
),
|
||||
IO.Float.Input(
|
||||
"guidance_scale",
|
||||
default=3.5,
|
||||
min=0.0,
|
||||
max=20.0,
|
||||
step=0.1,
|
||||
tooltip="Guidance scale (CFG). Not used for Schnell models.",
|
||||
advanced=True,
|
||||
),
|
||||
IO.Int.Input(
|
||||
"seed",
|
||||
default=0,
|
||||
min=0,
|
||||
max=0xFFFFFFFFFFFFFFFF,
|
||||
control_after_generate=True,
|
||||
tooltip="Seed for reproducibility. Use -1 for random.",
|
||||
),
|
||||
IO.Boolean.Input(
|
||||
"safety_checker",
|
||||
default=True,
|
||||
tooltip="Enable the safety checker.",
|
||||
advanced=True,
|
||||
),
|
||||
],
|
||||
outputs=[IO.Image.Output()],
|
||||
hidden=[
|
||||
IO.Hidden.auth_token_comfy_org,
|
||||
IO.Hidden.api_key_comfy_org,
|
||||
IO.Hidden.unique_id,
|
||||
],
|
||||
is_api_node=True,
|
||||
price_badge=IO.PriceBadge(
|
||||
depends_on=IO.PriceBadgeDepends(widgets=["model"]),
|
||||
expr="""
|
||||
(
|
||||
$prices := {
|
||||
"wavespeed-ai/flux-dev": 0.003,
|
||||
"wavespeed-ai/flux-dev-fp8": 0.003,
|
||||
"wavespeed-ai/flux-schnell": 0.001,
|
||||
"wavespeed-ai/flux-schnell-fp8": 0.001
|
||||
};
|
||||
{"type":"usd", "usd": $lookup($prices, widgets.model)}
|
||||
)
|
||||
""",
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def execute(
|
||||
cls,
|
||||
model: str,
|
||||
prompt: str,
|
||||
width: int,
|
||||
height: int,
|
||||
steps: int,
|
||||
guidance_scale: float,
|
||||
seed: int,
|
||||
safety_checker: bool,
|
||||
) -> IO.NodeOutput:
|
||||
endpoint_name = _MODEL_ENDPOINT[model]
|
||||
is_schnell = model in _SCHNELL_MODELS
|
||||
initial_res = await sync_op(
|
||||
cls,
|
||||
ApiEndpoint(
|
||||
path=f"/proxy/wavespeed/api/v3/wavespeed-ai/{endpoint_name}",
|
||||
method="POST",
|
||||
),
|
||||
response_model=TaskCreatedResponse,
|
||||
data=WavespeedTextToImageRequest(
|
||||
prompt=prompt,
|
||||
width=width,
|
||||
height=height,
|
||||
num_inference_steps=steps,
|
||||
guidance_scale=1.0 if is_schnell else guidance_scale,
|
||||
seed=seed,
|
||||
enable_safety_checker=safety_checker,
|
||||
),
|
||||
)
|
||||
if initial_res.code != 200:
|
||||
raise ValueError(f"Task creation failed with code={initial_res.code} and message={initial_res.message}")
|
||||
final_response = await poll_op(
|
||||
cls,
|
||||
ApiEndpoint(path=f"/proxy/wavespeed/api/v3/predictions/{initial_res.data.id}/result"),
|
||||
response_model=TaskResultResponse,
|
||||
status_extractor=lambda x: "failed" if x.data is None else x.data.status,
|
||||
poll_interval=3.0,
|
||||
max_poll_attempts=200,
|
||||
)
|
||||
if final_response.code != 200:
|
||||
raise ValueError(
|
||||
f"Task processing failed with code={final_response.code} and message={final_response.message}"
|
||||
)
|
||||
return IO.NodeOutput(await download_url_to_image_tensor(final_response.data.outputs[0]))
|
||||
|
||||
|
||||
class WavespeedExtension(ComfyExtension):
|
||||
@override
|
||||
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
||||
return [
|
||||
WavespeedTextToImageNode,
|
||||
WavespeedFlashVSRNode,
|
||||
WavespeedImageUpscaleNode,
|
||||
]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user