[v0.6.2-pre] — 2026-07-01 (WhitePaper §8.6 LOW: Virtual VRAM Support — virtual_vram_gb widget propagation)
Реализует WhitePaper §8.6 LOW: virtual_vram_gb — reserved VRAM gap, расширяет
effective cuda:0 cap (для projection / auto-strategy) БЕЗ реального alloc. На Kaggle
T4×2 (14.5 GB каждый) позволяет predict OOM-safety без free VRAM drains и без
downgrade Q5_K_M → Q4_K_M в edge-budget scenarios.
Clamp policy:
- Widget
INT [0..16]step 1 → user-facing range. - Store в
model_options["ltx2_multigpu_split"]["virtual_vram_gb"]clamped [0, 16]. - Projection (
_project,auto_select_strategy) clamped [0, 8] GB — safety против wild-widget range и pathological LLM Hallucinated GB values. - WARN ≥ 8 GB при verbose mode (визуальный guard).
Touchpoints (4 files, additive only — все defaults = 0 → no-op для legacy workflows):
- core/gguf_split.py:hybrid_split_gguf(...) — kwarg
virtual_vram_gb: float = 0.0, store вpatcher.model_options["ltx2_multigpu_split"]["virtual_vram_gb"]. - core/gguf_split.py:apply_strategy(...) — mirror change, identical parity с hybrid_split_gguf (hot-switch через DeviceStrategy ноду propagates тот же bonus).
- core/memory_tracker.py:estimate_vram_budget(...) + auto_select_strategy(...) —
virtual_vram_gb: float = 0.0kwarg;eff_cap0 = cap0 + max(0, min(8, vram)); bonus display line "cuda:0 cap = X + virtual_vram_gb=Y = Z effective" в report. - nodes.py — 3 nodes exposed
virtual_vram_gbwidget:LTX2_MultiGPU_HybridSplitLoader(DiT loader, required)LTX2_MultiGPU_DeviceStrategy(hot-switch strategy без reload, required)LTX2_MultiGPU_MemoryDiagnostics(pre-flight VRAM check + auto-recommendation, required)
Примеры использования:
- UD Q5_K_M (~18 GB file) на T4×2 с
virtual_vram_gb=4→ eff_cap0 = 18.5 GB → strategy выбирается когда без vram была бы None. - UD Q4_K_M (~14 GB file) tight-edge:
virtual_vram_gb=2→ стратегия fits, юзер не downgrad'нул на Q3.
-
BUGFIX-1 (CRITICAL, NameError-prevention, core/gguf_split.py): При предыдущем рефакторе cache-HIT snippet из
load_gemma_hybridбыл скопирован в телоhybrid_split_gguf. Snippet ссылался на undefinedencoder_name / projection_name / eject_models→ NameError at runtime при любом production-вызове DiT loader. tests/ не покрывали путь (только import-check). Удалён misplaced block. Gemma cache HIT корректно живёт ТОЛЬКО вload_gemma_hybrid(function-top, доcomfy.sd.load_clip). -
BUGFIX-2 (backward-compat restore, core/gguf_split.py:apply_strategy): В
apply_strategy()model_options["ltx2_multigpu_split"]dict отсутствовал"donor"legacy alias (был добавлен в v0.2.1 MED-3 follow-up, но потерян при v0.6.0-pre UI rework → silent regression для downstream consumers, читающих[...]["donor"]). Восстановлен — dict parity сhybrid_split_gguf:{strategy, primary, effective_donor, secondary, donor, donor_spec, block_split_index, virtual_vram_gb}. -
BUGFIX-3 (widget consistency, nodes.py:MemoryDiagnostics): При FEAT-§8.6 первой итерации
LTX2_MultiGPU_MemoryDiagnosticswidgetvirtual_vram_gbотсутствовал — юзер с HybridSplitLoader widgetvram=4видел bonus effect при sampling, но НЕ видел в Diagnostics report (неконсистентно). Добавлен required widget + threading вestimate_vram_budget(..., virtual_vram_gb=...). -
BUGFIX-4 (test math, tests/test_memory_tracker_virtual_vram.py): 3 unit-теста в initial §8.6 suite использовали c0-only math без учёта
c1 = dit_gb/fraction + gemma_gb + other_cuda1_gb(2.45 GB = audio_vae + sage_scratch), что в_project()проекции добавляется к secondary side. Результаты:test_virtual_vram_expands_cap0:dit_gb 35.0 → 18.0(c0=18.10 входит в eff_cap0=18.5, c1=11.45 входит в cap1=14.5).test_virtual_vram_clamp_above_8:cap0=cap1 14.0 → 16.0(c1=14.45 fits, чтобы демонстрировать clamp [0, 8] на cap0 без cap1 бутылочного горлышка).test_virtual_vram_does_not_affect_cap1:dit_gb 30.0 → 24.0+ explicitvirtual_vram_gb=8.0case (доказывает cap1 — bottleneck даже когда c0 fits).
-
BUGFIX-5 (vacuous test, tests/negative_clamp_to_zero): в initial draft
test_virtual_vram_negative_clamp_to_zeroимел NOassert*(vacuous pass). Добавленself.assertIsNone(result_neg)— фиксирует контракт "negative vram clamps to 0, preserves baseline behavior" с явным поведением.
$ python -m unittest discover tests -v | tail -1
OK
Totals (после BUGFIX-4 + BUGFIX-5):
- tests/test_memory_tracker_virtual_vram.py::TestAutoSelectVirtualVram: 5 / 5
- tests/test_memory_tracker_virtual_vram.py::TestEstimateVramBudgetVirtualVram: 2 / 2
- Baseline (128 тестов v0.6.1-pre): 128 / 128
- GRAND TOTAL: 135 тестов, 1 skipped (DEFERRED `test_runtime_falls_back_to_clip_when_text_encoders_empty`,
pre-existing), 0 failed, 0 errors.
python -m py_compile __init__.py nodes.py core/__init__.py core/gguf_split.py core/gguf_reader.py core/memory_tracker.py core/sage_attention.py core/vram_parking.py tests/test_memory_tracker_virtual_vram.py— all OK.
No breaking changes API:
hybrid_split_gguf/apply_strategysignatures получили NEW optional kwargvirtual_vram_gb: float = 0.0— backward-compat: existing callers без kwarg работают as-is (default 0 = no-op).estimate_vram_budget/auto_select_strategy— same.LTX2_MultiGPU_MemoryDiagnostics.diagnose(...)сигнатура получила NEW required paramvirtual_vram_gb: int. Однако widget SPEC auto-located → ComfyUI auto-fills default при отсутствии в workflow_api.json ⇒ existing API workflows загружаются без migration script.model_options["ltx2_multigpu_split"]dict получил NEW ключ"virtual_vram_gb"- восстановлен legacy alias
"donor". Consumers iterating keys теперь видят consistent shape в обоих loaders (hybrid_split_ggufиapply_strategy).
- восстановлен legacy alias
Position-widgets_values workflows: тот же длины массивов → no migration script needed.
API-format workflows (inputs dict): virtual_vram_gb: 0 default auto-fill →
existing API workflows JSON.load() work as-is. New workflows могут явно задать
virtual_vram_gb: 4 для Kaggle T4×2 + UD Q5_K_M tight scenarios.
Backward-compat guarantees:
core/gguf_splitexports (hybrid_split_gguf,apply_strategy,load_gemma_hybrid,resolve_donor_device,hybrid_split_gguf,_split_blocks_indices,clear_gemma_cache) — signatures unchanged.core/memory_trackerexports (estimate_vram_budget,auto_select_strategy,gguf_quant_aware_bytes,gguf_estimate_bytes,QUANT_BITS_APPROX) — unchanged.- Public
__version__synced:__init__.py+pyproject.toml—0.6.2-pre.
-
FIX IMPORTS-1 (ModuleNotFoundError: No module named 'core'): Все 14 внутренних импортов (
from core.X) заменены на relative (from .core.X/from .X). Это устраняетModuleNotFoundErrorпри запуске на Kaggle / standalone-окружениях, где ComfyUI загружает ноду черезimportlib.import_module('nodes')БЕЗ добавления base-папки пакета вsys.path.Файлы:
__init__.py: 1 import (clear_gemma_cache)nodes.py: 8 imports (STRATEGIES + 7 ленивых в try/except)core/vram_parking.py: 3 imports (_remove_stored_hooks / EMBED_AND_HEAD_REL / LTX2_DIT_BLOCK_COUNT / _unlock_inner_to_recursive / _lock_inner_to_recursive / apply_strategy)core/memory_tracker.py: 2 imports (read_gguf_header)
-
FIX IMPORTS-2 (sys.path shim): в
__init__.pyПЕРЕД_build_config()добавлен defensive shim:_BASE_DIR = _os.path.dirname(_os.path.abspath(__file__)) if _BASE_DIR not in _sys.path: _sys.path.append(_BASE_DIR)
Что это даёт:
tests/(gitignored) продолжают работать со старыми абсолютнымиfrom core import ...импортами — base_dir добавляется в sys.path, Python находитcore/*.pyнапрямую.- Защищает от edge-case'ов когда ComfyUI НЕ загружает ноду как
package (
from . import nodesfail) — абсолютные импорты в legacy-местах (если такие остались) получат base_dir в sys.path.
Decision:
append(неinsert(0)) сознательно —coreни с чем не конфликтует в ComfyUI окружении; append не shadow'ит системные пакеты в случае коллизии имени.
python -m py_compile __init__.py nodes.py core/**.py— все 8 файлов OK.- Code-reviewer approval: consistent import style, no regression.
- public API:
hybrid_split_gguf/load_gemma_hybrid/park_dit/apply_strategy/clear_gemma_cachesignatures НЕ изменились. - Widget keys v0.6.0-pre: не трогали.
- Node DISPLAY_NAMEs / CATEGORY hierarchy: не трогали.
Widget keys RENAMED for clarity per Agent_Info/node_fyx.md and WhitePaper §8.
No backward-compat aliases (per user request to clean up dead code).
UI-format workflows saved from canvas (positional widgets_values array)
continue working — ComfyUI maps by index, not by key name.
API-format workflows (inputs dict in workflow_api.json) DO need migration.
| Old key | New key | Used in |
|---|---|---|
unet_name |
gguf_model |
HybridSplitLoader, MemoryDiagnostics |
split_strategy |
split_mode |
HybridSplitLoader, DeviceStrategy |
donor_device |
memory_gpu |
HybridSplitLoader, GemmaHybridLoader, DeviceStrategy, VAELoader |
clip_name1/gemma_name |
clip_model (unified) |
GemmaHybridLoader, MemoryDiagnostics |
projection_name |
projection_path |
GemmaHybridLoader |
vae_name |
vae_model |
VAELoader |
park_in_cpu |
park_model |
VRAMParking |
eject_models |
unload_after_generation |
GemmaHybridLoader |
verbose_log |
verbose |
All 7 nodes |
purge_cache |
clear_cache_after |
MemoryDiagnostics |
strategy |
mode |
DeviceStrategy |
Re-pin API workflows manually in ComfyUI (right-click node → Convert Widgets
to Inputs, then rename), or wait for the v0.6.1 release which adds
scripts/migrate_workflow_api_v05_v06.py (deferred — UI users unaffected).
-
WhitePaper §8.1 CRITICAL — VRAM Parking unlock/relock (core/vram_parking.py): (Without this dance, Round 3 BUG-6 recursive lock silently no-ops .to(cpu) per block, leaving DiT on cuda:0/1 mid-VAE-decode -> OOM (the original user-report symptom). The unlock->move->relock pattern restores single-block moves while keeping sampler safe from blanket submodule migrations between KSampler-steps.) Round 3 BUG-6
_lock_inner_to_recursivewas silently no-op'ing per-block.to('cpu')— DiT blocks stayed on cuda:0/cuda:1 mid-VAE-decode → OOM. Fix: park_dit now does_unlock_inner_to_recursive(inner)→ per-block.to(cpu_dev)→_lock_inner_to_recursive(inner)(mirrors apply_strategy hot-switch pattern). -
WhitePaper §8.3 HIGH — Gemma encoder caching (core/gguf_split.py:_GEMMA_CACHE): Caches
load_gemma_hybridresult keyed by(encoder_name, projection_name, donor_device, eject_models). 2-pass workflows save ~10–30s by skipping redundantcomfy.sd.load_clipcalls. New publicclear_gemma_cache()exported for test isolation. -
WhitePaper §8.5 MEDIUM — Gemma module-level move (core/gguf_split.py:load_gemma_hybrid): Replaced ~4000-param per-
.to(device)loop with two module-level moves:inner.to(donor_dev)→proj_module.to(primary_dev). 10–30× faster, lower PCIe overhead. Module-level move lets PyTorch merge all param + buffer + Module-level state operations. -
node_fyx.md UI rework (nodes.py): 7 nodes rebranded with emoji DISPLAY_NAMEs: 🧠 Load LTX2 GGUF Model · 📝 Load Dual Text Encoder · 💾 VRAM Diagnostics · 🎯 Switch GPU Strategy ·
🅿️ Park DiT (VRAM ↔ CPU) · ⚡ SageAttention (T4 Turbo) · 🎨 Load LTX2 VAE. CATEGORY hierarchical split:THE-ANGEL-AI/LTX2(loaders + strategy) +THE-ANGEL-AI/Utilities(parking, diagnostics, sage).folder_pathspaths corrected: GGUF fromdiffusion_models/, CLIP fromtext_encoders/(withclip/fallback), VAE fromvae/.
- apply_strategy hot-switch silent no-op (core/gguf_split.py:apply_strategy):
Per-block split-mode
.to(target_dev)under Round 3 BUG-6 lock silently no-op'd → hot-switch didn't change GPU layout. Fix:_unlock_inner_to_recursive(inner)→ per-block move →_lock_inner_to_recursive(inner)(try/finally for re-lock guarantee).
- py_compile OK: 8 source files + 6 test files (new widget names, Round 3 BUG-5/6/7 helpers, Round 4 parking unlock/relock, _GEMMA_CACHE).
- unittest validation final pass — see commit message for actual count.
- _GEMMA_CACHE cache-hit returns identical ModelPatcher: no re-dequant, no re-load_clip, no fresh hook installation.
- WhitePaper §8.4 MEDIUM (memory tracker accuracy via GGUF quant header) → v0.6.1.
- WhitePaper §8.6 LOW (
virtual_vram_gbwidget) → v0.6.1. - WhitePaper §8.7 LOW (alternating pipeline strategy blocks bounce between GPUs) → v0.7.
- node_fyx.md
advanced_modecollapse toggle (depends on ComfyUI >= 0.4.0 widget visibility API) → deferred. - API-format workflow migration script
→ v0.6.1 (UI workflows unaffected by Batch 4 — positional
widgets_values).
- tests/test_init.py: deprecated helper stubs were already removed in Round 2
(migrated to
_patch_torch_cmcontextmanager). Confirmed in Batch 4 that no remaining_patch_torchor_make_cuda_mockaliases are dead code.
═══════════════════════════════════════════════════════════════════════════════
Все значимые изменения в этом проекте документируются в этом файле.
Формат основан на Keep a Changelog, и этот проект придерживается Semantic Versioning.
[v0.4.0-pre] — 2026-07-01 (Bugfix batch: loaders — GemmaHybridLoader folder fallback, CPU-only dropdown, None-safe file lists)
- BUG-1 (CRITICAL, nodes.py) — все 6 вызовов
folder_paths.get_filename_list(...)обёрнуты в... or []для защиты отNone-возврата от ComfyUI для пустых/несуществующих папок. Раньше —TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'вenc_folder + clip_folder, ломало dropdown rendering для GemmaHybridLoader, MemoryDiagnostics и HybridSplitLoader. - BUG-2 (CRITICAL, core/gguf_split.py:load_gemma_hybrid) —
projection_namelookup теперь ищет вtext_encoders/ПЕРВЫМ, fallback вclip/черезorchain. Раньше — хардкод толькоtext_encoders→FileNotFoundErrorесли projection лежит в городском-city96clip/-layout. Совмещает вFileNotFoundError messageобе папки. - BUG-3 (HIGH, nodes.py:_cuda_donor_choices) — dropdown widget
donor_deviceтеперь скрываетcuda:0/cuda:1на CPU-only машинах. Раньше они добавлялись безусловно как baseline, что выглядело как рабочие опции, но падали сRuntimeErrorна load. Контракт соответствует docstring intent "юзер не должен видеть то, что упадёт с RuntimeError на load". - BUG-4 (MEDIUM, core/sage_attention.py) —
except ImportErrorрасширен доexcept (ImportError, OSError, RuntimeError)+ defensiveexcept Exceptionfallback с WARN. Broken sageattn install (бинарный .so битый / несовместимый CUDA-Triton) больше не крашит node. - REFACTOR-1 (nodes.py:STRATEGIES import) — dropdown стратегий теперь импортируется из
core/gguf_split.STRATEGIES(single source of truth). Раньше strategy-list literal дублировался в HybridSplitLoader + DeviceStrategy (drift risk при добавлении новой стратегии в core). Safe-fallback tuple вtry: from core... except: ...сохраняет работоспособность в standalone-окружениях (тесты, CLI).
- tests/test_init.py::TestCudaDonorChoices — обновлён: новый
_patch_torch_cmчерез@contextmanager, добавлены тестыtest_cpu_only_no_cuda_in_choices,test_cuda_available_shows_cuda0_cuda1_baseline,test_cuda_available_with_more_gpus(4-GPU case). Существующиеtest_include_cpu_adds_cpu,test_dit_excludes_cpuобновлены под mocking API. - tests/test_gguf_split_gemma_hooks.py — Block 4
TestProjectionFolderFallback(3 source-inspect теста для BUG-2 fix contract) +test_runtime_falls_back_to_clip_when_text_encoders_empty(DEFERRED — runtime mock-pipeline в 4-5x coverage scope, отложен в next batch).
python -m py_compile __init__.py nodes.py core/...— все 8 source-файлов ОК.python -m unittest tests.* -v— 124 тестов, 1 skipped (DEFERRED) — все ОК.
-
VRAM Parking (White Paper §8.1) — нода
LTX2_MultiGPU_VRAMParkingи модульcore/vram_parking.py. Временно переносит ВСЕ DiT блоки (+ embed/head слои) на CPU между Pass 1 и Pass 2, освобождая VRAM для VAE decode → upscale → VAE encode. Идемпотентна (флагparkedвmodel_options). Безопасна для GGUF — использует_ltx2_original_toвместоinner.to()чтобы не триггерить dequant.unpark_dit()делегирует вapply_strategy()для точного восстановления GPU-layout с хуками. -
SageAttention-SM75 (White Paper §6) — нода
LTX2_MultiGPU_SageAttentionи модульcore/sage_attention.py. Интеграция черезmodel_options['attention_patch']— стандартный ComfyUI механизм. Автоопределение:import sageattnв рантайме; если не установлен — тихо возвращает модель без патча. Wrapper делегирует вsageattn.sageattn(q, k, v)для INT8 QK^T + FP16 PV на T4 (Turing SM75).model.clone()изолирует мутации от оригинального графа. -
Kaggle Edition memory tracker — полный реврайт
core/memory_tracker.py:gguf_quant_aware_bytes(path)— читает GGUF header (без загрузки весов!), считает реальный quant-размер поQUANT_BITS_APPROX(24 quant-типа: Q4_0..BF16), возвращает(file_size, vram_quant, fp16_equiv).- RAM бюджет:
KAGGLE_SYSTEM_RAM_GB = 29.0,KAGGLE_VRAM_PER_T4_GB = 14.5,KAGGLE_VRAM_RESERVED_GB = 1.0(резерв под драйверы),PYTHON_COMFY_RAM_OVERHEAD_GB = 3.5. auto_select_strategy()— приоритет: blocks_50_50 > blocks_30_70 > pipeline.- Quant-предупреждения: если Q5_K_M не влезает → советует Q4_K_M или Q3_K_XL.
- Совместимость: legacy
gguf_estimate_bytes()сохранён.
-
example_workflows/ — 3 готовых воркфлоу JSON для импорта в ComfyUI:
ltx2_full_2pass_video.json— полный 2-pass пайплайн со всеми 7 нодами.ltx2_strategy_switch.json— демо горячего переключения стратегий.ltx2_diagnostics_first.json— pre-flight сравнение 4 quant-типов.
-
VAE Loader с выбором GPU (node 7/7) — нода
LTX2_MultiGPU_VAELoader(🖼️ VAE Загрузчик (GPU)). Загружает VAE черезcomfy.sd.load_vae()и размещаетfirst_stage_modelна выбранном GPU (donor_device: auto/cuda:0/cuda:1/cpu). Совместим с VAE Decode/Encode — возвращает("VAE",). Позволяет разгрузить cuda:0 от VAE во время decode/encode на T4×2 (14.5 GB каждая). -
ComfyUI-Manager PR — подана заявка на регистрацию в центральной базе
custom-node-list.json(PR #3037 вComfy-Org/ComfyUI-Manager). После merge: автор = THE-ANGEL-AI, click-through URL = наш репо,dreamfastнавсегда вытеснен из Manager UI.
-
CATEGORY →
"THE-ANGEL-AI"(все 7 нод). Было"THE-ANGEL-AI / LTX-2 MultiGPU"— дублирование бренда в subcategory создавало шум в ComfyUI Add Node меню. Теперь чистое уникальное имя. -
DISPLAY_NAME с эмодзи (все 7 нод). Современные названия с иконками: 🔀 Разделитель DiT (2×GPU), 📝 Dual CLIP Загрузчик (Gemma 3), 🩺 Диагностика VRAM, ⚙️ Стратегия GPU (hot-switch),
🅿️ Парковка DiT (VRAM↔CPU), ⚡ SageAttention (T4 турбо), 🖼️ VAE Загрузчик (GPU). -
GemmaHybridLoader → Dual CLIP —
DISPLAY_NAMEизменён с"Загрузчик промптов (Gemma 3)"на"📝 Dual CLIP Загрузчик (Gemma 3)"по запросу пользователя («не загрузчик промптов, а Dual CLIP / текстовый энкодер»). -
pyproject.toml: добавлен
Icon = "assets/icon.png"(128×128, brand color#3B379E). Иконка отображается в ComfyUI Manager. -
init.py docstring: обновлён под 7 нод и новую CATEGORY.
-
FIX dropdown-bug (nodes.py):
if choices:guard вINPUT_TYPESсрезал dropdown-формат(choices,)когдаfolder_paths.get_filename_list()возвращал пустой список — ComfyUI показывал голое текстовое поле("STRING", ...)вместо выпадающего списка. Затронуты 3 ноды: HybridSplitLoader (unet_name), GemmaHybridLoader (clip_name1+projection_name), MemoryDiagnostics (unet_name+gemma_name). Теперь dropdown рендерится всегда (даже пустой). -
FIX (GemmaHybridLoader/MemoryDiagnostics): file-picker'ы для Gemma (.safetensors) теперь объединяют файлы из обеих папок —
text_encoders/Иclip/— черезdict.fromkeys()dedup. Раньше каждый виджет смотрел только в одну папку.
- CRITICAL (
__init__.py):_build_config()использовалimportlib.import_module('nodes')который импортировал ComfyUI rootnodes.pyвместо нашегоnodes.py→NODE_CLASS_MAPPINGSбыл пуст → все 6 нод были невидимы в ComfyUI. Заменён на относительный импортfrom . import nodes.
- nodes.py: добавлены ноды 5 (
VRAMParking), 6 (SageAttention), 7 (VAELoader). Всего 7 нод вNODE_CLASS_MAPPINGS. - pyproject.toml:
author="THE-ANGEL-AI",PublisherId="THE-ANGEL-AI",Icon="assets/icon.png",reference="https://github.com/THE-ANGEL-AI/...",DisplayName="LTX-2 MultiGPU". - README.md: миграционная заметка для пользователей dreamfast.
tests/test_vram_parking.py— 11 тестов (идемпотентность, round-trip, missing config).tests/test_sage_attention.py— 9 тестов (мокингsys.modules, делегирование wrapper).tests/test_init.py/tests/test_nodes.py— обновлены под 7 нод (+ VAE Loader контракт, + TestVAELoaderWidgets, CATEGORY/эмодзи/DISPLAY_NAME).- Все тесты (6 модулей, 119 тестов) проходят:
python -m unittest discover tests -v.
Пользователи в ComfyUI Manager видели атрибуцию чужого проекта (dreamfast) и
только английские длинные названия нод в меню Add Node → root → непонятно, какие
имена для workflow-линковки и как добавлять узлы. Этот релиз чётко ставит
атрибуцию (THE-ANGEL-AI / The Angel Studio / gi.the.angel@gmail.com) и делает
display-имена нод понятными русскоязычным пользователям.
- FIX ATTRIB-1 (
__init__.py): добавлены все required/optional metadata-поля, которые ComfyUI (включая ComfyUI Manager's registry) использует для attribution:__version__ = "0.2.2-pre"(синхронизирован с pyproject.toml).__author__ = "The Angel Studio"(был уже).__author_email__ = "gi.the.angel@gmail.com"(NEW).__author_github__ = "THE-ANGEL-AI"(NEW — ComfyUI Manager парсит это поле из некоторых forks и сопоставляет с GitHub API для верификации).__repo__ = "https://github.com/THE-ANGEL-AI/ComfyUI-LTX2-MultiGPU"(NEW — fallback для consumers, которые читают__repo__напрямую).- Большой header в module docstring с ascii-art-attribution banner: автор,
repo, sponsor, license, 4 ноды одной строкой, явное
«НЕ форк каких-либо из тех проектов» (anti-confusion guard против
dreamfast/ComfyUI-LTX2-MultiGPU— у нас другой проект).
- FIX ATTRIB-2 (
pyproject.toml):[project].descriptionтеперь явно указывает авторство: "Hybrid Multi-GPU split loader for LTX 2.3 GGUF on 2×T4. Made by THE-ANGEL-AI. Не fork dreamfast." (первая фраза осталась функциональной, второй абзац — attribution-guard). - FIX ATTRIB-3 (
pyproject.toml):versionbumped"0.2.1"→"0.2.2-pre". - FIX ATTRIB-4 (
__init__.py): консольный banner с author/repo/version opt-in через env-varLTX2_MULTIGPU_VERBOSE=1. По умолчанию — тихо, чтобы не засорять stdout ComfyUI при штатной загрузке. Stdout-fallback (try: print(...) except Exception: pass) на случай frozen-exe / systemd env.
- FIX UX-1 (
nodes.py): 4 DISPLAY_NAME'а переведены на русский для человекочитаемости в меню Add Node. Технические class-keys (NODE_ID) иmodel_classvalues вNODE_CLASS_MAPPINGSНЕ переименованы — все существующие workflow_api.json остаются совместимыми. Финальные имена:LTX2_MultiGPU_HybridSplitLoader→"Разделитель модели (2 GPU)"LTX2_MultiGPU_GemmaHybridLoader→"Загрузчик промптов (Gemma 3)"LTX2_MultiGPU_MemoryDiagnostics→"Диагностика видеопамяти"LTX2_MultiGPU_DeviceStrategy→"Переключатель стратегии"В ComfyUI Add Node меню теперь: Add Node → LTX-2 MultiGPU → четыре русских имени в алфавитном порядке (Д < З < П < П). CATEGORY"LTX-2 MultiGPU"обеспечивает group-prefix auto-add в UI, поэтому в DISPLAY_NAME мы НЕ дублируем бренд-префикс (reviewer-revised style).
- FIX UX-2 (
nodes.py): каждый DISPLAY_NAME прокомментирован: "Russian display name for users (grouped by CATEGORY). CATEGORY prefix adds the brand tag automatically in ComfyUI's Add Node menu, NODE_ID (technical class key) preserved for workflow_api/script compat."
NODE_CLASS_MAPPINGSkeys (technical) не изменены: все 4 ключа —LTX2_MultiGPU_*— те же что в v0.2.1. Existing workflow_api.json старых версий остаются load-compatible.NODE_DISPLAY_NAME_MAPPINGSvalues изменены — это OK, поскольку эти values используются только для UI-render. Если какой-то downstream serailizes workflow_api.json по display-name (не NODE_ID), они увидят русские имена после upgrade; это правильный путь для v0.2.2-pre.__version__ = "0.2.2-pre"— pre-release marker; users наpip install --preполучат эту версию, на stable pin получат v0.2.1 пока не выйдет v0.2.2 stable.
- FIXED (regression):
_split_blocks_indices("blocks_30_70")теперь возвращает(13,)(13 блоков @ primary, 31 @ donor = честные 30/70). Раньше возвращал(14,)→ 32/68 split (14 @ primary, 30 @ donor), что противоречило имени стратегии и обманывалоmemory_tracker.estimate_vram_budget: heuristic проецировал бюджет для 30/70, runtime получал 32/68 и OOM-ил на edge-budget сценариях. Docstring в_split_blocks_indicesсинхронизирован под новое(13,). - FIXED (apply_strategy блокировка):
apply_strategyтеперь используетinner._ltx2_original_to(unpatchednn.Module.to) вместоinner.toдля whole-model перемещений. Причина:_lock_inner_to(Risk #7 fix) monkey-патчитinner.toв no-op для device-moves, чтобы ComfyUI sampler не драгал split-блоки обратно на cuda:0. До этого fix'а -- первый вызовapply_strategyпослеhybrid_split_ggufНИЧЕГО не двигал (патч поглоцал вызов) и strategy-switch silently no-op'ил. Fallbackgetattr(_, ..., inner.to)-- применимо для случаяapply_strategyдоhybrid_split_gguf. - FIXED (apply_strategy embed/head drift): при переключении
whole-model→blocks_*стратегии черезapply_strategyтеперь вызывается_move_modules_with_prefix(diffusion, primary_dev, *EMBED_AND_HEAD_REL)после перемещения blocks 0..split_idx. Без этогоtime_embed/adaln/proj_in/proj_outоставались на cuda:1 из прошлой whole-model стратегии → device mismatch при sampling → runtime crash. Зеркалит ту же логику что вhybrid_split_gguf. - FIXED (apply_strategy defensive fallback): новая
else:-ветка послеtarget_dev/split-mode блоков. Имитирует поведениеhybrid_split_gguf: если какой-то split-translate'ор окажется без соответствующей ветки в_split_blocks_indices(будущаяblocks_70_30без branch), вместо silent no-op теперь UNCONDITIONAL[ComfyUI-LTX2-MultiGPU] WARN: apply_strategy: ...+whole-model move @ primary_dev-- ловится визуально + не разрушает model placement. - ADDED:
tests/test_gguf_split_blocks_indices.py-- regression-gate stdlib-unittest (без pytest). 10 методов:blocks_50_50/blocks_30_70(с share-percentage assertions) /pipeline/single_cuda0/1→ пустой split; defensive unknown-strategy subtest loop;all_STRATEGIES_resolvableс explicit caveat про false-negative trap; STRATEGIES-tuple integrity assertion;LTX2_DIT_BLOCK_COUNT == 44invariant;_split_blocks_indicesexport sanity. Запуск:python -m unittest tests.test_gguf_split_blocks_indices -v. - ADDED (apply_strategy docstring): новое
⚠️ DiT-only предупреждение наapply_strategy-- функция предполагает DiT-ModelPatcher отhybrid_split_ggufи НЕ предназначена для Gemma encoder patcher отload_gemma_hybrid(projections: text_projection@primary + encoder@donor делатсяя вload_gemma_hybrid, не через hot-swap). Если в будущем понадобится apply_strategy для Gemma -- нужна отдельная функция с проекцией на Gemma layout.
- Никаких breaking changes API:
_split_blocks_indices,hybrid_split_gguf,apply_strategysignatures не поменялись -- только contents/behavior. Downstream-форки, импортирующиеfrom core.gguf_split import _split_blocks_indices, полyчат корректное(13,)дляblocks_30_70вместо buggy(14,). tests/-- новая директория. Без__init__.py. Совместимо с stdlib unittest (run viapython -m unittest tests.* -v); pytest не требуется, ноtests/__init__.pyможет потребоваться в CI когда pytest добавится в workflow (вне scope этого patch).
- FIXED (HIGH-1, key unification):
scripts/diagnostic.pyиcore/memory_tracker.pyиспользуют разные импна для одного и того же ключа footprint dict (sage_scratchvssage_attention_scratch). Это silent-bug: components dict передавался через pipeline но.get()возвращал 0 для отсутствующего ключа → projection занижала VRAM на ~4.2 GB (2.1 GB × 2 карты). Canonical key теперьsage_attention_scratchв обоих файлах; backward-compat fallback.get("sage_attention_scratch", components.get("sage_scratch", 0.0))kept вdiagnostic.py:_compute_otherдля старых callers. test:tests/test_memory_tracker.py::TestV021PolishGuards::test_sage_attention_scratch_key_present. - FIXED (HIGH-2, silent dtype drop в
_lock_inner_to._no_op_to): Risk #7 fix из v0.2.1 ранеешеl перехватывал весь вызовinner.to(...)если видел device-like arg — тихо проглатывал сопутствующиеdtype=torch.float16/memory_format=torch.channels_last/non_blocking=True. KSampler иногда зовёт.to(dtype=...)для optimization — старый fix дропал dtype-cast → sampler-uncacheable weight-bits и потенциально OOM после merge. Новые helpers:_is_device_arg(a)(top-level device-detector),_strip_device(args, kwargs)(отфильтровывает device args/kwargs). Теперь: если device-move detected, вызов передаётся вoriginal_to(*cleaned_args, **cleaned_kwargs)с dtype/memory_format сохранёнными; pure non-device calls идут в original_to без изменений; pure device-call возвращаетinner(no-op).
- ADDED (MED-3, donor_device в
apply_strategy): NodeLTX2_MultiGPU_DeviceStrategyранеешеl захардкодил secondary_dev для всех strategy-switch на лету, из-за чего user override из HybridSplitLoader (напримерcuda:0илиcpu) молча игнорировался при переключении стратегии через DeviceStrategy ноду. Сигнатура apply_strategy теперь:apply_strategy(patcher, strategy, verbose=False, donor_device="auto"). Degenerate-guard дляcpuкак donor (для DiT — anti-pattern → fallback на secondary_dev с UNCONDITIONAL WARN зеркалит hybrid_split_gguf). DeviceStrategy INPUT_TYPES получил required widgetdonor_device ∈ {auto, cuda:0, cuda:1}(defaultauto). ComfyUI auto-fills default при отсутствии в workflow_api.json → backward-compat OK. - ADDED (MED-3 follow-up,
model_optionsrename): Key"secondary"вpatcher.model_options["ltx2_multigpu_split"]после добавленияdonor_devicewidget стал misleading: значение могло быть primary или secondary. Canonical key теперь"effective_donor". Legacy keys"secondary"/"donor"оставлены для backward-compat с явным комментарием# legacy alias. - FIXED (MED-4, loras_estimate в pipeline projection):
memory_tracker.estimate_vram_budget._project("pipeline")ранеешеl не включалloras_estimate(2.55 GB) → projection silent-bug для workflow с LoRами + pipeline-стратегией (false OK → real OOM). Теперь loras учитывается на стороне, где лжит DiT (cuda:1 для pipeline/single_cuda1, cuda:0 для остальных). test:tests/test_memory_tracker.py::TestProjectPerStrategy::test_pipeline_includes_loras_on_cuda1.
- CHANGED (MED-5, version sync):
pyproject.toml,__init__.py, README version-badge:0.2.0→0.2.1. CHANGELOG теперь имеет две подсекции под v0.2.1 (audit выше + polish ниже) — release-history закрыта. - ADDED (TEST-extend):
tests/test_memory_tracker.py— 4 unittest-класса с 18 тестами: TestV021PolishGuards (HIGH-1 guard), TestProjectPerStrategy (MED-4 math для каждой strategy), TestClassifyTensor (DiT tensor-name → category mapping), TestResolveDonorDevice (auto/cuda:0/cuda:1/cpu + edge cases). Stdlib-only, без pytest.
- Backward-compat API:
_split_blocks_indices,hybrid_split_gguf,apply_strategycore signatures не сломаны (MED-3 added positional kwargdonor_device="auto"→ optional). - Backward-compat УI: DeviceStrategy
donor_devicewidget теперь REQUIRED с default"auto"→ старые workflow_api.json (без поля) auto-fill default, runtime OK. - Backward-compat metadata:
patcher.model_options["ltx2_multigpu_split"]["effective_donor"]— новый canonical."secondary"/"donor"legacy keys сохранены. Consumers могут migrate toeffective_donorбез breaking old code.
- CHANGED: переход с MIT License на GNU GPL v3-or-later. Solo copyright
holder = The Angel Studio, переход унилатеральный (perpetual MIT grant позволяет
автору пере-licence собственные копии на любые совместимые conditions). Полный
текст — в
LICENSE(SPDX-License-Identifier:GPL-3.0-or-later); classifierLicense :: OSI Approved :: ... GPLv3+добавлен вpyproject.toml. - CHANGED: README раздел «Credits» + новый раздел «Лицензия и юридические моменты» — явно упоминают GPL-3.0-or-later для кода и отдельные licenses (LTX-Video Community License / Gemma License) для весов.
- CHANGED: README полностью переведён на русский с упрощённым языком для
обычных пользователей (раздел «Что это и зачем — простыми словами») при
сохранённой технической глубине:
- VRAM-диаграмма ASCII с русскими labels («DiT блоки», «Gemma 3 12B FP4», «~9 ГБ», …).
- Стратегии
blocks_50_50/blocks_30_70/pipeline/single_cuda0/1. - GGUF-квантизации (Q4_K_M / Q5_K_M / Q6_K / Q3 / Q2) с approximate file sizes.
- Hardware tested (T4×2 Kaggle / RTX 4090×2 / A5000+3090 asymmetric).
- Советы и грабли (
eject_models, prompt overflow на cuda:1, upscale OOM, livenvidia-smiчерез MemoryDiagnostics).
- ADDED:
.github/FUNDING.yml— нативная GitHub-кнопка Sponsor → Boosty (custom: ["https://boosty.to/the_angel/donate"]). GitHub читает этот файл из root репозитория и рендерит Sponsor-кнопку в правом sidebar. - ADDED: README — Boosty-badge
[]в шапке. - ADDED: README — секция «Если хотите поддержать проект 💚» в начале + повторная CTA-секция «Поддержать проект (повтор)» перед Credits. Деньги идут на оплату GPU-часов Kaggle / Colab для тестов разных конфигураций.
- FIXED (
c47a98a): Degenerate-WARN UNCONDITIONAL. WARN о single-GPU setup (secondary_dev == primary_dev) иeffective_donor == primary_devтеперь печатается всегда, не только приverbose_log=Trueв ноде Device Strategy Switch. Убирает silent normalisation, улучшает UX для пользователей на single-GPU машинах (видят явное WARN вместо тихого fallback). - FIXED (
cab22dc):apply_strategyverbose-routing. Нода Device Strategy Switch прокидывает свойverbose_logwidget параметр вcore.gguf_split.apply_strategy(verbose=verbose_log). Forward verbose без re-gating уже-unconditional WARN (# noqa: ARG003для backward-compat signature). - FIXED (
d9d0d6a, FIX LOW #4):_cuda_donor_choiceshelper в module-level. Заменяет два class-level_DONOR_DEVICE_CHOICES_*tuples. Всегда возвращает["auto", "cuda:0", "cuda:1"]baseline (даже приtorch is None); плюсcuda:0..Nотtorch.cuda.device_count()если torch+Cuda доступны. Tightenedexcept (RuntimeError, AssertionError)вместо broadexcept Exception. - FIXED (
d9d0d6a, FIX MEDIUM_apply_strategy):DeviceStrategy.apply_strategyverbose widget wiring. Пробрасывает widgetverbose_logвcore.apply_strategy(verbose=...). Закрывает loop между UI-виджетом и core-функцией. - FIXED (
d9d0d6a, FIX R4 contract):MemoryDiagnostics.diagnosereturns(report,)1-tuple. Совместимо с ComfyUI V1 unpackingval, = node.FUNCTION(...). УстраняетValueError: too many values to unpack (expected 1)на Kaggle zero-image и старых ComfyUI builds. Console preview остаётся черезprint()в ComfyUI log.
- SECURITY (BREAKING for downstream): переход MIT → GPL-3.0-or-later создаёт copyleft obligations для downstream-ов. Любая копия проекта, полученная после релиза v0.2.0 (2026-06-30), должна распространяться также под GPL-3.0-or-later (или совместимой лицензией). Старые форки, склонированные до 2026-06-30, сохраняют MIT-лицензию для своего снимка (perpetual grant), но upstream pull после этой даты обязывает ребрендинг под GPL-3.0.
- Solo copyright holder = The Angel Studio; relicense унилатеральный.
- SPDX-License-Identifier в корне репо:
GPL-3.0-or-later. Полный текст — вLICENSE; машиночитаемый classifier — вpyproject.toml.
- ADDED: явное замечание о компонентах в
LICENSE: веса LTX 2.3 (Lightricks, LTX License) и Gemma 3 (Google, Gemma License) — собственных conditions, не GPL-3.0. Только код этого проекта под GPL-3.0-or-later. Перед коммерческим использованием генерации убедитесь в соблюдении license term чекпоинтов.
- CHANGED: переезд
SECURITY.md/CHANGELOG.md/CONTRIBUTING.mdиз root вdocs/(git mv с сохранением истории).README.mdостаётся в root для GitHub landing-page visibility. Причина: чистая root-структура (только runtime metadata + код + .github/);docs/— расширяемая папка для future deep-docs (ARCHITECTURE.md/DESIGN.md/API.md/TROUBLESHOOTING.md— план). - ADDED:
docs/index.md— 11-строчный навигатор docs/: title# Documentation (docs/)+ intro-блок + 3 bullets с one-line описанием и markdown-links (./CHANGELOG.md/./SECURITY.md/./CONTRIBUTING.md) + back-link `README.md в корне + GH community-health-file URL citation + GPL-3.0 footer. - CHANGED: cross-references в переехавших файлах обновлены под новые relative-path'ы:
docs/SECURITY.mdтеперь ссылается на./CHANGELOG.md+../LICENSE;docs/CONTRIBUTING.mdтеперь ссылается на./SECURITY.md+./CHANGELOG.md;docs/CHANGELOG.md: wrong-prefixsubmodule/.github/FUNDING.yml→.github/FUNDING.yml(relic from superproject-era description, теперь относительный путь to submodule-root). - CHANGED: root
README.mdполучил новую секцию## Документы проекта(между## Лицензия...и## Хочется поковыряться) с markdown-links на 3 файла вdocs/.
- CHANGED:
pyproject.toml [project] version = "0.1.0"→"0.2.0". С комментарием выше строки:# 2026-06-30 — synchronized with CHANGELOG.md v0.2.0 (SoloAngel/SP-release). Effect:pip install --upgradeтеперь правильно детектит новую версию — раньше возвращал 'requirement already satisfied' из-за несинхронного metadata mismatch (pyproject был 0.1.0 а HEAD-коммит уже содержал CHANGELOG v0.2.0). - CHANGED:
__init__.py module-level __version__ = "0.1.0"→"0.2.0". С комментарием# VERSION синхронизирован с pyproject.toml (release v0.2.0, 2026-06-30). Runtime-variable, синхронизируется по manual-rule с pyproject.toml. - CHANGED: root
README.mdversion-badge URL:version-0.1.0-green.svg→version-0.2.0-green.svg. Visible visual cue for users browsing repo root.
- ADDED (
278f2ba):.github/workflows/ci.yml(Smoke CI). Triggers:push: branches: [main],pull_request: branches: [main],workflow_dispatch(manual). Matrix: Python 3.10 / 3.11 / 3.12,fail-fast: false(один упавший Python не отменяет остальные). Steps:actions/checkout@v4+actions/setup-python@v5(сcache: 'pip'+cache-dependency-path: requirements.txt,pyproject.toml) +pip install CPU-only torch (~200 МБ) + pip install -r requirements.txt+py_compile __init__.py nodes.py core/gguf_split.py core/gguf_reader.py core/memory_tracker.py+import-checkчерезpython -c "import sys; sys.path.insert(0, '.'); import nodes; print('OK')". Concurrency groupci-smoke-${{ github.ref }}+cancel-in-progress: true(экономит CI-minutes на force-push в PR'е). Permissions:contents: read(минимум). - Effect: ImportError regressions теперь ловятся ДО merge в
main. Раньше регрессия вapply_strategyили silentimport torchfailure проходила review без визуального smoke-test и обнаруживалась только через community issue-report.
- FIXED (
3fbf84b,74f08e54): pre-existing typonnodes.py→nodes.pyвdocs/SECURITY.md"Out of Scope" bullet. Источник: code-review pass во время docs-restructure (74f08e54). Также удалён redundant phrase "см. README «Советы и грабли» +" — ссылка на root README была prose-only описательная, новая формулировка стала точнее:\nodes.py` § `verbose_log``.
- ADDED:
LTX2_MultiGPU_HybridSplitLoader— загружает LTX 2.3 22B GGUF, делит 44 DiT блока между двумя картами (blocks_50_50default), cross-card forward-hook для передачи скрытых состояний между картами между шагами KSampler. - ADDED:
LTX2_MultiGPU_GemmaHybridLoader— загружает Gemma 3 12B FP4 +text_projectionкак единый CLIP, кладёт на нужные карты. - ADDED:
LTX2_MultiGPU_MemoryDiagnostics— пред-полётный VRAM-чек + projection +nvidia-smiснапшот. - ADDED:
LTX2_MultiGPU_DeviceStrategy— hot-swap стратегии split'а (blocks_30_70/pipeline/single_cuda0/1) без перезагрузки модели. - ADDED:
core/gguf_split.py— GGUF-сплиттер + forward-hook установка дляapply_strategy. - ADDED:
core/gguf_reader.py— низкоуровневый GGUF tensor reader (для лоадера). - ADDED:
core/memory_tracker.py— pre-flight VRAM projection (estimate_vram_budget). - ADDED: README в English с полной архитектурой, VRM layout ASCII, install steps (Windows PowerShell / Linux bash / Colab / Kaggle), hardware tested table (T4×2 / RTX 4090×2 / A5000+3090), tips (eject_models / nvidia-smi / upscale OOM), Verified GGUF quants table, Compatibility note для LTX-Video Community License.
- ADDED: LICENSE (MIT). SPDX-License-Identifier: MIT.
- ADDED:
core/gguf_split.py— GGUF-сплиттер + forward-hook установка (vector-aware dtype detection для_move_param/_move_buffer, kwargs hookswith_kwargs=True, lock.todtype, degenerate guardsif torch is None,_install_cross_device_hook). - ADDED:
core/gguf_reader.py— низкоуровневый GGUF tensor reader. - ADDED:
core/memory_tracker.py— pre-flight VRAM projection (estimate_vram_budget). - ADDED:
__init__.py— module entry с проверкой ComfyUI-folder paths иFOLDER_PATHS_OKфлагом graceful degradation.
- ADDED:
nodes.pyс четырьмя нодами (HybridSplitLoader/GemmaHybridLoader/MemoryDiagnostics/DeviceStrategy). - ADDED: README rewrite в OSS-style: hero, badges (License / ComfyUI / Python / Version), Quick-start, VRAM ASCII diagram, Hardware tested table, Tips, GGUF quants, Compatibility note.
- Проблема:
city96/ComfyUI-GGUFдеквантизирует LTX 2.3 → его 44 DiT-блока переименовываются изmodel.diffusion_model.layers.*(на которые нацелен regexpollockjj/ComfyUI-MultiGPUDisTorch2) вmodel.diffusion_model.transformer_blocks.*. DisTorch2 не находит этих блоков, тихо сваливает все 17 ГБ DiT наcuda:0одной карты и OOM-ит как только дело доходит до 720p-апскейла. - Решение: hand-roll split в
core/gguf_split.py(44 блока раскладываются по картам в соответствии со стратегией), forward-hook установлен для передачи скрытых состояний между картами между шагами KSampler. Никакого offload-to-CPU, никакого silent fallback.
CHANGELOG.md распространяется под GPL-3.0-or-later, как и код этого проекта.