Skip to content

Add IC-LoRA Ingredients remix mode (2-8 reference images) to local LTX video gen #3112

Description

@atomantic

Phase 3 of #3100 (Phase 1 — Control — shipped; see that PR). This is the only remaining phase that needs real new code, because Ingredients is multi-reference and its weight lives in a hostile repo.

The backbone from Phase 1 already handles most of it: --mode ic / run_ic_lora() in scripts/generate_ltx2.py accepts a repeatable --ic-reference and already carries IC_REFERENCE_LIMITS['ingredients'] = (2, 8); icLoraArgs in server/services/videoGen/local.js validates against the registry's min/max and emits one --ic-reference per clip; the route already accepts icReferenceVideoIds as a 1-8 array.

Work:

  1. Weight provisioning is the hard part. Upstream sources ltx-2.3-22b-ic-lora-ingredients-0.9.safetensors (~1.3 GB) from the DeepBeepMeep/LTX-2 mirror, which is a ~708 GB mega-repo — a snapshot download would be catastrophic. Requirements:
    • Try the official Lightricks/LTX-2.3-22b-IC-LoRA-Ingredients first; only fall back to the mirror.
    • The fallback MUST be a single-file hf_hub_download for the exact filename, never snapshot_download. server/lib/hfDownload.js currently spawns scripts/hf_download_repo.py, which enumerates and pulls the whole repo (it has --ignore globs but no single-file mode) — so this needs either a --only <filename> flag on that helper or a dedicated single-file path. Do not wire the mirror up until the single-file path exists and is tested; a mis-fire fills the user's disk.
    • resolveIcLoraWeight in server/lib/icLoraWeights.js already pins the exact filename inside the snapshot and falls back to the repo id when absent. That repo-id fallback hands the bare id to ICLoraPipeline._resolve_lora_path, which calls snapshot_download — so for the mirror the fallback must be suppressed (fail with a clear "download the weight first" error instead).
  2. Reference kind is images, not video. Ingredients recomposes from 2-8 stills. That means referenceKind: 'image' in the registry, and a different input surface: the route's icReference upload field is video/*-filtered and icReferenceVideoIds resolves against video history. Add an image path (gallery-file resolution mirroring keyframes, which already resolves 2-8 gallery images) rather than overloading the video fields.
  3. Client panel. IcLoraPanel.jsx renders a single reference; Ingredients needs a 2-8 row list. KeyframePanel.jsx is the existing multi-row gallery-picker to model it on. Keep the single-reference path unchanged.
  4. LoRA stacking is the payoff — the Phase 1 split already supports it: the IC weight rides lora_paths (fused by _fuse_loras pre-Stage-1) while user LoRAs ride _pending_loras, so an Ingredients x Character stack composes rather than replaces. Add a test that asserts both channels are present (server/services/videoGen/local.test.js has the Control version of this assertion).
  5. Test the 2-8 bounds at all three layers (Python IC_REFERENCE_LIMITS, icLoraArgs, route) — the counts are a weight contract, and a wrong count yields plausible-looking garbage rather than an error.

Validate on a real render before closing, and confirm the weight is un-gated (no HF token) as the Control and Colorize weights are.

Metadata

Metadata

Assignees

Labels

reference-watchProposed from a reference-repo watch

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions