Skip to content

Latest commit

 

History

History
693 lines (543 loc) · 55.4 KB

File metadata and controls

693 lines (543 loc) · 55.4 KB

[v0.6.2-pre] — 2026-07-01 (WhitePaper §8.6 LOW: Virtual VRAM Support — virtual_vram_gb widget propagation)

✨ Added (FEAT-§8.6)

Реализует 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.0 kwarg; 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_gb widget:
    • 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.

🔧 Fixed (5 bugfixes во время FEAT-§8.6 implementation)

  • BUGFIX-1 (CRITICAL, NameError-prevention, core/gguf_split.py): При предыдущем рефакторе cache-HIT snippet из load_gemma_hybrid был скопирован в тело hybrid_split_gguf. Snippet ссылался на undefined encoder_name / projection_name / eject_modelsNameError 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_MemoryDiagnostics widget virtual_vram_gb отсутствовал — юзер с HybridSplitLoader widget vram=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 + explicit virtual_vram_gb=8.0 case (доказывает cap1 — bottleneck даже когда c0 fits).
  • BUGFIX-5 (vacuous test, tests/negative_clamp_to_zero): в initial draft test_virtual_vram_negative_clamp_to_zero имел NO assert* (vacuous pass). Добавлен self.assertIsNone(result_neg) — фиксирует контракт "negative vram clamps to 0, preserves baseline behavior" с явным поведением.

✅ Tested-by

$ 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.

Compatibility note

No breaking changes API:

  • hybrid_split_gguf / apply_strategy signatures получили NEW optional kwarg virtual_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 param virtual_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).

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_split exports (hybrid_split_gguf, apply_strategy, load_gemma_hybrid, resolve_donor_device, hybrid_split_gguf, _split_blocks_indices, clear_gemma_cache) — signatures unchanged.
  • core/memory_tracker exports (estimate_vram_budget, auto_select_strategy, gguf_quant_aware_bytes, gguf_estimate_bytes, QUANT_BITS_APPROX) — unchanged.
  • Public __version__ synced: __init__.py + pyproject.toml0.6.2-pre.

[v0.6.1-pre] — 2026-07-01 (Hotfix: relative imports — Kaggle / V3 loader fix)

🚨 Critical fix

  • 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 nodes fail) — абсолютные импорты в legacy-местах (если такие остались) получат base_dir в sys.path.

    Decision: append (не insert(0)) сознательно — core ни с чем не конфликтует в ComfyUI окружении; append не shadow'ит системные пакеты в случае коллизии имени.

✅ Verified

  • 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_cache signatures НЕ изменились.
  • Widget keys v0.6.0-pre: не трогали.
  • Node DISPLAY_NAMEs / CATEGORY hierarchy: не трогали.

[v0.6.0-pre] — 2026-07-01 (Batch 4: Critical Multi-GPU + UI rework + Gemma caching)

🚨 BREAKING CHANGES (workflow key renames)

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).

✨ Added

  • 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_recursive was 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_hybrid result keyed by (encoder_name, projection_name, donor_device, eject_models). 2-pass workflows save ~10–30s by skipping redundant comfy.sd.load_clip calls. New public clear_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_paths paths corrected: GGUF from diffusion_models/, CLIP from text_encoders/ (with clip/ fallback), VAE from vae/.

🔧 Fixed (Batch 4 carryover)

  • 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).

✅ Verified

  • 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.

🚫 Not implemented (deferred)

  • WhitePaper §8.4 MEDIUM (memory tracker accuracy via GGUF quant header) → v0.6.1.
  • WhitePaper §8.6 LOW (virtual_vram_gb widget) → v0.6.1.
  • WhitePaper §8.7 LOW (alternating pipeline strategy blocks bounce between GPUs) → v0.7.
  • node_fyx.md advanced_mode collapse 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).

🧹 Cleanup

  • tests/test_init.py: deprecated helper stubs were already removed in Round 2 (migrated to _patch_torch_cm contextmanager). Confirmed in Batch 4 that no remaining _patch_torch or _make_cuda_mock aliases are dead code.

═══════════════════════════════════════════════════════════════════════════════


История изменений (Changelog)

Все значимые изменения в этом проекте документируются в этом файле.

Формат основан на 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)

Fixed (исправления зависаний лоадеров)

  • 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_name lookup теперь ищет в text_encoders/ ПЕРВЫМ, fallback в clip/ через or chain. Раньше — хардкод только text_encodersFileNotFoundError если projection лежит в городском-city96 clip/-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) + defensive except Exception fallback с 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 (added/regression coverage)

  • 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).

Verified

  • python -m py_compile __init__.py nodes.py core/... — все 8 source-файлов ОК.
  • python -m unittest tests.* -v124 тестов, 1 skipped (DEFERRED) — все ОК.

[v0.3.0-pre] — 2026-06-30 (Kaggle Edition: VRAM parking, SageAttention, VAE Loader, rebrand)

Added (новые фичи)

  • 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.

Changed (ребрендинг + UX)

  • 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 CLIPDISPLAY_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.

Fixed (dropdown-баг)

  • 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. Раньше каждый виджет смотрел только в одну папку.

Fixed (критические исправления)

  • CRITICAL (__init__.py): _build_config() использовал importlib.import_module('nodes') который импортировал ComfyUI root nodes.py вместо нашего nodes.pyNODE_CLASS_MAPPINGS был пуст → все 6 нод были невидимы в ComfyUI. Заменён на относительный импорт from . import nodes.

Changed (изменения)

  • 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.

Test coverage

  • 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.

[v0.2.2-pre] — 2026-06-30 (pre-T4x2 deploy: attribution + Russian UX)

Проблема, которую решает этот релиз

Пользователи в ComfyUI Manager видели атрибуцию чужого проекта (dreamfast) и только английские длинные названия нод в меню Add Node → root → непонятно, какие имена для workflow-линковки и как добавлять узлы. Этот релиз чётко ставит атрибуцию (THE-ANGEL-AI / The Angel Studio / gi.the.angel@gmail.com) и делает display-имена нод понятными русскоязычным пользователям.

Fixed (атрибуция для ComfyUI Manager)

  • 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): version bumped "0.2.1""0.2.2-pre".
  • FIX ATTRIB-4 (__init__.py): консольный banner с author/repo/version opt-in через env-var LTX2_MULTIGPU_VERBOSE=1. По умолчанию — тихо, чтобы не засорять stdout ComfyUI при штатной загрузке. Stdout-fallback (try: print(...) except Exception: pass) на случай frozen-exe / systemd env.

Fixed (Russian display names + clean menu grouping)

  • FIX UX-1 (nodes.py): 4 DISPLAY_NAME'а переведены на русский для человекочитаемости в меню Add Node. Технические class-keys (NODE_ID) и model_class values в 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."

Compatibility note

  • NODE_CLASS_MAPPINGS keys (technical) не изменены: все 4 ключа — LTX2_MultiGPU_* — те же что в v0.2.1. Existing workflow_api.json старых версий остаются load-compatible.
  • NODE_DISPLAY_NAME_MAPPINGS values изменены — это 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.

[v0.2.1] — 2026-06-30 (audit-driven bugfixes)

Fixed (аудит post-v0.2.0: regressive correction + strategy-switch reliability)

  • 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 (unpatched nn.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'ил. Fallback getattr(_, ..., inner.to) -- применимо для случая apply_strategy до hybrid_split_gguf.
  • FIXED (apply_strategy embed/head drift): при переключении whole-modelblocks_* стратегии через 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 == 44 invariant; _split_blocks_indices export 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.

Compatibility note

  • Никаких breaking changes API: _split_blocks_indices, hybrid_split_gguf, apply_strategy signatures не поменялись -- только contents/behavior. Downstream-форки, импортирующие from core.gguf_split import _split_blocks_indices, полyчат корректное (13,) для blocks_30_70 вместо buggy (14,).
  • tests/ -- новая директория. Без __init__.py. Совместимо с stdlib unittest (run via python -m unittest tests.* -v); pytest не требуется, но tests/__init__.py может потребоваться в CI когда pytest добавится в workflow (вне scope этого patch).

Polish (post-audit: HIGH-1 + HIGH-2 + MED дополнения поверх регрессионного раунда выше)

HIGH severity

  • FIXED (HIGH-1, key unification): scripts/diagnostic.py и core/memory_tracker.py используют разные импна для одного и того же ключа footprint dict (sage_scratch vs sage_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).

MED severity

  • ADDED (MED-3, donor_device в apply_strategy): Node LTX2_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 widget donor_device ∈ {auto, cuda:0, cuda:1} (default auto). ComfyUI auto-fills default при отсутствии в workflow_api.json → backward-compat OK.
  • ADDED (MED-3 follow-up, model_options rename): Key "secondary" в patcher.model_options["ltx2_multigpu_split"] после добавления donor_device widget стал 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.

LOW / cosmetic

  • CHANGED (MED-5, version sync): pyproject.toml, __init__.py, README version-badge: 0.2.00.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.

Compatibility note (cumulative)

  • Backward-compat API: _split_blocks_indices, hybrid_split_gguf, apply_strategy core signatures не сломаны (MED-3 added positional kwarg donor_device="auto" → optional).
  • Backward-compat УI: DeviceStrategy donor_device widget теперь 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 to effective_donor без breaking old code.

[v0.2.0] — 2026-06-30

License (LICENSING)

  • 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); classifier License :: OSI Approved :: ... GPLv3+ добавлен в pyproject.toml.
  • CHANGED: README раздел «Credits» + новый раздел «Лицензия и юридические моменты» — явно упоминают GPL-3.0-or-later для кода и отдельные licenses (LTX-Video Community License / Gemma License) для весов.

Documentation (документация)

  • 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, live nvidia-smi через MemoryDiagnostics).

Funding (финансирование)

  • ADDED: .github/FUNDING.yml — нативная GitHub-кнопка Sponsor → Boosty (custom: ["https://boosty.to/the_angel/donate"]). GitHub читает этот файл из root репозитория и рендерит Sponsor-кнопку в правом sidebar.
  • ADDED: README — Boosty-badge [![Sponsor: Boosty](orange.svg)] в шапке.
  • ADDED: README — секция «Если хотите поддержать проект 💚» в начале + повторная CTA-секция «Поддержать проект (повтор)» перед Credits. Деньги идут на оплату GPU-часов Kaggle / Colab для тестов разных конфигураций.

Fixed (стабилизация UI-стороны)

  • 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_strategy verbose-routing. Нода Device Strategy Switch прокидывает свой verbose_log widget параметр в 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_choices helper в 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 доступны. Tightened except (RuntimeError, AssertionError) вместо broad except Exception.
  • FIXED (d9d0d6a, FIX MEDIUM_apply_strategy): DeviceStrategy.apply_strategy verbose widget wiring. Пробрасывает widget verbose_log в core.apply_strategy(verbose=...). Закрывает loop между UI-виджетом и core-функцией.
  • FIXED (d9d0d6a, FIX R4 contract): MemoryDiagnostics.diagnose returns (report,) 1-tuple. Совместимо с ComfyUI V1 unpacking val, = node.FUNCTION(...). Устраняет ValueError: too many values to unpack (expected 1) на Kaggle zero-image и старых ComfyUI builds. Console preview остаётся через print() в ComfyUI log.

Security (copyleft implications)

  • 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.

Compatibility note

  • ADDED: явное замечание о компонентах в LICENSE: веса LTX 2.3 (Lightricks, LTX License) и Gemma 3 (Google, Gemma License) — собственных conditions, не GPL-3.0. Только код этого проекта под GPL-3.0-or-later. Перед коммерческим использованием генерации убедитесь в соблюдении license term чекпоинтов.

Documentation (структура папок)

  • 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-prefix submodule/.github/FUNDING.yml.github/FUNDING.yml (relic from superproject-era description, теперь относительный путь to submodule-root).
  • CHANGED: root README.md получил новую секцию ## Документы проекта (между ## Лицензия... и ## Хочется поковыряться) с markdown-links на 3 файла в docs/.

Build (version sync package metadata)

  • 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.md version-badge URL: version-0.1.0-green.svgversion-0.2.0-green.svg. Visible visual cue for users browsing repo root.

CI

  • 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@v5cache: '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 group ci-smoke-${{ github.ref }} + cancel-in-progress: true (экономит CI-minutes на force-push в PR'е). Permissions: contents: read (минимум).
  • Effect: ImportError regressions теперь ловятся ДО merge в main. Раньше регрессия в apply_strategy или silent import torch failure проходила review без визуального smoke-test и обнаруживалась только через community issue-report.

Fixed (audit trail)

  • FIXED (3fbf84b, 74f08e54): pre-existing typo nnodes.pynodes.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``.

[v0.1.0] — 2026-06-29

First public release

  • ADDED: LTX2_MultiGPU_HybridSplitLoader — загружает LTX 2.3 22B GGUF, делит 44 DiT блока между двумя картами (blocks_50_50 default), 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.

[pre-0.1.0] — разработка (phase 1 + phase 2 + phase 3)

Phase 2 — core foundation (d8e0a7d)

  • ADDED: core/gguf_split.py — GGUF-сплиттер + forward-hook установка (vector-aware dtype detection для _move_param / _move_buffer, kwargs hooks with_kwargs=True, lock .to dtype, degenerate guards if 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.

Phase 3 — LTX-Video-specific nodes (f8d553d)

  • 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.

Архитектурный контекст (Why we exist)

  • Проблема: city96/ComfyUI-GGUF деквантизирует LTX 2.3 → его 44 DiT-блока переименовываются из model.diffusion_model.layers.* (на которые нацелен regex pollockjj/ComfyUI-MultiGPU DisTorch2) в 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.

License этого CHANGELOG

CHANGELOG.md распространяется под GPL-3.0-or-later, как и код этого проекта.