mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-24 21:30:15 +08:00
api nodes: price badges moved to nodes code
This commit is contained in:
parent
b7d7cc1d49
commit
0e944babd6
@ -1213,6 +1213,7 @@ class NodeInfoV1:
|
|||||||
deprecated: bool=None
|
deprecated: bool=None
|
||||||
experimental: bool=None
|
experimental: bool=None
|
||||||
api_node: bool=None
|
api_node: bool=None
|
||||||
|
price_badge: dict | None = None
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NodeInfoV3:
|
class NodeInfoV3:
|
||||||
@ -1222,11 +1223,52 @@ class NodeInfoV3:
|
|||||||
name: str=None
|
name: str=None
|
||||||
display_name: str=None
|
display_name: str=None
|
||||||
description: str=None
|
description: str=None
|
||||||
|
python_module: Any = None
|
||||||
category: str=None
|
category: str=None
|
||||||
output_node: bool=None
|
output_node: bool=None
|
||||||
deprecated: bool=None
|
deprecated: bool=None
|
||||||
experimental: bool=None
|
experimental: bool=None
|
||||||
api_node: bool=None
|
api_node: bool=None
|
||||||
|
price_badge: dict | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PriceBadgeDepends:
|
||||||
|
widgets: list[str] = field(default_factory=list)
|
||||||
|
inputs: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
def validate(self) -> None:
|
||||||
|
if not isinstance(self.widgets, list) or any(not isinstance(x, str) for x in self.widgets):
|
||||||
|
raise ValueError("PriceBadgeDepends.widgets must be a list[str].")
|
||||||
|
if not isinstance(self.inputs, list) or any(not isinstance(x, str) for x in self.inputs):
|
||||||
|
raise ValueError("PriceBadgeDepends.inputs must be a list[str].")
|
||||||
|
|
||||||
|
def as_dict(self) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"widgets": self.widgets,
|
||||||
|
"inputs": self.inputs,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PriceBadge:
|
||||||
|
expr: str
|
||||||
|
depends_on: PriceBadgeDepends = field(default_factory=PriceBadgeDepends)
|
||||||
|
engine: str = field(default="jsonata")
|
||||||
|
|
||||||
|
def validate(self) -> None:
|
||||||
|
if self.engine != "jsonata":
|
||||||
|
raise ValueError(f"Unsupported PriceBadge.engine '{self.engine}'. Only 'jsonata' is supported.")
|
||||||
|
if not isinstance(self.expr, str) or not self.expr.strip():
|
||||||
|
raise ValueError("PriceBadge.expr must be a non-empty string.")
|
||||||
|
self.depends_on.validate()
|
||||||
|
|
||||||
|
def as_dict(self) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"engine": self.engine,
|
||||||
|
"depends_on": self.depends_on.as_dict(),
|
||||||
|
"expr": self.expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -1272,6 +1314,8 @@ class Schema:
|
|||||||
"""Flags a node as experimental, informing users that it may change or not work as expected."""
|
"""Flags a node as experimental, informing users that it may change or not work as expected."""
|
||||||
is_api_node: bool=False
|
is_api_node: bool=False
|
||||||
"""Flags a node as an API node. See: https://docs.comfy.org/tutorials/api-nodes/overview."""
|
"""Flags a node as an API node. See: https://docs.comfy.org/tutorials/api-nodes/overview."""
|
||||||
|
price_badge: PriceBadge | None = None
|
||||||
|
"""Optional client-evaluated pricing badge declaration for this node."""
|
||||||
not_idempotent: bool=False
|
not_idempotent: bool=False
|
||||||
"""Flags a node as not idempotent; when True, the node will run and not reuse the cached outputs when identical inputs are provided on a different node in the graph."""
|
"""Flags a node as not idempotent; when True, the node will run and not reuse the cached outputs when identical inputs are provided on a different node in the graph."""
|
||||||
enable_expand: bool=False
|
enable_expand: bool=False
|
||||||
@ -1302,6 +1346,8 @@ class Schema:
|
|||||||
input.validate()
|
input.validate()
|
||||||
for output in self.outputs:
|
for output in self.outputs:
|
||||||
output.validate()
|
output.validate()
|
||||||
|
if self.price_badge is not None:
|
||||||
|
self.price_badge.validate()
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
"""Add hidden based on selected schema options, and give outputs without ids default ids."""
|
"""Add hidden based on selected schema options, and give outputs without ids default ids."""
|
||||||
@ -1375,7 +1421,8 @@ class Schema:
|
|||||||
deprecated=self.is_deprecated,
|
deprecated=self.is_deprecated,
|
||||||
experimental=self.is_experimental,
|
experimental=self.is_experimental,
|
||||||
api_node=self.is_api_node,
|
api_node=self.is_api_node,
|
||||||
python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes")
|
python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes"),
|
||||||
|
price_badge=self.price_badge.as_dict() if self.price_badge is not None else None,
|
||||||
)
|
)
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@ -1407,7 +1454,8 @@ class Schema:
|
|||||||
deprecated=self.is_deprecated,
|
deprecated=self.is_deprecated,
|
||||||
experimental=self.is_experimental,
|
experimental=self.is_experimental,
|
||||||
api_node=self.is_api_node,
|
api_node=self.is_api_node,
|
||||||
python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes")
|
python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes"),
|
||||||
|
price_badge=self.price_badge.as_dict() if self.price_badge is not None else None,
|
||||||
)
|
)
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@ -1958,4 +2006,6 @@ __all__ = [
|
|||||||
"add_to_dict_v1",
|
"add_to_dict_v1",
|
||||||
"add_to_dict_v3",
|
"add_to_dict_v3",
|
||||||
"V3Data",
|
"V3Data",
|
||||||
|
"PriceBadgeDepends",
|
||||||
|
"PriceBadge",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -97,6 +97,9 @@ class FluxProUltraImageNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
expr="""{"type":"usd","usd":0.06}""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -352,6 +355,9 @@ class FluxProExpandNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
expr="""{"type":"usd","usd":0.05}""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -458,6 +464,9 @@ class FluxProFillNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
expr="""{"type":"usd","usd":0.05}""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -511,6 +520,21 @@ class Flux2ProImageNode(IO.ComfyNode):
|
|||||||
NODE_ID = "Flux2ProImageNode"
|
NODE_ID = "Flux2ProImageNode"
|
||||||
DISPLAY_NAME = "Flux.2 [pro] Image"
|
DISPLAY_NAME = "Flux.2 [pro] Image"
|
||||||
API_ENDPOINT = "/proxy/bfl/flux-2-pro/generate"
|
API_ENDPOINT = "/proxy/bfl/flux-2-pro/generate"
|
||||||
|
PRICE_BADGE_EXPR = """
|
||||||
|
(
|
||||||
|
$MP := 1024 * 1024;
|
||||||
|
$outMP := $max([1, $floor(((w.width.n * w.height.n) + $MP - 1) / $MP)]);
|
||||||
|
$outputCost := 0.03 + 0.015 * ($outMP - 1);
|
||||||
|
i.images.connected
|
||||||
|
? {
|
||||||
|
"type":"range_usd",
|
||||||
|
"min_usd": $outputCost + 0.015,
|
||||||
|
"max_usd": $outputCost + 0.12,
|
||||||
|
"format": { "approximate": true }
|
||||||
|
}
|
||||||
|
: {"type":"usd","usd": $outputCost}
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls) -> IO.Schema:
|
def define_schema(cls) -> IO.Schema:
|
||||||
@ -563,6 +587,10 @@ class Flux2ProImageNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["width", "height"], inputs=["images"]),
|
||||||
|
expr=cls.PRICE_BADGE_EXPR,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -623,6 +651,22 @@ class Flux2MaxImageNode(Flux2ProImageNode):
|
|||||||
NODE_ID = "Flux2MaxImageNode"
|
NODE_ID = "Flux2MaxImageNode"
|
||||||
DISPLAY_NAME = "Flux.2 [max] Image"
|
DISPLAY_NAME = "Flux.2 [max] Image"
|
||||||
API_ENDPOINT = "/proxy/bfl/flux-2-max/generate"
|
API_ENDPOINT = "/proxy/bfl/flux-2-max/generate"
|
||||||
|
PRICE_BADGE_EXPR = """
|
||||||
|
(
|
||||||
|
$MP := 1024 * 1024;
|
||||||
|
$outMP := $max([1, $floor(((w.width.n * w.height.n) + $MP - 1) / $MP)]);
|
||||||
|
$outputCost := 0.07 + 0.03 * ($outMP - 1);
|
||||||
|
|
||||||
|
i.images.connected
|
||||||
|
? {
|
||||||
|
"type":"range_usd",
|
||||||
|
"min_usd": $outputCost + 0.03,
|
||||||
|
"max_usd": $outputCost + 0.24,
|
||||||
|
"format": { "approximate": true }
|
||||||
|
}
|
||||||
|
: {"type":"usd","usd": $outputCost}
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class BFLExtension(ComfyExtension):
|
class BFLExtension(ComfyExtension):
|
||||||
|
|||||||
@ -126,6 +126,9 @@ class ByteDanceImageNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
expr="""{"type":"usd","usd":0.03}""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -367,6 +370,19 @@ class ByteDanceSeedreamNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$price := $contains(w.model.s, "seedream-4-5-251128") ? 0.04 : 0.03;
|
||||||
|
{
|
||||||
|
"type":"usd",
|
||||||
|
"usd": $price,
|
||||||
|
"format": { "suffix":" x images/Run", "approximate": true }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -522,6 +538,7 @@ class ByteDanceTextToVideoNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE_VIDEO,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -632,6 +649,7 @@ class ByteDanceImageToVideoNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE_VIDEO,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -754,6 +772,7 @@ class ByteDanceFirstLastFrameNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE_VIDEO,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -877,6 +896,7 @@ class ByteDanceImageReferenceNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE_VIDEO,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -946,6 +966,52 @@ def raise_if_text_params(prompt: str, text_params: list[str]) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PRICE_BADGE_VIDEO = IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$priceByModel := {
|
||||||
|
"seedance-1-0-pro": {
|
||||||
|
"480p":[0.23,0.24],
|
||||||
|
"720p":[0.51,0.56],
|
||||||
|
"1080p":[1.18,1.22]
|
||||||
|
},
|
||||||
|
"seedance-1-0-pro-fast": {
|
||||||
|
"480p":[0.09,0.1],
|
||||||
|
"720p":[0.21,0.23],
|
||||||
|
"1080p":[0.47,0.49]
|
||||||
|
},
|
||||||
|
"seedance-1-0-lite": {
|
||||||
|
"480p":[0.17,0.18],
|
||||||
|
"720p":[0.37,0.41],
|
||||||
|
"1080p":[0.85,0.88]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$model := w.model.s;
|
||||||
|
$modelKey :=
|
||||||
|
$contains($model, "seedance-1-0-pro-fast") ? "seedance-1-0-pro-fast" :
|
||||||
|
$contains($model, "seedance-1-0-pro") ? "seedance-1-0-pro" :
|
||||||
|
"seedance-1-0-lite";
|
||||||
|
$resolution := w.resolution.s;
|
||||||
|
$resKey :=
|
||||||
|
$contains($resolution, "1080") ? "1080p" :
|
||||||
|
$contains($resolution, "720") ? "720p" :
|
||||||
|
"480p";
|
||||||
|
$modelPrices := $lookup($priceByModel, $modelKey);
|
||||||
|
$baseRange := $lookup($modelPrices, $resKey);
|
||||||
|
$min10s := $baseRange[0];
|
||||||
|
$max10s := $baseRange[1];
|
||||||
|
$scale := w.duration.n / 10;
|
||||||
|
$minCost := $min10s * $scale;
|
||||||
|
$maxCost := $max10s * $scale;
|
||||||
|
($minCost = $maxCost)
|
||||||
|
? {"type":"usd","usd": $minCost}
|
||||||
|
: {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ByteDanceExtension(ComfyExtension):
|
class ByteDanceExtension(ComfyExtension):
|
||||||
@override
|
@override
|
||||||
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
||||||
|
|||||||
@ -309,6 +309,22 @@ class GeminiNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$m := w.model.s;
|
||||||
|
|
||||||
|
$contains($m, "gemini-2.5-flash")
|
||||||
|
? {"type":"list_usd","usd":[0.0003,0.0025]}
|
||||||
|
: $contains($m, "gemini-2.5-pro")
|
||||||
|
? {"type":"list_usd","usd":[0.00125,0.01]}
|
||||||
|
: $contains($m, "gemini-3-pro-preview")
|
||||||
|
? {"type":"list_usd","usd":[0.002,0.012]}
|
||||||
|
: {"type":"text","text":"Token-based"}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -570,6 +586,9 @@ class GeminiImage(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
expr="""{"type":"usd","usd":0.039,"format":{"suffix":"/Image (1K)","approximate":true}}""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -700,6 +719,19 @@ class GeminiImage2(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["resolution"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$r := w.resolution.s;
|
||||||
|
($contains($r,"1k") or $contains($r,"2k"))
|
||||||
|
? {"type":"usd","usd":0.134,"format":{"suffix":"/Image","approximate":true}}
|
||||||
|
: $contains($r,"4k")
|
||||||
|
? {"type":"usd","usd":0.24,"format":{"suffix":"/Image","approximate":true}}
|
||||||
|
: {"type":"text","text":"Token-based"}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -236,7 +236,6 @@ class IdeogramV1(IO.ComfyNode):
|
|||||||
display_name="Ideogram V1",
|
display_name="Ideogram V1",
|
||||||
category="api node/image/Ideogram",
|
category="api node/image/Ideogram",
|
||||||
description="Generates images using the Ideogram V1 model.",
|
description="Generates images using the Ideogram V1 model.",
|
||||||
is_api_node=True,
|
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.String.Input(
|
IO.String.Input(
|
||||||
"prompt",
|
"prompt",
|
||||||
@ -298,6 +297,17 @@ class IdeogramV1(IO.ComfyNode):
|
|||||||
IO.Hidden.api_key_comfy_org,
|
IO.Hidden.api_key_comfy_org,
|
||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["num_images", "turbo"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$n := w.num_images.n;
|
||||||
|
$base := (w.turbo.b = true) ? 0.0286 : 0.0858;
|
||||||
|
{"type":"usd","usd": $round($base * $n, 2)}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -351,7 +361,6 @@ class IdeogramV2(IO.ComfyNode):
|
|||||||
display_name="Ideogram V2",
|
display_name="Ideogram V2",
|
||||||
category="api node/image/Ideogram",
|
category="api node/image/Ideogram",
|
||||||
description="Generates images using the Ideogram V2 model.",
|
description="Generates images using the Ideogram V2 model.",
|
||||||
is_api_node=True,
|
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.String.Input(
|
IO.String.Input(
|
||||||
"prompt",
|
"prompt",
|
||||||
@ -436,6 +445,17 @@ class IdeogramV2(IO.ComfyNode):
|
|||||||
IO.Hidden.api_key_comfy_org,
|
IO.Hidden.api_key_comfy_org,
|
||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["num_images", "turbo"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$n := w.num_images.n;
|
||||||
|
$base := (w.turbo.b = true) ? 0.0715 : 0.1144;
|
||||||
|
{"type":"usd","usd": $round($base * $n, 2)}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -506,7 +526,6 @@ class IdeogramV3(IO.ComfyNode):
|
|||||||
category="api node/image/Ideogram",
|
category="api node/image/Ideogram",
|
||||||
description="Generates images using the Ideogram V3 model. "
|
description="Generates images using the Ideogram V3 model. "
|
||||||
"Supports both regular image generation from text prompts and image editing with mask.",
|
"Supports both regular image generation from text prompts and image editing with mask.",
|
||||||
is_api_node=True,
|
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.String.Input(
|
IO.String.Input(
|
||||||
"prompt",
|
"prompt",
|
||||||
@ -591,6 +610,23 @@ class IdeogramV3(IO.ComfyNode):
|
|||||||
IO.Hidden.api_key_comfy_org,
|
IO.Hidden.api_key_comfy_org,
|
||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["rendering_speed", "num_images"], inputs=["character_image"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$n := w.num_images.n;
|
||||||
|
$speed := w.rendering_speed.s;
|
||||||
|
$hasChar := i.character_image.connected;
|
||||||
|
$base :=
|
||||||
|
$contains($speed,"quality") ? ($hasChar ? 0.286 : 0.1287) :
|
||||||
|
$contains($speed,"default") ? ($hasChar ? 0.2145 : 0.0858) :
|
||||||
|
$contains($speed,"turbo") ? ($hasChar ? 0.143 : 0.0429) :
|
||||||
|
0.0858;
|
||||||
|
{"type":"usd","usd": $round($base * $n, 2)}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -28,6 +28,21 @@ class ExecuteTaskRequest(BaseModel):
|
|||||||
image_uri: str | None = Field(None)
|
image_uri: str | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
PRICE_BADGE = IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$pps := {
|
||||||
|
"ltx-2 (pro)": {"1920x1080":0.06,"2560x1440":0.12,"3840x2160":0.24},
|
||||||
|
"ltx-2 (fast)": {"1920x1080":0.04,"2560x1440":0.08,"3840x2160":0.16}
|
||||||
|
}[w.model.s][w.resolution.s];
|
||||||
|
|
||||||
|
{"type":"usd","usd": $pps * w.duration.n}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TextToVideoNode(IO.ComfyNode):
|
class TextToVideoNode(IO.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
@ -69,6 +84,7 @@ class TextToVideoNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -145,6 +161,7 @@ class ImageToVideoNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -189,6 +189,19 @@ class LumaImageGenerationNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$m := w.model.s;
|
||||||
|
$contains($m,"photon-flash-1")
|
||||||
|
? {"type":"usd","usd":0.0027}
|
||||||
|
: $contains($m,"photon-1")
|
||||||
|
? {"type":"usd","usd":0.0104}
|
||||||
|
: {"type":"usd","usd":0.0246}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -303,6 +316,19 @@ class LumaImageModifyNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$m := w.model.s;
|
||||||
|
$contains($m,"photon-flash-1")
|
||||||
|
? {"type":"usd","usd":0.0027}
|
||||||
|
: $contains($m,"photon-1")
|
||||||
|
? {"type":"usd","usd":0.0104}
|
||||||
|
: {"type":"usd","usd":0.0246}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -395,6 +421,7 @@ class LumaTextToVideoGenerationNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE_VIDEO,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -505,6 +532,8 @@ class LumaImageToVideoGenerationNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
price_badge=PRICE_BADGE_VIDEO,
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -568,6 +597,53 @@ class LumaImageToVideoGenerationNode(IO.ComfyNode):
|
|||||||
return LumaKeyframes(frame0=frame0, frame1=frame1)
|
return LumaKeyframes(frame0=frame0, frame1=frame1)
|
||||||
|
|
||||||
|
|
||||||
|
PRICE_BADGE_VIDEO = IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model", "resolution", "duration"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$p := {
|
||||||
|
"ray-flash-2": {
|
||||||
|
"5s": {"4k":3.13,"1080p":0.79,"720p":0.34,"540p":0.2},
|
||||||
|
"9s": {"4k":5.65,"1080p":1.42,"720p":0.61,"540p":0.36}
|
||||||
|
},
|
||||||
|
"ray-2": {
|
||||||
|
"5s": {"4k":9.11,"1080p":2.27,"720p":1.02,"540p":0.57},
|
||||||
|
"9s": {"4k":16.4,"1080p":4.1,"720p":1.83,"540p":1.03}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$m := w.model.s;
|
||||||
|
$d := w.duration.s;
|
||||||
|
$r := w.resolution.s;
|
||||||
|
|
||||||
|
$modelKey :=
|
||||||
|
$contains($m,"ray-flash-2") ? "ray-flash-2" :
|
||||||
|
$contains($m,"ray-2") ? "ray-2" :
|
||||||
|
$contains($m,"ray-1-6") ? "ray-1-6" :
|
||||||
|
"other";
|
||||||
|
|
||||||
|
$durKey := $contains($d,"5s") ? "5s" : $contains($d,"9s") ? "9s" : "";
|
||||||
|
$resKey :=
|
||||||
|
$contains($r,"4k") ? "4k" :
|
||||||
|
$contains($r,"1080p") ? "1080p" :
|
||||||
|
$contains($r,"720p") ? "720p" :
|
||||||
|
$contains($r,"540p") ? "540p" : "";
|
||||||
|
|
||||||
|
$modelPrices := $lookup($p, $modelKey);
|
||||||
|
$durPrices := $lookup($modelPrices, $durKey);
|
||||||
|
$v := $lookup($durPrices, $resKey);
|
||||||
|
|
||||||
|
$price :=
|
||||||
|
($modelKey = "ray-1-6") ? 0.5 :
|
||||||
|
($modelKey = "other") ? 0.79 :
|
||||||
|
($exists($v) ? $v : 0.79);
|
||||||
|
|
||||||
|
{"type":"usd","usd": $price}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LumaExtension(ComfyExtension):
|
class LumaExtension(ComfyExtension):
|
||||||
@override
|
@override
|
||||||
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user