inline-core 1.2.52__tar.gz → 1.2.53__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.52 → inline_core-1.2.53}/PKG-INFO +5 -1
- {inline_core-1.2.52 → inline_core-1.2.53}/pyproject.toml +7 -1
- inline_core-1.2.53/src/inline_core/__init__.py +14 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/memory.py +4 -3
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/policy.py +5 -1
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/cache.py +35 -2
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/executor.py +16 -4
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/schema.py +5 -1
- inline_core-1.2.53/src/inline_core/models/controlspace.py +26 -0
- inline_core-1.2.53/src/inline_core/models/krea2/depth_control.py +149 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/img2img.py +2 -2
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/requirements.py +75 -1
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/runner.py +62 -3
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/loaders.py +108 -3
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/pipeline_runtime.py +60 -19
- inline_core-1.2.53/src/inline_core/models/preprocess/__init__.py +5 -0
- inline_core-1.2.53/src/inline_core/models/preprocess/requirements.py +59 -0
- inline_core-1.2.53/src/inline_core/models/preprocess/runner.py +174 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/requirements.py +3 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/requirements.py +90 -4
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/runner.py +69 -5
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/bootstrap.py +16 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/fal.py +33 -4
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/generation.py +26 -5
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/graph_build.py +85 -2
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/handlers.py +12 -2
- inline_core-1.2.53/src/inline_core/studio/image_meta.py +36 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/models.py +6 -2
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/moodboard.py +37 -0
- inline_core-1.2.53/src/inline_core/studio/recipe.py +109 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/helpers.py +1 -0
- inline_core-1.2.53/tests/test_cache.py +116 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_executor.py +26 -1
- inline_core-1.2.53/tests/test_krea2_depth_control.py +104 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_krea2_requirements.py +35 -2
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_krea2_runner.py +4 -3
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_loaders.py +35 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_model_requirements.py +25 -0
- inline_core-1.2.53/tests/test_pipeline_cache.py +82 -0
- inline_core-1.2.53/tests/test_recipe.py +100 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_generation.py +38 -0
- inline_core-1.2.53/tests/test_studio_graph_build.py +111 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_models.py +20 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_rpc.py +12 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_zimage_resolve.py +6 -2
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_zimage_runner.py +109 -13
- {inline_core-1.2.52 → inline_core-1.2.53}/uv.lock +258 -1
- {inline_core-1.2.52 → inline_core-1.2.53}/webui.bat +3 -1
- {inline_core-1.2.52 → inline_core-1.2.53}/webui.sh +4 -1
- inline_core-1.2.52/src/inline_core/__init__.py +0 -7
- inline_core-1.2.52/tests/test_cache.py +0 -48
- {inline_core-1.2.52 → inline_core-1.2.53}/.gitignore +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/.python-version +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/CLAUDE.md +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/README.md +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/main.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/scripts/reference.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/components/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/components/conditioning.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/components/interfaces.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/config.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/auto.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/detect.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/types.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/errors.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/api.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/constraints.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/fetch.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/handlers.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/importer.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/install.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/loader.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/manifest.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/models.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/paths.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/resolve.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/scanner.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/state.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/tools.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/ffmpeg.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/descriptor.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/loader_runners.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/primitives.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/registry.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/runners.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/topo.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/validate.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/media.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/catalog.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/checkpoint.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/convert.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/provider.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/lora.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/sampling.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/primitives.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/provider.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/config.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/group.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/launch.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/protocol.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/registry.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/worker.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/context.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/file_store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/progress.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/run.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/sampling/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/sampling/batch.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/__main__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/app.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/assets.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/frontend.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/manager.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/rpc.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/run_store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/serialize.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/assets.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/config.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/frames.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/peaks.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/schema.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/system_stats.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/compose.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/ffmpeg.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/render.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/resolve.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/training.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/training_store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/takes.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/__init__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/__main__.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/arch.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/caption.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/dataset.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/models.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/protocol.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/trainer.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_catalog.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_checkpoint.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_config.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_device_detect.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_api.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_install.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_manifest.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_resolve.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_scanner.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_spine.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_state.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_file_store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_frontend_serving.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_hidden_nodes.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_krea2_convert.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_loader_runners.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_lora.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_lora_download.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_memory_policy.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_parallel_group.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_primitives.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_rpc_bridge.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_run_store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_sampling.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_schema.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_server.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_assets.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_fal.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_frames.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_moodboard.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_peaks.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_schema.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_store.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_timeline.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_training.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_take_bytes.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_topo.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_arch.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_dataset.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_models.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_resolve.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_validate.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_xfuser_sampler.py +0 -0
- {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_zimage_primitives.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: inline-core
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.53
|
|
4
4
|
Summary: The generation engine behind Inline Studio.
|
|
5
5
|
License-Expression: GPL-3.0-or-later
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -9,12 +9,14 @@ Requires-Dist: psutil>=5.9
|
|
|
9
9
|
Provides-Extra: all
|
|
10
10
|
Requires-Dist: accelerate>=0.30; extra == 'all'
|
|
11
11
|
Requires-Dist: bitsandbytes>=0.43; (platform_system != 'Darwin') and extra == 'all'
|
|
12
|
+
Requires-Dist: controlnet-aux>=0.0.7; extra == 'all'
|
|
12
13
|
Requires-Dist: diffusers>=0.39; extra == 'all'
|
|
13
14
|
Requires-Dist: einops>=0.7; extra == 'all'
|
|
14
15
|
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
15
16
|
Requires-Dist: huggingface-hub>=0.23; extra == 'all'
|
|
16
17
|
Requires-Dist: imageio-ffmpeg>=0.4; extra == 'all'
|
|
17
18
|
Requires-Dist: nvidia-ml-py>=12; extra == 'all'
|
|
19
|
+
Requires-Dist: onnxruntime>=1.17; extra == 'all'
|
|
18
20
|
Requires-Dist: peft>=0.11; extra == 'all'
|
|
19
21
|
Requires-Dist: pillow>=10; extra == 'all'
|
|
20
22
|
Requires-Dist: psutil>=5.9; extra == 'all'
|
|
@@ -35,8 +37,10 @@ Requires-Dist: nvidia-ml-py>=12; extra == 'parallel'
|
|
|
35
37
|
Requires-Dist: xfuser>=0.4; extra == 'parallel'
|
|
36
38
|
Provides-Extra: runtime
|
|
37
39
|
Requires-Dist: accelerate>=0.30; extra == 'runtime'
|
|
40
|
+
Requires-Dist: controlnet-aux>=0.0.7; extra == 'runtime'
|
|
38
41
|
Requires-Dist: diffusers>=0.39; extra == 'runtime'
|
|
39
42
|
Requires-Dist: huggingface-hub>=0.23; extra == 'runtime'
|
|
43
|
+
Requires-Dist: onnxruntime>=1.17; extra == 'runtime'
|
|
40
44
|
Requires-Dist: safetensors>=0.4; extra == 'runtime'
|
|
41
45
|
Requires-Dist: scipy>=1.11; extra == 'runtime'
|
|
42
46
|
Requires-Dist: torch>=2.2; 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.53"
|
|
5
5
|
description = "The generation engine behind Inline Studio."
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = "GPL-3.0-or-later"
|
|
@@ -28,6 +28,10 @@ runtime = [
|
|
|
28
28
|
"scipy>=1.11",
|
|
29
29
|
# We call snapshot_download directly for the model popup, so pin it rather than rely on transit.
|
|
30
30
|
"huggingface_hub>=0.23",
|
|
31
|
+
# ControlNet preprocessors (the Apply ControlNet node): OpenPose/DWPose, MiDaS/Zoe depth, canny,
|
|
32
|
+
# HED, lineart, MLSD, scribble, normal. DWPose runs its detector on ONNX Runtime.
|
|
33
|
+
"controlnet-aux>=0.0.7",
|
|
34
|
+
"onnxruntime>=1.17",
|
|
31
35
|
]
|
|
32
36
|
server = [
|
|
33
37
|
"fastapi>=0.110",
|
|
@@ -74,6 +78,8 @@ all = [
|
|
|
74
78
|
"torchao>=0.14",
|
|
75
79
|
"scipy>=1.11",
|
|
76
80
|
"huggingface_hub>=0.23",
|
|
81
|
+
"controlnet-aux>=0.0.7",
|
|
82
|
+
"onnxruntime>=1.17",
|
|
77
83
|
# server
|
|
78
84
|
"fastapi>=0.110",
|
|
79
85
|
"uvicorn[standard]>=0.29",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Inline Core: the generation engine behind Inline.
|
|
2
|
+
|
|
3
|
+
Takes a typed node graph and returns immutable takes. See PLAN.md for the architecture and
|
|
4
|
+
docs/contract.md for the Storyline API.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
#: Resolved from the installed package, so pyproject.toml stays the only place a release is
|
|
11
|
+
#: bumped. An editable install records this at install time; reinstall after bumping.
|
|
12
|
+
__version__ = version("inline-core")
|
|
13
|
+
except PackageNotFoundError: # a source tree that was never installed
|
|
14
|
+
__version__ = "0.0.0"
|
|
@@ -202,9 +202,10 @@ class MemoryPolicy(DevicePolicy):
|
|
|
202
202
|
return None
|
|
203
203
|
cap = max(0.0, budget - _ACTIVATION_HEADROOM_GB)
|
|
204
204
|
big = (fp.diffusion_bytes + fp.text_encoder_bytes) / 1e9
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
# The VAE and a ControlNet are never quantized, so they cost the same under every plan.
|
|
206
|
+
fixed = (fp.vae_bytes + fp.controlnet_bytes) / 1e9
|
|
207
|
+
full = big + fixed
|
|
208
|
+
int8 = big * _INT8_FACTOR + fixed
|
|
208
209
|
forced = _env_profile() is not None # explicit --profile pins the profile; fit picks quant
|
|
209
210
|
|
|
210
211
|
def prof(auto: Profile) -> Profile:
|
|
@@ -87,10 +87,14 @@ class ModelFootprint:
|
|
|
87
87
|
diffusion_bytes: int = 0
|
|
88
88
|
text_encoder_bytes: int = 0
|
|
89
89
|
vae_bytes: int = 0
|
|
90
|
+
#: A ControlNet loaded alongside the denoiser. Never quantized, so it counts full in every plan.
|
|
91
|
+
controlnet_bytes: int = 0
|
|
90
92
|
|
|
91
93
|
@property
|
|
92
94
|
def total_bytes(self) -> int:
|
|
93
|
-
return
|
|
95
|
+
return (
|
|
96
|
+
self.diffusion_bytes + self.text_encoder_bytes + self.vae_bytes + self.controlnet_bytes
|
|
97
|
+
)
|
|
94
98
|
|
|
95
99
|
|
|
96
100
|
@dataclass(frozen=True)
|
|
@@ -12,7 +12,7 @@ from typing import Any
|
|
|
12
12
|
|
|
13
13
|
from ..takes import Take
|
|
14
14
|
from .registry import Registry
|
|
15
|
-
from .schema import Graph, Node
|
|
15
|
+
from .schema import Graph, Node, PortKind
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class NodeCache(ABC):
|
|
@@ -40,8 +40,15 @@ def _canonical_params(node: Node, registry: Registry) -> dict[str, Any]:
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def is_cache_eligible(node: Node, registry: Registry) -> bool:
|
|
43
|
-
"""False when any seed param resolves to a negative (random) value.
|
|
43
|
+
"""False when a control map is wired, or any seed param resolves to a negative (random) value.
|
|
44
|
+
|
|
45
|
+
A node driven by a control map re-runs every time: the user iterates on the pose/depth and
|
|
46
|
+
expects each run to apply the current control, so a cached take would read as "control not
|
|
47
|
+
taking effect" (even a re-render at the same seed must re-apply it)."""
|
|
44
48
|
descriptor = registry.get(node.type)
|
|
49
|
+
for port in descriptor.inputs:
|
|
50
|
+
if port.kind is PortKind.CONTROL and node.inputs.get(port.id):
|
|
51
|
+
return False
|
|
45
52
|
defaults = descriptor.defaults()
|
|
46
53
|
for key in descriptor.seed_keys():
|
|
47
54
|
value = node.params.get(key, defaults.get(key))
|
|
@@ -81,3 +88,29 @@ def node_cache_key(
|
|
|
81
88
|
digest = hashlib.sha256(json.dumps(payload, sort_keys=True, default=str).encode()).hexdigest()
|
|
82
89
|
memo[node_id] = digest
|
|
83
90
|
return digest
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def asset_content_hashes(graph: Graph) -> dict[str, str]:
|
|
94
|
+
"""The byte hash of each file-backed source node's asset, keyed by node id. Feeds
|
|
95
|
+
``node_cache_key`` so the cache invalidates when a file's *content* changes even though its path
|
|
96
|
+
did not (a re-rendered control map, an in-place-replaced input image). Only ``ref="path"`` refs
|
|
97
|
+
are hashable; a missing file is skipped - its path still keys the node through its params."""
|
|
98
|
+
import os
|
|
99
|
+
|
|
100
|
+
hashes: dict[str, str] = {}
|
|
101
|
+
for node in graph.nodes:
|
|
102
|
+
asset = node.params.get("asset")
|
|
103
|
+
if not isinstance(asset, dict) or asset.get("ref") != "path":
|
|
104
|
+
continue
|
|
105
|
+
path = asset.get("path")
|
|
106
|
+
if isinstance(path, str) and os.path.isfile(path):
|
|
107
|
+
hashes[node.id] = _file_hash(path)
|
|
108
|
+
return hashes
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _file_hash(path: str) -> str:
|
|
112
|
+
digest = hashlib.sha256()
|
|
113
|
+
with open(path, "rb") as handle:
|
|
114
|
+
for chunk in iter(lambda: handle.read(1 << 20), b""):
|
|
115
|
+
digest.update(chunk)
|
|
116
|
+
return digest.hexdigest()
|
|
@@ -6,6 +6,7 @@ It orchestrates cheap work inline. A model node's runner submits the denoise to
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import logging
|
|
9
10
|
from dataclasses import replace
|
|
10
11
|
from typing import Any
|
|
11
12
|
|
|
@@ -13,12 +14,14 @@ from ..errors import CancelledError, GraphValidationError, InlineCoreError
|
|
|
13
14
|
from ..runtime.context import ExecutionContext
|
|
14
15
|
from ..runtime.progress import CancelledEvent, ErrorEvent, NodeDoneEvent, RunDoneEvent
|
|
15
16
|
from ..runtime.run import NodeRuntimeState, RunState, RunStatus, StateTrackingEmitter
|
|
16
|
-
from .cache import NodeCache, is_cache_eligible, node_cache_key
|
|
17
|
+
from .cache import NodeCache, asset_content_hashes, is_cache_eligible, node_cache_key
|
|
17
18
|
from .registry import Registry
|
|
18
19
|
from .schema import Graph, Node
|
|
19
20
|
from .topo import topo_sort, upstream_closure
|
|
20
21
|
from .validate import validate
|
|
21
22
|
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
22
25
|
|
|
23
26
|
class Executor:
|
|
24
27
|
def __init__(self, registry: Registry, cache: NodeCache) -> None:
|
|
@@ -33,10 +36,13 @@ class Executor:
|
|
|
33
36
|
order = self._plan(graph, target, state)
|
|
34
37
|
state.status = RunStatus.RUNNING
|
|
35
38
|
outputs: dict[str, dict[str, Any]] = {}
|
|
39
|
+
# Hash the input files once so the node cache is content-addressed: a re-rendered
|
|
40
|
+
# control map (or any replaced input) invalidates even when its path is unchanged.
|
|
41
|
+
asset_hashes = asset_content_hashes(graph)
|
|
36
42
|
for node_id in order:
|
|
37
43
|
if ctx.cancel.cancelled:
|
|
38
44
|
raise CancelledError("Run cancelled.")
|
|
39
|
-
self._run_node(graph, node_id, outputs, run_ctx)
|
|
45
|
+
self._run_node(graph, node_id, outputs, run_ctx, asset_hashes)
|
|
40
46
|
emitter.emit(RunDoneEvent(run_id=ctx.run_id))
|
|
41
47
|
except CancelledError:
|
|
42
48
|
emitter.emit(CancelledEvent(run_id=ctx.run_id))
|
|
@@ -44,6 +50,12 @@ class Executor:
|
|
|
44
50
|
emitter.emit(ErrorEvent(run_id=ctx.run_id, message=str(error), node_id=error.node_id))
|
|
45
51
|
except InlineCoreError as error:
|
|
46
52
|
emitter.emit(ErrorEvent(run_id=ctx.run_id, message=str(error)))
|
|
53
|
+
except Exception as error: # noqa: BLE001
|
|
54
|
+
# A runner that raises a non-InlineCoreError (e.g. a diffusers/HF load error) must still
|
|
55
|
+
# terminate the run: otherwise it escapes to the worker thread, the terminal event is
|
|
56
|
+
# never sent, and the run wedges in "queued" while the UI hangs on "loading model".
|
|
57
|
+
logger.exception("Run %s failed with an unhandled error", ctx.run_id)
|
|
58
|
+
emitter.emit(ErrorEvent(run_id=ctx.run_id, message=str(error) or type(error).__name__))
|
|
47
59
|
|
|
48
60
|
def _plan(self, graph: Graph, target: str, state: RunState) -> list[str]:
|
|
49
61
|
validate(graph, target, self._registry)
|
|
@@ -59,6 +71,7 @@ class Executor:
|
|
|
59
71
|
node_id: str,
|
|
60
72
|
outputs: dict[str, dict[str, Any]],
|
|
61
73
|
ctx: ExecutionContext,
|
|
74
|
+
asset_hashes: dict[str, str],
|
|
62
75
|
) -> None:
|
|
63
76
|
node = graph.node(node_id)
|
|
64
77
|
runner = self._registry.runner(node.type)
|
|
@@ -66,8 +79,7 @@ class Executor:
|
|
|
66
79
|
|
|
67
80
|
key: str | None = None
|
|
68
81
|
if runner.produces_takes and is_cache_eligible(node, self._registry):
|
|
69
|
-
|
|
70
|
-
key = node_cache_key(graph, node_id, self._registry, asset_hashes={})
|
|
82
|
+
key = node_cache_key(graph, node_id, self._registry, asset_hashes=asset_hashes)
|
|
71
83
|
cached = self._cache.get(key)
|
|
72
84
|
if cached is not None:
|
|
73
85
|
ctx.emitter.emit(
|
|
@@ -26,6 +26,7 @@ class PortKind(str, Enum):
|
|
|
26
26
|
LORA = "lora"
|
|
27
27
|
CONDITIONING = "conditioning"
|
|
28
28
|
LATENT = "latent"
|
|
29
|
+
CONTROL = "control"
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def port_satisfies(source: PortKind, target: PortKind) -> bool:
|
|
@@ -33,7 +34,10 @@ def port_satisfies(source: PortKind, target: PortKind) -> bool:
|
|
|
33
34
|
if source == target:
|
|
34
35
|
return True
|
|
35
36
|
# a single image satisfies a list input (a one-element list)
|
|
36
|
-
|
|
37
|
+
if source is PortKind.IMAGE and target is PortKind.IMAGE_LIST:
|
|
38
|
+
return True
|
|
39
|
+
# a control input accepts any image output (the control map is just an image)
|
|
40
|
+
return source is PortKind.IMAGE and target is PortKind.CONTROL
|
|
37
41
|
|
|
38
42
|
|
|
39
43
|
@dataclass(frozen=True)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Requirements for the client-side "Control Space" node: just the suggested ControlNet model, so
|
|
2
|
+
the node can offer a one-click download when ``models/controlnet/`` is empty. Torch-free (pure
|
|
3
|
+
filesystem), so it registers even on a runtime-less install - a download only needs the models dir.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from ..config import models_dir
|
|
12
|
+
from .requirements import ModelComponent
|
|
13
|
+
from .zimage.requirements import controlnet_component
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ControlSpaceProvider:
|
|
17
|
+
"""The ControlNet a Control Space render needs downstream - offered as a suggested download."""
|
|
18
|
+
|
|
19
|
+
def components(self, params: dict[str, object] | None = None) -> list[ModelComponent]:
|
|
20
|
+
return [controlnet_component()]
|
|
21
|
+
|
|
22
|
+
def download_target(self, component: ModelComponent) -> Path:
|
|
23
|
+
return models_dir() / component.category
|
|
24
|
+
|
|
25
|
+
def estimate(self, policy: Any) -> dict[str, Any] | None:
|
|
26
|
+
return None
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Krea 2 depth control: the public ``Patil/Krea-2-depth-controlnet`` control-LoRA, ported onto the
|
|
2
|
+
diffusers ``Krea2Transformer2DModel``.
|
|
3
|
+
|
|
4
|
+
The adapter is a rank-64 LoRA on every transformer block **plus a full replacement input
|
|
5
|
+
projection** (``first.weight [6144, 128]``): the base projection takes 64 packed latent channels,
|
|
6
|
+
expanded to 128 so a VAE-encoded depth latent rides alongside the noisy latent, concatenated on the
|
|
7
|
+
channel dim. The base stays frozen and the depth latent is constant across the whole denoise.
|
|
8
|
+
|
|
9
|
+
The checkpoint uses the reference krea-2 names (``blocks.N.attn.wq``, ``first``); ``convert_key``
|
|
10
|
+
already remaps those to diffusers names (``transformer_blocks.N.attn.to_q``, ``img_in``), the same
|
|
11
|
+
rename the base checkpoint needs. Wrapping ``img_in`` lets the depth concat happen transparently
|
|
12
|
+
inside the pipeline's own denoise loop - no fork of ``Krea2Pipeline.__call__``. Reference:
|
|
13
|
+
github.com/Tanmaypatil123/Krea-2-controlnet.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import torch
|
|
21
|
+
import torch.nn as nn
|
|
22
|
+
import torch.nn.functional as F
|
|
23
|
+
|
|
24
|
+
from ...errors import ComponentError
|
|
25
|
+
from .convert import convert_key
|
|
26
|
+
from .img2img import encode_image
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LoRALinear(nn.Module):
|
|
30
|
+
"""``y = W x + scale * (x A^T) B^T``. Base frozen; ``scale`` is the live control strength."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, base: nn.Linear, rank: int, scale: float = 1.0) -> None:
|
|
33
|
+
super().__init__()
|
|
34
|
+
self.base = base
|
|
35
|
+
self.scale = scale
|
|
36
|
+
self.A = nn.Parameter(torch.zeros(rank, base.in_features, dtype=torch.float32))
|
|
37
|
+
self.B = nn.Parameter(torch.zeros(base.out_features, rank, dtype=torch.float32))
|
|
38
|
+
|
|
39
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
40
|
+
lora = (x @ self.A.T.to(x.dtype)) @ self.B.T.to(x.dtype)
|
|
41
|
+
return self.base(x) + lora * self.scale
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ControlInputLayer(nn.Module):
|
|
45
|
+
"""Replaces ``img_in``: input width doubled (64 -> 128) to accept
|
|
46
|
+
``[noisy latent ; depth latent]`` concatenated on the channel dim. The depth latent is stashed
|
|
47
|
+
on ``self.ctrl`` once per run and broadcast over the batch (so CFG's doubled batch lines up)."""
|
|
48
|
+
|
|
49
|
+
def __init__(self, pretrained: nn.Linear) -> None:
|
|
50
|
+
super().__init__()
|
|
51
|
+
self._in = pretrained.in_features
|
|
52
|
+
# The checkpoint always carries the full trained input projection (both the base and depth
|
|
53
|
+
# halves), so zeros are enough here - and this avoids dequantizing a possibly-int8 base.
|
|
54
|
+
self.weight = nn.Parameter(torch.zeros(pretrained.out_features, self._in * 2))
|
|
55
|
+
self.bias = nn.Parameter(torch.zeros(pretrained.out_features))
|
|
56
|
+
self.ctrl: torch.Tensor | None = None
|
|
57
|
+
|
|
58
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
59
|
+
if self.ctrl is None: # a control pipe always has ctrl set before a run; zeros = no depth
|
|
60
|
+
ctrl = x.new_zeros((*x.shape[:-1], self._in))
|
|
61
|
+
else:
|
|
62
|
+
ctrl = self.ctrl.to(device=x.device, dtype=x.dtype)
|
|
63
|
+
if ctrl.shape[0] != x.shape[0]:
|
|
64
|
+
ctrl = ctrl.expand(x.shape[0], -1, -1)
|
|
65
|
+
combined = torch.cat([x, ctrl], dim=-1)
|
|
66
|
+
return F.linear(combined, self.weight.to(x.dtype), self.bias.to(x.dtype))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _get(root: Any, path: str) -> Any:
|
|
70
|
+
for part in path.split("."):
|
|
71
|
+
root = root[int(part)] if part.isdigit() else getattr(root, part)
|
|
72
|
+
return root
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _set(root: Any, path: str, value: Any) -> None:
|
|
76
|
+
parts = path.split(".")
|
|
77
|
+
parent = _get(root, ".".join(parts[:-1])) if len(parts) > 1 else root
|
|
78
|
+
last = parts[-1]
|
|
79
|
+
if last.isdigit():
|
|
80
|
+
parent[int(last)] = value
|
|
81
|
+
else:
|
|
82
|
+
setattr(parent, last, value)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _module_paths(state: dict[str, torch.Tensor]) -> list[str]:
|
|
86
|
+
"""The block-linear module paths the adapter references (``img_in`` handled separately)."""
|
|
87
|
+
paths: list[str] = []
|
|
88
|
+
for key in state:
|
|
89
|
+
if key.startswith("img_in."):
|
|
90
|
+
continue
|
|
91
|
+
stem = key.rsplit(".", 1)[0] # drop the trailing .A / .B
|
|
92
|
+
if stem not in paths:
|
|
93
|
+
paths.append(stem)
|
|
94
|
+
return paths
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def install_depth_control(transformer: Any, lora_file: str) -> None:
|
|
98
|
+
"""Wrap ``img_in`` + every block linear the LoRA touches, then load the trained weights in.
|
|
99
|
+
|
|
100
|
+
Idempotent per pipeline: only called on a cache miss, so a cached control pipe keeps its work.
|
|
101
|
+
``set_control`` / ``set_control_strength`` are the per-run knobs on top of it.
|
|
102
|
+
"""
|
|
103
|
+
import safetensors.torch
|
|
104
|
+
|
|
105
|
+
st: Any = safetensors.torch
|
|
106
|
+
raw: dict[str, Any] = st.load_file(lora_file)
|
|
107
|
+
state = {convert_key(k): v for k, v in raw.items()}
|
|
108
|
+
a_shapes = [t.shape[0] for k, t in state.items() if k.endswith(".A")]
|
|
109
|
+
if not a_shapes:
|
|
110
|
+
raise ComponentError("Krea 2 depth control file has no LoRA tensors; not the adapter.")
|
|
111
|
+
rank = a_shapes[0]
|
|
112
|
+
|
|
113
|
+
device = next(transformer.parameters()).device
|
|
114
|
+
transformer.img_in = ControlInputLayer(transformer.img_in).to(device)
|
|
115
|
+
for path in _module_paths(state):
|
|
116
|
+
_set(transformer, path, LoRALinear(_get(transformer, path), rank).to(device))
|
|
117
|
+
|
|
118
|
+
missing, unexpected = transformer.load_state_dict(state, strict=False)
|
|
119
|
+
if unexpected:
|
|
120
|
+
raise ComponentError(
|
|
121
|
+
f"Krea 2 depth control-LoRA has {len(unexpected)} tensors that do not map onto the "
|
|
122
|
+
f"model (e.g. {', '.join(unexpected[:3])}). It may be for a different Krea 2 build."
|
|
123
|
+
)
|
|
124
|
+
# The expanded input projection is zero-initialised, so it MUST come from the checkpoint.
|
|
125
|
+
if "img_in.weight" in missing or "img_in.bias" in missing:
|
|
126
|
+
raise ComponentError(
|
|
127
|
+
"Krea 2 depth control-LoRA is missing its input projection (img_in); not the adapter."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def set_control_strength(transformer: Any, scale: float) -> None:
|
|
132
|
+
"""Dial the block LoRA delta (the depth ``img_in`` expansion always applies). No rebuild."""
|
|
133
|
+
for module in transformer.modules():
|
|
134
|
+
if isinstance(module, LoRALinear):
|
|
135
|
+
module.scale = scale
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def set_control(transformer: Any, ctrl_latent: torch.Tensor | None) -> None:
|
|
139
|
+
img_in = transformer.img_in
|
|
140
|
+
if isinstance(img_in, ControlInputLayer):
|
|
141
|
+
img_in.ctrl = ctrl_latent
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def encode_depth_latent(
|
|
145
|
+
pipe: Any, depth_image: Any, *, width: int, height: int, device: str, generator: Any
|
|
146
|
+
) -> torch.Tensor:
|
|
147
|
+
"""The depth map as a packed Krea 2 latent, ready to concat onto the noisy latent - the same VAE
|
|
148
|
+
encode + pack the img2img path uses, so the two latents share a layout."""
|
|
149
|
+
return encode_image(pipe, depth_image, width, height, device, generator)
|
|
@@ -54,14 +54,14 @@ def img2img_kwargs(
|
|
|
54
54
|
schedule.set_timesteps(sigmas=raw, mu=mu, device=device)
|
|
55
55
|
sigma = schedule.sigmas[start].to(device)
|
|
56
56
|
|
|
57
|
-
latents =
|
|
57
|
+
latents = encode_image(pipe, image, width, height, device, generator)
|
|
58
58
|
noise = torch.randn(
|
|
59
59
|
latents.shape, generator=generator, device=latents.device, dtype=latents.dtype
|
|
60
60
|
)
|
|
61
61
|
return {"latents": (1.0 - sigma) * latents + sigma * noise, "sigmas": raw[start:].tolist()}
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
def
|
|
64
|
+
def encode_image(
|
|
65
65
|
pipe: Any, image: Any, width: int, height: int, device: str, generator: Any
|
|
66
66
|
) -> Any:
|
|
67
67
|
"""The input image as packed, normalized Krea 2 latents. The Qwen-Image VAE is a video codec, so
|
|
@@ -27,6 +27,11 @@ VAE_FILE = "qwen_image_vae_diffusers.safetensors"
|
|
|
27
27
|
VAE_REPO_FILE = "vae/diffusion_pytorch_model.safetensors"
|
|
28
28
|
TEXT_ENCODER_FILE = "qwen3vl_4b_bf16.safetensors"
|
|
29
29
|
|
|
30
|
+
#: The public Krea 2 depth control-LoRA (rank-64 + expanded input projection). It lands in
|
|
31
|
+
#: ``controlnet/`` alongside any Z-Image controlnet; resolution is name-scoped so the two never mix.
|
|
32
|
+
DEPTH_CONTROL_REPO = "Patil/Krea-2-depth-controlnet"
|
|
33
|
+
DEPTH_CONTROL_FILE = "depth-control-lora.safetensors"
|
|
34
|
+
|
|
30
35
|
#: variant -> the file the popup downloads for that node.
|
|
31
36
|
DIFFUSION_FILES = {
|
|
32
37
|
"turbo": "krea2_turbo_bf16.safetensors",
|
|
@@ -61,6 +66,18 @@ def _env_path(kind: str) -> Path | None:
|
|
|
61
66
|
return path if path.exists() else None
|
|
62
67
|
|
|
63
68
|
|
|
69
|
+
def foreign_model_message(path: str) -> str | None:
|
|
70
|
+
"""A clear error when a diffusion file plainly belongs to another architecture (a Z-Image file
|
|
71
|
+
picked for a Krea 2 node) - name-based, best-effort, to avoid silently distorted output."""
|
|
72
|
+
name = Path(path).name.lower()
|
|
73
|
+
if ("z_image" in name or "z-image" in name) and "krea" not in name:
|
|
74
|
+
return (
|
|
75
|
+
f"'{Path(path).name}' is a Z-Image model, but this is a Krea 2 node. Pick a "
|
|
76
|
+
"krea2_*.safetensors in the Diffusion file dropdown (or clear it to auto-select)."
|
|
77
|
+
)
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
64
81
|
def resolve_diffusion(variant: str, params: dict[str, object] | None = None) -> Path | None:
|
|
65
82
|
"""The Krea 2 transformer file for this node: the dropdown pick, the env override, the exact
|
|
66
83
|
recommended file, else any krea2 file matching the variant. A user holding both RAW and Turbo
|
|
@@ -86,6 +103,57 @@ def resolve_diffusion(variant: str, params: dict[str, object] | None = None) ->
|
|
|
86
103
|
return krea[0] if krea else None
|
|
87
104
|
|
|
88
105
|
|
|
106
|
+
def resolve_depth_control(params: dict[str, object] | None = None) -> Path | None:
|
|
107
|
+
"""The depth control-LoRA file, or None. Opt-in like Z-Image control: resolves only from
|
|
108
|
+
``INLINE_KREA2_CONTROL`` or an explicit ``depth_controlnet`` dropdown pick (never auto)."""
|
|
109
|
+
env = os.environ.get("INLINE_KREA2_CONTROL", "").strip()
|
|
110
|
+
if env:
|
|
111
|
+
path = Path(env)
|
|
112
|
+
return path if path.exists() else None
|
|
113
|
+
chosen = str((params or {}).get("depth_controlnet") or "").strip()
|
|
114
|
+
if not chosen:
|
|
115
|
+
return None
|
|
116
|
+
picked = models_dir() / "controlnet" / chosen
|
|
117
|
+
return picked if picked.is_file() else None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def auto_depth_control() -> Path | None:
|
|
121
|
+
"""The depth control-LoRA to use when a control map is wired but none was picked - the exact
|
|
122
|
+
downloaded file, else a krea+depth-named weight. None if absent. Only consulted when a control
|
|
123
|
+
input is actually connected, so depth control stays opt-in and never touches a plain run."""
|
|
124
|
+
root = models_dir() / "controlnet"
|
|
125
|
+
if not root.is_dir():
|
|
126
|
+
return None
|
|
127
|
+
exact = root / DEPTH_CONTROL_FILE
|
|
128
|
+
if exact.is_file():
|
|
129
|
+
return exact
|
|
130
|
+
for path in sorted(root.iterdir()):
|
|
131
|
+
name = path.name.lower()
|
|
132
|
+
if path.is_file() and path.suffix.lower() in _WEIGHT_SUFFIXES:
|
|
133
|
+
if "krea" in name and "depth" in name:
|
|
134
|
+
return path
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def depth_control_present() -> bool:
|
|
139
|
+
return auto_depth_control() is not None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def depth_control_component() -> ModelComponent:
|
|
143
|
+
"""The suggested depth control-LoRA download, offered on both Krea 2 nodes. Optional: it never
|
|
144
|
+
blocks a plain run."""
|
|
145
|
+
return ModelComponent(
|
|
146
|
+
id="depth_controlnet",
|
|
147
|
+
label="Depth control-LoRA",
|
|
148
|
+
category="controlnet",
|
|
149
|
+
present=depth_control_present(),
|
|
150
|
+
filename=DEPTH_CONTROL_FILE,
|
|
151
|
+
repo=DEPTH_CONTROL_REPO,
|
|
152
|
+
repo_file=DEPTH_CONTROL_FILE,
|
|
153
|
+
optional=True,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
89
157
|
def resolve_vae(params: dict[str, object] | None = None) -> Path | None:
|
|
90
158
|
return _resolve_shared("vae", "vae", VAE_FILE, params)
|
|
91
159
|
|
|
@@ -148,6 +216,8 @@ def krea2_requirements(
|
|
|
148
216
|
repo=VAE_REPO,
|
|
149
217
|
repo_file=VAE_REPO_FILE,
|
|
150
218
|
),
|
|
219
|
+
# Suggested, not required: offered in the popup so a depth map has an adapter to run with.
|
|
220
|
+
depth_control_component(),
|
|
151
221
|
]
|
|
152
222
|
|
|
153
223
|
|
|
@@ -170,11 +240,15 @@ def _file_bytes(path: object) -> int:
|
|
|
170
240
|
|
|
171
241
|
|
|
172
242
|
def footprint_bytes(
|
|
173
|
-
diffusion: object = None,
|
|
243
|
+
diffusion: object = None,
|
|
244
|
+
vae: object = None,
|
|
245
|
+
text_encoder: object = None,
|
|
246
|
+
controlnet: object = None,
|
|
174
247
|
) -> dict[str, int]:
|
|
175
248
|
"""On-disk sizes keyed to match ``ModelFootprint``. Torch-free (a plain ``stat``)."""
|
|
176
249
|
return {
|
|
177
250
|
"diffusion_bytes": _file_bytes(diffusion),
|
|
178
251
|
"text_encoder_bytes": _file_bytes(text_encoder),
|
|
179
252
|
"vae_bytes": _file_bytes(vae),
|
|
253
|
+
"controlnet_bytes": _file_bytes(controlnet),
|
|
180
254
|
}
|