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.
- {inline_core-1.2.65 → inline_core-1.2.67}/CLAUDE.md +37 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/PKG-INFO +4 -4
- {inline_core-1.2.65 → inline_core-1.2.67}/pyproject.toml +13 -5
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/keymap.py +23 -8
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/lora.py +70 -28
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/adaln.py +102 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/keys.py +29 -5
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/load.py +107 -14
- inline_core-1.2.67/src/inline_core/models/minimaxh3/lora_keys.py +235 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/pipeline.py +9 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/requirements.py +83 -13
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/runner.py +14 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/pipeline_runtime.py +76 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/fal.py +18 -8
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/generation.py +18 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/handlers.py +10 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/arch.py +12 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/h3.py +19 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/models.py +9 -6
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/trainer.py +45 -6
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_lora.py +43 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_load.py +148 -0
- inline_core-1.2.67/tests/test_minimaxh3_lora_keys.py +249 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_nodes.py +243 -8
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_training.py +12 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_fal.py +16 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_generation.py +40 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_models.py +29 -4
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_webui_install.py +40 -2
- {inline_core-1.2.65 → inline_core-1.2.67}/uv.lock +5 -3
- {inline_core-1.2.65 → inline_core-1.2.67}/webui.bat +11 -18
- {inline_core-1.2.65 → inline_core-1.2.67}/webui.sh +10 -23
- {inline_core-1.2.65 → inline_core-1.2.67}/.gitignore +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/.python-version +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/README.md +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/main.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/scripts/flux2_train_matrix.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/scripts/minimax_h3_lora_check.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/scripts/minimax_h3_train_matrix.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/scripts/reference.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/components/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/components/conditioning.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/components/interfaces.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/config.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/auto.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/detect.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/memory.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/policy.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/probe.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/device/types.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/errors.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/api.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/constraints.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/fetch.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/handlers.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/importer.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/install.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/loader.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/manifest.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/models.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/paths.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/resolve.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/scanner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/state.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/extensions/tools.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/ffmpeg.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/cache.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/descriptor.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/executor.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/loader_runners.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/primitives.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/registry.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/runners.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/schema.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/topo.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/graph/validate.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/media.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/catalog.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/checkpoint.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/controlspace.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/controlnet.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/embeds.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/provider.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/requirements.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/runner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/flux2/variants.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/convert.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/depth_control.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/img2img.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/provider.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/requirements.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/krea2/runner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/loaders.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/provider.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vae_keys.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/autoencoder_kl_minimax_h3.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/autoencoder_kl_minimax_h3_audio.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/before_denoise.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/before_encoder.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/decoders.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/denoise.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/encoders.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/modular_blocks_minimax_h3.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/modular_pipeline.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/packing.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/packing_ref2va.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/scheduling_minimax_h3.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/minimaxh3/vendor/transformer_minimax_h3.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/offload.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/prepared.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/preprocess/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/preprocess/requirements.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/preprocess/runner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/references.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/requirements.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/sampling.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/video_params.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/primitives.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/provider.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/requirements.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/models/zimage/runner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/config.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/group.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/launch.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/protocol.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/registry.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/parallel/worker.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/context.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/file_store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/progress.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/run.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/runtime/video_encode.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/sampling/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/sampling/batch.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/__main__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/app.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/assets.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/bootstrap.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/frontend.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/manager.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/rpc.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/run_store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/server/serialize.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/assets.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/config.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/frames.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/graph_build.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/image_meta.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/models.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/moodboard.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/peaks.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/recipe.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/schema.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/system_stats.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/compose.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/ffmpeg.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/render.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/timeline/resolve.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/training.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/studio/training_store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/takes.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/__init__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/__main__.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/cache.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/caption.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/dataset.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/src/inline_core/training/protocol.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/conftest.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/helpers.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_cache.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_catalog.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_checkpoint.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_config.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_device_detect.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_executor.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_api.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_install.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_manifest.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_resolve.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_scanner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_spine.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_extension_state.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_file_store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_controlnet.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_folder.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_resolve.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_runner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_training.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_flux2_variants.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_frontend_serving.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_hidden_nodes.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_keymap.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_convert.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_depth_control.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_requirements.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_krea2_runner.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_loader_runners.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_loaders.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_lora_download.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_memory_policy.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_adaln.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_minimaxh3_keys.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_model_requirements.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_offload_prepared.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_output_kind_contract.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_parallel_group.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_pipeline_cache.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_primitives.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_recipe.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_references.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_rpc_bridge.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_run_store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_sampling.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_schema.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_server.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_staged_residency.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_assets.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_frames.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_graph_build.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_models.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_moodboard.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_multi_reference.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_node_size.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_peaks.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_rpc.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_schema.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_store.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_timeline.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_studio_training.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_take_bytes.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_topo.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_arch.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_dataset.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_training_resolve.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_validate.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_video_encode.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_video_params.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_xfuser_sampler.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_zimage_primitives.py +0 -0
- {inline_core-1.2.65 → inline_core-1.2.67}/tests/test_zimage_resolve.py +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: inline-core
|
|
3
|
-
Version: 1.2.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
115
|
-
#
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
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:
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
143
|
+
_accumulate(weight, up, down, scale, weight.device)
|
|
118
144
|
except torch.cuda.OutOfMemoryError:
|
|
119
|
-
weight
|
|
145
|
+
_accumulate(weight, up, down, scale, "cpu")
|
|
120
146
|
|
|
121
147
|
|
|
122
|
-
def
|
|
123
|
-
"""
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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")
|