feat: enhance LightX2VBridge with distillation configuration support and improve runner management for efficient resource handling
This commit is contained in:
+184
-281
@@ -9,10 +9,11 @@ from typing import Any, Dict, List, Union, Optional
|
||||
from PIL import Image
|
||||
import tempfile
|
||||
from easydict import EasyDict
|
||||
import asyncio
|
||||
|
||||
from .config import get_available_attn_ops, get_available_quant_ops
|
||||
|
||||
from ..lightx2v.lightx2v.utils.set_config import get_default_config
|
||||
from ..lightx2v.lightx2v.utils.set_config import get_default_config, set_config
|
||||
from ..lightx2v.lightx2v.infer import init_runner
|
||||
|
||||
|
||||
@@ -23,7 +24,8 @@ class LightX2VBridge:
|
||||
self._model_registry = None
|
||||
self._config_registry = None
|
||||
self._quantization_registry = None
|
||||
self._runners = {} # Cache initialized runners
|
||||
self._current_runner = None # Only store one runner at a time
|
||||
self._current_runner_key = None # Track current runner identity
|
||||
|
||||
@property
|
||||
def model_registry(self):
|
||||
@@ -48,7 +50,7 @@ class LightX2VBridge:
|
||||
|
||||
def _discover_models(self) -> List[str]:
|
||||
"""Discover available model classes from LightX2V."""
|
||||
return ["wan2.1", "hunyuan", "wan2.1_audio"]
|
||||
return ["wan2.1", "hunyuan", "wan2.1_audio", "wan2.1_distill"]
|
||||
|
||||
def _discover_configs(self) -> Dict[str, Dict]:
|
||||
"""Discover all available config files for single-card inference."""
|
||||
@@ -65,6 +67,20 @@ class LightX2VBridge:
|
||||
if any(exclude in path_str for exclude in ["deploy", "causvid", "skyreels", "cogvideox"]):
|
||||
continue
|
||||
|
||||
# 包含蒸馏模型配置
|
||||
if "distill" in path_str:
|
||||
config_info = {
|
||||
"path": config_file,
|
||||
"model_type": "wan2.1_distill",
|
||||
"task": self._extract_task_from_path(path_str),
|
||||
"is_quantized": False,
|
||||
"is_distilled": True,
|
||||
"description": self._generate_config_description(relative_path, is_distilled=True),
|
||||
}
|
||||
key = str(relative_path).replace("/", "_").replace(".json", "")
|
||||
configs[key] = config_info
|
||||
continue
|
||||
|
||||
# 排除量化配置(单独处理)
|
||||
if "quantization" in path_str:
|
||||
continue
|
||||
@@ -115,7 +131,7 @@ class LightX2VBridge:
|
||||
else:
|
||||
return "unknown"
|
||||
|
||||
def _generate_config_description(self, relative_path: Path, is_quantized: bool = False) -> str:
|
||||
def _generate_config_description(self, relative_path: Path, is_quantized: bool = False, is_distilled: bool = False) -> str:
|
||||
"""Generate human-readable description for config."""
|
||||
parts = relative_path.parts
|
||||
model_type = parts[0] if not is_quantized else parts[1]
|
||||
@@ -124,6 +140,8 @@ class LightX2VBridge:
|
||||
desc = f"{model_type.upper()} {task.upper()}"
|
||||
if is_quantized:
|
||||
desc += " (Quantized)"
|
||||
if is_distilled:
|
||||
desc += " (Distilled 4-step)"
|
||||
|
||||
return desc
|
||||
|
||||
@@ -151,18 +169,27 @@ class LightX2VBridge:
|
||||
|
||||
def get_runner(self, config: EasyDict):
|
||||
"""Get or create a runner for the given config."""
|
||||
# Create a unique key for this configuration
|
||||
key = f"{config.model_cls}_{config.model_path}_{hash(str(config))}"
|
||||
# Create a simple key based on essential parameters only
|
||||
# Only model_cls and model_path are essential for runner identity
|
||||
key = f"{config.model_cls}_{config.model_path}"
|
||||
|
||||
if key not in self._runners:
|
||||
self._runners[key] = init_runner(config)
|
||||
# If we have a different runner, clear the old one first
|
||||
if self._current_runner_key != key:
|
||||
self.clear_cache()
|
||||
self._current_runner = init_runner(config)
|
||||
self._current_runner_key = key
|
||||
|
||||
return self._runners[key]
|
||||
return self._current_runner
|
||||
|
||||
def clear_cache(self):
|
||||
"""Clear cached runners to free memory."""
|
||||
self._runners.clear()
|
||||
torch.cuda.empty_cache()
|
||||
"""Clear cached runner to free memory."""
|
||||
if self._current_runner is not None:
|
||||
# Clean up the runner if it has cleanup methods
|
||||
if hasattr(self._current_runner, "cleanup"):
|
||||
self._current_runner.cleanup()
|
||||
self._current_runner = None
|
||||
self._current_runner_key = None
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
|
||||
class InputOutputConverter:
|
||||
@@ -375,6 +402,10 @@ class LightX2VConfigBuilder:
|
||||
"LIGHTX2V_CONFIG",
|
||||
{"tooltip": "缓存配置"},
|
||||
),
|
||||
"distill_config": (
|
||||
"LIGHTX2V_CONFIG",
|
||||
{"tooltip": "蒸馏配置"},
|
||||
),
|
||||
"lora_path": (
|
||||
"STRING",
|
||||
{"default": "", "tooltip": "LoRA权重路径"},
|
||||
@@ -418,6 +449,7 @@ class LightX2VConfigBuilder:
|
||||
quantization_config=None,
|
||||
attention_config=None,
|
||||
caching_config=None,
|
||||
distill_config=None,
|
||||
lora_path="",
|
||||
strength_model=1.0,
|
||||
**kwargs,
|
||||
@@ -445,6 +477,8 @@ class LightX2VConfigBuilder:
|
||||
base_config.update(attention_config)
|
||||
if caching_config:
|
||||
base_config.update(caching_config)
|
||||
if distill_config:
|
||||
base_config.update(distill_config)
|
||||
|
||||
# 准备覆盖参数
|
||||
overrides = {
|
||||
@@ -599,9 +633,9 @@ class LightX2VCachingConfig:
|
||||
return {
|
||||
"required": {
|
||||
"feature_caching": (
|
||||
["None", "Tea", "Ada", "Custom"],
|
||||
["NoCaching", "Tea", "TaylorSeer", "Ada", "Custom"],
|
||||
{
|
||||
"default": "None",
|
||||
"default": "NoCaching",
|
||||
"tooltip": "特征缓存类型",
|
||||
},
|
||||
),
|
||||
@@ -650,7 +684,7 @@ class LightX2VCachingConfig:
|
||||
"""Build caching configuration."""
|
||||
config = {}
|
||||
|
||||
if feature_caching != "None":
|
||||
if feature_caching != "NoCaching":
|
||||
config["feature_caching"] = feature_caching
|
||||
|
||||
if feature_caching == "Tea":
|
||||
@@ -662,6 +696,8 @@ class LightX2VCachingConfig:
|
||||
config["coefficients"] = json.loads(coefficients)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Failed to parse coefficients: {coefficients}")
|
||||
else:
|
||||
config["feature_caching"] = "NoCaching"
|
||||
|
||||
return (config,)
|
||||
|
||||
@@ -717,37 +753,63 @@ class LightX2VInference:
|
||||
):
|
||||
"""Generate video using LightX2V."""
|
||||
|
||||
# 添加提示词到配置
|
||||
# 直接使用传入的config (已经是EasyDict)
|
||||
# 添加运行时需要的参数
|
||||
config.prompt = prompt
|
||||
config.negative_prompt = negative_prompt
|
||||
config.mode = "infer"
|
||||
|
||||
# 处理i2v任务的图像输入
|
||||
if config.task == "i2v" and image is not None:
|
||||
# 保存图像到临时文件
|
||||
pil_image = self.converter.comfy_image_to_pil(image)
|
||||
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
||||
pil_image.save(tmp.name)
|
||||
config.image_path = tmp.name
|
||||
# 处理输出路径
|
||||
if not hasattr(config, "save_video_path") or config.save_video_path is None:
|
||||
config.save_video_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
|
||||
|
||||
# 处理音频输入
|
||||
if audio is not None and "audio" in config.model_cls:
|
||||
audio_path = self.converter.comfy_audio_to_path(audio)
|
||||
config.audio_path = audio_path
|
||||
# 临时文件列表,用于清理
|
||||
temp_files = []
|
||||
|
||||
# 创建临时配置文件(set_config需要)
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp:
|
||||
json.dump(config.to_dict(), tmp)
|
||||
config.config_json = tmp.name
|
||||
|
||||
# 获取或创建runner
|
||||
try:
|
||||
# 处理i2v任务的图像输入
|
||||
if config.task == "i2v" and image is not None:
|
||||
# 保存图像到临时文件
|
||||
pil_image = self.converter.comfy_image_to_pil(image)
|
||||
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
||||
pil_image.save(tmp.name)
|
||||
config.image_path = tmp.name
|
||||
temp_files.append(tmp.name)
|
||||
|
||||
# 处理音频输入
|
||||
if audio is not None and "audio" in config.model_cls:
|
||||
audio_path = self.converter.comfy_audio_to_path(audio)
|
||||
config.audio_path = audio_path
|
||||
temp_files.append(audio_path)
|
||||
|
||||
# 检查模型路径的config.json
|
||||
model_config_path = os.path.join(config.model_path, "config.json")
|
||||
if os.path.exists(model_config_path):
|
||||
with open(model_config_path, "r") as f:
|
||||
model_config = json.load(f)
|
||||
config.update(model_config)
|
||||
|
||||
# 获取或创建runner
|
||||
runner = self.bridge.get_runner(config)
|
||||
|
||||
# 运行生成
|
||||
result = runner.run_pipeline()
|
||||
assert runner is not None, "Runner is None"
|
||||
|
||||
# 转换输出
|
||||
if hasattr(result, "save_video_path"):
|
||||
# 运行生成 - run_pipeline 是异步方法
|
||||
if asyncio.iscoroutinefunction(runner.run_pipeline):
|
||||
# 在同步环境中运行异步方法
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
try:
|
||||
result = loop.run_until_complete(runner.run_pipeline())
|
||||
finally:
|
||||
loop.close()
|
||||
else:
|
||||
result = runner.run_pipeline()
|
||||
|
||||
# 检查结果是否已经保存到文件
|
||||
if os.path.exists(config.save_video_path):
|
||||
return (self.converter.video_to_latent(config.save_video_path),)
|
||||
elif hasattr(result, "save_video_path") and os.path.exists(result.save_video_path):
|
||||
return (self.converter.video_to_latent(result.save_video_path),)
|
||||
else:
|
||||
# 假设张量输出
|
||||
@@ -755,265 +817,106 @@ class LightX2VInference:
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
if hasattr(config, "image_path") and os.path.exists(config.image_path):
|
||||
os.unlink(config.image_path)
|
||||
if hasattr(config, "audio_path") and os.path.exists(config.audio_path):
|
||||
os.unlink(config.audio_path)
|
||||
if hasattr(config, "config_json") and os.path.exists(config.config_json):
|
||||
os.unlink(config.config_json)
|
||||
for temp_file in temp_files:
|
||||
if os.path.exists(temp_file):
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
class LightX2VConfigViewer:
|
||||
"""View and display LightX2V configuration details."""
|
||||
class LightX2VDistillConfig:
|
||||
"""Configuration node for distillation settings."""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
"""Define inputs for the config viewer."""
|
||||
"""Define inputs for distillation config."""
|
||||
return {
|
||||
"required": {
|
||||
"config": ("LIGHTX2V_CONFIG", {"tooltip": "LightX2V配置"}),
|
||||
"enable_distill": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": True,
|
||||
"tooltip": "启用蒸馏模型",
|
||||
},
|
||||
),
|
||||
"infer_steps": (
|
||||
"INT",
|
||||
{
|
||||
"default": 4,
|
||||
"min": 1,
|
||||
"max": 50,
|
||||
"tooltip": "推理步数(蒸馏模型通常使用4步)",
|
||||
},
|
||||
),
|
||||
"denoising_steps": (
|
||||
"STRING",
|
||||
{
|
||||
"default": "[999, 750, 500, 250]",
|
||||
"tooltip": "去噪步骤列表(JSON格式)",
|
||||
},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"enable_cfg": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": False,
|
||||
"tooltip": "启用CFG(分类器自由引导)",
|
||||
},
|
||||
),
|
||||
"enable_dynamic_cfg": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": False,
|
||||
"tooltip": "启用动态CFG",
|
||||
},
|
||||
),
|
||||
"cfg_scale": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 4.0,
|
||||
"min": 0.1,
|
||||
"max": 30.0,
|
||||
"step": 0.1,
|
||||
"tooltip": "动态CFG缩放比例",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("config_info",)
|
||||
FUNCTION = "view_config"
|
||||
CATEGORY = "LightX2V/Util"
|
||||
RETURN_TYPES = ("LIGHTX2V_CONFIG",)
|
||||
RETURN_NAMES = ("distill_config",)
|
||||
FUNCTION = "build_distill_config"
|
||||
CATEGORY = "LightX2V/Config"
|
||||
|
||||
def view_config(self, config):
|
||||
"""View configuration details."""
|
||||
if hasattr(config, "to_dict"):
|
||||
config_dict = config.to_dict()
|
||||
else:
|
||||
config_dict = dict(config)
|
||||
|
||||
config_str = json.dumps(config_dict, indent=2, ensure_ascii=False)
|
||||
print(f"LightX2V Configuration:\n{config_str}")
|
||||
return (config_str,)
|
||||
|
||||
|
||||
class UniversalLightX2VNode:
|
||||
"""Universal ComfyUI node for all LightX2V models (Legacy compatibility)."""
|
||||
|
||||
def __init__(self):
|
||||
self.bridge = LightX2VBridge()
|
||||
self.converter = InputOutputConverter()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
"""Define inputs for the node."""
|
||||
bridge = LightX2VBridge()
|
||||
|
||||
# Get available models and configs
|
||||
model_choices = bridge.model_registry
|
||||
|
||||
# 构建配置选项
|
||||
config_choices = ["custom"]
|
||||
config_descriptions = {}
|
||||
|
||||
# 添加普通配置
|
||||
for key, config_info in bridge.config_registry.items():
|
||||
config_choices.append(key)
|
||||
config_descriptions[key] = config_info["description"]
|
||||
|
||||
# 添加量化配置
|
||||
for key, config_info in bridge.quantization_registry.items():
|
||||
config_choices.append(key)
|
||||
config_descriptions[key] = config_info["description"]
|
||||
|
||||
# 构建用户可配置参数
|
||||
required_inputs = {
|
||||
"model_cls": (model_choices, {"tooltip": "选择模型类型"}),
|
||||
"model_path": (
|
||||
"STRING",
|
||||
{"default": "", "tooltip": "模型权重路径(留空使用默认路径)"},
|
||||
),
|
||||
"task": (
|
||||
["t2v", "i2v"],
|
||||
{
|
||||
"default": "t2v",
|
||||
"tooltip": "任务类型: 文本到视频或图像到视频",
|
||||
},
|
||||
),
|
||||
"config_preset": (
|
||||
config_choices,
|
||||
{
|
||||
"default": "custom",
|
||||
"tooltip": "配置预设或'custom'自定义",
|
||||
},
|
||||
),
|
||||
"prompt": (
|
||||
"STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": "",
|
||||
"tooltip": "生成提示词",
|
||||
},
|
||||
),
|
||||
"negative_prompt": (
|
||||
"STRING",
|
||||
{"multiline": True, "default": "", "tooltip": "负面提示词"},
|
||||
),
|
||||
}
|
||||
|
||||
# 添加用户可配置参数
|
||||
for param_name, param_config in ConfigManager.USER_CONFIGURABLE_PARAMS.items():
|
||||
required_inputs[param_name] = (
|
||||
param_config["type"],
|
||||
{
|
||||
"default": param_config["default"],
|
||||
"min": param_config["min"],
|
||||
"max": param_config["max"],
|
||||
"tooltip": param_config["tooltip"],
|
||||
},
|
||||
)
|
||||
if "step" in param_config:
|
||||
required_inputs[param_name][1]["step"] = param_config["step"]
|
||||
|
||||
optional_inputs = {
|
||||
"image": ("IMAGE", {"tooltip": "i2v任务的输入图像"}),
|
||||
"audio": (
|
||||
"AUDIO",
|
||||
{"tooltip": "音频驱动生成的输入音频"},
|
||||
),
|
||||
"custom_config": (
|
||||
"STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": "{}",
|
||||
"tooltip": "自定义配置(JSON格式)",
|
||||
},
|
||||
),
|
||||
"lora_path": (
|
||||
"STRING",
|
||||
{"default": "", "tooltip": "LoRA权重路径"},
|
||||
),
|
||||
"strength_model": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 1.0,
|
||||
"min": 0.0,
|
||||
"max": 2.0,
|
||||
"step": 0.1,
|
||||
"tooltip": "LoRA模型强度",
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
return {
|
||||
"required": required_inputs,
|
||||
"optional": optional_inputs,
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LATENT",)
|
||||
RETURN_NAMES = ("video",)
|
||||
FUNCTION = "generate"
|
||||
CATEGORY = "LightX2V/Universal"
|
||||
|
||||
def generate(
|
||||
def build_distill_config(
|
||||
self,
|
||||
model_cls,
|
||||
model_path,
|
||||
task,
|
||||
config_preset,
|
||||
prompt,
|
||||
negative_prompt,
|
||||
steps,
|
||||
cfg_scale,
|
||||
seed,
|
||||
height,
|
||||
width,
|
||||
video_length,
|
||||
sample_shift,
|
||||
image=None,
|
||||
audio=None,
|
||||
custom_config="{}",
|
||||
lora_path="",
|
||||
strength_model=1.0,
|
||||
**kwargs,
|
||||
enable_distill,
|
||||
infer_steps=4,
|
||||
denoising_steps="[999, 750, 500, 250]",
|
||||
enable_cfg=False,
|
||||
enable_dynamic_cfg=False,
|
||||
cfg_scale=4.0,
|
||||
):
|
||||
"""Generate video using LightX2V."""
|
||||
"""Build distillation configuration."""
|
||||
config = {}
|
||||
|
||||
# 加载基础配置
|
||||
if config_preset == "custom":
|
||||
base_config = {}
|
||||
quantization_config = None
|
||||
else:
|
||||
# 检查是否为量化配置
|
||||
if config_preset in self.bridge.quantization_registry:
|
||||
config_info = self.bridge.quantization_registry[config_preset]
|
||||
base_config = self.bridge.load_config(config_info["path"])
|
||||
quantization_config = base_config
|
||||
else:
|
||||
config_info = self.bridge.config_registry.get(config_preset)
|
||||
if config_info:
|
||||
base_config = self.bridge.load_config(config_info["path"])
|
||||
quantization_config = None
|
||||
else:
|
||||
raise ValueError(f"配置预设 '{config_preset}' 未找到")
|
||||
if enable_distill:
|
||||
config["infer_steps"] = infer_steps
|
||||
config["enable_cfg"] = enable_cfg
|
||||
config["enable_dynamic_cfg"] = enable_dynamic_cfg
|
||||
|
||||
# 解析自定义配置
|
||||
custom_cfg = ConfigManager.parse_custom_config(custom_config)
|
||||
base_config.update(custom_cfg)
|
||||
if enable_dynamic_cfg:
|
||||
config["cfg_scale"] = cfg_scale
|
||||
|
||||
# 准备覆盖参数
|
||||
overrides = {
|
||||
"seed": seed if seed != -1 else None,
|
||||
"steps": steps,
|
||||
"cfg_scale": cfg_scale,
|
||||
"height": height,
|
||||
"width": width,
|
||||
"video_length": video_length,
|
||||
"sample_shift": sample_shift,
|
||||
"lora_path": lora_path if lora_path else None,
|
||||
"strength_model": strength_model,
|
||||
"prompt": prompt,
|
||||
"negative_prompt": negative_prompt,
|
||||
}
|
||||
try:
|
||||
config["denoising_step_list"] = json.loads(denoising_steps)
|
||||
# 确保去噪步骤列表长度与推理步数匹配
|
||||
if len(config["denoising_step_list"]) != infer_steps:
|
||||
print(f"Warning: denoising_step_list length ({len(config['denoising_step_list'])}) doesn't match infer_steps ({infer_steps})")
|
||||
except json.JSONDecodeError:
|
||||
print(f"Failed to parse denoising steps, using default: {denoising_steps}")
|
||||
config["denoising_step_list"] = [999, 750, 500, 250]
|
||||
|
||||
# 处理i2v任务的图像输入
|
||||
if task == "i2v" and image is not None:
|
||||
# 保存图像到临时文件
|
||||
pil_image = self.converter.comfy_image_to_pil(image)
|
||||
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
||||
pil_image.save(tmp.name)
|
||||
overrides["image_path"] = tmp.name
|
||||
|
||||
# 处理音频输入
|
||||
if audio is not None and "audio" in model_cls:
|
||||
audio_path = self.converter.comfy_audio_to_path(audio)
|
||||
overrides["audio_path"] = audio_path
|
||||
|
||||
# 创建最终配置
|
||||
config = ConfigManager.create_config(model_cls, model_path, task, base_config, overrides, quantization_config)
|
||||
|
||||
# 创建临时配置文件(set_config需要)
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp:
|
||||
json.dump(base_config, tmp)
|
||||
config.config_json = tmp.name
|
||||
|
||||
# 获取或创建runner
|
||||
try:
|
||||
runner = self.bridge.get_runner(config)
|
||||
|
||||
# 运行生成
|
||||
result = runner.run_pipeline()
|
||||
|
||||
# 转换输出
|
||||
if hasattr(result, "save_video_path"):
|
||||
return (self.converter.video_to_latent(result.save_video_path),)
|
||||
else:
|
||||
# 假设张量输出
|
||||
return (self.converter.tensor_to_latent(result),)
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
if "image_path" in overrides and os.path.exists(overrides["image_path"]):
|
||||
os.unlink(overrides["image_path"])
|
||||
if "audio_path" in overrides and os.path.exists(overrides["audio_path"]):
|
||||
os.unlink(overrides["audio_path"])
|
||||
if hasattr(config, "config_json") and os.path.exists(config.config_json):
|
||||
os.unlink(config.config_json)
|
||||
return (config,)
|
||||
|
||||
|
||||
# Node class mapping
|
||||
@@ -1023,8 +926,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"LightX2VQuantizationConfig": LightX2VQuantizationConfig,
|
||||
"LightX2VAttentionConfig": LightX2VAttentionConfig,
|
||||
"LightX2VCachingConfig": LightX2VCachingConfig,
|
||||
"LightX2VDistillConfig": LightX2VDistillConfig,
|
||||
"LightX2VInference": LightX2VInference,
|
||||
"LightX2VConfigViewer": LightX2VConfigViewer,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -1033,6 +936,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LightX2VQuantizationConfig": "LightX2V Quantization Config",
|
||||
"LightX2VAttentionConfig": "LightX2V Attention Config",
|
||||
"LightX2VCachingConfig": "LightX2V Caching Config",
|
||||
"LightX2VDistillConfig": "LightX2V Distill Config",
|
||||
"LightX2VInference": "LightX2V Inference",
|
||||
"LightX2VConfigViewer": "LightX2V Config Viewer",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user