mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-06-20 14:59:28 +08:00
Some checks are pending
Detect Unreviewed Merge / detect (push) Waiting to run
Python Linting / Run Ruff (push) Waiting to run
Python Linting / Run Pylint (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Waiting to run
Execution Tests / test (macos-latest) (push) Waiting to run
Execution Tests / test (ubuntu-latest) (push) Waiting to run
Execution Tests / test (windows-latest) (push) Waiting to run
Test server launches without errors / test (push) Waiting to run
Unit Tests / test (macos-latest) (push) Waiting to run
Unit Tests / test (ubuntu-latest) (push) Waiting to run
Unit Tests / test (windows-2022) (push) Waiting to run
Signed-off-by: bigcat88 <bigcat88@icloud.com>
334 lines
11 KiB
Python
334 lines
11 KiB
Python
from __future__ import annotations
|
|
|
|
from enum import Enum
|
|
from typing import Optional, Union
|
|
|
|
import torch
|
|
from pydantic import BaseModel, Field, confloat
|
|
|
|
|
|
class LumaIO:
|
|
LUMA_REF = "LUMA_REF"
|
|
LUMA_CONCEPTS = "LUMA_CONCEPTS"
|
|
LUMA_RAY32_KEYFRAME = "LUMA_RAY32_KEYFRAME"
|
|
|
|
|
|
class LumaReference:
|
|
def __init__(self, image: torch.Tensor, weight: float):
|
|
self.image = image
|
|
self.weight = weight
|
|
|
|
def create_api_model(self, download_url: str):
|
|
return LumaImageRef(url=download_url, weight=self.weight)
|
|
|
|
|
|
class LumaReferenceChain:
|
|
def __init__(self, first_ref: LumaReference = None):
|
|
self.refs: list[LumaReference] = []
|
|
if first_ref:
|
|
self.refs.append(first_ref)
|
|
|
|
def add(self, luma_ref: LumaReference = None):
|
|
self.refs.append(luma_ref)
|
|
|
|
def create_api_model(self, download_urls: list[str], max_refs=4):
|
|
if len(self.refs) == 0:
|
|
return None
|
|
api_refs: list[LumaImageRef] = []
|
|
for ref, url in zip(self.refs, download_urls):
|
|
api_ref = LumaImageRef(url=url, weight=ref.weight)
|
|
api_refs.append(api_ref)
|
|
return api_refs
|
|
|
|
def clone(self):
|
|
c = LumaReferenceChain()
|
|
for ref in self.refs:
|
|
c.add(ref)
|
|
return c
|
|
|
|
|
|
class LumaConcept:
|
|
def __init__(self, key: str):
|
|
self.key = key
|
|
|
|
|
|
class LumaConceptChain:
|
|
def __init__(self, str_list: list[str] = None):
|
|
self.concepts: list[LumaConcept] = []
|
|
if str_list is not None:
|
|
for c in str_list:
|
|
if c != "None":
|
|
self.add(LumaConcept(key=c))
|
|
|
|
def add(self, concept: LumaConcept):
|
|
self.concepts.append(concept)
|
|
|
|
def create_api_model(self):
|
|
if len(self.concepts) == 0:
|
|
return None
|
|
api_concepts: list[LumaConceptObject] = []
|
|
for concept in self.concepts:
|
|
if concept.key == "None":
|
|
continue
|
|
api_concepts.append(LumaConceptObject(key=concept.key))
|
|
if len(api_concepts) == 0:
|
|
return None
|
|
return api_concepts
|
|
|
|
def clone(self):
|
|
c = LumaConceptChain()
|
|
for concept in self.concepts:
|
|
c.add(concept)
|
|
return c
|
|
|
|
def clone_and_merge(self, other: LumaConceptChain):
|
|
c = self.clone()
|
|
for concept in other.concepts:
|
|
c.add(concept)
|
|
return c
|
|
|
|
|
|
def get_luma_concepts(include_none=False):
|
|
concepts = []
|
|
if include_none:
|
|
concepts.append("None")
|
|
return concepts + [
|
|
"truck_left",
|
|
"pan_right",
|
|
"pedestal_down",
|
|
"low_angle",
|
|
"pedestal_up",
|
|
"selfie",
|
|
"pan_left",
|
|
"roll_right",
|
|
"zoom_in",
|
|
"over_the_shoulder",
|
|
"orbit_right",
|
|
"orbit_left",
|
|
"static",
|
|
"tiny_planet",
|
|
"high_angle",
|
|
"bolt_cam",
|
|
"dolly_zoom",
|
|
"overhead",
|
|
"zoom_out",
|
|
"handheld",
|
|
"roll_left",
|
|
"pov",
|
|
"aerial_drone",
|
|
"push_in",
|
|
"crane_down",
|
|
"truck_right",
|
|
"tilt_down",
|
|
"elevator_doors",
|
|
"tilt_up",
|
|
"ground_level",
|
|
"pull_out",
|
|
"aerial",
|
|
"crane_up",
|
|
"eye_level",
|
|
]
|
|
|
|
|
|
class LumaImageModel(str, Enum):
|
|
photon_1 = "photon-1"
|
|
photon_flash_1 = "photon-flash-1"
|
|
|
|
|
|
class LumaVideoModel(str, Enum):
|
|
ray_2 = "ray-2"
|
|
ray_flash_2 = "ray-flash-2"
|
|
ray_1_6 = "ray-1-6"
|
|
|
|
|
|
class LumaAspectRatio(str, Enum):
|
|
ratio_1_1 = "1:1"
|
|
ratio_16_9 = "16:9"
|
|
ratio_9_16 = "9:16"
|
|
ratio_4_3 = "4:3"
|
|
ratio_3_4 = "3:4"
|
|
ratio_21_9 = "21:9"
|
|
ratio_9_21 = "9:21"
|
|
|
|
|
|
class LumaVideoOutputResolution(str, Enum):
|
|
res_540p = "540p"
|
|
res_720p = "720p"
|
|
res_1080p = "1080p"
|
|
res_4k = "4k"
|
|
|
|
|
|
class LumaVideoModelOutputDuration(str, Enum):
|
|
dur_5s = "5s"
|
|
dur_9s = "9s"
|
|
|
|
|
|
class LumaGenerationType(str, Enum):
|
|
video = "video"
|
|
image = "image"
|
|
|
|
|
|
class LumaState(str, Enum):
|
|
queued = "queued"
|
|
dreaming = "dreaming"
|
|
completed = "completed"
|
|
failed = "failed"
|
|
|
|
|
|
class LumaAssets(BaseModel):
|
|
video: Optional[str] = Field(None, description="The URL of the video")
|
|
image: Optional[str] = Field(None, description="The URL of the image")
|
|
progress_video: Optional[str] = Field(None, description="The URL of the progress video")
|
|
|
|
|
|
class LumaImageRef(BaseModel):
|
|
"""Used for image gen"""
|
|
|
|
url: str = Field(..., description="The URL of the image reference")
|
|
weight: confloat(ge=0.0, le=1.0) = Field(..., description="The weight of the image reference")
|
|
|
|
|
|
class LumaImageReference(BaseModel):
|
|
"""Used for video gen"""
|
|
|
|
type: Optional[str] = Field("image", description="Input type, defaults to image")
|
|
url: str = Field(..., description="The URL of the image")
|
|
|
|
|
|
class LumaModifyImageRef(BaseModel):
|
|
url: str = Field(..., description="The URL of the image reference")
|
|
weight: confloat(ge=0.0, le=1.0) = Field(..., description="The weight of the image reference")
|
|
|
|
|
|
class LumaCharacterRef(BaseModel):
|
|
identity0: LumaImageIdentity = Field(..., description="The image identity object")
|
|
|
|
|
|
class LumaImageIdentity(BaseModel):
|
|
images: list[str] = Field(..., description="The URLs of the image identity")
|
|
|
|
|
|
class LumaGenerationReference(BaseModel):
|
|
type: str = Field("generation", description="Input type, defaults to generation")
|
|
id: str = Field(..., description="The ID of the generation")
|
|
|
|
|
|
class LumaKeyframes(BaseModel):
|
|
frame0: Optional[Union[LumaImageReference, LumaGenerationReference]] = Field(None, description="")
|
|
frame1: Optional[Union[LumaImageReference, LumaGenerationReference]] = Field(None, description="")
|
|
|
|
|
|
class LumaConceptObject(BaseModel):
|
|
key: str = Field(..., description="Camera Concept name")
|
|
|
|
|
|
class LumaImageGenerationRequest(BaseModel):
|
|
prompt: str = Field(..., description="The prompt of the generation")
|
|
model: LumaImageModel = Field(LumaImageModel.photon_1, description="The image model used for the generation")
|
|
aspect_ratio: Optional[LumaAspectRatio] = Field(LumaAspectRatio.ratio_16_9)
|
|
image_ref: Optional[list[LumaImageRef]] = Field(None, description="List of image reference objects")
|
|
style_ref: Optional[list[LumaImageRef]] = Field(None, description="List of style reference objects")
|
|
character_ref: Optional[LumaCharacterRef] = Field(None, description="The image identity object")
|
|
modify_image_ref: Optional[LumaModifyImageRef] = Field(None, description="The modify image reference object")
|
|
|
|
|
|
class LumaGenerationRequest(BaseModel):
|
|
prompt: str = Field(..., description="The prompt of the generation")
|
|
model: LumaVideoModel = Field(LumaVideoModel.ray_2, description="The video model used for the generation")
|
|
duration: Optional[LumaVideoModelOutputDuration] = Field(None, description="The duration of the generation")
|
|
aspect_ratio: Optional[LumaAspectRatio] = Field(None, description="The aspect ratio of the generation")
|
|
resolution: Optional[LumaVideoOutputResolution] = Field(None, description="The resolution of the generation")
|
|
loop: Optional[bool] = Field(None, description="Whether to loop the video")
|
|
keyframes: Optional[LumaKeyframes] = Field(None, description="The keyframes of the generation")
|
|
concepts: Optional[list[LumaConceptObject]] = Field(None, description="Camera Concepts to apply to generation")
|
|
|
|
|
|
class LumaGeneration(BaseModel):
|
|
id: str = Field(..., description="The ID of the generation")
|
|
generation_type: LumaGenerationType = Field(..., description="Generation type, image or video")
|
|
state: LumaState = Field(..., description="The state of the generation")
|
|
failure_reason: Optional[str] = Field(None, description="The reason for the state of the generation")
|
|
created_at: str = Field(..., description="The date and time when the generation was created")
|
|
assets: Optional[LumaAssets] = Field(None, description="The assets of the generation")
|
|
model: str = Field(..., description="The model used for the generation")
|
|
request: Union[LumaGenerationRequest, LumaImageGenerationRequest] = Field(...)
|
|
|
|
|
|
class Luma2ImageRef(BaseModel):
|
|
url: str | None = None
|
|
data: str | None = None
|
|
media_type: str | None = None
|
|
generation_id: str | None = Field(None, description="reference a prior generation (extend / source reuse)")
|
|
|
|
|
|
class Luma2VideoEdit(BaseModel):
|
|
"""Edit controls for Ray 3.2 ``video_edit`` generations."""
|
|
|
|
auto_controls: bool | None = Field(None, description="derive a conditioning schedule from the source (recommended)")
|
|
strength: str | None = Field(None, description="'adhere_1' .. 'reimagine_3'; constrained by IO.Combo")
|
|
|
|
|
|
class Luma2VideoOptions(BaseModel):
|
|
"""Ray 3.2 ``video`` output settings (text / image / keyframe / edit / extend)."""
|
|
|
|
resolution: str | None = Field(None, description="360p | 540p | 720p | 1080p")
|
|
duration: str | None = Field(None, description="5s | 10s")
|
|
loop: bool | None = Field(None)
|
|
start_frame: Luma2ImageRef | None = Field(None)
|
|
end_frame: Luma2ImageRef | None = Field(None)
|
|
keyframes: list[Luma2ImageRef] | None = Field(None)
|
|
keyframe_indexes: list[int] | None = Field(None)
|
|
edit: Luma2VideoEdit | None = Field(None)
|
|
|
|
|
|
class Luma2GenerationRequest(BaseModel):
|
|
prompt: str = Field(..., min_length=1, max_length=6000)
|
|
model: str | None = None
|
|
type: str | None = None
|
|
aspect_ratio: str | None = None
|
|
style: str | None = None
|
|
output_format: str | None = None
|
|
web_search: bool | None = None
|
|
image_ref: list[Luma2ImageRef] | None = None
|
|
source: Luma2ImageRef | None = None
|
|
video: Luma2VideoOptions | None = Field(None)
|
|
|
|
|
|
class Luma2Generation(BaseModel):
|
|
id: str | None = None
|
|
type: str | None = None
|
|
state: str | None = None
|
|
model: str | None = None
|
|
created_at: str | None = None
|
|
output: list[LumaImageReference] | None = None
|
|
failure_reason: str | None = None
|
|
failure_code: str | None = None
|
|
|
|
|
|
# --- Ray 3.2 multi-keyframe chain ---
|
|
|
|
LUMA_KEYFRAME_MODE_FRACTION = "fraction" # value in [0.0, 1.0] of the output video duration
|
|
LUMA_KEYFRAME_MODE_SECONDS = "seconds" # absolute time, in seconds, from the start of the output
|
|
|
|
|
|
class LumaRay32KeyframeItem:
|
|
"""One guide image anchored at a position on the Ray 3.2 output timeline."""
|
|
|
|
def __init__(self, image: torch.Tensor, mode: str, value: float):
|
|
self.image = image
|
|
self.mode = mode # LUMA_KEYFRAME_MODE_FRACTION | LUMA_KEYFRAME_MODE_SECONDS
|
|
self.value = value
|
|
|
|
|
|
class LumaRay32KeyframeChain:
|
|
def __init__(self):
|
|
self.items: list[LumaRay32KeyframeItem] = []
|
|
|
|
def add(self, item: LumaRay32KeyframeItem) -> None:
|
|
self.items.append(item)
|
|
|
|
def clone(self) -> "LumaRay32KeyframeChain":
|
|
c = LumaRay32KeyframeChain()
|
|
c.items = list(self.items)
|
|
return c
|