inline-core 1.2.65__tar.gz → 1.2.67__tar.gz

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 (258) hide show
  1. {inline_core-1.2.65 → inline_core-1.2.67}/CLAUDE.md +37 -0
  2. {inline_core-1.2.65 → inline_core-1.2.67}/PKG-INFO +4 -4
  3. {inline_core-1.2.65 → inline_core-1.2.67}/pyproject.toml +13 -5
  4. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/keymap.py +23 -8
  5. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/lora.py +70 -28
  6. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/adaln.py +102 -0
  7. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/keys.py +29 -5
  8. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/load.py +107 -14
  9. inline_core-1.2.67/src/inline_core/models/minimaxh3/lora_keys.py +235 -0
  10. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/pipeline.py +9 -0
  11. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/requirements.py +83 -13
  12. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/runner.py +14 -0
  13. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/pipeline_runtime.py +76 -0
  14. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/fal.py +18 -8
  15. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/generation.py +18 -0
  16. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/handlers.py +10 -0
  17. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/arch.py +12 -0
  18. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/h3.py +19 -0
  19. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/models.py +9 -6
  20. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/trainer.py +45 -6
  21. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_lora.py +43 -0
  22. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_load.py +148 -0
  23. inline_core-1.2.67/tests/test_minimaxh3_lora_keys.py +249 -0
  24. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_nodes.py +243 -8
  25. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_training.py +12 -0
  26. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_fal.py +16 -0
  27. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_generation.py +40 -0
  28. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_models.py +29 -4
  29. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_webui_install.py +40 -2
  30. {inline_core-1.2.65 → inline_core-1.2.67}/uv.lock +5 -3
  31. {inline_core-1.2.65 → inline_core-1.2.67}/webui.bat +11 -18
  32. {inline_core-1.2.65 → inline_core-1.2.67}/webui.sh +10 -23
  33. {inline_core-1.2.65 → inline_core-1.2.67}/.gitignore +0 -0
  34. {inline_core-1.2.65 → inline_core-1.2.67}/.python-version +0 -0
  35. {inline_core-1.2.65 → inline_core-1.2.67}/README.md +0 -0
  36. {inline_core-1.2.65 → inline_core-1.2.67}/main.py +0 -0
  37. {inline_core-1.2.65 → inline_core-1.2.67}/scripts/flux2_train_matrix.py +0 -0
  38. {inline_core-1.2.65 → inline_core-1.2.67}/scripts/minimax_h3_lora_check.py +0 -0
  39. {inline_core-1.2.65 → inline_core-1.2.67}/scripts/minimax_h3_train_matrix.py +0 -0
  40. {inline_core-1.2.65 → inline_core-1.2.67}/scripts/reference.py +0 -0
  41. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/__init__.py +0 -0
  42. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/components/__init__.py +0 -0
  43. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/components/conditioning.py +0 -0
  44. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/components/interfaces.py +0 -0
  45. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/config.py +0 -0
  46. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/__init__.py +0 -0
  47. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/auto.py +0 -0
  48. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/detect.py +0 -0
  49. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/memory.py +0 -0
  50. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/policy.py +0 -0
  51. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/probe.py +0 -0
  52. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/types.py +0 -0
  53. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/errors.py +0 -0
  54. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/__init__.py +0 -0
  55. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/api.py +0 -0
  56. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/constraints.py +0 -0
  57. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/fetch.py +0 -0
  58. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/handlers.py +0 -0
  59. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/importer.py +0 -0
  60. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/install.py +0 -0
  61. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/loader.py +0 -0
  62. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/manifest.py +0 -0
  63. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/models.py +0 -0
  64. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/paths.py +0 -0
  65. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/resolve.py +0 -0
  66. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/scanner.py +0 -0
  67. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/state.py +0 -0
  68. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/tools.py +0 -0
  69. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/ffmpeg.py +0 -0
  70. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/__init__.py +0 -0
  71. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/cache.py +0 -0
  72. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/descriptor.py +0 -0
  73. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/executor.py +0 -0
  74. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/loader_runners.py +0 -0
  75. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/primitives.py +0 -0
  76. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/registry.py +0 -0
  77. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/runners.py +0 -0
  78. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/schema.py +0 -0
  79. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/topo.py +0 -0
  80. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/validate.py +0 -0
  81. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/media.py +0 -0
  82. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/__init__.py +0 -0
  83. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/catalog.py +0 -0
  84. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/checkpoint.py +0 -0
  85. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/controlspace.py +0 -0
  86. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/__init__.py +0 -0
  87. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/controlnet.py +0 -0
  88. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/embeds.py +0 -0
  89. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/provider.py +0 -0
  90. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/requirements.py +0 -0
  91. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/runner.py +0 -0
  92. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/variants.py +0 -0
  93. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/__init__.py +0 -0
  94. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/convert.py +0 -0
  95. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/depth_control.py +0 -0
  96. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/img2img.py +0 -0
  97. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/provider.py +0 -0
  98. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/requirements.py +0 -0
  99. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/runner.py +0 -0
  100. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/loaders.py +0 -0
  101. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/__init__.py +0 -0
  102. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/provider.py +0 -0
  103. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vae_keys.py +0 -0
  104. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/__init__.py +0 -0
  105. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/autoencoder_kl_minimax_h3.py +0 -0
  106. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/autoencoder_kl_minimax_h3_audio.py +0 -0
  107. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/before_denoise.py +0 -0
  108. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/before_encoder.py +0 -0
  109. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/decoders.py +0 -0
  110. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/denoise.py +0 -0
  111. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/encoders.py +0 -0
  112. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/modular_blocks_minimax_h3.py +0 -0
  113. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/modular_pipeline.py +0 -0
  114. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/packing.py +0 -0
  115. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/packing_ref2va.py +0 -0
  116. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/scheduling_minimax_h3.py +0 -0
  117. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/transformer_minimax_h3.py +0 -0
  118. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/offload.py +0 -0
  119. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/prepared.py +0 -0
  120. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/preprocess/__init__.py +0 -0
  121. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/preprocess/requirements.py +0 -0
  122. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/preprocess/runner.py +0 -0
  123. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/references.py +0 -0
  124. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/requirements.py +0 -0
  125. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/sampling.py +0 -0
  126. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/video_params.py +0 -0
  127. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/__init__.py +0 -0
  128. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/primitives.py +0 -0
  129. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/provider.py +0 -0
  130. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/requirements.py +0 -0
  131. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/runner.py +0 -0
  132. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/__init__.py +0 -0
  133. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/config.py +0 -0
  134. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/group.py +0 -0
  135. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/launch.py +0 -0
  136. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/protocol.py +0 -0
  137. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/registry.py +0 -0
  138. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/worker.py +0 -0
  139. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/__init__.py +0 -0
  140. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/context.py +0 -0
  141. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/file_store.py +0 -0
  142. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/progress.py +0 -0
  143. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/run.py +0 -0
  144. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/store.py +0 -0
  145. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/video_encode.py +0 -0
  146. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/sampling/__init__.py +0 -0
  147. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/sampling/batch.py +0 -0
  148. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/__init__.py +0 -0
  149. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/__main__.py +0 -0
  150. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/app.py +0 -0
  151. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/assets.py +0 -0
  152. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/bootstrap.py +0 -0
  153. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/frontend.py +0 -0
  154. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/manager.py +0 -0
  155. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/rpc.py +0 -0
  156. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/run_store.py +0 -0
  157. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/serialize.py +0 -0
  158. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/__init__.py +0 -0
  159. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/assets.py +0 -0
  160. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/config.py +0 -0
  161. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/frames.py +0 -0
  162. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/graph_build.py +0 -0
  163. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/image_meta.py +0 -0
  164. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/models.py +0 -0
  165. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/moodboard.py +0 -0
  166. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/peaks.py +0 -0
  167. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/recipe.py +0 -0
  168. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/schema.py +0 -0
  169. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/store.py +0 -0
  170. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/system_stats.py +0 -0
  171. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/__init__.py +0 -0
  172. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/compose.py +0 -0
  173. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/ffmpeg.py +0 -0
  174. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/render.py +0 -0
  175. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/resolve.py +0 -0
  176. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/training.py +0 -0
  177. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/training_store.py +0 -0
  178. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/takes.py +0 -0
  179. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/__init__.py +0 -0
  180. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/__main__.py +0 -0
  181. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/cache.py +0 -0
  182. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/caption.py +0 -0
  183. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/dataset.py +0 -0
  184. {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/protocol.py +0 -0
  185. {inline_core-1.2.65 → inline_core-1.2.67}/tests/conftest.py +0 -0
  186. {inline_core-1.2.65 → inline_core-1.2.67}/tests/helpers.py +0 -0
  187. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_cache.py +0 -0
  188. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_catalog.py +0 -0
  189. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_checkpoint.py +0 -0
  190. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_config.py +0 -0
  191. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_device_detect.py +0 -0
  192. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_executor.py +0 -0
  193. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_api.py +0 -0
  194. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_install.py +0 -0
  195. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_manifest.py +0 -0
  196. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_resolve.py +0 -0
  197. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_scanner.py +0 -0
  198. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_spine.py +0 -0
  199. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_state.py +0 -0
  200. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_file_store.py +0 -0
  201. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_controlnet.py +0 -0
  202. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_folder.py +0 -0
  203. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_resolve.py +0 -0
  204. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_runner.py +0 -0
  205. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_training.py +0 -0
  206. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_variants.py +0 -0
  207. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_frontend_serving.py +0 -0
  208. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_hidden_nodes.py +0 -0
  209. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_keymap.py +0 -0
  210. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_convert.py +0 -0
  211. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_depth_control.py +0 -0
  212. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_requirements.py +0 -0
  213. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_runner.py +0 -0
  214. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_loader_runners.py +0 -0
  215. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_loaders.py +0 -0
  216. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_lora_download.py +0 -0
  217. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_memory_policy.py +0 -0
  218. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_adaln.py +0 -0
  219. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_keys.py +0 -0
  220. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_model_requirements.py +0 -0
  221. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_offload_prepared.py +0 -0
  222. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_output_kind_contract.py +0 -0
  223. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_parallel_group.py +0 -0
  224. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_pipeline_cache.py +0 -0
  225. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_primitives.py +0 -0
  226. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_recipe.py +0 -0
  227. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_references.py +0 -0
  228. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_rpc_bridge.py +0 -0
  229. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_run_store.py +0 -0
  230. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_sampling.py +0 -0
  231. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_schema.py +0 -0
  232. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_server.py +0 -0
  233. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_staged_residency.py +0 -0
  234. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_assets.py +0 -0
  235. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_frames.py +0 -0
  236. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_graph_build.py +0 -0
  237. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_models.py +0 -0
  238. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_moodboard.py +0 -0
  239. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_multi_reference.py +0 -0
  240. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_node_size.py +0 -0
  241. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_peaks.py +0 -0
  242. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_rpc.py +0 -0
  243. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_schema.py +0 -0
  244. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_store.py +0 -0
  245. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_timeline.py +0 -0
  246. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_training.py +0 -0
  247. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_take_bytes.py +0 -0
  248. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_topo.py +0 -0
  249. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_arch.py +0 -0
  250. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_dataset.py +0 -0
  251. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_resolve.py +0 -0
  252. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_validate.py +0 -0
  253. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_video_encode.py +0 -0
  254. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_video_params.py +0 -0
  255. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_xfuser_sampler.py +0 -0
  256. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_zimage_primitives.py +0 -0
  257. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_zimage_resolve.py +0 -0
  258. {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_zimage_runner.py +0 -0
@@ -126,6 +126,8 @@ between nodes and are never takes.
126
126
  every stop/start**, taking the models and leaving dangling symlinks in `models/`. This has cost two
127
127
  full re-downloads of MiniMax H3 at ~130 GB each. Weights go on the persistent root volume, or on an
128
128
  attached volume that survives a restart. Scratch is fine for logs and temporary output only.
129
+ - **`huggingface_hub>=0.32`** - below it `hf_xet` is only an extra, and files over ~50GB (every
130
+ H3 transformer) refuse to download at all.
129
131
  - **Models root** - `INLINE_MODELS_DIR`, else `./models`. **Bring your own weights; nothing is
130
132
  downloaded.** ComfyUI-style category subfolders (`diffusion_models/`, `vae/`, `text_encoders/`,
131
133
  `loras/`, `controlnet/`, `checkpoints/`, `clip_vision/`, `upscale_models/`, `embeddings/`). The
@@ -161,6 +163,17 @@ between nodes and are never takes.
161
163
  → sequential offload → wont-fit. NF4 is what makes a 32B model viable on a 24 GB card. Note int8
162
164
  forces bf16 (torchao's weight-only int8 silently no-ops under fp16) while NF4 does not, so a Turing
163
165
  card keeps its fp16 tensor cores under NF4.
166
+ - **A pre-reduced checkpoint must not be re-reduced, structurally or numerically.** MiniMax H3's
167
+ `pruned` builds ship the AdaLN branch already factorised to rank 8 and drop the timestep path
168
+ entirely, so re-running our factorisation multiplies a `[96768, 8]` projection by a full-width
169
+ basis. `minimaxh3/pipeline.py` turns `factorise_adaln` off for those, the same way the rule below
170
+ turns quantization off for a prequantized file. Both are the same rule: the source is already in
171
+ the target form.
172
+ - **Size a checkpoint by what it becomes, not by what it weighs.** A pruned file has already lost
173
+ its AdaLN branch and an fp8 file stores half the bytes it will occupy once dequantised, so scaling
174
+ the on-disk number under-sizes both, by up to 3x. `minimaxh3.requirements.resident_bytes` counts
175
+ from the header. Under-sizing is the dangerous direction: the fit ladder then promises a machine
176
+ that dies to a host-RAM OOM kill instead of raising.
164
177
  - **Prequantized checkpoints must not be re-quantized.** A checkpoint that ships already quantized
165
178
  (`flux2/variants.is_prequantized`) has an on-disk size that already _is_ its resident size, so the
166
179
  ladder's assumption that quantization halves it does not hold, and handing diffusers a second,
@@ -228,11 +241,35 @@ real codec that moves tensors lives with the model runner.
228
241
  Don't scatter it.
229
242
  - **Bring-your-own models.** Nothing is downloaded by the engine. The catalog scans; the user places
230
243
  files. A model picker is a `SELECT` param with `options_from="<category>"`.
244
+ - **Adapter strength is not a quality metric, and a threshold on it is a false-positive machine.**
245
+ Measured against real bases, published LoRAs that work well span `|B@A| / |W|` from 0.017%
246
+ (a style LoRA) to 1.2% (a restoration LoRA), so "this adapter looks weak" is not a finding. What
247
+ predicts a LoRA doing nothing is whether its per-weight change clears one quantization step. Warn
248
+ on that, and only when the base is actually quantized.
249
+ - **Patching a diffusers object may patch a copy, and it will not tell you.**
250
+ `ModularPipeline.blocks` is a property returning `deepcopy(self._blocks)`, so hooking the block
251
+ graph through it installs cleanly onto a throwaway and reports success. That is how H3's denoise
252
+ ran with no per-step progress while the hook said it was attached. Reach for the backing
253
+ attribute, and prove a hook fires against the real object rather than trusting a return value.
231
254
  - **Verify image models by rendering.** The FLUX.2 work shipped five bugs past a green test suite,
232
255
  and every one produced a _wrong image rather than an error_: a mis-keyed checkpoint, a
233
256
  vision-language encoder loaded in place of a text one, an unnormalized latent, and a control context
234
257
  cast to a quantized weight's `uint8` storage dtype. A unit test cannot see a plausible-but-wrong
235
258
  image. Render something and look at it.
259
+ - **Launchers are twins.** `webui.sh` and `webui.bat` change together; only the `launcher` CI job
260
+ can prove the `.bat`, since it cannot run on a dev box.
261
+ - **Match CUDA arches by within-major compatibility, never exactly.** An `sm_8x` cubin runs on any
262
+ `sm_8y` where `y >= x`, so `sm_86` covers Ada's `sm_89`.
263
+ - **Pass the widest `uv` flag that works.** `--no-sources-package` needs uv 0.10+; `--no-sources`
264
+ works back to 0.4 and means the same while torch is the only `[tool.uv.sources]` entry.
265
+ - **An explicit user setting beats a heuristic.** Test `on`/`off` before any auto rule, or the
266
+ control is silently dead for whichever arch the rule excludes.
267
+ - **Report a phase before the slow work, not after.** Progress emitted only on completion makes a
268
+ slow step look like a hung previous phase.
269
+ - **In a `.bat`, `call` anything that might be a `.bat`.** `nvidia-smi` is sometimes a shim, and
270
+ without `call` it takes over the script and never returns.
271
+ - **Log allocated AND reserved VRAM.** `nvidia-smi` shows only reserved, so allocator cache and a
272
+ leaked reference look identical from outside.
236
273
  - **Tests (pytest).** Cover the logic that matters: graph validate/topo/executor/cache, the catalog
237
274
  scan, the run store + server contract, the device/memory policy, the parallel group + xfuser seam,
238
275
  and each model runner (import-guarded, no GPU needed). See `tests/`.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: inline-core
3
- Version: 1.2.65
3
+ Version: 1.2.67
4
4
  Summary: The generation engine behind Inline Studio.
5
5
  License-Expression: GPL-3.0-or-later
6
6
  Requires-Python: >=3.11
@@ -14,7 +14,7 @@ Requires-Dist: controlnet-aux>=0.0.7; extra == 'all'
14
14
  Requires-Dist: diffusers==0.39.0; extra == 'all'
15
15
  Requires-Dist: einops>=0.7; extra == 'all'
16
16
  Requires-Dist: fastapi>=0.110; extra == 'all'
17
- Requires-Dist: huggingface-hub>=0.23; extra == 'all'
17
+ Requires-Dist: huggingface-hub>=0.32; extra == 'all'
18
18
  Requires-Dist: imageio-ffmpeg>=0.4; extra == 'all'
19
19
  Requires-Dist: nvidia-ml-py>=12; extra == 'all'
20
20
  Requires-Dist: onnxruntime>=1.17; extra == 'all'
@@ -41,7 +41,7 @@ Requires-Dist: accelerate>=0.30; extra == 'runtime'
41
41
  Requires-Dist: av>=12; extra == 'runtime'
42
42
  Requires-Dist: controlnet-aux>=0.0.7; extra == 'runtime'
43
43
  Requires-Dist: diffusers==0.39.0; extra == 'runtime'
44
- Requires-Dist: huggingface-hub>=0.23; extra == 'runtime'
44
+ Requires-Dist: huggingface-hub>=0.32; extra == 'runtime'
45
45
  Requires-Dist: onnxruntime>=1.17; extra == 'runtime'
46
46
  Requires-Dist: safetensors>=0.4; extra == 'runtime'
47
47
  Requires-Dist: scipy>=1.11; extra == 'runtime'
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  # PyPI name; the import package is `inline_core` (src/inline_core).
3
3
  name = "inline-core"
4
- version = "1.2.65"
4
+ version = "1.2.67"
5
5
  description = "The generation engine behind Inline Studio."
6
6
  readme = "README.md"
7
7
  license = "GPL-3.0-or-later"
@@ -29,7 +29,10 @@ runtime = [
29
29
  # Backs the Beta sigma schedule (diffusers gates use_beta_sigmas on scipy).
30
30
  "scipy>=1.11",
31
31
  # We call snapshot_download directly for the model popup, so pin it rather than rely on transit.
32
- "huggingface_hub>=0.23",
32
+ # 0.32 is where hf_xet became a hard dependency rather than an extra. Below it, Hugging Face
33
+ # refuses the largest files outright ("too large to be downloaded using the regular download
34
+ # method"), which is every H3 transformer at 66GB.
35
+ "huggingface_hub>=0.32",
33
36
  # ControlNet preprocessors (the Apply ControlNet node): OpenPose/DWPose, MiDaS/Zoe depth, canny,
34
37
  # HED, lineart, MLSD, scribble, normal. DWPose runs its detector on ONNX Runtime.
35
38
  "controlnet-aux>=0.0.7",
@@ -85,7 +88,10 @@ all = [
85
88
  # Clip decode for MiniMax H3 LoRA training, and H3's reference node.
86
89
  "av>=12",
87
90
  "scipy>=1.11",
88
- "huggingface_hub>=0.23",
91
+ # 0.32 is where hf_xet became a hard dependency rather than an extra. Below it, Hugging Face
92
+ # refuses the largest files outright ("too large to be downloaded using the regular download
93
+ # method"), which is every H3 transformer at 66GB.
94
+ "huggingface_hub>=0.32",
89
95
  "controlnet-aux>=0.0.7",
90
96
  "onnxruntime>=1.17",
91
97
  # server
@@ -111,8 +117,10 @@ name = "pytorch-cu126"
111
117
  url = "https://download.pytorch.org/whl/cu126"
112
118
  explicit = true
113
119
 
114
- # Launcher installs pass `--no-sources-package torch` so their detected index wins over this pin;
115
- # anything else added to this table would still apply to them.
120
+ # Launcher installs pass `--no-sources` so their detected index wins over this pin. That flag is
121
+ # broad rather than per-package because the per-package form is too new for the uv versions people
122
+ # have. torch being the ONLY entry here is what makes that equivalent: adding another would silently
123
+ # start ignoring it on launcher installs too.
116
124
  [tool.uv.sources]
117
125
  torch = [{ index = "pytorch-cu126", marker = "sys_platform == 'win32'" }]
118
126
 
@@ -262,7 +262,7 @@ def transform(
262
262
  raise ComponentError(f"{key} has {tensor.shape[0]} rows, not divisible into {parts} parts.")
263
263
  if verify_layout:
264
264
  assert_layout(tensor, action, key=key)
265
- source = _deinterleave(tensor, parts, action.head_dim) if (
265
+ source = deinterleave_rows(tensor, parts, action.head_dim) if (
266
266
  action.layout is RowLayout.INTERLEAVED
267
267
  ) else tensor
268
268
  block = source.shape[0] // parts
@@ -270,16 +270,31 @@ def transform(
270
270
  yield target, source[index * block : (index + 1) * block]
271
271
 
272
272
 
273
- def _deinterleave(tensor: Any, parts: int, head_dim: int) -> Any:
274
- """``[p0_h0; p1_h0; p2_h0][p0_h1; …]`` to ``[p0_all; p1_all; p2_all]``.
275
-
276
- ``transpose`` is not the same call in torch and numpy - torch swaps two axes, numpy wants a full
277
- permutation - so the swap is spelled per backend rather than duck-typed.
278
- """
273
+ def deinterleave_rows(tensor: Any, parts: int, head_dim: int) -> Any:
274
+ """``[p0_h0; p1_h0; p2_h0][p0_h1; …]`` to ``[p0_all; p1_all; p2_all]``."""
279
275
  if head_dim < 1:
280
276
  raise ComponentError("De-interleaving needs the head dimension the parts are grouped by.")
281
277
  heads = tensor.shape[0] // (parts * head_dim)
282
- reshaped = tensor.reshape(heads, parts, head_dim, *tensor.shape[1:])
278
+ return _swap01(tensor, (heads, parts, head_dim))
279
+
280
+
281
+ def interleave_rows(tensor: Any, parts: int, head_dim: int) -> Any:
282
+ """``[p0_all; p1_all; p2_all]`` back to per-head groups: the inverse of ``deinterleave_rows``.
283
+
284
+ Needed to *write* a checkpoint or adapter in a publisher's interleaved layout, where the load
285
+ path only ever reads one."""
286
+ if head_dim < 1:
287
+ raise ComponentError("Interleaving needs the head dimension the parts are grouped by.")
288
+ heads = tensor.shape[0] // (parts * head_dim)
289
+ return _swap01(tensor, (parts, heads, head_dim))
290
+
291
+
292
+ def _swap01(tensor: Any, shape: tuple[int, int, int]) -> Any:
293
+ """Reshape to ``shape`` plus the trailing dims, exchange the first two, flatten back.
294
+
295
+ ``transpose`` is not the same call in torch and numpy - torch swaps two axes, numpy wants a full
296
+ permutation - so the swap is spelled per backend rather than duck-typed."""
297
+ reshaped = tensor.reshape(*shape, *tensor.shape[1:])
283
298
  if _is_torch(tensor):
284
299
  moved = reshaped.transpose(0, 1).contiguous()
285
300
  else:
@@ -29,6 +29,10 @@ if TYPE_CHECKING:
29
29
  #: while ostris' training adapter uses the reference names.
30
30
  Alias = Callable[[str], str | None]
31
31
 
32
+ #: Rewrites a whole adapter before it is matched, for an arch whose checkpoint keys need more than a
33
+ #: rename - MiniMax H3 ships attention fused, so three of our modules are one of theirs.
34
+ Translate = Callable[[dict[str, Any]], dict[str, Any]]
35
+
32
36
  _DOWN = ("lora_down.weight", "lora_A.weight", "lora_A.default.weight")
33
37
  _UP = ("lora_up.weight", "lora_B.weight", "lora_B.default.weight")
34
38
  # Prefixes checkpoints put in front of the module path; stripped when matching against the model.
@@ -39,12 +43,22 @@ _PREFIXES = ("diffusion_model.", "transformer.", "lora_unet_", "lora_te_", "base
39
43
  LoraPlan = dict[str, list[tuple[Any, Any, float]]]
40
44
 
41
45
 
42
- def fuse_loras(model: Any, loras: tuple[LoraRef, ...], alias: Alias | None = None) -> None:
46
+ def fuse_loras(
47
+ model: Any,
48
+ loras: tuple[LoraRef, ...],
49
+ alias: Alias | None = None,
50
+ translate: Translate | None = None,
51
+ ) -> None:
43
52
  """Merge each LoRA into ``model``'s weights in order. No-op for an empty stack."""
44
- apply_plan(model, plan_loras(model, loras, alias))
53
+ apply_plan(model, plan_loras(model, loras, alias, translate))
45
54
 
46
55
 
47
- def plan_loras(model: Any, loras: tuple[LoraRef, ...], alias: Alias | None = None) -> LoraPlan:
56
+ def plan_loras(
57
+ model: Any,
58
+ loras: tuple[LoraRef, ...],
59
+ alias: Alias | None = None,
60
+ translate: Translate | None = None,
61
+ ) -> LoraPlan:
48
62
  """Resolve every LoRA against ``model``'s module names, without touching any weights.
49
63
 
50
64
  Split from the fusing so a streaming loader can validate the whole stack **before** reading a
@@ -53,7 +67,7 @@ def plan_loras(model: Any, loras: tuple[LoraRef, ...], alias: Alias | None = Non
53
67
  plan: LoraPlan = {}
54
68
  names = _linear_module_names(model)
55
69
  for lora in loras:
56
- _plan_one(plan, names, lora.file, lora.strength, alias)
70
+ _plan_one(plan, names, lora.file, lora.strength, alias, translate)
57
71
  return plan
58
72
 
59
73
 
@@ -74,7 +88,12 @@ def apply_plan(module: Any, plan: LoraPlan, prefix: str = "") -> None:
74
88
 
75
89
 
76
90
  def _plan_one(
77
- plan: LoraPlan, names: dict[str, None], path: str, strength: float, alias: Alias | None
91
+ plan: LoraPlan,
92
+ names: dict[str, None],
93
+ path: str,
94
+ strength: float,
95
+ alias: Alias | None,
96
+ translate: Translate | None = None,
78
97
  ) -> None:
79
98
  from safetensors.torch import load_file
80
99
 
@@ -83,6 +102,8 @@ def _plan_one(
83
102
  except Exception as exc: # noqa: BLE001
84
103
  raise ComponentError(f"Could not read LoRA {path!r}: {exc}") from exc
85
104
 
105
+ if translate is not None:
106
+ state = translate(state)
86
107
  pairs, alphas = _group(state)
87
108
  if not pairs:
88
109
  raise ComponentError(f"LoRA {path!r} contains no recognisable lora_down/lora_up pairs.")
@@ -104,27 +125,39 @@ def _plan_one(
104
125
  )
105
126
 
106
127
 
128
+ #: Most fp32 delta held at once. The product is computed a slice of output rows at a time because
129
+ #: the whole of it is enormous: one MiniMax H3 block's six Linears come to 1.5GB and the model to
130
+ #: 80GB, which is host RAM during a staged load and took a 60GB box down three times.
131
+ _DELTA_CHUNK_BYTES = 64 * 1024 * 1024
132
+
133
+
107
134
  def _add_delta(weight: Any, up: Any, down: Any, scale: float) -> None:
108
135
  """Fuse ``scale * (up @ down)`` into ``weight`` in place.
109
136
 
110
- Computed on the weight's own device: a big LoRA (Krea 2's are ~260 modules on a 12.9B model)
111
- materializes tens of GB of fp32 deltas, and doing that on the CPU costs ~20s of maths plus the
112
- transfer where the GPU takes ~2s. Falls back to the CPU if the device runs out of memory, so a
113
- tight card still fuses, just slowly."""
137
+ Computed on the weight's own device: doing it on the CPU costs ~20s of maths plus the transfer
138
+ where the GPU takes ~2s. Falls back to the CPU if the device runs out of memory, so a tight card
139
+ still fuses, just slowly."""
114
140
  import torch
115
141
 
116
142
  try:
117
- weight.add_(_delta(up, down, weight, weight.device) * scale)
143
+ _accumulate(weight, up, down, scale, weight.device)
118
144
  except torch.cuda.OutOfMemoryError:
119
- weight.add_((_delta(up, down, weight, "cpu") * scale).to(weight.device, weight.dtype))
145
+ _accumulate(weight, up, down, scale, "cpu")
120
146
 
121
147
 
122
- def _delta(up: Any, down: Any, weight: Any, device: Any) -> Any:
123
- """``up @ down`` on ``device``, shaped and typed for the target weight. Conv LoRAs flatten the
124
- spatial dims."""
148
+ def _accumulate(weight: Any, up: Any, down: Any, scale: float, device: Any) -> None:
149
+ """Add the product into ``weight`` in row slices. Conv LoRAs flatten the spatial dims."""
125
150
  dtype = _fuse_dtype(up)
126
- delta = up.to(device, dtype=dtype).flatten(1) @ down.to(device, dtype=dtype).flatten(1)
127
- return delta.reshape(weight.shape).to(weight.dtype)
151
+ rows = up.to(device, dtype=dtype).flatten(1)
152
+ cols = down.to(device, dtype=dtype).flatten(1)
153
+ if not weight.is_contiguous(): # a non-contiguous target cannot be written through a view
154
+ weight.add_((rows @ cols).reshape(weight.shape).to(weight.dtype) * scale)
155
+ return
156
+ target = weight.view(rows.shape[0], -1)
157
+ step = max(1, _DELTA_CHUNK_BYTES // max(1, cols.shape[1] * 4))
158
+ for start in range(0, rows.shape[0], step):
159
+ stop = start + step
160
+ target[start:stop].add_((rows[start:stop] @ cols).to(weight.dtype) * scale)
128
161
 
129
162
 
130
163
  def _fuse_dtype(tensor: Any) -> Any:
@@ -141,20 +174,29 @@ def _alpha_scale(alpha: Any, rank: int) -> float:
141
174
  return float(alpha.item() if hasattr(alpha, "item") else alpha) / float(rank)
142
175
 
143
176
 
177
+ def split_key(key: str) -> tuple[str, str] | None:
178
+ """``…to_q.lora_A.weight`` to ``("…to_q", "down")``. None for anything that is not a LoRA key.
179
+
180
+ Public so a per-arch key translator groups by exactly the suffixes the fuser recognises: a
181
+ convention known to one and not the other would drop tensors silently."""
182
+ for suffix in _DOWN:
183
+ if key.endswith("." + suffix):
184
+ return key[: -len(suffix) - 1], "down"
185
+ for suffix in _UP:
186
+ if key.endswith("." + suffix):
187
+ return key[: -len(suffix) - 1], "up"
188
+ if key.endswith(".alpha"):
189
+ return key[: -len(".alpha")], "alpha"
190
+ return None
191
+
192
+
144
193
  def _group(state: dict[str, Any]) -> tuple[dict[str, tuple[Any, Any]], dict[str, Any]]:
145
- downs: dict[str, Any] = {}
146
- ups: dict[str, Any] = {}
147
- alphas: dict[str, Any] = {}
194
+ parts: dict[str, dict[str, Any]] = {}
148
195
  for key, value in state.items():
149
- for suffix in _DOWN:
150
- if key.endswith("." + suffix):
151
- downs[key[: -len(suffix) - 1]] = value
152
- for suffix in _UP:
153
- if key.endswith("." + suffix):
154
- ups[key[: -len(suffix) - 1]] = value
155
- if key.endswith(".alpha"):
156
- alphas[key[: -len(".alpha")]] = value
157
- return {k: (downs[k], ups[k]) for k in downs if k in ups}, alphas
196
+ if (split := split_key(key)) is not None:
197
+ parts.setdefault(split[0], {})[split[1]] = value
198
+ pairs = {k: (v["down"], v["up"]) for k, v in parts.items() if "down" in v and "up" in v}
199
+ return pairs, {k: v["alpha"] for k, v in parts.items() if "alpha" in v}
158
200
 
159
201
 
160
202
  def _linear_module_names(model: Any) -> dict[str, None]:
@@ -41,6 +41,11 @@ from typing import Any
41
41
  import torch
42
42
  from torch import nn
43
43
 
44
+ from .vendor.transformer_minimax_h3 import (
45
+ MiniMaxH3AdaLayerNormModulation,
46
+ MiniMaxH3AdaLayerNormOut,
47
+ )
48
+
44
49
  logger = logging.getLogger("inline_core.minimaxh3")
45
50
 
46
51
  #: Rank kept. Five directions carry the energy; eight matches the published build and leaves slack.
@@ -144,3 +149,100 @@ def factorise(model: Any, *, rank: int = RANK) -> int:
144
149
  len(model.transformer_blocks), saved / 1e9,
145
150
  )
146
151
  return saved
152
+
153
+
154
+ # --- the published pruned builds ------------------------------------------------------------
155
+ #
156
+ # MiniMax ship `pruned` checkpoints that do this same rank-8 reduction ahead of time, and go one
157
+ # step further: the timestep path itself is gone. There is no `time_embedder` in the file at all,
158
+ # only `adaln_t_table [1025, 8]`, holding `silu(temb)` already projected into their basis at 1025
159
+ # points across t in [0, 1]. So the branch cannot be rebuilt as a basis applied to a `silu(temb)` we
160
+ # compute; the table has to be read directly.
161
+ #
162
+ # Off-grid timesteps are interpolated. Measured against the full bf16 weights at 24 random t, the
163
+ # table reaches 1.636e-4 relative with linear interpolation and 1.874e-4 taking the nearest row,
164
+ # where one bf16 ulp of the reference is 1.307e-3. The grid is dense enough that interpolating costs
165
+ # nothing and removes the sampler constraint a lookup would otherwise impose.
166
+
167
+ #: Rows in the published table. Checked, not assumed: a build on a different grid must not be read
168
+ #: as though it were on this one.
169
+ TABLE_ROWS = 1025
170
+
171
+
172
+ class TableEmbedder(nn.Module):
173
+ """Stands in for ``time_proj`` + ``time_embedder``, returning the pruned build's rank-8 row.
174
+
175
+ ``linear_1`` exists because the port reads ``time_embedder.linear_1.weight.dtype`` to cast its
176
+ input; it carries the table's dtype and nothing else, which keeps ``vendor/`` verbatim.
177
+ """
178
+
179
+ #: Declared so the buffer reads as a tensor; ``register_buffer`` alone types as ``Module``.
180
+ table: torch.Tensor
181
+
182
+ def __init__(self, table: torch.Tensor) -> None:
183
+ super().__init__()
184
+ self.register_buffer("table", table, persistent=True)
185
+ self.linear_1 = nn.Linear(1, 1, bias=False, dtype=table.dtype)
186
+
187
+ def forward(self, timestep: torch.Tensor) -> torch.Tensor:
188
+ rows = self.table.shape[0]
189
+ position = timestep.to(self.table.dtype).flatten() * (rows - 1)
190
+ low = position.floor().long().clamp(0, rows - 2)
191
+ frac = (position - low).unsqueeze(1)
192
+ return self.table[low] * (1 - frac) + self.table[low + 1] * frac
193
+
194
+
195
+ class TabulatedModulation(MiniMaxH3AdaLayerNormModulation):
196
+ """``adaln_proj`` reading a table row. It already holds ``silu(temb)``, so no activation."""
197
+
198
+ def forward(self, temb: torch.Tensor) -> tuple[torch.Tensor, ...]:
199
+ out = self.linear(temb.to(self.linear.weight.dtype)).view(-1, 6 * self.hidden_size)
200
+ return out.chunk(6, dim=-1)
201
+
202
+
203
+ class TabulatedNormOut(MiniMaxH3AdaLayerNormOut):
204
+ """``norm_out`` reading a table row, otherwise the port's own forward."""
205
+
206
+ def forward(
207
+ self, hidden_states: torch.Tensor, temb: torch.Tensor, timestep_indices: torch.Tensor
208
+ ) -> torch.Tensor:
209
+ shift, scale = self.linear(temb.to(self.linear.weight.dtype)).chunk(2, dim=-1)
210
+ hidden_states = self.norm(hidden_states)
211
+ return hidden_states * (1.0 + scale.index_select(0, timestep_indices)) + shift.index_select(
212
+ 0, timestep_indices
213
+ )
214
+
215
+
216
+ @torch.no_grad()
217
+ def tabulate(model: Any, table: torch.Tensor) -> None:
218
+ """Rebuild the timestep path around a pruned build's table, on the meta device before streaming.
219
+
220
+ Every module the table feeds changes shape, so this has to happen before any weight is placed:
221
+ the rank-8 ``adaln_proj`` in the file would otherwise be assigned into a ``[96768, 2688]`` slot.
222
+ """
223
+ if table.ndim != 2 or table.shape[0] != TABLE_ROWS:
224
+ raise ValueError(
225
+ f"adaln_t_table is {tuple(table.shape)}, not [{TABLE_ROWS}, rank]. This build is on a "
226
+ "different timestep grid from the one measured, and reading it as if it were not would "
227
+ "shift the modulation at every step while still rendering."
228
+ )
229
+ rank = int(table.shape[1])
230
+ model.time_proj = nn.Identity()
231
+ model.time_embedder = TableEmbedder(table)
232
+ for block in model.transformer_blocks:
233
+ block.adaln_proj = _retyped(block.adaln_proj, TabulatedModulation, rank)
234
+ model.norm_out = _retyped(model.norm_out, TabulatedNormOut, rank)
235
+
236
+
237
+ def _retyped(module: Any, cls: type, rank: int) -> Any:
238
+ """The same module with its projection narrowed to ``rank`` inputs, still on meta."""
239
+ replacement = module
240
+ replacement.__class__ = cls
241
+ old = module.linear
242
+ with torch.device("meta"):
243
+ replacement.linear = nn.Linear(rank, old.out_features, bias=old.bias is not None)
244
+ return replacement
245
+
246
+
247
+ #: Parameters ``tabulate`` creates that no checkpoint fills.
248
+ TABULATED_SELF_COMPUTED = ("time_embedder.linear_1.weight", "time_embedder.table")
@@ -23,7 +23,7 @@ fused QKV into three.
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
- from ..keymap import AssertEqual, KeyPlan, Rename, RowLayout, Split, SwapHalves
26
+ from ..keymap import AssertEqual, Drop, KeyPlan, Rename, RowLayout, Split, SwapHalves
27
27
 
28
28
  #: Bumping this invalidates every prepared artifact built by the old plan.
29
29
  PLAN_VERSION = "minimax-h3.keys.1"
@@ -81,9 +81,17 @@ def build_plan(
81
81
  num_blocks: int = NUM_BLOCKS,
82
82
  num_refiner_blocks: int = NUM_REFINER_BLOCKS,
83
83
  head_dim: int = HEAD_DIM,
84
+ pruned: bool = False,
85
+ sidecars: tuple[str, ...] = (),
84
86
  ) -> KeyPlan:
85
87
  """The plan for a publisher's layout. ``source`` selects how the fused QKV rows are arranged.
86
88
 
89
+ ``pruned`` is the published rank-8 build: it ships ``adaln_t_table`` in place of the whole
90
+ timestep path, so the two ``time_embedder`` projections are simply not in the file.
91
+
92
+ ``sidecars`` are the quantisation tensors an fp8 build carries beside each weight. They are
93
+ consumed while streaming and dropped here, so the coverage check still accounts for every key.
94
+
87
95
  The counts are arguments so a round-trip test can exercise the same code at a size that fits in
88
96
  memory; the defaults are the released geometry.
89
97
  """
@@ -95,7 +103,16 @@ def build_plan(
95
103
  ) from None
96
104
 
97
105
  actions: dict[str, object] = {}
98
- for stem, target in _TOP_LEVEL.items():
106
+ top_level = {
107
+ stem: target
108
+ for stem, target in _TOP_LEVEL.items()
109
+ if not (pruned and stem.startswith("time_embedder."))
110
+ }
111
+ if pruned:
112
+ actions["adaln_t_table"] = Drop("read before streaming, to rebuild the timestep path")
113
+ for key in sidecars:
114
+ actions[key] = Drop("a quantisation scale, applied to its weight while streaming")
115
+ for stem, target in top_level.items():
99
116
  for suffix in ("weight", "bias"):
100
117
  actions[f"{stem}.{suffix}"] = Rename(f"{target}.{suffix}")
101
118
  for key, target in _WEIGHT_ONLY.items():
@@ -126,9 +143,16 @@ def build_plan(
126
143
  continue
127
144
  actions[f"{src}.{stem}"] = Rename(f"{dst}.{target}")
128
145
 
129
- return KeyPlan(version=f"{PLAN_VERSION}+{source}", actions=actions) # type: ignore[arg-type]
146
+ suffix = source + ("+pruned" if pruned else "") + ("+fp8" if sidecars else "")
147
+ return KeyPlan(version=f"{PLAN_VERSION}+{suffix}", actions=actions) # type: ignore[arg-type]
130
148
 
131
149
 
132
- def self_computed_targets() -> set[str]:
150
+ def self_computed_targets(*, pruned: bool = False) -> set[str]:
133
151
  """Targets the port builds itself, which ``check_coverage`` must not demand be filled."""
134
- return {"rope.inv_freq"}
152
+ if not pruned:
153
+ return {"rope.inv_freq"}
154
+ return {"rope.inv_freq", *_PRUNED_SELF_COMPUTED}
155
+
156
+
157
+ #: What ``adaln.tabulate`` creates in place of the timestep path. No checkpoint fills these.
158
+ _PRUNED_SELF_COMPUTED = ("time_embedder.table", "time_embedder.linear_1.weight")