Views
No views yet
| Name | Notes |
|---|---|
| ltx-2.3-22b-dev | The full model, flexible and trainable in bf16 |
| ltx-2.3-22b-distilled | The distilled version of the full model, 8 steps, CFG=1 |
| ltx-2.3-22b-distilled-1.1 | The distilled v1.1 version of the full model, 8 steps, CFG=1 - A different aesthetic experience and improved audio compared to v1.0 |
| ltx-2.3-22b-distilled-lora-384 | A LoRA version of the distilled model applicable to the full model |
| ltx-2.3-22b-distilled-lora-384-1.1 | A LoRA version of the v1.1 distilled model applicable to the full model |
| ltx-2.3-spatial-upscaler-x2-1.1 | An x2 spatial upscaler for the ltx-2.3 latents, used in multi stage (multiscale) pipelines for higher resolution |
| ltx-2.3-spatial-upscaler-x1.5-1.0 | An x1.5 spatial upscaler for the ltx-2.3 latents, used in multi stage (multiscale) pipelines for higher resolution |
| ltx-2.3-temporal-upscaler-x2-1.0 | An x2 temporal upscaler for the ltx-2.3 latents, used in multi stage (multiscale) pipelines for higher FPS |
1git clone https://github.com/Lightricks/LTX-2.git
2cd LTX-2
3
4# From the repository root
5uv sync
6source .venv/bin/activate1@article{hacohen2025ltx2,
2 title={LTX-2: Efficient Joint Audio-Visual Foundation Model},
3 author={HaCohen, Yoav and Brazowski, Benny and Chiprut, Nisan and Bitterman, Yaki and Kvochko, Andrew and Berkowitz, Avishai and Shalem, Daniel and Lifschitz, Daphna and Moshe, Dudu and Porat, Eitan and Richardson, Eitan and Guy Shiran and Itay Chachy and Jonathan Chetboun and Michael Finkelson and Michael Kupchick and Nir Zabari and Nitzan Guetta and Noa Kotler and Ofir Bibi and Ori Gordon and Poriya Panet and Roi Benita and Shahar Armon and Victor Kulikov and Yaron Inger and Yonatan Shiftan and Zeev Melumian and Zeev Farbman},
4 journal={arXiv preprint arXiv:2601.03233},
5 year={2025}
6}LTX2.3_Music_Video_Creator_Prompt_Creator_V5.jsonLTX2.3_Music_Video_Creator_T2V_V5.1.jsonLTX2.3_Music_Video_Creator_I2V_V5.1.json1from huggingface_hub import hf_hub_download
2import os
3HF_TOKEN = os.environ.get("HF_TOKEN")
4vanilla_base_path = hf_hub_download(
5 repo_id="ibyteohdear/Lightricks-LTX-2.3",
6 filename="ltx-2.3-22b-dev.safetensors", # or sulphur
7 token=HF_TOKEN,
8 cache_dir="/tmp/hf_cache"
9)
10
11lora_path = hf_hub_download(
12 repo_id="ibyteohdear/Lightricks-LTX-2.3",
13 filename="10Eros_v1.2_bf16.safetensors",
14 token=HF_TOKEN,
15 cache_dir="/tmp/hf_cache"
16)
17!pip install -q safetensors tqdm
18
19import torch
20from safetensors import safe_open
21from safetensors.torch import save_file
22from tqdm import tqdm
23import os
24
25base_path = vanilla_base_path
26fine_path = lora_path
27
28out_path = "/content/out/LTX_10Eros_LoRA_r768.safetensors"
29
30rank = 768
31device = "cuda"
32
33os.makedirs("/content/out", exist_ok=True)
34
35lora = {}
36
37with safe_open(base_path, framework="pt", device="cpu") as base_f, \
38 safe_open(fine_path, framework="pt", device="cpu") as fine_f:
39
40 keys = list(fine_f.keys())
41
42 targets = [
43 k for k in keys
44 if k.startswith("model.diffusion_model.")
45 and k.endswith(".weight")
46 and "norm" not in k.lower()
47 and "bias" not in k.lower()
48 ]
49
50 print("Extracting", len(targets), "layers")
51
52 for key in tqdm(targets):
53
54 base = base_f.get_tensor(key)
55 fine = fine_f.get_tensor(key)
56
57 # Only LoRA-compatible matrices
58 if base.ndim != 2:
59 continue
60
61 delta = (fine - base).float().to(device)
62
63 # SVD
64 U, S, Vh = torch.linalg.svd(delta, full_matrices=False)
65
66 r = min(rank, S.shape[0])
67
68 U = U[:, :r]
69 S = S[:r]
70 Vh = Vh[:r, :]
71
72 # LoRA convention
73 A = Vh
74 B = U @ torch.diag(S)
75
76 name = key
77
78 lora[f"{name}.lora_A.weight"] = A.cpu().to(torch.bfloat16)
79 lora[f"{name}.lora_B.weight"] = B.cpu().to(torch.bfloat16)
80
81
82save_file(lora, out_path)
83
84print("Saved:", out_path)
85print("Tensors:", len(lora))
86from huggingface_hub import HfApi
87api = HfApi()
88DEST_REPO = "ibyteohdear/Lightricks-LTX-2.3"
89output_filename = "/content/out/LTX_10Eros_LoRA_r768.safetensors"
90
91api.upload_file(
92 path_or_fileobj=output_filename,
93 path_in_repo="10Eros_v12.1_r768.safetensors",
94 repo_id=DEST_REPO,
95 token=HF_TOKEN,
96)
97print("Pipeline execution complete.")1
2import spaces
3import os
4import sys
5import torch
6import shutil
7from huggingface_hub import hf_hub_download, snapshot_download
8from safetensors.torch import load_file, save_file, safe_open
9
10HF_TOKEN = os.environ.get("HF_TOKEN")
11BASE = "/tmp/hf"
12
13@spaces.GPU(duration=600)
14def run_pipeline():
15
16 device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
17
18 # 1. Download base weights (The complete 46.1 GB flat file)
19 print("Downloading base BF16 checkpoint...")
20 vanilla_base_path = hf_hub_download(
21 repo_id="ibyteohdear/Lightricks-LTX-2.3",
22 filename="ltx-2.3-22b-distilled-1.1.safetensors",
23 token=HF_TOKEN,
24 cache_dir="/tmp/hf_cache"
25 )
26
27 # 2. Download LoRA
28 print("Downloading LoRA weights...")
29 lora_path = hf_hub_download(
30 repo_id="ibyteohdear/Lightricks-LTX-2.3",
31 filename="10Eros_v14_r768.safetensors",
32 token=HF_TOKEN,
33 cache_dir="/tmp/hf_cache"
34 )
35
36 # 4. Load the entire original 46.1 GB dictionary into RAM memory
37 print("Loading tensors into RAM...")
38 base_state_dict = load_file(vanilla_base_path, device="cpu")
39
40 # 5. Load LoRA weights
41 lora_state_dict = load_file(lora_path, device="cpu")
42
43 # 6. Apply LoRA updates directly onto matching base keys
44 print("Starting LoRA fusion loop...")
45 baked_count = 0
46 added_count = 0
47 skipped_count = 0
48 lora_strength = 1.0
49
50 print("Example LoRA keys:")
51 for k in list(lora_state_dict.keys())[:10]:
52 print(k)
53
54 print("\nExample Base keys:")
55 for k in list(base_state_dict.keys())[:10]:
56 print(k)
57
58 matches = 0
59 total = 0
60
61 for k in lora_state_dict:
62 if ".lora_A.weight" in k:
63 total += 1
64 prefix = k.replace(".lora_A.weight","")
65 candidates = [
66 prefix,
67 prefix+".weight",
68 prefix.replace("diffusion_model.",""),
69 prefix.replace("model.diffusion_model.","")+".weight"
70 ]
71
72 if any(x in base_state_dict for x in candidates):
73 matches += 1
74
75 print(f"Compatible LoRA layers: {matches}/{total}")
76
77 # Scan the LoRA dictionary keys
78 for lora_key in list(lora_state_dict.keys()):
79 # Path variation A: Standard low-rank naming (.lora_down / .lora_up)
80 if ".lora_down.weight" in lora_key:
81 prefix = lora_key.split(".lora_down.weight")[0]
82 down_key = f"{prefix}.lora_down.weight"
83 up_key = f"{prefix}.lora_up.weight"
84
85 if up_key in lora_state_dict:
86 target_base_key = prefix
87
88 # CRITICAL: Only touch the base model if the key exists there natively!
89 if target_base_key in base_state_dict:
90 try:
91 W_base = base_state_dict[target_base_key].to(
92 device=device,
93 dtype=torch.bfloat16
94 )
95
96 A = lora_state_dict[a_key].to(
97 device=device,
98 dtype=torch.bfloat16
99 )
100
101 B = lora_state_dict[b_key].to(
102 device=device,
103 dtype=torch.bfloat16
104 )
105
106 rank = A.shape[0] # usually rank dimension
107 alpha = 768 # replace if metadata gives another value
108
109 scale = alpha / rank
110
111 delta_W = torch.matmul(B, A) * scale
112
113 base_state_dict[target_base_key] = (
114 W_base + delta_W
115 ).cpu()
116 except Exception as e:
117 print(f"Failed to bake layer {target_base_key}: {e}")
118 skipped_count += 1
119 else:
120 skipped_count += 1
121
122 # Path variation B: Low-rank dimension naming (.lora_A / .lora_B)
123 elif ".lora_A.weight" in lora_key:
124 prefix = lora_key.split(".lora_A.weight")[0]
125 a_key = f"{prefix}.lora_A.weight"
126 b_key = f"{prefix}.lora_B.weight"
127
128 if b_key not in lora_state_dict:
129 continue
130
131 target_base_key = prefix
132
133 if target_base_key in base_state_dict:
134 try:
135 W_base = base_state_dict[target_base_key].to(
136 device=device,
137 dtype=torch.bfloat16
138 )
139
140 A = lora_state_dict[a_key].to(
141 device=device,
142 dtype=torch.bfloat16
143 )
144
145 B = lora_state_dict[b_key].to(
146 device=device,
147 dtype=torch.bfloat16
148 )
149
150 rank = A.shape[0]
151 alpha = 768 # replace after checking metadata
152 scale = alpha / rank
153
154 delta_W = torch.matmul(B, A) * scale
155
156 if delta_W.shape != W_base.shape:
157 print("SHAPE FAIL:", target_base_key, W_base.shape, delta_W.shape)
158 skipped_count += 1
159 continue
160
161 base_state_dict[target_base_key] = (
162 W_base + delta_W
163 ).cpu()
164
165 baked_count += 1
166 except Exception as e:
167 print(f"Failed to bake layer {target_base_key}: {e}")
168 skipped_count += 1
169 else:
170 skipped_count += 1
171
172 # --- PROOF & LOGGING REGION ---
173 print("\n==================================================")
174 print(" FUSION VERIFICATION ")
175 print("==================================================")
176 print(f" Successfully Baked Layers : {baked_count}")
177 print(f" Newly Injected Multi-Layers: {added_count}")
178 print(f" Skipped / Mismatched Keys : {skipped_count}")
179 print("==================================================")
180
181 if baked_count == 0 and added_count == 0:
182 print("❌ CRITICAL WARNING: Zero operations were completed. Output will be unmodified!")
183 return
184 else:
185 print("✅ SUCCESS: BF16 loop fusion complete.\n")
186
187 # 7. Extract the original header metadata so the inference app knows the exact shapes
188 try:
189 with safe_open(vanilla_base_path, framework="pt", device="cpu") as f:
190 original_metadata = f.metadata()
191 except Exception as e:
192 original_metadata = None
193
194 # 8. DISK MANAGEMENT: Wipe cache down to free disk space before exporting
195 print("Cleaning cache directory...")
196 try:
197 if os.path.exists("/tmp/hf_cache"):
198 shutil.rmtree("/tmp/hf_cache")
199 except Exception as e:
200 pass
201
202 # 9. Write out and upload file
203 from huggingface_hub import HfApi
204 api = HfApi()
205
206 DEST_REPO = "ibyteohdear/Lightricks-LTX-2.3"
207 output_filename = "/tmp/LTX2.3_DISTILLED_BAKED.safetensors"
208
209 print("Saving the new baked safetensors file...")
210 if original_metadata:
211 save_file(base_state_dict, output_filename, metadata=original_metadata)
212 else:
213 save_file(base_state_dict, output_filename)
214
215 print(f"Uploading target file to Hugging Face: {DEST_REPO}...")
216 api.upload_file(
217 path_or_fileobj=output_filename,
218 path_in_repo="LTX2.3_DISTILLED-1.1_BAKED_LTX_10Eros_v14_r768.safetensors",
219 repo_id=DEST_REPO,
220 token=HF_TOKEN,
221 )
222 print("Pipeline execution complete.")
223
224if __name__ == "__main__":
225 run_pipeline()
2261
2import spaces
3import os
4import sys
5import torch
6import shutil
7from huggingface_hub import hf_hub_download, snapshot_download
8from safetensors.torch import load_file, save_file, safe_open
9
10HF_TOKEN = os.environ.get("HF_TOKEN")
11BASE = "/tmp/hf"
12
13@spaces.GPU(duration=600)
14def run_pipeline():
15
16 device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
17
18 # 1. Download base weights (The complete 46.1 GB flat file)
19 print("Downloading base BF16 checkpoint...")
20 vanilla_base_path = hf_hub_download(
21 repo_id="ibyteohdear/Lightricks-LTX-2.3",
22 filename="sulphur_distil_bf16.safetensors",
23 token=HF_TOKEN,
24 cache_dir="/tmp/hf_cache"
25 )
26
27 # 2. Download LoRA
28 print("Downloading LoRA weights...")
29 lora_path = hf_hub_download(
30 repo_id="ibyteohdear/Lightricks-LTX-2.3",
31 filename="10Eros_S_v12_r128.safetensors",
32 token=HF_TOKEN,
33 cache_dir="/tmp/hf_cache"
34 )
35
36 # 4. Load the entire original 46.1 GB dictionary into RAM memory
37 print("Loading tensors into RAM...")
38 base_state_dict = load_file(vanilla_base_path, device="cpu")
39
40 # 5. Load LoRA weights
41 lora_state_dict = load_file(lora_path, device="cpu")
42
43 # 6. Apply LoRA updates directly onto matching base keys
44 print("Starting LoRA fusion loop...")
45 baked_count = 0
46 added_count = 0
47 skipped_count = 0
48 lora_strength = 1.0
49
50 print("Example LoRA keys:")
51 for k in list(lora_state_dict.keys())[:10]:
52 print(k)
53
54 print("\nExample Base keys:")
55 for k in list(base_state_dict.keys())[:10]:
56 print(k)
57
58 matches = 0
59 total = 0
60
61 for k in lora_state_dict:
62 if ".lora_A.weight" in k:
63 total += 1
64 prefix = k.replace(".lora_A.weight","")
65 candidates = [
66 prefix,
67 prefix+".weight",
68 prefix.replace("diffusion_model.",""),
69 prefix.replace("model.diffusion_model.","")+".weight"
70 ]
71
72 if any(x in base_state_dict for x in candidates):
73 matches += 1
74
75 print(f"Compatible LoRA layers: {matches}/{total}")
76
77 STYLE_LAYERS = [
78 "to_q",
79 "to_k",
80 "to_v",
81 "to_out",
82 "ff.net.0.proj",
83 "ff.net.2"
84 ]
85
86 # Your logic inside the loop:
87 for lora_key in list(lora_state_dict.keys()):
88 # Path variation A: Standard low-rank naming (.lora_down / .lora_up)
89 if ".lora_down.weight" in lora_key:
90 prefix = lora_key.split(".lora_down.weight")[0]
91 down_key = f"{prefix}.lora_down.weight"
92 up_key = f"{prefix}.lora_up.weight"
93
94 if up_key in lora_state_dict:
95 target_base_key = prefix
96
97 if not any(x in target_base_key for x in STYLE_LAYERS):
98 skipped_count += 1
99 continue
100
101 # CRITICAL: Only touch the base model if the key exists there natively!
102 if target_base_key in base_state_dict:
103 try:
104 W_base = base_state_dict[target_base_key].to(
105 device=device,
106 dtype=torch.bfloat16
107 )
108
109 A = lora_state_dict[a_key].to(
110 device=device,
111 dtype=torch.bfloat16
112 )
113
114 B = lora_state_dict[b_key].to(
115 device=device,
116 dtype=torch.bfloat16
117 )
118
119 rank = A.shape[0] # usually rank dimension
120 alpha = 128 # replace if metadata gives another value
121
122 scale = alpha / rank
123
124 delta_W = torch.matmul(B, A) * scale
125
126 base_state_dict[target_base_key] = (
127 W_base + delta_W
128 ).cpu()
129 except Exception as e:
130 print(f"Failed to bake layer {target_base_key}: {e}")
131 skipped_count += 1
132 else:
133 skipped_count += 1
134
135 # Path variation B: Low-rank dimension naming (.lora_A / .lora_B)
136 elif ".lora_A.weight" in lora_key:
137 prefix = lora_key.split(".lora_A.weight")[0]
138 a_key = f"{prefix}.lora_A.weight"
139 b_key = f"{prefix}.lora_B.weight"
140
141 if b_key not in lora_state_dict:
142 continue
143
144 target_base_key = prefix
145
146 if not any(x in target_base_key for x in STYLE_LAYERS):
147 skipped_count += 1
148 continue
149
150 if target_base_key in base_state_dict:
151 try:
152 W_base = base_state_dict[target_base_key].to(
153 device=device,
154 dtype=torch.bfloat16
155 )
156
157 A = lora_state_dict[a_key].to(
158 device=device,
159 dtype=torch.bfloat16
160 )
161
162 B = lora_state_dict[b_key].to(
163 device=device,
164 dtype=torch.bfloat16
165 )
166
167 rank = A.shape[0]
168 alpha = 128 # replace after checking metadata
169 scale = alpha / rank
170
171 delta_W = torch.matmul(B, A) * scale
172
173 if delta_W.shape != W_base.shape:
174 print("SHAPE FAIL:", target_base_key, W_base.shape, delta_W.shape)
175 skipped_count += 1
176 continue
177
178 base_state_dict[target_base_key] = (
179 W_base + delta_W
180 ).cpu()
181
182 baked_count += 1
183 except Exception as e:
184 print(f"Failed to bake layer {target_base_key}: {e}")
185 skipped_count += 1
186 else:
187 skipped_count += 1
188
189 # --- PROOF & LOGGING REGION ---
190 print("\n==================================================")
191 print(" FUSION VERIFICATION ")
192 print("==================================================")
193 print(f" Successfully Baked Layers : {baked_count}")
194 print(f" Newly Injected Multi-Layers: {added_count}")
195 print(f" Skipped / Mismatched Keys : {skipped_count}")
196 print("==================================================")
197
198 if baked_count == 0 and added_count == 0:
199 print("❌ CRITICAL WARNING: Zero operations were completed. Output will be unmodified!")
200 return
201 else:
202 print("✅ SUCCESS: BF16 loop fusion complete.\n")
203
204 # 7. Extract the original header metadata so the inference app knows the exact shapes
205 try:
206 with safe_open(vanilla_base_path, framework="pt", device="cpu") as f:
207 original_metadata = f.metadata()
208 except Exception as e:
209 original_metadata = None
210
211 # 8. DISK MANAGEMENT: Wipe cache down to free disk space before exporting
212 print("Cleaning cache directory...")
213 try:
214 if os.path.exists("/tmp/hf_cache"):
215 shutil.rmtree("/tmp/hf_cache")
216 except Exception as e:
217 pass
218
219 # 9. Write out and upload file
220 from huggingface_hub import HfApi
221 api = HfApi()
222
223 DEST_REPO = "ibyteohdear/Lightricks-LTX-2.3"
224 output_filename = "/tmp/LTX2.3_DISTILLED_BAKED.safetensors"
225
226 print("Saving the new baked safetensors file...")
227 if original_metadata:
228 save_file(base_state_dict, output_filename, metadata=original_metadata)
229 else:
230 save_file(base_state_dict, output_filename)
231
232 print(f"Uploading target file to Hugging Face: {DEST_REPO}...")
233 api.upload_file(
234 path_or_fileobj=output_filename,
235 path_in_repo="LTX2.3_DISTILLED_BAKED_LTX_SULPHUR_STYLE_IS_10Eros_v12_r128.safetensors",
236 repo_id=DEST_REPO,
237 token=HF_TOKEN,
238 )
239 print("Pipeline execution complete.")
240
241if __name__ == "__main__":
242 run_pipeline()
2431import os
2import sys
3import torch
4import shutil
5from huggingface_hub import hf_hub_download
6from safetensors.torch import load_file, save_file, safe_open
7
8HF_TOKEN = os.environ.get("HF_TOKEN")
9
10def run_pipeline():
11 device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
12
13 # ============== CONFIG ==============
14 # 10Eros: style at 0.85 (first pass)
15 FIRST_LORA_REPO = "ibyteohdear/Lightricks-LTX-2-10Eros-lora"
16 FIRST_LORA_FILE = "10Eros_v15_r768.safetensors"
17
18 # Sulphur: audio at 0.9, everything else at 0.9 (second pass, only skipped)
19 SECOND_LORA_REPO = "ibyteohdear/Lightricks-LTX-2-10Eros-lora"
20 SECOND_LORA_FILE = "10Eros_v15_r768.safetensors"
21
22 # Base model
23 BASE_REPO = "ibyteohdear/Lightricks-LTX-2"
24 BASE_FILE = "ltx-2.5-22b-distilled-transformer-bf16.safetensors"
25
26 # Output
27 DEST_REPO = "ibyteohdear/Lightricks-LTX-2-DISTILLED-10-Eros"
28 OUTPUT_FILENAME = "LTX2.5_DISTILLED_10Eros_15.safetensors"
29 # ===================================
30
31 # 1. Download base weights
32 print("Downloading base BF16 checkpoint...")
33 vanilla_base_path = hf_hub_download(
34 repo_id=BASE_REPO,
35 filename=BASE_FILE,
36 token=HF_TOKEN,
37 cache_dir="/tmp/hf_cache"
38 )
39
40 # 2. Download first LoRA
41 print("Downloading first LoRA weights...")
42 first_lora_path = hf_hub_download(
43 repo_id=FIRST_LORA_REPO,
44 filename=FIRST_LORA_FILE,
45 token=HF_TOKEN,
46 cache_dir="/tmp/hf_cache"
47 )
48
49 # 3. Download second LoRA
50 print("Downloading second LoRA weights...")
51 second_lora_path = hf_hub_download(
52 repo_id=SECOND_LORA_REPO,
53 filename=SECOND_LORA_FILE,
54 token=HF_TOKEN,
55 cache_dir="/tmp/hf_cache"
56 )
57
58 # 4. Load tensors into RAM
59 print("Loading base tensors into RAM...")
60 base_state_dict = load_file(vanilla_base_path, device="cpu")
61
62 # 5. Load LoRA weights
63 print("Loading first LoRA weights...")
64 first_lora_state_dict = load_file(first_lora_path, device="cpu")
65
66 print("Loading second LoRA weights...")
67 second_lora_state_dict = load_file(second_lora_path, device="cpu")
68
69 # 6. PASS 1: Apply 10Eros — ONLY style layers at 0.85
70 print("\n" + "="*60)
71 print("PASS 1: 10Eros — style layers at 0.85")
72 print("="*60)
73
74 AUDIO_PRESERVE_LAYERS = [
75 "audio_attn1",
76 "audio_attn2",
77 "audio_ff",
78 "audio_to_video_attn",
79 "video_to_audio_attn",
80 ]
81
82 STYLE_LAYERS = [
83 "to_q",
84 "to_k",
85 "to_v",
86 "to_out",
87 "ff.net.0.proj",
88 "ff.net.2"
89 ]
90
91 pass1_baked = 0
92 pass1_skipped = []
93 pass1_processed_keys = set() # Track keys we actually modified
94
95 for lora_key in list(first_lora_state_dict.keys()):
96 # Identify keys and weights based on naming pattern
97 if ".lora_down.weight" in lora_key:
98 prefix = lora_key.split(".lora_down.weight")[0]
99 down_key = f"{prefix}.lora_down.weight"
100 up_key = f"{prefix}.lora_up.weight"
101 if up_key not in first_lora_state_dict:
102 continue
103 target_base_key = prefix
104 a_weight_key = down_key
105 b_weight_key = up_key
106
107 elif ".lora_A.weight" in lora_key:
108 prefix = lora_key.split(".lora_A.weight")[0]
109 a_key = f"{prefix}.lora_A.weight"
110 b_key = f"{prefix}.lora_B.weight"
111 if b_key not in first_lora_state_dict:
112 continue
113 target_base_key = prefix
114 a_weight_key = a_key
115 b_weight_key = b_key
116 else:
117 continue
118
119 # PASS 1: Only style layers
120 is_style = any(style_layer in target_base_key for style_layer in STYLE_LAYERS)
121
122 if not is_style:
123 pass1_skipped.append(target_base_key)
124 continue
125
126 layer_strength = 0.85
127
128 if target_base_key in base_state_dict:
129 try:
130 W_base = base_state_dict[target_base_key].to(device=device, dtype=torch.bfloat16)
131 A = first_lora_state_dict[a_weight_key].to(device=device, dtype=torch.bfloat16)
132 B = first_lora_state_dict[b_weight_key].to(device=device, dtype=torch.bfloat16)
133
134 rank = A.shape[0]
135 alpha = 768
136 scale = (alpha / rank) * layer_strength
137
138 delta_W = torch.matmul(B, A) * scale
139
140 if delta_W.shape != W_base.shape:
141 print(f"SHAPE FAIL: {target_base_key} {W_base.shape} {delta_W.shape}")
142 pass1_skipped.append(target_base_key)
143 continue
144
145 base_state_dict[target_base_key] = (W_base + delta_W).cpu()
146 pass1_baked += 1
147 pass1_processed_keys.add(target_base_key) # Mark as processed
148
149 except Exception as e:
150 print(f"Failed to bake layer {target_base_key}: {e}")
151 pass1_skipped.append(target_base_key)
152 else:
153 pass1_skipped.append(target_base_key)
154
155 print(f"\nPass 1 complete: {pass1_baked} baked, {len(pass1_skipped)} skipped")
156
157 # 7. PASS 2: Apply Sulphur — ONLY to keys that were skipped in Pass 1
158 print("\n" + "="*60)
159 print("PASS 2: Sulphur — skipped keys only (audio 0.9, rest 0.9)")
160 print("="*60)
161
162 # Strict: only process keys that Pass 1 skipped
163 eligible_set = set(pass1_skipped)
164
165 pass2_baked = 0
166 pass2_skipped = 0
167 pass2_notfound = 0
168 pass2_overlap_blocked = 0 # Track keys we blocked to prevent overlap
169
170 for lora_key in list(second_lora_state_dict.keys()):
171 # Identify keys and weights based on naming pattern
172 if ".lora_down.weight" in lora_key:
173 prefix = lora_key.split(".lora_down.weight")[0]
174 down_key = f"{prefix}.lora_down.weight"
175 up_key = f"{prefix}.lora_up.weight"
176 if up_key not in second_lora_state_dict:
177 continue
178 target_base_key = prefix
179 a_weight_key = down_key
180 b_weight_key = up_key
181
182 elif ".lora_A.weight" in lora_key:
183 prefix = lora_key.split(".lora_A.weight")[0]
184 a_key = f"{prefix}.lora_A.weight"
185 b_key = f"{prefix}.lora_B.weight"
186 if b_key not in second_lora_state_dict:
187 continue
188 target_base_key = prefix
189 a_weight_key = a_key
190 b_weight_key = b_key
191 else:
192 continue
193
194 # STRICT: Skip if this key was already processed in Pass 1
195 if target_base_key in pass1_processed_keys:
196 pass2_overlap_blocked += 1
197 continue
198
199 # STRICT: Skip if this key was NOT in Pass 1's skip list
200 if target_base_key not in eligible_set:
201 pass2_skipped += 1
202 continue
203
204 # Determine strength: 0.9 for audio, 0.85 for everything else
205 is_audio = any(audio_layer in target_base_key for audio_layer in AUDIO_PRESERVE_LAYERS)
206 layer_strength = 0.9 if is_audio else 0.85
207
208 if target_base_key in base_state_dict:
209 try:
210 W_base = base_state_dict[target_base_key].to(device=device, dtype=torch.bfloat16)
211 A = second_lora_state_dict[a_weight_key].to(device=device, dtype=torch.bfloat16)
212 B = second_lora_state_dict[b_weight_key].to(device=device, dtype=torch.bfloat16)
213
214 rank = A.shape[0]
215 alpha = 768
216 scale = (alpha / rank) * layer_strength
217
218 delta_W = torch.matmul(B, A) * scale
219
220 if delta_W.shape != W_base.shape:
221 print(f"SHAPE FAIL: {target_base_key} {W_base.shape} {delta_W.shape}")
222 pass2_skipped += 1
223 continue
224
225 base_state_dict[target_base_key] = (W_base + delta_W).cpu()
226 pass2_baked += 1
227
228 except Exception as e:
229 print(f"Failed to bake layer {target_base_key}: {e}")
230 pass2_skipped += 1
231 else:
232 print(f"Key not in base: {target_base_key}")
233 pass2_notfound += 1
234
235 print(f"\nPass 2 complete: {pass2_baked} baked, {pass2_skipped} skipped, {pass2_notfound} not in base, {pass2_overlap_blocked} overlap-blocked")
236
237 # 8. Final verification
238 print("\n" + "="*60)
239 print("FINAL VERIFICATION")
240 print("="*60)
241 print(f"Pass 1 (10Eros style 0.85): {pass1_baked} baked")
242 print(f"Pass 2 (Sulphur 0.9): {pass2_baked} baked")
243 print(f"Total modified: {pass1_baked + pass2_baked}")
244 print(f"Still unprocessed: {len(pass1_skipped) - pass2_baked}")
245 print(f"Overlap blocked (safety): {pass2_overlap_blocked}")
246
247 if pass2_baked > len(pass1_skipped):
248 print("\nERROR: Pass 2 baked more than were skipped! Overlap occurred.")
249 elif pass2_baked < len(pass1_skipped):
250 print(f"\nWARNING: {len(pass1_skipped) - pass2_baked} skipped keys were not found in second LoRA")
251
252 # 9. Extract original header metadata
253 try:
254 with safe_open(vanilla_base_path, framework="pt", device="cpu") as f:
255 original_metadata = f.metadata()
256 except Exception as e:
257 original_metadata = None
258 print(f"Could not read metadata: {e}")
259
260 # 10. Cleanup cache
261 print("\nCleaning cache directory...")
262 try:
263 if os.path.exists("/tmp/hf_cache"):
264 shutil.rmtree("/tmp/hf_cache")
265 except Exception as e:
266 pass
267
268 # 11. Write out and upload
269 from huggingface_hub import HfApi
270 api = HfApi()
271
272 output_path = "/tmp/LTX2.5_DISTILLED_COMBO_BAKED.safetensors"
273
274 print(f"\nSaving to {output_path}...")
275 if original_metadata:
276 save_file(base_state_dict, output_path, metadata=original_metadata)
277 else:
278 save_file(base_state_dict, output_path)
279
280 print(f"Uploading to {DEST_REPO}...")
281 api.upload_file(
282 path_or_fileobj=output_path,
283 path_in_repo=OUTPUT_FILENAME,
284 repo_id=DEST_REPO,
285 token=HF_TOKEN,
286 )
287 print("Pipeline execution complete.")
288
289if __name__ == "__main__":
290 run_pipeline()