diffsynth 2.1.2__py3-none-any.whl → 2.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. diffsynth/configs/model_configs.py +90 -7
  2. diffsynth/configs/vram_management_module_maps.py +27 -1
  3. diffsynth/core/attention/attention.py +33 -7
  4. diffsynth/core/data/operators.py +2 -2
  5. diffsynth/core/device/npu_compatible_device.py +9 -9
  6. diffsynth/core/loader/file.py +17 -1
  7. diffsynth/core/loader/model.py +2 -3
  8. diffsynth/core/quant/__init__.py +1 -1
  9. diffsynth/core/quant/backends/__init__.py +28 -1
  10. diffsynth/core/quant/backends/bitsandbytes.py +42 -39
  11. diffsynth/core/quant/backends/comfy_kitchen.py +318 -0
  12. diffsynth/core/quant/backends/torchao.py +76 -52
  13. diffsynth/core/quant/base.py +27 -25
  14. diffsynth/core/quant/config.py +106 -53
  15. diffsynth/core/vram/disk_map.py +1 -1
  16. diffsynth/core/vram/layers.py +4 -2
  17. diffsynth/diffusion/flow_match.py +10 -2
  18. diffsynth/diffusion/logger.py +26 -0
  19. diffsynth/diffusion/parsers.py +6 -0
  20. diffsynth/diffusion/training_module.py +42 -5
  21. diffsynth/models/flux_redux.py +13 -0
  22. diffsynth/models/ideogram4_dit.py +7 -6
  23. diffsynth/models/{minimax_h3_dit_comfy_pruned.py → minimax_h3_dit_comfy.py} +10 -4
  24. diffsynth/models/minimax_h3_video_vae.py +69 -0
  25. diffsynth/models/minimax_music3_condition_encoder.py +48 -0
  26. diffsynth/models/minimax_music3_dit.py +159 -0
  27. diffsynth/models/minimax_music3_rvq_depth_decoder.py +92 -0
  28. diffsynth/models/minimax_music3_text_encoder.py +57 -0
  29. diffsynth/models/minimax_music3_vocoder.py +119 -0
  30. diffsynth/models/model_loader.py +11 -8
  31. diffsynth/models/qwen_image_dit.py +18 -0
  32. diffsynth/pipelines/flux2_image.py +3 -1
  33. diffsynth/pipelines/flux_image.py +300 -4
  34. diffsynth/pipelines/ltx2_audio_video.py +1 -1
  35. diffsynth/pipelines/minimax_music3.py +351 -0
  36. diffsynth/pipelines/qwen_image.py +7 -0
  37. diffsynth/utils/state_dict_converters/flux_ipadapter.py +9 -1
  38. diffsynth/utils/state_dict_converters/minimax_h3_text_encoder.py +18 -0
  39. diffsynth/utils/state_dict_converters/minimax_music3_text_encoder.py +2 -0
  40. diffsynth/utils/xfuser/xdit_context_parallel.py +1 -1
  41. diffsynth/version.py +1 -1
  42. diffsynth-2.1.4.dist-info/METADATA +819 -0
  43. {diffsynth-2.1.2.dist-info → diffsynth-2.1.4.dist-info}/RECORD +46 -37
  44. diffsynth-2.1.2.dist-info/METADATA +0 -51
  45. {diffsynth-2.1.2.dist-info → diffsynth-2.1.4.dist-info}/WHEEL +0 -0
  46. {diffsynth-2.1.2.dist-info → diffsynth-2.1.4.dist-info}/licenses/LICENSE +0 -0
  47. {diffsynth-2.1.2.dist-info → diffsynth-2.1.4.dist-info}/top_level.txt +0 -0
@@ -339,6 +339,14 @@ flux_series = [
339
339
  "model_class": "diffsynth.models.flux_dit.FluxDiT",
340
340
  "state_dict_converter": "diffsynth.utils.state_dict_converters.flux_dit.FluxDiTStateDictConverterFromDiffusers",
341
341
  },
342
+ {
343
+ # Example: ModelConfig(model_id="black-forest-labs/FLUX.1-Fill-dev", origin_file_pattern="flux1-fill-dev.safetensors")
344
+ "model_hash": "f876d2a6fb247fb7da0f84fc7fb8a823",
345
+ "model_name": "flux_dit",
346
+ "model_class": "diffsynth.models.flux_dit.FluxDiT",
347
+ "extra_kwargs": {"input_dim": 384},
348
+ "state_dict_converter": "diffsynth.utils.state_dict_converters.flux_dit.FluxDiTStateDictConverter",
349
+ },
342
350
  {
343
351
  # Example: ModelConfig(model_id="black-forest-labs/FLUX.1-dev", origin_file_pattern="text_encoder/model.safetensors")
344
352
  "model_hash": "94eefa3dac9cec93cb1ebaf1747d7b78",
@@ -481,6 +489,21 @@ flux_series = [
481
489
  "model_class": "diffsynth.models.flux_ipadapter.SiglipVisionModelSO400M",
482
490
  "state_dict_converter": "diffsynth.utils.state_dict_converters.flux_ipadapter.SiglipStateDictConverter",
483
491
  },
492
+ {
493
+ # Example: ModelConfig(model_id="black-forest-labs/FLUX.1-Redux-dev", origin_file_pattern="image_encoder/model.safetensors")
494
+ "model_hash": "6bd5343828144a2135d783519c665e2c",
495
+ "model_name": "flux_redux_image_encoder",
496
+ "model_class": "diffsynth.models.flux_ipadapter.SiglipVisionModelSO400M",
497
+ "state_dict_converter": "diffsynth.utils.state_dict_converters.flux_ipadapter.SiglipReduxStateDictConverter",
498
+ },
499
+ {
500
+ # Example: ModelConfig(model_id="black-forest-labs/FLUX.1-Redux-dev", origin_file_pattern="image_embedder/diffusion_pytorch_model.safetensors")
501
+ # 2-layer MLP projecting SigLIP tokens (1152) to the 4096-dim FLUX text
502
+ # space; keys already match FluxReduxImageEncoder, so no converter needed.
503
+ "model_hash": "d8ea4618391460bc831fddaae5fb5288",
504
+ "model_name": "flux_redux_image_embedder",
505
+ "model_class": "diffsynth.models.flux_redux.FluxReduxImageEncoder",
506
+ },
484
507
  {
485
508
  # Example: ModelConfig(model_id="stepfun-ai/Step1X-Edit", origin_file_pattern="step1x-edit-i1258.safetensors"),
486
509
  "model_hash": "d30fb9e02b1dbf4e509142f05cf7dd50",
@@ -1394,18 +1417,46 @@ minimax_h3_series = [
1394
1417
  "state_dict_converter": "diffsynth.utils.state_dict_converters.minimax_h3_text_encoder.MiniMaxH3TextEncoderStateDictConverter",
1395
1418
  "quant_config": {"method": "bitsandbytes_nf4", "load_prequantized": True, "exclude_modules": ["qkv", "proj", "linear_fc1", "linear_fc2"]},
1396
1419
  },
1420
+ {
1421
+ # Example: ModelConfig(model_id="Comfy-Org/MiniMax-H3", origin_file_pattern="text_encoders/qwen3vl_32b_minimax_h3_int8_convrot.safetensors")
1422
+ "model_hash": "d6473c909b72b73afc4fa00c273d1da2",
1423
+ "model_name": "minimax_h3_text_encoder",
1424
+ "model_class": "diffsynth.models.minimax_h3_text_encoder.MiniMaxH3TextEncoder",
1425
+ "state_dict_converter": "diffsynth.utils.state_dict_converters.minimax_h3_text_encoder.MiniMaxH3TextEncoderComfyOrgStateDictConverter",
1426
+ "quant_config": {"method": "comfy_kitchen_int8_w8a8", "load_prequantized": True, "exclude_modules": ["qkv", "proj", "linear_fc1", "linear_fc2"]},
1427
+ },
1397
1428
  {
1398
1429
  # Example: ModelConfig(model_id="MiniMax/MiniMax-H3", origin_file_pattern="FL2VA/transformer/model*.safetensors")
1399
1430
  "model_hash": "db0197b6919425a5c7102c54e73affc1",
1400
1431
  "model_name": "minimax_h3_dit",
1401
1432
  "model_class": "diffsynth.models.minimax_h3_dit.MiniMaxH3DiT",
1402
1433
  },
1434
+ {
1435
+ # Example: ModelConfig(model_id="Comfy-Org/MiniMax-H3", origin_file_pattern="diffusion_models/minimax_h3_fl2va_int8_convrot.safetensors")
1436
+ "model_hash": "fb6c399412920c6b817d5c1f43ec62cd",
1437
+ "model_name": "minimax_h3_dit",
1438
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfy",
1439
+ "quant_config": {"method": "comfy_kitchen_int8_w8a8", "load_prequantized": True, "exclude_modules": ["video_patch_proj", "audio_patch_proj", "condition_proj", "time_embedder.proj_in", "time_embedder.proj_out", "final_layer.adaln_proj.linear", "final_layer.video_out", "final_layer.audio_out", "token_refiner.blocks.0.attn.qkv_proj", "token_refiner.blocks.0.attn.out_proj", "token_refiner.blocks.0.mlp.fc1", "token_refiner.blocks.0.mlp.fc2", "token_refiner.blocks.1.attn.qkv_proj", "token_refiner.blocks.1.attn.out_proj", "token_refiner.blocks.1.mlp.fc1", "token_refiner.blocks.1.mlp.fc2"]},
1440
+ },
1441
+ {
1442
+ # Example: ModelConfig(model_id="Comfy-Org/MiniMax-H3", origin_file_pattern="diffusion_models/minimax_h3_fl2va_pruned_int8_convrot.safetensors")
1443
+ "model_hash": "ac9fdcd56900c9a1a5ef21eaacf6cb76",
1444
+ "model_name": "minimax_h3_dit",
1445
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfyPruned",
1446
+ "quant_config": {"method": "comfy_kitchen_int8_w8a8", "load_prequantized": True, "exclude_modules": ["video_patch_proj", "audio_patch_proj", "condition_proj", "adaln_proj.linear", "final_layer.video_out", "final_layer.audio_out", "token_refiner.blocks.0.attn.qkv_proj", "token_refiner.blocks.0.attn.out_proj", "token_refiner.blocks.0.mlp.fc1", "token_refiner.blocks.0.mlp.fc2", "token_refiner.blocks.1.attn.qkv_proj", "token_refiner.blocks.1.attn.out_proj", "token_refiner.blocks.1.mlp.fc1", "token_refiner.blocks.1.mlp.fc2"]},
1447
+ },
1448
+ {
1449
+ # Example: ModelConfig(model_id="Comfy-Org/MiniMax-H3", origin_file_pattern="diffusion_models/minimax_h3_fl2va_pruned_fp8_scaled.safetensors")
1450
+ "model_hash": "7e410be9acd325ef8db3b8bed3d814cd",
1451
+ "model_name": "minimax_h3_dit",
1452
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfyPruned",
1453
+ "quant_config": {"method": "comfy_kitchen_fp8_w8a8", "load_prequantized": True, "exclude_modules": ["video_patch_proj", "audio_patch_proj", "condition_proj", "adaln_proj.linear", "final_layer.video_out", "final_layer.audio_out", "token_refiner.blocks.0.attn.qkv_proj", "token_refiner.blocks.0.attn.out_proj", "token_refiner.blocks.0.mlp.fc1", "token_refiner.blocks.0.mlp.fc2", "token_refiner.blocks.1.attn.qkv_proj", "token_refiner.blocks.1.attn.out_proj", "token_refiner.blocks.1.mlp.fc1", "token_refiner.blocks.1.mlp.fc2"]},
1454
+ },
1403
1455
  {
1404
1456
  # Example: ModelConfig(model_id="Comfy-Org/MiniMax-H3", origin_file_pattern="diffusion_models/minimax_h3_fl2va_pruned_bf16.safetensors")
1405
1457
  "model_hash": "b4e0df87da0c079979ae857edbcef49f",
1406
1458
  "model_name": "minimax_h3_dit",
1407
- "model_class": "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned",
1408
- "extra_kwargs": {"adaln_curve_grid": 1025, "time_embed_dim": 8},
1459
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfyPruned",
1409
1460
  },
1410
1461
  {
1411
1462
  # Example: ModelConfig(model_id="DiffSynth-Studio/MiniMax-H3-NF4", origin_file_pattern="minimax-h3-fl2va-nf4.safetensors")
@@ -1425,16 +1476,14 @@ minimax_h3_series = [
1425
1476
  # Example: ModelConfig(model_id="DiffSynth-Studio/MiniMax-H3-NF4", origin_file_pattern="minimax-h3-fl2va-pruned-nf4.safetensors")
1426
1477
  "model_hash": "9b8f1cbc6fb5fcd2e2c02b6691480427",
1427
1478
  "model_name": "minimax_h3_dit",
1428
- "model_class": "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned",
1429
- "extra_kwargs": {"adaln_curve_grid": 1025, "time_embed_dim": 8},
1479
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfyPruned",
1430
1480
  "quant_config": {"method": "bitsandbytes_nf4", "load_prequantized": True, "exclude_modules": ["time_embedder.proj_in", "time_embedder.proj_out", "video_patch_proj", "audio_patch_proj", "condition_proj", "final_layer.video_out", "final_layer.audio_out", "adaln_proj.linear"]},
1431
1481
  },
1432
1482
  {
1433
1483
  # Example: ModelConfig(model_id="DiffSynth-Studio/MiniMax-H3-NF4", origin_file_pattern="minimax-h3-ref2va-pruned-nf4.safetensors")
1434
1484
  "model_hash": "072bd13242d6d67df79a154e7c4274b3",
1435
1485
  "model_name": "minimax_h3_dit",
1436
- "model_class": "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned",
1437
- "extra_kwargs": {"adaln_curve_grid": 1025, "time_embed_dim": 8},
1486
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfyPruned",
1438
1487
  "quant_config": {"method": "bitsandbytes_nf4", "load_prequantized": True, "exclude_modules": ["time_embedder.proj_in", "time_embedder.proj_out", "video_patch_proj", "audio_patch_proj", "condition_proj", "final_layer.video_out", "final_layer.audio_out", "adaln_proj.linear"]},
1439
1488
  },
1440
1489
  {
@@ -1462,8 +1511,42 @@ minimax_h3_series = [
1462
1511
  },
1463
1512
  ]
1464
1513
 
1514
+ minimax_music3_series = [
1515
+ {
1516
+ # Example: ModelConfig(model_id="MiniMax/MiniMax-Music3", origin_file_pattern="transformer/diffusion_pytorch_model*.safetensors")
1517
+ "model_hash": "8d1f1aba6a5801220ccc3a531865f694",
1518
+ "model_name": "minimax_music3_dit",
1519
+ "model_class": "diffsynth.models.minimax_music3_dit.MiniMaxMusic3DiT",
1520
+ },
1521
+ {
1522
+ # Example: ModelConfig(model_id="MiniMax/MiniMax-Music3", origin_file_pattern="condition_encoder/diffusion_pytorch_model.safetensors")
1523
+ "model_hash": "1370ef6377206e5ceaa78b125b2d4e1f",
1524
+ "model_name": "minimax_music3_condition_encoder",
1525
+ "model_class": "diffsynth.models.minimax_music3_condition_encoder.MiniMaxMusic3ConditionEncoder",
1526
+ },
1527
+ {
1528
+ # Example: ModelConfig(model_id="MiniMax/MiniMax-Music3", origin_file_pattern="vocoder/diffusion_pytorch_model.safetensors")
1529
+ "model_hash": "b6eded627c26c85b39804628ba4e5d1d",
1530
+ "model_name": "minimax_music3_vocoder",
1531
+ "model_class": "diffsynth.models.minimax_music3_vocoder.MiniMaxMusic3Vocoder",
1532
+ },
1533
+ {
1534
+ # Example: ModelConfig(model_id="MiniMax/MiniMax-Music3", origin_file_pattern="rvq_depth_decoder/diffusion_pytorch_model.safetensors")
1535
+ "model_hash": "5e571ded532a131c02e9d8256cb3fded",
1536
+ "model_name": "minimax_music3_rvq_depth_decoder",
1537
+ "model_class": "diffsynth.models.minimax_music3_rvq_depth_decoder.MiniMaxMusic3RVQDepthDecoder",
1538
+ },
1539
+ {
1540
+ # Example: ModelConfig(model_id="MiniMax/MiniMax-Music3", origin_file_pattern="language_model/model*.safetensors")
1541
+ "model_hash": "474b2197ab43bd2d4b69746506a952ef",
1542
+ "model_name": "minimax_music3_text_encoder",
1543
+ "model_class": "diffsynth.models.minimax_music3_text_encoder.MiniMaxMusic3TextEncoder",
1544
+ "state_dict_converter": "diffsynth.utils.state_dict_converters.minimax_music3_text_encoder.MiniMaxMusic3TextEncoderStateDictConverter",
1545
+ },
1546
+ ]
1547
+
1465
1548
  MODEL_CONFIGS = (
1466
1549
  stable_diffusion_xl_series + stable_diffusion_series + qwen_image_series + wan_series + flux_series + flux2_series + ernie_image_series
1467
1550
  + z_image_series + ltx2_series + anima_series + mova_series + joyai_image_series + boogu_image_series + ace_step_series + hidream_o1_image_series
1468
- + image_metrics_series + ideogram4_series + krea2_series + lingbot_video_series + minimax_h3_series
1551
+ + image_metrics_series + ideogram4_series + krea2_series + lingbot_video_series + minimax_h3_series + minimax_music3_series
1469
1552
  )
@@ -14,7 +14,11 @@ VRAM_MANAGEMENT_MODULE_MAPS = {
14
14
  "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
15
15
  "torch.nn.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
16
16
  },
17
- "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned": {
17
+ "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfy": {
18
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
19
+ "torch.nn.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
20
+ },
21
+ "diffsynth.models.minimax_h3_dit_comfy.MiniMaxH3DiTComfyPruned": {
18
22
  "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
19
23
  "torch.nn.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
20
24
  },
@@ -470,6 +474,28 @@ VRAM_MANAGEMENT_MODULE_MAPS = {
470
474
  "diffsynth.models.lingbot_video_dit.LingBotVideoRMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
471
475
  "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
472
476
  },
477
+ "diffsynth.models.minimax_music3_dit.MiniMaxMusic3DiT": {
478
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
479
+ "torch.nn.Conv1d": "diffsynth.core.vram.layers.AutoWrappedModule",
480
+ "torch.nn.LayerNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
481
+ "diffsynth.models.minimax_music3_dit.MiniMaxMusic3FourierEmbedding": "diffsynth.core.vram.layers.AutoWrappedModule",
482
+ },
483
+ "diffsynth.models.minimax_music3_rvq_depth_decoder.MiniMaxMusic3RVQDepthDecoder": {
484
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
485
+ "torch.nn.Embedding": "diffsynth.core.vram.layers.AutoWrappedModule",
486
+ "diffsynth.models.minimax_music3_rvq_depth_decoder.MiniMaxMusic3RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
487
+ },
488
+ "diffsynth.models.minimax_music3_vocoder.MiniMaxMusic3Vocoder": {
489
+ "torch.nn.Conv1d": "diffsynth.core.vram.layers.AutoWrappedModule",
490
+ "diffsynth.models.minimax_music3_vocoder.MiniMaxMusic3WeightNormConv1d": "diffsynth.core.vram.layers.AutoWrappedModule",
491
+ "diffsynth.models.minimax_music3_vocoder.MiniMaxMusic3WeightNormConvTranspose1d": "diffsynth.core.vram.layers.AutoWrappedModule",
492
+ "diffsynth.models.minimax_music3_vocoder.MiniMaxMusic3Snake1d": "diffsynth.core.vram.layers.AutoWrappedModule",
493
+ },
494
+ "diffsynth.models.minimax_music3_text_encoder.MiniMaxMusic3TextEncoder": {
495
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
496
+ "torch.nn.Embedding": "diffsynth.core.vram.layers.AutoWrappedModule",
497
+ "transformers.models.qwen3.modeling_qwen3.Qwen3RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
498
+ },
473
499
  }
474
500
 
475
501
  def QwenImageTextEncoder_Module_Map_Updater():
@@ -1,41 +1,52 @@
1
1
  import torch, os, inspect
2
2
  from einops import rearrange, repeat
3
3
 
4
+ if os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_REPO_ID") is not None:
5
+ try:
6
+ from kernels import get_kernel
7
+ kernel_module = get_kernel(os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_REPO_ID"), version=int(os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_VERSION")))
8
+ customized_flash_attn_func = kernel_module.flash_attn_func
9
+ CUSTOMIZED_FA_KERNEL_AVAILABLE = True
10
+ except Exception:
11
+ CUSTOMIZED_FA_KERNEL_AVAILABLE = False
12
+ else:
13
+ CUSTOMIZED_FA_KERNEL_AVAILABLE = False
14
+
4
15
  try:
5
16
  from flash_attn.cute import flash_attn_func as flash_attn_func_cute
6
17
  FLASH_ATTN_4_AVAILABLE = True
7
- except ModuleNotFoundError:
18
+ except Exception:
8
19
  FLASH_ATTN_4_AVAILABLE = False
9
20
 
10
21
  try:
11
22
  import flash_attn_interface
12
23
  FLASH_ATTN_3_AVAILABLE = True
13
- except ModuleNotFoundError:
24
+ except Exception:
14
25
  FLASH_ATTN_3_AVAILABLE = False
15
26
 
16
27
  try:
17
28
  import flash_attn
18
29
  FLASH_ATTN_2_AVAILABLE = True
19
- except ModuleNotFoundError:
30
+ except Exception:
20
31
  FLASH_ATTN_2_AVAILABLE = False
21
32
 
22
33
  try:
23
34
  from sageattention import sageattn
24
35
  SAGE_ATTN_AVAILABLE = True
25
- except ModuleNotFoundError:
36
+ except Exception:
26
37
  SAGE_ATTN_AVAILABLE = False
27
38
 
28
39
  try:
29
40
  import xformers.ops as xops
30
41
  XFORMERS_AVAILABLE = True
31
- except ModuleNotFoundError:
42
+ except Exception:
32
43
  XFORMERS_AVAILABLE = False
33
44
 
34
45
  try:
35
46
  from torch.nn.attention.flex_attention import flex_attention as flex_attention_func
36
47
  flex_attention_func = torch.compile(flex_attention_func, dynamic=False, mode="max-autotune-no-cudagraphs", fullgraph=True, backend="inductor")
37
48
  FLEX_ATTN_AVAILABLE = True
38
- except (ModuleNotFoundError, ImportError):
49
+ except Exception:
39
50
  FLEX_ATTN_AVAILABLE = False
40
51
 
41
52
  try:
@@ -43,13 +54,15 @@ try:
43
54
  TORCH_SUPPORT_GQA = True
44
55
  else:
45
56
  TORCH_SUPPORT_GQA = False
46
- except:
57
+ except Exception:
47
58
  TORCH_SUPPORT_GQA = False
48
59
 
49
60
 
50
61
  def initialize_attention_priority():
51
62
  if os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION') is not None:
52
63
  return os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION').lower()
64
+ elif CUSTOMIZED_FA_KERNEL_AVAILABLE:
65
+ return "customized_fa_kernel"
53
66
  elif FLASH_ATTN_4_AVAILABLE:
54
67
  return "flash_attention_4"
55
68
  elif FLASH_ATTN_3_AVAILABLE:
@@ -178,6 +191,17 @@ def flash_attention_2(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_patte
178
191
  return out
179
192
 
180
193
 
194
+ def flash_attention_customized_kernel(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
195
+ required_in_pattern, required_out_pattern = "b s n d", "b s n d"
196
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
197
+ window_size = (window_size, window_size) if window_size is not None else (-1, -1)
198
+ out = customized_flash_attn_func(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
199
+ if isinstance(out, tuple):
200
+ out = out[0]
201
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
202
+ return out
203
+
204
+
181
205
  def sage_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
182
206
  required_in_pattern, required_out_pattern= "b n s d", "b n s d"
183
207
  q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
@@ -213,6 +237,8 @@ def attention_forward(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_patte
213
237
  # Sliding Window Attention is not compatible with `is_causal` and `attn_mask`.
214
238
  assert is_causal == False and attn_mask is None
215
239
  return torch_sdpa_sliding_window(q, k, v, window_size, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
240
+ elif ATTENTION_IMPLEMENTATION == "customized_fa_kernel":
241
+ return flash_attention_customized_kernel(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale, is_causal=is_causal, window_size=window_size)
216
242
  elif ATTENTION_IMPLEMENTATION == "flash_attention_4":
217
243
  return flash_attention_4(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale, is_causal=is_causal, window_size=window_size)
218
244
  elif ATTENTION_IMPLEMENTATION == "flash_attention_3":
@@ -307,8 +307,8 @@ class LoadAudioWithTorchaudio(DataProcessingOperator, FrameSamplerByRateMixin):
307
307
  padding = target_samples - current_samples
308
308
  waveform = torch.nn.functional.pad(waveform, (0, padding))
309
309
  return waveform, sample_rate
310
- except:
311
- warnings.warn(f"Cannot load audio in {data}. The audio will be `None`.")
310
+ except Exception as e:
311
+ warnings.warn(f"Cannot load audio in {data} due to {e}. The audio will be `None`.")
312
312
  return None
313
313
 
314
314
 
@@ -83,17 +83,17 @@ def enable_high_precision_for_bf16():
83
83
 
84
84
 
85
85
  def parse_device_type(device):
86
- if isinstance(device, str):
87
- if device.startswith("cuda"):
88
- return "cuda"
89
- elif device.startswith("npu"):
86
+ if isinstance(device, torch.device):
87
+ return device.type
88
+ elif isinstance(device, str):
89
+ # `torch.device("npu")` raises when torch_npu is not installed, so keep npu ahead of the generic parse.
90
+ if device.startswith("npu"):
90
91
  return "npu"
91
- elif device.startswith("mps"):
92
- return "mps"
93
- else:
92
+ try:
93
+ return torch.device(device).type
94
+ except RuntimeError:
95
+ # Strings that are not device names at all
94
96
  return "cpu"
95
- elif isinstance(device, torch.device):
96
- return device.type
97
97
 
98
98
 
99
99
  def parse_nccl_backend(device_type):
@@ -1,5 +1,5 @@
1
1
  from safetensors import safe_open
2
- import torch, hashlib
2
+ import torch, hashlib, json
3
3
 
4
4
 
5
5
  def load_state_dict(file_path, torch_dtype=None, device="cpu", pin_memory=False, verbose=0):
@@ -34,10 +34,26 @@ def load_state_dict_from_safetensors(file_path, torch_dtype=None, device="cpu"):
34
34
 
35
35
 
36
36
  def load_metadata_from_safetensors(file_path):
37
+ if isinstance(file_path, list):
38
+ metadata = {}
39
+ for file_path_ in file_path:
40
+ for key, value in load_metadata_from_safetensors(file_path_).items():
41
+ metadata[key] = merge_metadata_value(metadata[key], value) if key in metadata else value
42
+ return metadata
37
43
  with safe_open(file_path, framework="pt", device="cpu") as f:
38
44
  return f.metadata() or {}
39
45
 
40
46
 
47
+ def merge_metadata_value(old_value, new_value):
48
+ try:
49
+ old_items, new_items = json.loads(old_value), json.loads(new_value)
50
+ except (json.JSONDecodeError, TypeError):
51
+ return new_value
52
+ if isinstance(old_items, list) and isinstance(new_items, list):
53
+ return json.dumps(old_items + [i for i in new_items if i not in old_items])
54
+ return new_value
55
+
56
+
41
57
  def load_state_dict_from_bin(file_path, torch_dtype=None, device="cpu"):
42
58
  state_dict = torch.load(file_path, map_location=device, weights_only=True)
43
59
  if len(state_dict) == 1:
@@ -37,14 +37,13 @@ def load_model(model_class, path, config=None, torch_dtype=torch.bfloat16, devic
37
37
  devices = [vram_config[k] for k in ("offload_device", "onload_device", "preparing_device", "computation_device")]
38
38
  load_device = [d for d in devices if d != "disk"][0]
39
39
  disk_map = DiskMap(path, load_device, torch_dtype=None, state_dict_converter=state_dict_converter)
40
- metadata = load_metadata_from_safetensors(path[0] if isinstance(path, list) else path)
40
+ metadata = load_metadata_from_safetensors(path)
41
41
  model = quantize.prepare_for_prequantized_load(model, compute_dtype=vram_config["computation_dtype"])
42
42
  model = enable_vram_management(model, module_map, vram_config=vram_config, disk_map=disk_map, vram_limit=vram_limit, quantize=quantize, metadata=metadata)
43
43
  else:
44
44
  offload_device = vram_config["offload_device"]
45
45
  computation_device = vram_config["computation_device"]
46
46
  computation_dtype = vram_config["computation_dtype"]
47
- offload_dtype = vram_config["offload_dtype"]
48
47
  load_dtype = None if quantize.load_prequantized else computation_dtype
49
48
  if state_dict is None: state_dict = DiskMap(path, offload_device, torch_dtype=load_dtype)
50
49
  if state_dict_converter is not None:
@@ -61,7 +60,7 @@ def load_model(model_class, path, config=None, torch_dtype=torch.bfloat16, devic
61
60
 
62
61
  model = quantize.quantize_model(model, compute_device=computation_device, model_device=offload_device)
63
62
  model = quantize.dequantize_model(model, compute_dtype=computation_dtype, compute_device=computation_device, model_device=offload_device)
64
- model = model.to(dtype=offload_dtype, device=offload_device)
63
+ model = model.to(dtype=computation_dtype, device=offload_device)
65
64
  model = enable_vram_management(model, module_map, vram_config=vram_config, disk_map=None, vram_limit=vram_limit, quantize=quantize)
66
65
  elif quantize is not None:
67
66
  # Weight-only quantization (see `diffsynth.core.quant`), isolated from the normal path below.
@@ -1,3 +1,3 @@
1
- from .base import QuantBackend, QUANT_BACKENDS, register_quant_backend, check_differentiable, check_backend_contract, resolve_checkpoint_keys
1
+ from .base import QuantBackend, BackendConfig, QUANT_BACKENDS, register_quant_backend, check_differentiable, check_backend_contract
2
2
  from .config import QuantizeConfig, MixedQuantizeConfig, QuantMethodSpec, QUANT_METHODS, register_quant_method, describe_quant_method
3
3
  from . import backends
@@ -1 +1,28 @@
1
- from . import bitsandbytes, torchao
1
+ import importlib
2
+
3
+ _LAZY_BACKENDS = {
4
+ "bitsandbytes": ".bitsandbytes",
5
+ "torchao": ".torchao",
6
+ "comfy_kitchen": ".comfy_kitchen",
7
+ }
8
+ _loaded = set()
9
+
10
+
11
+ def load_backend(name):
12
+ module = _LAZY_BACKENDS.get(name)
13
+ if module is not None and name not in _loaded:
14
+ importlib.import_module(module, __name__)
15
+ _loaded.add(name)
16
+
17
+
18
+ def load_all_backends():
19
+ for name in _LAZY_BACKENDS:
20
+ load_backend(name)
21
+
22
+
23
+ def load_backend_for_method(method):
24
+ from ..config import QUANT_METHODS
25
+ for name in _LAZY_BACKENDS:
26
+ load_backend(name)
27
+ if method in QUANT_METHODS:
28
+ return
@@ -1,7 +1,15 @@
1
+ from dataclasses import dataclass, field
2
+
1
3
  import torch
2
- from ..base import QuantBackend, register_quant_backend
4
+ from ..base import QuantBackend, BackendConfig, register_quant_backend
3
5
  from ..config import register_quant_method
4
6
 
7
+ try:
8
+ import bitsandbytes as bnb
9
+ BITSANDBYTES_AVAILABLE = True
10
+ except ImportError:
11
+ BITSANDBYTES_AVAILABLE = False
12
+
5
13
 
6
14
  def _assign_params_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs):
7
15
  """`_load_from_state_dict` replacement for shells: assigns tensors as-is, since packed weights do not pass the standard shape check."""
@@ -29,10 +37,10 @@ def _assign_params_from_state_dict(self, state_dict, prefix, local_metadata, str
29
37
  class BitsAndBytesQuantBackend(QuantBackend):
30
38
  """Adapter over `bitsandbytes.nn.Linear4bit` (NF4 / FP4, weight-only)."""
31
39
 
40
+ project_url = "https://github.com/bitsandbytes-foundation/bitsandbytes"
41
+
32
42
  def validate_environment(self):
33
- try:
34
- import bitsandbytes # noqa: F401
35
- except ImportError:
43
+ if not BITSANDBYTES_AVAILABLE:
36
44
  raise ImportError(
37
45
  "bitsandbytes is required for bitsandbytes quantization methods. "
38
46
  'Please install it via `pip install bitsandbytes` or `pip install "diffsynth[quant]"`.'
@@ -47,23 +55,14 @@ class BitsAndBytesQuantBackend(QuantBackend):
47
55
  }
48
56
 
49
57
  def quantized_linear_classes(self):
50
- try:
51
- import bitsandbytes as bnb
52
- except ImportError:
53
- return ()
54
58
  return (bnb.nn.Linear4bit,)
55
59
 
56
60
  def create_quantized_linear(self, linear, compute_device=None, model_device=None):
57
61
  """The `meta` shell avoids allocating an fp weight; bnb quantizes while `Params4bit` moves to `compute_device`."""
58
- import bitsandbytes as bnb
59
-
62
+ cfg = self.config
60
63
  compute_dtype = linear.weight.dtype
61
64
  if compute_device is None:
62
65
  compute_device = linear.weight.device
63
- quant_type = self.config.get("quant_type", "nf4")
64
- compress_statistics = self.config.get("compress_statistics", True)
65
- blocksize = self.config.get("blocksize")
66
- quant_storage = self.config.get("quant_storage", torch.uint8)
67
66
 
68
67
  with torch.device("meta"):
69
68
  quant_linear = bnb.nn.Linear4bit(
@@ -71,17 +70,17 @@ class BitsAndBytesQuantBackend(QuantBackend):
71
70
  linear.out_features,
72
71
  bias=linear.bias is not None,
73
72
  compute_dtype=compute_dtype,
74
- compress_statistics=compress_statistics,
75
- quant_type=quant_type,
76
- quant_storage=quant_storage,
73
+ compress_statistics=cfg.compress_statistics,
74
+ quant_type=cfg.quant_type,
75
+ quant_storage=cfg.quant_storage,
77
76
  )
78
77
  weight = bnb.nn.Params4bit(
79
78
  linear.weight.data.contiguous(),
80
79
  requires_grad=False,
81
- compress_statistics=compress_statistics,
82
- blocksize=blocksize,
83
- quant_type=quant_type,
84
- quant_storage=quant_storage,
80
+ compress_statistics=cfg.compress_statistics,
81
+ blocksize=cfg.blocksize,
82
+ quant_type=cfg.quant_type,
83
+ quant_storage=cfg.quant_storage,
85
84
  )
86
85
  quant_linear.weight = weight.to(compute_device)
87
86
  if linear.bias is not None:
@@ -89,22 +88,20 @@ class BitsAndBytesQuantBackend(QuantBackend):
89
88
  return quant_linear.to(device=model_device)
90
89
 
91
90
  def create_quantized_linear_shell(self, linear, compute_dtype):
92
- import bitsandbytes as bnb
93
91
  with torch.device("meta"):
94
92
  shell = bnb.nn.Linear4bit(
95
93
  linear.in_features,
96
94
  linear.out_features,
97
95
  bias=linear.bias is not None,
98
96
  compute_dtype=compute_dtype,
99
- compress_statistics=self.config.get("compress_statistics", True),
100
- quant_type=self.config.get("quant_type", "nf4"),
101
- quant_storage=self.config.get("quant_storage", torch.uint8),
97
+ compress_statistics=self.config.compress_statistics,
98
+ quant_type=self.config.quant_type,
99
+ quant_storage=self.config.quant_storage,
102
100
  )
103
101
  shell._load_from_state_dict = _assign_params_from_state_dict.__get__(shell)
104
102
  return shell
105
103
 
106
104
  def unflatten_state_dict(self, state_dict, metadata):
107
- import bitsandbytes as bnb
108
105
  rebuilt = {}
109
106
  quant_state = {}
110
107
  for key, value in state_dict.items():
@@ -122,7 +119,6 @@ class BitsAndBytesQuantBackend(QuantBackend):
122
119
  return rebuilt
123
120
 
124
121
  def dequantize_to_linear(self, module, compute_dtype, compute_device=None, model_device=None):
125
- import bitsandbytes as bnb
126
122
  if compute_device is not None:
127
123
  module = module.to(device=compute_device)
128
124
  weight = module.weight
@@ -136,17 +132,24 @@ class BitsAndBytesQuantBackend(QuantBackend):
136
132
  return linear
137
133
 
138
134
 
139
- def _linear4bit_config(quant_type):
140
- def factory(backend_config_kwargs):
141
- return {
142
- "quant_type": quant_type,
143
- "compress_statistics": True,
144
- "blocksize": None,
145
- "quant_storage": torch.uint8,
146
- **backend_config_kwargs,
147
- }
148
- return factory
135
+ @dataclass
136
+ class BitsAndBytes4bitConfig(BackendConfig):
137
+ """Shared 4bit knobs; `quant_type` is pinned by the per-method subclass."""
138
+
139
+ compress_statistics: bool = True
140
+ blocksize: int = None # None -> bnb default (64)
141
+ quant_storage: torch.dtype = torch.uint8
142
+
143
+
144
+ @dataclass
145
+ class BitsAndBytesNF4Config(BitsAndBytes4bitConfig):
146
+ quant_type: str = field(init=False, default="nf4")
147
+
148
+
149
+ @dataclass
150
+ class BitsAndBytesFP4Config(BitsAndBytes4bitConfig):
151
+ quant_type: str = field(init=False, default="fp4")
149
152
 
150
153
 
151
- register_quant_method("bitsandbytes_nf4", "bitsandbytes", _linear4bit_config("nf4"), label="4bit, nf4, weight-only")
152
- register_quant_method("bitsandbytes_fp4", "bitsandbytes", _linear4bit_config("fp4"), label="4bit, fp4, weight-only")
154
+ register_quant_method("bitsandbytes_nf4", "bitsandbytes", BitsAndBytesNF4Config.from_kwargs, label="4bit, nf4, weight-only")
155
+ register_quant_method("bitsandbytes_fp4", "bitsandbytes", BitsAndBytesFP4Config.from_kwargs, label="4bit, fp4, weight-only")