yujiepan commited on
Commit
bbae6ac
·
verified ·
1 Parent(s): 4fe7cca

Upload folder using huggingface_hub

Browse files
.meta/create_model.py ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def create_model(source_model_id: str, source_revision: str, save_folder: str) -> str:
2
+ import json
3
+ from pathlib import Path
4
+
5
+ import torch
6
+ from diffusers import AutoencoderKLLTX2Audio, AutoencoderKLLTX2Video, LTX2VideoTransformer3DModel
7
+ from diffusers.pipelines.ltx2.connectors import LTX2TextConnectors
8
+ from diffusers.pipelines.ltx2.vocoder import LTX2Vocoder
9
+ from huggingface_hub import hf_hub_download
10
+ from safetensors.torch import save_file
11
+ from transformers import (
12
+ Gemma4UnifiedConfig,
13
+ Gemma4UnifiedForConditionalGeneration,
14
+ Gemma4UnifiedTextConfig,
15
+ Gemma4UnifiedVisionConfig,
16
+ set_seed,
17
+ )
18
+
19
+ tokenizer_source = "google/gemma-4-12b-it"
20
+ tokenizer_revision = "43c9d014b231474fa7212dbb48a762566c2b255d"
21
+ folder = Path(save_folder)
22
+ folder.mkdir(parents=True, exist_ok=True)
23
+ for name in ("diffusion_models", "text_encoders", "vae"):
24
+ (folder / name).mkdir(exist_ok=True)
25
+ set_seed(42)
26
+
27
+ # The split single-file layout carries no config; record the reduced
28
+ # dimensions so the components can be rebuilt from the published tensors.
29
+ config = {
30
+ "transformer": {
31
+ "in_channels": 128,
32
+ "out_channels": 128,
33
+ "audio_in_channels": 8,
34
+ "audio_out_channels": 8,
35
+ "num_layers": 4,
36
+ "num_attention_heads": 2,
37
+ "attention_head_dim": 32,
38
+ "audio_num_attention_heads": 2,
39
+ "audio_attention_head_dim": 32,
40
+ "cross_attention_dim": 64,
41
+ "audio_cross_attention_dim": 64,
42
+ "caption_channels": 8,
43
+ "use_prompt_embeddings": False,
44
+ "cross_attn_mod": True,
45
+ "audio_cross_attn_mod": True,
46
+ "use_prompt_adaln_single": False,
47
+ "use_keyframes_abs_pos_embedding": True,
48
+ },
49
+ "connectors": {
50
+ "caption_channels": 8,
51
+ "text_proj_in_factor": 3,
52
+ "video_connector_num_attention_heads": 2,
53
+ "video_connector_attention_head_dim": 32,
54
+ "video_connector_num_layers": 2,
55
+ "video_connector_num_learnable_registers": 4,
56
+ "audio_connector_num_attention_heads": 2,
57
+ "audio_connector_attention_head_dim": 32,
58
+ "audio_connector_num_layers": 2,
59
+ "audio_connector_num_learnable_registers": 4,
60
+ "video_hidden_dim": 64,
61
+ "audio_hidden_dim": 64,
62
+ "per_modality_projections": True,
63
+ },
64
+ "vae": {
65
+ "in_channels": 3,
66
+ "out_channels": 3,
67
+ "latent_channels": 128,
68
+ "block_out_channels": [16, 32],
69
+ "decoder_block_out_channels": [16],
70
+ "layers_per_block": [1, 1, 1],
71
+ "decoder_layers_per_block": [1, 1],
72
+ "spatio_temporal_scaling": [True, True],
73
+ "decoder_spatio_temporal_scaling": [True],
74
+ "downsample_type": ["spatiotemporal", "spatiotemporal"],
75
+ "upsample_type": ["spatiotemporal"],
76
+ "upsample_residual": [True],
77
+ "upsample_factor": [2],
78
+ "patch_size": 4,
79
+ "patch_size_t": 1,
80
+ },
81
+ "audio_vae": {
82
+ "base_channels": 8,
83
+ "output_channels": 2,
84
+ "ch_mult": [1, 2],
85
+ "num_res_blocks": 1,
86
+ "in_channels": 2,
87
+ "resolution": 64,
88
+ "latent_channels": 8,
89
+ "mel_bins": 4,
90
+ },
91
+ "vocoder": {
92
+ "in_channels": 8,
93
+ "hidden_channels": 32,
94
+ "out_channels": 2,
95
+ "upsample_kernel_sizes": [8, 8],
96
+ "upsample_factors": [4, 4],
97
+ "resnet_kernel_sizes": [3],
98
+ "resnet_dilations": [[1, 1, 1]],
99
+ },
100
+ "text_encoder": {
101
+ "vocab_size": 262144,
102
+ "hidden_size": 8,
103
+ "intermediate_size": 16,
104
+ "num_hidden_layers": 2,
105
+ "num_attention_heads": 1,
106
+ "num_key_value_heads": 1,
107
+ "head_dim": 32,
108
+ "max_position_embeddings": 4096,
109
+ "tie_word_embeddings": True,
110
+ },
111
+ }
112
+ (folder / "config.json").write_text(json.dumps(config, indent=2) + "\n")
113
+
114
+ transformer_renames = [
115
+ ("audio_proj_in", "audio_patchify_proj"),
116
+ ("proj_in", "patchify_proj"),
117
+ ("av_cross_attn_video_scale_shift", "av_ca_video_scale_shift_adaln_single"),
118
+ ("av_cross_attn_video_a2v_gate", "av_ca_a2v_gate_adaln_single"),
119
+ ("av_cross_attn_audio_scale_shift", "av_ca_audio_scale_shift_adaln_single"),
120
+ ("av_cross_attn_audio_v2a_gate", "av_ca_v2a_gate_adaln_single"),
121
+ ("video_a2v_cross_attn_scale_shift_table", "scale_shift_table_a2v_ca_video"),
122
+ ("audio_a2v_cross_attn_scale_shift_table", "scale_shift_table_a2v_ca_audio"),
123
+ ("norm_q", "q_norm"),
124
+ ("norm_k", "k_norm"),
125
+ ("audio_time_embed", "audio_adaln_single"),
126
+ ("time_embed", "adaln_single"),
127
+ ]
128
+ vae_renames = [
129
+ ("down_blocks.1", "down_blocks.0.downsamplers.0"),
130
+ ("down_blocks.2", "down_blocks.1"),
131
+ ("down_blocks.3", "down_blocks.1.downsamplers.0"),
132
+ ("down_blocks.4", "down_blocks.2"),
133
+ ("down_blocks.5", "down_blocks.2.downsamplers.0"),
134
+ ("down_blocks.6", "down_blocks.3"),
135
+ ("down_blocks.7", "down_blocks.3.downsamplers.0"),
136
+ ("down_blocks.8", "mid_block"),
137
+ ("up_blocks.0", "mid_block"),
138
+ ("up_blocks.1", "up_blocks.0.upsamplers.0"),
139
+ ("up_blocks.2", "up_blocks.0"),
140
+ ("up_blocks.3", "up_blocks.1.upsamplers.0"),
141
+ ("up_blocks.4", "up_blocks.1"),
142
+ ("up_blocks.5", "up_blocks.2.upsamplers.0"),
143
+ ("up_blocks.6", "up_blocks.2"),
144
+ ("resnets", "res_blocks"),
145
+ ("latents_mean", "per_channel_statistics.mean-of-means"),
146
+ ("latents_std", "per_channel_statistics.std-of-means"),
147
+ ]
148
+
149
+ def to_comfy(state, renames):
150
+ output = {}
151
+ for key, value in state.items():
152
+ for comfy, diffusers in reversed(renames):
153
+ key = key.replace(diffusers, comfy)
154
+ output[key] = value
155
+ return output
156
+
157
+ def cast(state):
158
+ # The source stores modulation tables in FP32 and every other weight in BF16.
159
+ return {k: v.float() if "scale_shift_table" in k else v.to(torch.bfloat16) for k, v in state.items()}
160
+
161
+ transformer = LTX2VideoTransformer3DModel(**config["transformer"])
162
+ connectors = LTX2TextConnectors(**config["connectors"])
163
+ state = {
164
+ f"model.diffusion_model.{k}": v
165
+ for k, v in to_comfy(cast(transformer.state_dict()), transformer_renames).items()
166
+ }
167
+ connector_weights = cast(connectors.state_dict())
168
+ for key, value in connector_weights.items():
169
+ if key.startswith("video_connector.transformer_blocks."):
170
+ name = key.replace(
171
+ "video_connector.transformer_blocks.", "video_embeddings_connector.transformer_1d_blocks."
172
+ )
173
+ elif key.startswith("audio_connector.transformer_blocks."):
174
+ name = key.replace(
175
+ "audio_connector.transformer_blocks.", "audio_embeddings_connector.transformer_1d_blocks."
176
+ )
177
+ else:
178
+ continue
179
+ state[f"model.diffusion_model.{name.replace('norm_q', 'q_norm').replace('norm_k', 'k_norm')}"] = value
180
+ for side in ("video", "audio"):
181
+ state[f"model.diffusion_model.{side}_embeddings_connector.learnable_registers"] = connector_weights[
182
+ f"{side}_connector.learnable_registers"
183
+ ]
184
+ save_file(state, str(folder / "diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors"))
185
+
186
+ vae = AutoencoderKLLTX2Video(**config["vae"])
187
+ save_file(cast(to_comfy(vae.state_dict(), vae_renames)), str(folder / "vae/ltx-2.5-video-vae-bf16.safetensors"))
188
+
189
+ audio_vae = AutoencoderKLLTX2Audio(**config["audio_vae"])
190
+ vocoder = LTX2Vocoder(**config["vocoder"])
191
+ audio_state = {f"audio_vae.{k}": v for k, v in cast(audio_vae.state_dict()).items()}
192
+ audio_state.update({f"vocoder.{k}": v for k, v in cast(vocoder.state_dict()).items()})
193
+ save_file(audio_state, str(folder / "vae/ltx-2.5-audio-vae-bf16.safetensors"))
194
+
195
+ text = Gemma4UnifiedTextConfig(**{k: v for k, v in config["text_encoder"].items() if k != "tie_word_embeddings"})
196
+ vision = Gemma4UnifiedVisionConfig(
197
+ patch_size=16, pooling_kernel_size=2, mm_embed_dim=8, mm_posemb_size=16, output_proj_dims=8
198
+ )
199
+ text_config = Gemma4UnifiedConfig(text_config=text.to_dict(), vision_config=vision.to_dict())
200
+ text_config.tie_word_embeddings = True
201
+ text_encoder = Gemma4UnifiedForConditionalGeneration(text_config)
202
+ text_state = {}
203
+ for key, value in cast(text_encoder.state_dict()).items():
204
+ if key == "lm_head.weight":
205
+ continue
206
+ # The source names the text tower `model.*` and the vision tower `vision_model.*`.
207
+ key = key.replace("model.language_model.", "model.", 1)
208
+ key = key.replace("model.embed_vision.", "vision_model.", 1)
209
+ text_state[key] = value
210
+ # The source keeps the connector projections and the two modality projectors
211
+ # inside the text encoder file.
212
+ projection = cast(connectors.state_dict())
213
+ text_state["text_embedding_projection.video_aggregate_embed.weight"] = projection["video_text_proj_in.weight"]
214
+ text_state["text_embedding_projection.audio_aggregate_embed.weight"] = projection["audio_text_proj_in.weight"]
215
+ hidden = config["text_encoder"]["hidden_size"]
216
+ text_state["multi_modal_projector.embedding_projection.weight"] = torch.randn(hidden, hidden).to(torch.bfloat16)
217
+ text_state["audio_projector.embedding_projection.weight"] = torch.randn(
218
+ hidden, config["connectors"]["audio_hidden_dim"]
219
+ ).to(torch.bfloat16)
220
+ # The source embeds the tokenizer and processor assets as U8 tensors; keep
221
+ # that layout instead of publishing a second copy in a tokenizer folder.
222
+ assets = {
223
+ "tokenizer_json": "tokenizer.json",
224
+ "hf_asset__tokenizer_config.json": "tokenizer_config.json",
225
+ "hf_asset__chat_template.jinja": "chat_template.jinja",
226
+ "hf_asset__generation_config.json": "generation_config.json",
227
+ "hf_asset__processor_config.json": "processor_config.json",
228
+ }
229
+ for tensor_name, file_name in assets.items():
230
+ data = Path(hf_hub_download(tokenizer_source, file_name, revision=tokenizer_revision)).read_bytes()
231
+ text_state[tensor_name] = torch.frombuffer(bytearray(data), dtype=torch.uint8)
232
+ save_file(text_state, str(folder / "text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors"))
233
+
234
+ loader_source = '''"""Load the tiny LTX-2.5 split checkpoint into a Diffusers pipeline."""
235
+
236
+ import json
237
+ import tempfile
238
+ from pathlib import Path
239
+
240
+ import torch
241
+ from diffusers import (
242
+ AutoencoderKLLTX2Audio,
243
+ AutoencoderKLLTX2Video,
244
+ FlowMatchEulerDiscreteScheduler,
245
+ LTX2Pipeline,
246
+ LTX2VideoTransformer3DModel,
247
+ )
248
+ from diffusers.loaders.single_file_utils import (
249
+ convert_ltx2_audio_vae_to_diffusers,
250
+ convert_ltx2_transformer_to_diffusers,
251
+ convert_ltx2_vae_to_diffusers,
252
+ )
253
+ from diffusers.pipelines.ltx2.connectors import LTX2TextConnectors
254
+ from diffusers.pipelines.ltx2.vocoder import LTX2Vocoder
255
+ from safetensors.torch import load_file
256
+ from transformers import (
257
+ AutoTokenizer,
258
+ Gemma4UnifiedConfig,
259
+ Gemma4UnifiedForConditionalGeneration,
260
+ Gemma4UnifiedTextConfig,
261
+ Gemma4UnifiedVisionConfig,
262
+ )
263
+
264
+ FILES = {
265
+ "transformer": "diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors",
266
+ "text_encoder": "text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors",
267
+ "vae": "vae/ltx-2.5-video-vae-bf16.safetensors",
268
+ "audio_vae": "vae/ltx-2.5-audio-vae-bf16.safetensors",
269
+ }
270
+ ASSETS = {
271
+ "tokenizer_json": "tokenizer.json",
272
+ "hf_asset__tokenizer_config.json": "tokenizer_config.json",
273
+ "hf_asset__chat_template.jinja": "chat_template.jinja",
274
+ }
275
+ # Diffusers name -> Comfy single-file name for the tensors the converters drop.
276
+ CONNECTOR_PREFIX = {
277
+ "video_connector.transformer_blocks.": "video_embeddings_connector.transformer_1d_blocks.",
278
+ "audio_connector.transformer_blocks.": "audio_embeddings_connector.transformer_1d_blocks.",
279
+ "video_connector.learnable_registers": "video_embeddings_connector.learnable_registers",
280
+ "audio_connector.learnable_registers": "audio_embeddings_connector.learnable_registers",
281
+ }
282
+ TEXT_PREFIX = {
283
+ "model.language_model.": "model.",
284
+ "model.embed_vision.": "vision_model.",
285
+ }
286
+ PROJECTIONS = {
287
+ "video_text_proj_in.weight": "text_embedding_projection.video_aggregate_embed.weight",
288
+ "audio_text_proj_in.weight": "text_embedding_projection.audio_aggregate_embed.weight",
289
+ }
290
+
291
+
292
+ def _connector_name(name):
293
+ for local, source in CONNECTOR_PREFIX.items():
294
+ if name.startswith(local):
295
+ name = source + name[len(local) :]
296
+ break
297
+ return name.replace("norm_q", "q_norm").replace("norm_k", "k_norm")
298
+
299
+
300
+ def _text_name(name):
301
+ for local, source in TEXT_PREFIX.items():
302
+ if name.startswith(local):
303
+ return source + name[len(local) :]
304
+ return None
305
+
306
+
307
+ def load_transformer(config, state):
308
+ model = LTX2VideoTransformer3DModel(**config["transformer"])
309
+ plain = {name: value for name, value in state.items() if "embeddings_connector" not in name}
310
+ model.load_state_dict(convert_ltx2_transformer_to_diffusers(plain))
311
+ return model
312
+
313
+
314
+ def load_connectors(config, transformer_state, text_state):
315
+ model = LTX2TextConnectors(**config["connectors"])
316
+ weights = {}
317
+ for name in model.state_dict():
318
+ if name in PROJECTIONS:
319
+ weights[name] = text_state[PROJECTIONS[name]]
320
+ else:
321
+ weights[name] = transformer_state["model.diffusion_model." + _connector_name(name)]
322
+ model.load_state_dict(weights, strict=True)
323
+ return model
324
+
325
+
326
+ def load_text_encoder(config, text_state):
327
+ text = Gemma4UnifiedTextConfig(
328
+ **{key: value for key, value in config["text_encoder"].items() if key != "tie_word_embeddings"}
329
+ )
330
+ gemma = Gemma4UnifiedConfig(
331
+ text_config=text.to_dict(),
332
+ vision_config=Gemma4UnifiedVisionConfig(
333
+ patch_size=16, pooling_kernel_size=2, mm_embed_dim=8, mm_posemb_size=16, output_proj_dims=8
334
+ ).to_dict(),
335
+ )
336
+ gemma.tie_word_embeddings = True
337
+ model = Gemma4UnifiedForConditionalGeneration(gemma)
338
+ weights = {}
339
+ for name in model.state_dict():
340
+ source = _text_name(name)
341
+ if source is not None and source in text_state:
342
+ weights[name] = text_state[source]
343
+ model.load_state_dict(weights, strict=False)
344
+ return model
345
+
346
+
347
+ def load_tokenizer(text_state):
348
+ with tempfile.TemporaryDirectory() as directory:
349
+ for tensor_name, file_name in ASSETS.items():
350
+ Path(directory, file_name).write_bytes(text_state[tensor_name].numpy().tobytes())
351
+ return AutoTokenizer.from_pretrained(directory)
352
+
353
+
354
+ def load_pipeline(model_id, device=None):
355
+ model_id = Path(model_id)
356
+ config = json.loads((model_id / "config.json").read_text())
357
+ transformer_state = load_file(str(model_id / FILES["transformer"]))
358
+ text_state = load_file(str(model_id / FILES["text_encoder"]))
359
+
360
+ transformer = load_transformer(config, transformer_state)
361
+ connectors = load_connectors(config, transformer_state, text_state)
362
+ vae = AutoencoderKLLTX2Video(**config["vae"])
363
+ vae.load_state_dict(convert_ltx2_vae_to_diffusers(load_file(str(model_id / FILES["vae"]))))
364
+
365
+ audio_state = load_file(str(model_id / FILES["audio_vae"]))
366
+ audio_vae = AutoencoderKLLTX2Audio(**config["audio_vae"])
367
+ audio_vae.load_state_dict(
368
+ convert_ltx2_audio_vae_to_diffusers(
369
+ {name: value for name, value in audio_state.items() if not name.startswith("vocoder.")}
370
+ )
371
+ )
372
+ vocoder = LTX2Vocoder(**config["vocoder"])
373
+ vocoder.load_state_dict(
374
+ {
375
+ name.removeprefix("vocoder."): value
376
+ for name, value in audio_state.items()
377
+ if name.startswith("vocoder.")
378
+ }
379
+ )
380
+
381
+ text_encoder = load_text_encoder(config, text_state)
382
+ pipeline = LTX2Pipeline(
383
+ scheduler=FlowMatchEulerDiscreteScheduler(),
384
+ vae=vae,
385
+ audio_vae=audio_vae,
386
+ text_encoder=text_encoder,
387
+ tokenizer=load_tokenizer(text_state),
388
+ connectors=connectors,
389
+ transformer=transformer,
390
+ vocoder=vocoder,
391
+ )
392
+ for module in (transformer, vae, audio_vae, text_encoder, connectors, vocoder):
393
+ module.eval()
394
+ pipeline.to(device or ("cuda" if torch.cuda.is_available() else "cpu"))
395
+ return pipeline
396
+ '''
397
+
398
+ (folder / "ltx2_loader.py").write_text(loader_source)
399
+ return repr(transformer)
400
+
401
+
402
+ create_model(
403
+ source_model_id="Lightricks/LTX-2.5",
404
+ source_revision="49b093a39c19169a8a81721dcc541e07e945232f",
405
+ save_folder="/tmp/ltx-2-5",
406
+ )
.meta/env.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source_model": {
3
+ "name": "Lightricks/LTX-2.5",
4
+ "commit_id": "49b093a39c19169a8a81721dcc541e07e945232f"
5
+ },
6
+ "environments": {
7
+ "creation": {
8
+ "python": "3.12.13",
9
+ "packages": {
10
+ "diffusers": "0.42.0.dev0",
11
+ "huggingface_hub": "2.3.0",
12
+ "safetensors": "0.8.0",
13
+ "tokenizers": "0.23.3",
14
+ "torch": "2.13.0+cu126",
15
+ "transformers": "5.22.0.dev0"
16
+ },
17
+ "cuda": "12.6",
18
+ "hardware": {
19
+ "gpu_model": "NVIDIA H20"
20
+ }
21
+ },
22
+ "diffusers": {
23
+ "python": "3.12.13",
24
+ "packages": {
25
+ "diffusers": "0.42.0.dev0",
26
+ "huggingface_hub": "2.3.0",
27
+ "safetensors": "0.8.0",
28
+ "tokenizers": "0.23.3",
29
+ "torch": "2.13.0+cu126",
30
+ "transformers": "5.22.0.dev0"
31
+ },
32
+ "cuda": "12.6",
33
+ "hardware": {
34
+ "gpu_model": "NVIDIA H20"
35
+ }
36
+ }
37
+ }
38
+ }
.meta/model.log ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ LTX2VideoTransformer3DModel(
2
+ (proj_in): Linear(in_features=128, out_features=64, bias=True)
3
+ (audio_proj_in): Linear(in_features=8, out_features=64, bias=True)
4
+ (time_embed): LTX2AdaLayerNormSingle(
5
+ (emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
6
+ (time_proj): Timesteps()
7
+ (timestep_embedder): TimestepEmbedding(
8
+ (linear_1): Linear(in_features=256, out_features=64, bias=True)
9
+ (act): SiLU()
10
+ (linear_2): Linear(in_features=64, out_features=64, bias=True)
11
+ )
12
+ )
13
+ (silu): SiLU()
14
+ (linear): Linear(in_features=64, out_features=576, bias=True)
15
+ )
16
+ (audio_time_embed): LTX2AdaLayerNormSingle(
17
+ (emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
18
+ (time_proj): Timesteps()
19
+ (timestep_embedder): TimestepEmbedding(
20
+ (linear_1): Linear(in_features=256, out_features=64, bias=True)
21
+ (act): SiLU()
22
+ (linear_2): Linear(in_features=64, out_features=64, bias=True)
23
+ )
24
+ )
25
+ (silu): SiLU()
26
+ (linear): Linear(in_features=64, out_features=576, bias=True)
27
+ )
28
+ (av_cross_attn_video_scale_shift): LTX2AdaLayerNormSingle(
29
+ (emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
30
+ (time_proj): Timesteps()
31
+ (timestep_embedder): TimestepEmbedding(
32
+ (linear_1): Linear(in_features=256, out_features=64, bias=True)
33
+ (act): SiLU()
34
+ (linear_2): Linear(in_features=64, out_features=64, bias=True)
35
+ )
36
+ )
37
+ (silu): SiLU()
38
+ (linear): Linear(in_features=64, out_features=256, bias=True)
39
+ )
40
+ (av_cross_attn_audio_scale_shift): LTX2AdaLayerNormSingle(
41
+ (emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
42
+ (time_proj): Timesteps()
43
+ (timestep_embedder): TimestepEmbedding(
44
+ (linear_1): Linear(in_features=256, out_features=64, bias=True)
45
+ (act): SiLU()
46
+ (linear_2): Linear(in_features=64, out_features=64, bias=True)
47
+ )
48
+ )
49
+ (silu): SiLU()
50
+ (linear): Linear(in_features=64, out_features=256, bias=True)
51
+ )
52
+ (av_cross_attn_video_a2v_gate): LTX2AdaLayerNormSingle(
53
+ (emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
54
+ (time_proj): Timesteps()
55
+ (timestep_embedder): TimestepEmbedding(
56
+ (linear_1): Linear(in_features=256, out_features=64, bias=True)
57
+ (act): SiLU()
58
+ (linear_2): Linear(in_features=64, out_features=64, bias=True)
59
+ )
60
+ )
61
+ (silu): SiLU()
62
+ (linear): Linear(in_features=64, out_features=64, bias=True)
63
+ )
64
+ (av_cross_attn_audio_v2a_gate): LTX2AdaLayerNormSingle(
65
+ (emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
66
+ (time_proj): Timesteps()
67
+ (timestep_embedder): TimestepEmbedding(
68
+ (linear_1): Linear(in_features=256, out_features=64, bias=True)
69
+ (act): SiLU()
70
+ (linear_2): Linear(in_features=64, out_features=64, bias=True)
71
+ )
72
+ )
73
+ (silu): SiLU()
74
+ (linear): Linear(in_features=64, out_features=64, bias=True)
75
+ )
76
+ (rope): LTX2AudioVideoRotaryPosEmbed()
77
+ (audio_rope): LTX2AudioVideoRotaryPosEmbed()
78
+ (cross_attn_rope): LTX2AudioVideoRotaryPosEmbed()
79
+ (cross_attn_audio_rope): LTX2AudioVideoRotaryPosEmbed()
80
+ (transformer_blocks): ModuleList(
81
+ (0-3): 4 x LTX2VideoTransformerBlock(
82
+ (norm1): RMSNorm()
83
+ (attn1): LTX2Attention(
84
+ (norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
85
+ (norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
86
+ (to_q): Linear(in_features=64, out_features=64, bias=True)
87
+ (to_k): Linear(in_features=64, out_features=64, bias=True)
88
+ (to_v): Linear(in_features=64, out_features=64, bias=True)
89
+ (to_out): ModuleList(
90
+ (0): Linear(in_features=64, out_features=64, bias=True)
91
+ (1): Dropout(p=0.0, inplace=False)
92
+ )
93
+ )
94
+ (audio_norm1): RMSNorm()
95
+ (audio_attn1): LTX2Attention(
96
+ (norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
97
+ (norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
98
+ (to_q): Linear(in_features=64, out_features=64, bias=True)
99
+ (to_k): Linear(in_features=64, out_features=64, bias=True)
100
+ (to_v): Linear(in_features=64, out_features=64, bias=True)
101
+ (to_out): ModuleList(
102
+ (0): Linear(in_features=64, out_features=64, bias=True)
103
+ (1): Dropout(p=0.0, inplace=False)
104
+ )
105
+ )
106
+ (norm2): RMSNorm()
107
+ (attn2): LTX2Attention(
108
+ (norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
109
+ (norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
110
+ (to_q): Linear(in_features=64, out_features=64, bias=True)
111
+ (to_k): Linear(in_features=64, out_features=64, bias=True)
112
+ (to_v): Linear(in_features=64, out_features=64, bias=True)
113
+ (to_out): ModuleList(
114
+ (0): Linear(in_features=64, out_features=64, bias=True)
115
+ (1): Dropout(p=0.0, inplace=False)
116
+ )
117
+ )
118
+ (audio_norm2): RMSNorm()
119
+ (audio_attn2): LTX2Attention(
120
+ (norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
121
+ (norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
122
+ (to_q): Linear(in_features=64, out_features=64, bias=True)
123
+ (to_k): Linear(in_features=64, out_features=64, bias=True)
124
+ (to_v): Linear(in_features=64, out_features=64, bias=True)
125
+ (to_out): ModuleList(
126
+ (0): Linear(in_features=64, out_features=64, bias=True)
127
+ (1): Dropout(p=0.0, inplace=False)
128
+ )
129
+ )
130
+ (audio_to_video_norm): RMSNorm()
131
+ (audio_to_video_attn): LTX2Attention(
132
+ (norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
133
+ (norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
134
+ (to_q): Linear(in_features=64, out_features=64, bias=True)
135
+ (to_k): Linear(in_features=64, out_features=64, bias=True)
136
+ (to_v): Linear(in_features=64, out_features=64, bias=True)
137
+ (to_out): ModuleList(
138
+ (0): Linear(in_features=64, out_features=64, bias=True)
139
+ (1): Dropout(p=0.0, inplace=False)
140
+ )
141
+ )
142
+ (video_to_audio_norm): RMSNorm()
143
+ (video_to_audio_attn): LTX2Attention(
144
+ (norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
145
+ (norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
146
+ (to_q): Linear(in_features=64, out_features=64, bias=True)
147
+ (to_k): Linear(in_features=64, out_features=64, bias=True)
148
+ (to_v): Linear(in_features=64, out_features=64, bias=True)
149
+ (to_out): ModuleList(
150
+ (0): Linear(in_features=64, out_features=64, bias=True)
151
+ (1): Dropout(p=0.0, inplace=False)
152
+ )
153
+ )
154
+ (norm3): RMSNorm()
155
+ (ff): FeedForward(
156
+ (net): ModuleList(
157
+ (0): GELU(
158
+ (proj): Linear(in_features=64, out_features=256, bias=True)
159
+ )
160
+ (1): Dropout(p=0.0, inplace=False)
161
+ (2): Linear(in_features=256, out_features=64, bias=True)
162
+ )
163
+ )
164
+ (audio_norm3): RMSNorm()
165
+ (audio_ff): FeedForward(
166
+ (net): ModuleList(
167
+ (0): GELU(
168
+ (proj): Linear(in_features=64, out_features=256, bias=True)
169
+ )
170
+ (1): Dropout(p=0.0, inplace=False)
171
+ (2): Linear(in_features=256, out_features=64, bias=True)
172
+ )
173
+ )
174
+ )
175
+ )
176
+ (norm_out): LayerNorm((64,), eps=1e-06, elementwise_affine=False, bias=False)
177
+ (proj_out): Linear(in_features=64, out_features=128, bias=True)
178
+ (audio_norm_out): LayerNorm((64,), eps=1e-06, elementwise_affine=False, bias=False)
179
+ (audio_proj_out): Linear(in_features=64, out_features=8, bias=True)
180
+ )
README.md ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: diffusers
3
+ pipeline_tag: image-to-video
4
+ base_model:
5
+ - Lightricks/LTX-2.5
6
+ ---
7
+
8
+ This is a randomly initialized tiny model based on [Lightricks/LTX-2.5](https://huggingface.co/Lightricks/LTX-2.5) for testing and debugging.
9
+
10
+ | Feature | Source | This repo |
11
+ |---|---|---|
12
+ | Checkpoint tensor elements | 35.03B BF16, 4.73M F32, 32.19M U8 | 4.68M BF16, 8.45K F32, 32.18M U8 |
13
+ | Safetensors weight files | 70.12 GB (4 files) | 41.65 MB (4 files) |
14
+ | Transformer layers / video inner width | 48 / 4,096 | 4 / 64 |
15
+ | Video heads / head dimension | 32 / 128 | 2 / 32 |
16
+ | Audio inner width / heads / head dimension | 2,048 / 32 / 64 | 64 / 2 / 32 |
17
+ | Video / audio cross-attention width | 4,096 / 2,048 | 64 / 64 |
18
+ | Text connector blocks / registers | 8 / 128 | 2 / 4 |
19
+ | Text projection factor (layers + 1) | 49 | 3 |
20
+ | Text encoder layers / width / head dimension | 48 / 3,840 / 256 | 2 / 8 / 32 |
21
+ | Video VAE latent channels / patch size | 128 / 4 | Same |
22
+ | Audio VAE latent channels / mel bins | 8 / 64 | 8 / 4 |
23
+ | Vocoder hidden width | 1,024 | 32 |
24
+ | Embedded tokenizer and processor assets | U8 tensors in the text encoder file | Same layout |
25
+ | Stored precision | BF16 weights, FP32 modulation tables | Same |
26
+
27
+ The source is a ComfyUI split single-file pack: the transformer file also carries the text connectors, and the text encoder file carries the connector projections, two modality projectors and the tokenizer assets as U8 tensors. This repo keeps that layout and adds `config.json` plus `ltx2_loader.py`, which the split files do not contain, so the reduced components can be rebuilt. LTX-2.5's tokenizer is embedded in a 26 GB file that cannot be fetched alone, so `tokenizer_json` and the `hf_asset__*` entries come from the pinned `google/gemma-4-12b-it` release, which has the same 262,144-row vocabulary. The embedded 32.17 MB tokenizer is the floor for that file; the remaining non-tokenizer weights are about 9.5 MB, so the four safetensors files total 41.65 MB. The text encoder keeps a 32-wide attention head on an 8-wide residual width to preserve the minimum head dimension.
28
+
29
+ | File path | Size |
30
+ |------|------|
31
+ | diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors | 2.3MB |
32
+ | text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors | 36.5MB |
33
+ | vae/ltx-2.5-audio-vae-bf16.safetensors | 0.1MB |
34
+ | vae/ltx-2.5-video-vae-bf16.safetensors | 2.7MB |
35
+
36
+ ### Example usage
37
+
38
+ #### Diffusers (✅ verified)
39
+
40
+ `ltx2_loader.py` maps the split files back to Diffusers components; this runs a 2-step text-to-video request that also decodes the audio branch.
41
+
42
+ ```python
43
+ def run_diffusers(model_id: str):
44
+ import sys
45
+ from pathlib import Path
46
+
47
+ import torch
48
+
49
+ model_id = Path(model_id).resolve()
50
+ sys.path.insert(0, str(model_id))
51
+ from ltx2_loader import load_pipeline
52
+
53
+ pipeline = load_pipeline(model_id)
54
+ with torch.no_grad():
55
+ result = pipeline(
56
+ prompt="A tiny red cube rotating on a white table",
57
+ height=64,
58
+ width=64,
59
+ num_frames=9,
60
+ num_inference_steps=2,
61
+ output_type="pt",
62
+ guidance_scale=1.0,
63
+ stg_scale=1.0,
64
+ modality_scale=1.0,
65
+ audio_guidance_scale=1.0,
66
+ audio_stg_scale=1.0,
67
+ audio_modality_scale=1.0,
68
+ generator=torch.Generator(pipeline.device).manual_seed(0),
69
+ )
70
+ frames, audio = result.frames, result.audio
71
+ assert frames.ndim == 5 and frames.shape[0] == 1
72
+ assert audio.shape[0] == 1 and audio.shape[1] == 2
73
+ assert torch.isfinite(frames).all() and torch.isfinite(audio).all()
74
+ print("frames", tuple(frames.shape), "audio", tuple(audio.shape))
75
+
76
+
77
+ if __name__ == "__main__":
78
+ run_diffusers(model_id="tiny-random/ltx-2-5")
79
+ ```
80
+
81
+ ### Creating this repo
82
+
83
+ - Code: [.meta/create_model.py](.meta/create_model.py)
84
+ - Model structure: [.meta/model.log](.meta/model.log)
85
+ - Environment: [.meta/env.json](.meta/env.json)
86
+
87
+ ### Change log
88
+
89
+ - 2026-11-01: Initial tiny LTX-2.5 split checkpoint with video and audio branches.
90
+
91
+ *This repo is generated by DeepSeek-V4.1-Flash.*
config.json ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "transformer": {
3
+ "in_channels": 128,
4
+ "out_channels": 128,
5
+ "audio_in_channels": 8,
6
+ "audio_out_channels": 8,
7
+ "num_layers": 4,
8
+ "num_attention_heads": 2,
9
+ "attention_head_dim": 32,
10
+ "audio_num_attention_heads": 2,
11
+ "audio_attention_head_dim": 32,
12
+ "cross_attention_dim": 64,
13
+ "audio_cross_attention_dim": 64,
14
+ "caption_channels": 8,
15
+ "use_prompt_embeddings": false,
16
+ "cross_attn_mod": true,
17
+ "audio_cross_attn_mod": true,
18
+ "use_prompt_adaln_single": false,
19
+ "use_keyframes_abs_pos_embedding": true
20
+ },
21
+ "connectors": {
22
+ "caption_channels": 8,
23
+ "text_proj_in_factor": 3,
24
+ "video_connector_num_attention_heads": 2,
25
+ "video_connector_attention_head_dim": 32,
26
+ "video_connector_num_layers": 2,
27
+ "video_connector_num_learnable_registers": 4,
28
+ "audio_connector_num_attention_heads": 2,
29
+ "audio_connector_attention_head_dim": 32,
30
+ "audio_connector_num_layers": 2,
31
+ "audio_connector_num_learnable_registers": 4,
32
+ "video_hidden_dim": 64,
33
+ "audio_hidden_dim": 64,
34
+ "per_modality_projections": true
35
+ },
36
+ "vae": {
37
+ "in_channels": 3,
38
+ "out_channels": 3,
39
+ "latent_channels": 128,
40
+ "block_out_channels": [
41
+ 16,
42
+ 32
43
+ ],
44
+ "decoder_block_out_channels": [
45
+ 16
46
+ ],
47
+ "layers_per_block": [
48
+ 1,
49
+ 1,
50
+ 1
51
+ ],
52
+ "decoder_layers_per_block": [
53
+ 1,
54
+ 1
55
+ ],
56
+ "spatio_temporal_scaling": [
57
+ true,
58
+ true
59
+ ],
60
+ "decoder_spatio_temporal_scaling": [
61
+ true
62
+ ],
63
+ "downsample_type": [
64
+ "spatiotemporal",
65
+ "spatiotemporal"
66
+ ],
67
+ "upsample_type": [
68
+ "spatiotemporal"
69
+ ],
70
+ "upsample_residual": [
71
+ true
72
+ ],
73
+ "upsample_factor": [
74
+ 2
75
+ ],
76
+ "patch_size": 4,
77
+ "patch_size_t": 1
78
+ },
79
+ "audio_vae": {
80
+ "base_channels": 8,
81
+ "output_channels": 2,
82
+ "ch_mult": [
83
+ 1,
84
+ 2
85
+ ],
86
+ "num_res_blocks": 1,
87
+ "in_channels": 2,
88
+ "resolution": 64,
89
+ "latent_channels": 8,
90
+ "mel_bins": 4
91
+ },
92
+ "vocoder": {
93
+ "in_channels": 8,
94
+ "hidden_channels": 32,
95
+ "out_channels": 2,
96
+ "upsample_kernel_sizes": [
97
+ 8,
98
+ 8
99
+ ],
100
+ "upsample_factors": [
101
+ 4,
102
+ 4
103
+ ],
104
+ "resnet_kernel_sizes": [
105
+ 3
106
+ ],
107
+ "resnet_dilations": [
108
+ [
109
+ 1,
110
+ 1,
111
+ 1
112
+ ]
113
+ ]
114
+ },
115
+ "text_encoder": {
116
+ "vocab_size": 262144,
117
+ "hidden_size": 8,
118
+ "intermediate_size": 16,
119
+ "num_hidden_layers": 2,
120
+ "num_attention_heads": 1,
121
+ "num_key_value_heads": 1,
122
+ "head_dim": 32,
123
+ "max_position_embeddings": 4096,
124
+ "tie_word_embeddings": true
125
+ }
126
+ }
diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a75074c834af4c9280a902807a5a6f56d1d049a1f6ed5bf1e003b0244696e402
3
+ size 2338424
ltx2_loader.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Load the tiny LTX-2.5 split checkpoint into a Diffusers pipeline."""
2
+
3
+ import json
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+ import torch
8
+ from diffusers import (
9
+ AutoencoderKLLTX2Audio,
10
+ AutoencoderKLLTX2Video,
11
+ FlowMatchEulerDiscreteScheduler,
12
+ LTX2Pipeline,
13
+ LTX2VideoTransformer3DModel,
14
+ )
15
+ from diffusers.loaders.single_file_utils import (
16
+ convert_ltx2_audio_vae_to_diffusers,
17
+ convert_ltx2_transformer_to_diffusers,
18
+ convert_ltx2_vae_to_diffusers,
19
+ )
20
+ from diffusers.pipelines.ltx2.connectors import LTX2TextConnectors
21
+ from diffusers.pipelines.ltx2.vocoder import LTX2Vocoder
22
+ from safetensors.torch import load_file
23
+ from transformers import (
24
+ AutoTokenizer,
25
+ Gemma4UnifiedConfig,
26
+ Gemma4UnifiedForConditionalGeneration,
27
+ Gemma4UnifiedTextConfig,
28
+ Gemma4UnifiedVisionConfig,
29
+ )
30
+
31
+ FILES = {
32
+ "transformer": "diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors",
33
+ "text_encoder": "text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors",
34
+ "vae": "vae/ltx-2.5-video-vae-bf16.safetensors",
35
+ "audio_vae": "vae/ltx-2.5-audio-vae-bf16.safetensors",
36
+ }
37
+ ASSETS = {
38
+ "tokenizer_json": "tokenizer.json",
39
+ "hf_asset__tokenizer_config.json": "tokenizer_config.json",
40
+ "hf_asset__chat_template.jinja": "chat_template.jinja",
41
+ }
42
+ # Diffusers name -> Comfy single-file name for the tensors the converters drop.
43
+ CONNECTOR_PREFIX = {
44
+ "video_connector.transformer_blocks.": "video_embeddings_connector.transformer_1d_blocks.",
45
+ "audio_connector.transformer_blocks.": "audio_embeddings_connector.transformer_1d_blocks.",
46
+ "video_connector.learnable_registers": "video_embeddings_connector.learnable_registers",
47
+ "audio_connector.learnable_registers": "audio_embeddings_connector.learnable_registers",
48
+ }
49
+ TEXT_PREFIX = {
50
+ "model.language_model.": "model.",
51
+ "model.embed_vision.": "vision_model.",
52
+ }
53
+ PROJECTIONS = {
54
+ "video_text_proj_in.weight": "text_embedding_projection.video_aggregate_embed.weight",
55
+ "audio_text_proj_in.weight": "text_embedding_projection.audio_aggregate_embed.weight",
56
+ }
57
+
58
+
59
+ def _connector_name(name):
60
+ for local, source in CONNECTOR_PREFIX.items():
61
+ if name.startswith(local):
62
+ name = source + name[len(local) :]
63
+ break
64
+ return name.replace("norm_q", "q_norm").replace("norm_k", "k_norm")
65
+
66
+
67
+ def _text_name(name):
68
+ for local, source in TEXT_PREFIX.items():
69
+ if name.startswith(local):
70
+ return source + name[len(local) :]
71
+ return None
72
+
73
+
74
+ def load_transformer(config, state):
75
+ model = LTX2VideoTransformer3DModel(**config["transformer"])
76
+ plain = {name: value for name, value in state.items() if "embeddings_connector" not in name}
77
+ model.load_state_dict(convert_ltx2_transformer_to_diffusers(plain))
78
+ return model
79
+
80
+
81
+ def load_connectors(config, transformer_state, text_state):
82
+ model = LTX2TextConnectors(**config["connectors"])
83
+ weights = {}
84
+ for name in model.state_dict():
85
+ if name in PROJECTIONS:
86
+ weights[name] = text_state[PROJECTIONS[name]]
87
+ else:
88
+ weights[name] = transformer_state["model.diffusion_model." + _connector_name(name)]
89
+ model.load_state_dict(weights, strict=True)
90
+ return model
91
+
92
+
93
+ def load_text_encoder(config, text_state):
94
+ text = Gemma4UnifiedTextConfig(
95
+ **{key: value for key, value in config["text_encoder"].items() if key != "tie_word_embeddings"}
96
+ )
97
+ gemma = Gemma4UnifiedConfig(
98
+ text_config=text.to_dict(),
99
+ vision_config=Gemma4UnifiedVisionConfig(
100
+ patch_size=16, pooling_kernel_size=2, mm_embed_dim=8, mm_posemb_size=16, output_proj_dims=8
101
+ ).to_dict(),
102
+ )
103
+ gemma.tie_word_embeddings = True
104
+ model = Gemma4UnifiedForConditionalGeneration(gemma)
105
+ weights = {}
106
+ for name in model.state_dict():
107
+ source = _text_name(name)
108
+ if source is not None and source in text_state:
109
+ weights[name] = text_state[source]
110
+ model.load_state_dict(weights, strict=False)
111
+ return model
112
+
113
+
114
+ def load_tokenizer(text_state):
115
+ with tempfile.TemporaryDirectory() as directory:
116
+ for tensor_name, file_name in ASSETS.items():
117
+ Path(directory, file_name).write_bytes(text_state[tensor_name].numpy().tobytes())
118
+ return AutoTokenizer.from_pretrained(directory)
119
+
120
+
121
+ def load_pipeline(model_id, device=None):
122
+ model_id = Path(model_id)
123
+ config = json.loads((model_id / "config.json").read_text())
124
+ transformer_state = load_file(str(model_id / FILES["transformer"]))
125
+ text_state = load_file(str(model_id / FILES["text_encoder"]))
126
+
127
+ transformer = load_transformer(config, transformer_state)
128
+ connectors = load_connectors(config, transformer_state, text_state)
129
+ vae = AutoencoderKLLTX2Video(**config["vae"])
130
+ vae.load_state_dict(convert_ltx2_vae_to_diffusers(load_file(str(model_id / FILES["vae"]))))
131
+
132
+ audio_state = load_file(str(model_id / FILES["audio_vae"]))
133
+ audio_vae = AutoencoderKLLTX2Audio(**config["audio_vae"])
134
+ audio_vae.load_state_dict(
135
+ convert_ltx2_audio_vae_to_diffusers(
136
+ {name: value for name, value in audio_state.items() if not name.startswith("vocoder.")}
137
+ )
138
+ )
139
+ vocoder = LTX2Vocoder(**config["vocoder"])
140
+ vocoder.load_state_dict(
141
+ {
142
+ name.removeprefix("vocoder."): value
143
+ for name, value in audio_state.items()
144
+ if name.startswith("vocoder.")
145
+ }
146
+ )
147
+
148
+ text_encoder = load_text_encoder(config, text_state)
149
+ pipeline = LTX2Pipeline(
150
+ scheduler=FlowMatchEulerDiscreteScheduler(),
151
+ vae=vae,
152
+ audio_vae=audio_vae,
153
+ text_encoder=text_encoder,
154
+ tokenizer=load_tokenizer(text_state),
155
+ connectors=connectors,
156
+ transformer=transformer,
157
+ vocoder=vocoder,
158
+ )
159
+ for module in (transformer, vae, audio_vae, text_encoder, connectors, vocoder):
160
+ module.eval()
161
+ pipeline.to(device or ("cuda" if torch.cuda.is_available() else "cpu"))
162
+ return pipeline
text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:56630d832fc655dbb30211c578fb7b9b9d1b89b243489d8183db2a8194014335
3
+ size 36488059
vae/ltx-2.5-audio-vae-bf16.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ad3fecd37ef0b7e53ec7773d50dbe7e857a398ac33a411deebc19fab5bf1eb96
3
+ size 120144
vae/ltx-2.5-video-vae-bf16.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ec970706a5b559838e97bd663dea51d2e26135f43b12c479e249192a6f67d56
3
+ size 2706862