inline-core 1.2.2__tar.gz → 1.2.5__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.2 → inline_core-1.2.5}/CLAUDE.md +3 -6
- {inline_core-1.2.2 → inline_core-1.2.5}/PKG-INFO +39 -16
- {inline_core-1.2.2 → inline_core-1.2.5}/README.md +27 -13
- {inline_core-1.2.2 → inline_core-1.2.5}/pyproject.toml +19 -4
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/fetch.py +8 -2
- inline_core-1.2.5/src/inline_core/ffmpeg.py +30 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/loader_runners.py +32 -1
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/primitives.py +16 -1
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/schema.py +1 -0
- inline_core-1.2.5/src/inline_core/models/checkpoint.py +87 -0
- inline_core-1.2.5/src/inline_core/models/krea2/__init__.py +9 -0
- inline_core-1.2.5/src/inline_core/models/krea2/convert.py +138 -0
- inline_core-1.2.5/src/inline_core/models/krea2/img2img.py +89 -0
- inline_core-1.2.5/src/inline_core/models/krea2/provider.py +57 -0
- inline_core-1.2.5/src/inline_core/models/krea2/requirements.py +180 -0
- inline_core-1.2.5/src/inline_core/models/krea2/runner.py +407 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/loaders.py +381 -34
- inline_core-1.2.5/src/inline_core/models/lora.py +190 -0
- inline_core-1.2.5/src/inline_core/models/pipeline_runtime.py +522 -0
- inline_core-1.2.5/src/inline_core/models/zimage/runner.py +465 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/__main__.py +6 -1
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/app.py +9 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/bootstrap.py +10 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/rpc.py +5 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/config.py +0 -1
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/frames.py +4 -17
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/graph_build.py +13 -2
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/handlers.py +39 -10
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/models.py +42 -59
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/moodboard.py +114 -23
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/schema.py +59 -3
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/store.py +0 -8
- inline_core-1.2.5/src/inline_core/studio/system_stats.py +92 -0
- inline_core-1.2.5/src/inline_core/studio/training.py +477 -0
- inline_core-1.2.5/src/inline_core/studio/training_store.py +274 -0
- inline_core-1.2.5/src/inline_core/training/__init__.py +10 -0
- inline_core-1.2.5/src/inline_core/training/__main__.py +74 -0
- inline_core-1.2.5/src/inline_core/training/arch.py +184 -0
- inline_core-1.2.5/src/inline_core/training/caption.py +127 -0
- inline_core-1.2.5/src/inline_core/training/dataset.py +158 -0
- inline_core-1.2.5/src/inline_core/training/models.py +288 -0
- inline_core-1.2.5/src/inline_core/training/protocol.py +56 -0
- inline_core-1.2.5/src/inline_core/training/trainer.py +236 -0
- inline_core-1.2.5/tests/test_checkpoint.py +79 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_install.py +27 -0
- inline_core-1.2.5/tests/test_krea2_convert.py +157 -0
- inline_core-1.2.5/tests/test_krea2_requirements.py +115 -0
- inline_core-1.2.5/tests/test_krea2_runner.py +163 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_loaders.py +15 -3
- inline_core-1.2.5/tests/test_lora.py +218 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_frames.py +3 -2
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_generation.py +25 -1
- inline_core-1.2.5/tests/test_studio_models.py +90 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_rpc.py +3 -3
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_store.py +2 -5
- inline_core-1.2.5/tests/test_studio_training.py +81 -0
- inline_core-1.2.5/tests/test_training_arch.py +113 -0
- inline_core-1.2.5/tests/test_training_dataset.py +103 -0
- inline_core-1.2.5/tests/test_training_models.py +106 -0
- inline_core-1.2.5/tests/test_training_resolve.py +90 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_zimage_runner.py +27 -24
- {inline_core-1.2.2 → inline_core-1.2.5}/uv.lock +130 -4
- {inline_core-1.2.2 → inline_core-1.2.5}/webui.sh +1 -1
- inline_core-1.2.2/src/inline_core/importer/__init__.py +0 -1
- inline_core-1.2.2/src/inline_core/importer/comfy.py +0 -162
- inline_core-1.2.2/src/inline_core/models/zimage/runner.py +0 -945
- inline_core-1.2.2/tests/test_comfy_import.py +0 -80
- inline_core-1.2.2/tests/test_studio_models.py +0 -84
- {inline_core-1.2.2 → inline_core-1.2.5}/.gitignore +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/.python-version +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/main.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/scripts/reference.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/components/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/components/conditioning.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/components/interfaces.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/config.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/auto.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/detect.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/memory.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/policy.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/types.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/errors.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/api.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/constraints.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/handlers.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/importer.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/install.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/loader.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/manifest.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/models.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/paths.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/resolve.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/scanner.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/state.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/tools.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/cache.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/descriptor.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/executor.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/registry.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/runners.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/topo.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/validate.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/media.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/catalog.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/requirements.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/sampling.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/primitives.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/provider.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/requirements.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/config.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/group.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/launch.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/protocol.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/registry.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/worker.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/context.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/file_store.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/progress.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/run.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/store.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/sampling/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/sampling/batch.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/assets.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/frontend.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/manager.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/run_store.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/serialize.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/assets.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/fal.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/generation.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/peaks.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/__init__.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/compose.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/ffmpeg.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/render.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/resolve.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/takes.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/helpers.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_cache.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_catalog.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_config.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_device_detect.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_executor.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_api.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_manifest.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_resolve.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_scanner.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_spine.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_state.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_file_store.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_frontend_serving.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_hidden_nodes.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_loader_runners.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_memory_policy.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_model_requirements.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_parallel_group.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_primitives.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_rpc_bridge.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_run_store.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_sampling.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_schema.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_server.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_assets.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_fal.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_moodboard.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_peaks.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_schema.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_timeline.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_take_bytes.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_topo.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_validate.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_xfuser_sampler.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_zimage_primitives.py +0 -0
- {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_zimage_resolve.py +0 -0
|
@@ -82,8 +82,6 @@ HTTP/WS → server/app.py → RunManager → Executor → Registry (desc
|
|
|
82
82
|
- **`runtime/`** - `context.py` (`ExecutionContext`, `CancelToken`), `run.py` (`RunState`),
|
|
83
83
|
`progress.py` (events + emitters), `store.py` / `file_store.py` (`TakeStore`: owns take bytes/hash/
|
|
84
84
|
uri).
|
|
85
|
-
- **`importer/`** - `comfy.py`: best-effort ComfyUI-workflow → Inline-Core-graph mapping. All ComfyUI
|
|
86
|
-
format knowledge lives here.
|
|
87
85
|
- **`config.py`** - all env config, small and explicit. **`takes.py`**, **`media.py`**, **`errors.py`**
|
|
88
86
|
- domain primitives (`Take`/`AssetRef`, `MediaKind`, the error hierarchy).
|
|
89
87
|
|
|
@@ -200,13 +198,13 @@ real codec that moves tensors lives with the model runner.
|
|
|
200
198
|
subpackages import torch/diffusers at module top **on purpose**: an absent extra makes the import
|
|
201
199
|
raise, and `server/bootstrap.py` skips that model best-effort so a core install still boots and
|
|
202
200
|
serves source nodes. Never import a heavy dep at package top level outside a runner subpackage.
|
|
203
|
-
- **Engine isolation.** All
|
|
204
|
-
|
|
201
|
+
- **Engine isolation.** All xDiT/worker knowledge lives behind `parallel/` and the sampler seam.
|
|
202
|
+
Don't scatter it.
|
|
205
203
|
- **Bring-your-own models.** Nothing is downloaded by the engine. The catalog scans; the user places
|
|
206
204
|
files. A model picker is a `SELECT` param with `options_from="<category>"`.
|
|
207
205
|
- **Tests (pytest).** Cover the logic that matters: graph validate/topo/executor/cache, the catalog
|
|
208
206
|
scan, the run store + server contract, the device/memory policy, the parallel group + xfuser seam,
|
|
209
|
-
|
|
207
|
+
and each model runner (import-guarded, no GPU needed). See `tests/`.
|
|
210
208
|
- **Commits.** Conventional Commits (`feat:`, `fix:`, `chore:`), small and scoped.
|
|
211
209
|
|
|
212
210
|
## Commands
|
|
@@ -239,4 +237,3 @@ uv run pytest -q # tests (no GPU; model code is import-
|
|
|
239
237
|
as `{error:{code,message}}` with the right status. Update the API list in `README.md`.
|
|
240
238
|
- **New port/handle type** → `PortKind` in `graph/schema.py` (+ `port_satisfies` if it has coercions).
|
|
241
239
|
- **New device/memory behaviour** → behind `DevicePolicy` in `device/`; never in a component.
|
|
242
|
-
- **New ComfyUI import behaviour** → `importer/comfy.py` only.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: inline-core
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
4
4
|
Summary: The generation engine behind Inline Studio.
|
|
5
|
+
License-Expression: GPL-3.0-or-later
|
|
5
6
|
Requires-Python: >=3.11
|
|
6
7
|
Requires-Dist: numpy>=1.26
|
|
7
8
|
Requires-Dist: psutil>=5.9
|
|
8
9
|
Provides-Extra: all
|
|
9
10
|
Requires-Dist: accelerate>=0.30; extra == 'all'
|
|
10
|
-
Requires-Dist: diffusers>=0.
|
|
11
|
+
Requires-Dist: diffusers>=0.39; extra == 'all'
|
|
11
12
|
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
12
13
|
Requires-Dist: huggingface-hub>=0.23; extra == 'all'
|
|
13
14
|
Requires-Dist: imageio-ffmpeg>=0.4; extra == 'all'
|
|
@@ -27,7 +28,7 @@ Requires-Dist: nvidia-ml-py>=12; extra == 'parallel'
|
|
|
27
28
|
Requires-Dist: xfuser>=0.4; extra == 'parallel'
|
|
28
29
|
Provides-Extra: runtime
|
|
29
30
|
Requires-Dist: accelerate>=0.30; extra == 'runtime'
|
|
30
|
-
Requires-Dist: diffusers>=0.
|
|
31
|
+
Requires-Dist: diffusers>=0.39; extra == 'runtime'
|
|
31
32
|
Requires-Dist: huggingface-hub>=0.23; extra == 'runtime'
|
|
32
33
|
Requires-Dist: safetensors>=0.4; extra == 'runtime'
|
|
33
34
|
Requires-Dist: scipy>=1.11; extra == 'runtime'
|
|
@@ -38,6 +39,14 @@ Provides-Extra: server
|
|
|
38
39
|
Requires-Dist: fastapi>=0.110; extra == 'server'
|
|
39
40
|
Requires-Dist: imageio-ffmpeg>=0.4; extra == 'server'
|
|
40
41
|
Requires-Dist: uvicorn[standard]>=0.29; extra == 'server'
|
|
42
|
+
Provides-Extra: training
|
|
43
|
+
Requires-Dist: bitsandbytes>=0.43; (platform_system != 'Darwin') and extra == 'training'
|
|
44
|
+
Requires-Dist: einops>=0.7; extra == 'training'
|
|
45
|
+
Requires-Dist: nvidia-ml-py>=12; extra == 'training'
|
|
46
|
+
Requires-Dist: peft>=0.11; extra == 'training'
|
|
47
|
+
Requires-Dist: pillow>=10; extra == 'training'
|
|
48
|
+
Requires-Dist: psutil>=5.9; extra == 'training'
|
|
49
|
+
Requires-Dist: timm>=1.0; extra == 'training'
|
|
41
50
|
Description-Content-Type: text/markdown
|
|
42
51
|
|
|
43
52
|
# Inline Core
|
|
@@ -47,13 +56,14 @@ The generation engine behind Inline. It takes a typed node graph (JSON) and retu
|
|
|
47
56
|
low-VRAM laptops up to multi-GPU machines that split a single image's sampling across GPUs (via
|
|
48
57
|
xDiT). It is Inline Studio's built-in render backend.
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
already supports, so the multi-GPU split works on it from the start)
|
|
59
|
+
Models today: **Z-Image** (Alibaba Tongyi), a 6B rectified-flow diffusion transformer and the model
|
|
60
|
+
xDiT already supports, so the multi-GPU split works on it from the start; and **Krea 2** (Krea AI),
|
|
61
|
+
a 12.9B single-stream MMDiT released as an undistilled RAW base plus an 8-step distilled Turbo.
|
|
52
62
|
|
|
53
63
|
> Status: early, and running end to end against a stub engine. In place and tested: the graph engine,
|
|
54
64
|
> the typed `/v1` HTTP + websocket API (durable runs, streamed progress, coalescing), the model-dir
|
|
55
65
|
> scan, the device + memory policy (profiles, dtype, offload, int8), the low-level primitive node
|
|
56
|
-
> vocabulary
|
|
66
|
+
> vocabulary. The Z-Image loader is written and validates on a GPU.
|
|
57
67
|
> Cross-request batching, single-image multi-GPU (an xDiT worker group behind the sampler seam, with
|
|
58
68
|
> the policy and IPC round-trip tested), and out-of-process custom nodes are built as seams but not
|
|
59
69
|
> yet running on real hardware.
|
|
@@ -94,9 +104,13 @@ by category:
|
|
|
94
104
|
|
|
95
105
|
```
|
|
96
106
|
models/
|
|
97
|
-
diffusion_models/ z_image_turbo_bf16.safetensors
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
diffusion_models/ z_image_turbo_bf16.safetensors <- Z-Image
|
|
108
|
+
krea2_turbo_bf16.safetensors <- Krea 2 Turbo (generate)
|
|
109
|
+
krea2_raw_bf16.safetensors <- Krea 2 RAW (fine-tune)
|
|
110
|
+
vae/ ae.safetensors <- Z-Image
|
|
111
|
+
qwen_image_vae_diffusers.safetensors <- Krea 2
|
|
112
|
+
text_encoders/ qwen_3_4b.safetensors <- Z-Image
|
|
113
|
+
qwen3vl_4b_bf16.safetensors <- Krea 2
|
|
100
114
|
loras/ controlnet/ checkpoints/ ...
|
|
101
115
|
```
|
|
102
116
|
|
|
@@ -115,8 +129,17 @@ paths:
|
|
|
115
129
|
`models/`** (never the hidden HF cache) with visible progress.
|
|
116
130
|
|
|
117
131
|
Override the diffusion source with `INLINE_ZIMAGE_MODEL` (a file or a diffusers dir), and the supporting
|
|
118
|
-
components with `INLINE_ZIMAGE_VAE` / `INLINE_ZIMAGE_TEXT_ENCODER
|
|
119
|
-
start; a node's model pickers list what is present.
|
|
132
|
+
components with `INLINE_ZIMAGE_VAE` / `INLINE_ZIMAGE_TEXT_ENCODER`; Krea 2 has the same three under
|
|
133
|
+
`INLINE_KREA2_*`. The engine scans the models dir on start; a node's model pickers list what is present.
|
|
134
|
+
|
|
135
|
+
**Krea 2 loads from the same ComfyUI single files**, with two constraints worth knowing. Only the
|
|
136
|
+
**bf16** builds in [`Comfy-Org/Krea-2`](https://huggingface.co/Comfy-Org/Krea-2) are loadable: the
|
|
137
|
+
fp8/int8/nvfp4 variants carry ComfyUI-specific scale tensors, and the loader refuses them by name
|
|
138
|
+
rather than failing mid-load. And the **VAE must be the diffusers-format** Qwen-Image file (the
|
|
139
|
+
node's popup fetches it from `Qwen/Qwen-Image`), because ComfyUI's copy of the same weights uses a
|
|
140
|
+
module layout diffusers has no converter for. `Krea2Transformer2DModel` has no `from_single_file`, so
|
|
141
|
+
the transformer checkpoint is renamed into diffusers naming on load (`models/krea2/convert.py`) and
|
|
142
|
+
streamed tensor by tensor onto the GPU.
|
|
120
143
|
|
|
121
144
|
## Nodes
|
|
122
145
|
|
|
@@ -124,15 +147,15 @@ start; a node's model pickers list what is present.
|
|
|
124
147
|
renders any node generically - adding a node type needs no UI release.
|
|
125
148
|
|
|
126
149
|
**High-level model nodes are what the user sees.** Generation is one-click: you drop a single
|
|
127
|
-
**Z-Image Turbo** node, wire a Prompt into it, and hit Run. The
|
|
128
|
-
and text-encoder behind the scenes - no loader/sampler
|
|
150
|
+
**Z-Image Turbo**, **Krea 2 Turbo** or **Krea 2 RAW** node, wire a Prompt into it, and hit Run. The
|
|
151
|
+
node hooks up the diffusion model, VAE, and text-encoder behind the scenes - no loader/sampler
|
|
152
|
+
wiring.
|
|
129
153
|
|
|
130
154
|
Underneath, a **low-level primitive vocabulary** exists (`load/diffusion-model`, `load/vae`,
|
|
131
155
|
`load/text-encoder`, `encode/text`, `latent/empty`, `sample`, `vae/decode`, `vae/encode`) - the
|
|
132
156
|
ComfyUI-equivalent decomposed graph, kept for validation/execution - but these are marked **`hidden`**
|
|
133
157
|
and never appear in the add-node menu. Engine handles (`model`, `vae`, `text-encoder`, `conditioning`,
|
|
134
|
-
`latent`) are typed sockets between nodes; only media outputs become Frames with take history.
|
|
135
|
-
best-effort ComfyUI importer maps existing workflows onto the primitives.
|
|
158
|
+
`latent`) are typed sockets between nodes; only media outputs become Frames with take history.
|
|
136
159
|
|
|
137
160
|
## Multi-GPU: split one image across GPUs
|
|
138
161
|
|
|
@@ -215,7 +238,7 @@ The easy path is `webui.sh`, which maps friendly flags onto the engine's `INLINE
|
|
|
215
238
|
./webui.sh --listen --port 9000 # bind all interfaces on 9000
|
|
216
239
|
./webui.sh --multi-gpu # split one image across GPUs (auto with 2+ GPUs)
|
|
217
240
|
./webui.sh --lowvram # tight-VRAM profile
|
|
218
|
-
./webui.sh --install --extra
|
|
241
|
+
./webui.sh --install --extra runtime # set up ./.venv with the model runtime, then exit
|
|
219
242
|
```
|
|
220
243
|
|
|
221
244
|
`./webui.sh --help` lists every flag (networking, multi-GPU, device/memory profile, paths). The same
|
|
@@ -5,13 +5,14 @@ The generation engine behind Inline. It takes a typed node graph (JSON) and retu
|
|
|
5
5
|
low-VRAM laptops up to multi-GPU machines that split a single image's sampling across GPUs (via
|
|
6
6
|
xDiT). It is Inline Studio's built-in render backend.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
already supports, so the multi-GPU split works on it from the start)
|
|
8
|
+
Models today: **Z-Image** (Alibaba Tongyi), a 6B rectified-flow diffusion transformer and the model
|
|
9
|
+
xDiT already supports, so the multi-GPU split works on it from the start; and **Krea 2** (Krea AI),
|
|
10
|
+
a 12.9B single-stream MMDiT released as an undistilled RAW base plus an 8-step distilled Turbo.
|
|
10
11
|
|
|
11
12
|
> Status: early, and running end to end against a stub engine. In place and tested: the graph engine,
|
|
12
13
|
> the typed `/v1` HTTP + websocket API (durable runs, streamed progress, coalescing), the model-dir
|
|
13
14
|
> scan, the device + memory policy (profiles, dtype, offload, int8), the low-level primitive node
|
|
14
|
-
> vocabulary
|
|
15
|
+
> vocabulary. The Z-Image loader is written and validates on a GPU.
|
|
15
16
|
> Cross-request batching, single-image multi-GPU (an xDiT worker group behind the sampler seam, with
|
|
16
17
|
> the policy and IPC round-trip tested), and out-of-process custom nodes are built as seams but not
|
|
17
18
|
> yet running on real hardware.
|
|
@@ -52,9 +53,13 @@ by category:
|
|
|
52
53
|
|
|
53
54
|
```
|
|
54
55
|
models/
|
|
55
|
-
diffusion_models/ z_image_turbo_bf16.safetensors
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
diffusion_models/ z_image_turbo_bf16.safetensors <- Z-Image
|
|
57
|
+
krea2_turbo_bf16.safetensors <- Krea 2 Turbo (generate)
|
|
58
|
+
krea2_raw_bf16.safetensors <- Krea 2 RAW (fine-tune)
|
|
59
|
+
vae/ ae.safetensors <- Z-Image
|
|
60
|
+
qwen_image_vae_diffusers.safetensors <- Krea 2
|
|
61
|
+
text_encoders/ qwen_3_4b.safetensors <- Z-Image
|
|
62
|
+
qwen3vl_4b_bf16.safetensors <- Krea 2
|
|
58
63
|
loras/ controlnet/ checkpoints/ ...
|
|
59
64
|
```
|
|
60
65
|
|
|
@@ -73,8 +78,17 @@ paths:
|
|
|
73
78
|
`models/`** (never the hidden HF cache) with visible progress.
|
|
74
79
|
|
|
75
80
|
Override the diffusion source with `INLINE_ZIMAGE_MODEL` (a file or a diffusers dir), and the supporting
|
|
76
|
-
components with `INLINE_ZIMAGE_VAE` / `INLINE_ZIMAGE_TEXT_ENCODER
|
|
77
|
-
start; a node's model pickers list what is present.
|
|
81
|
+
components with `INLINE_ZIMAGE_VAE` / `INLINE_ZIMAGE_TEXT_ENCODER`; Krea 2 has the same three under
|
|
82
|
+
`INLINE_KREA2_*`. The engine scans the models dir on start; a node's model pickers list what is present.
|
|
83
|
+
|
|
84
|
+
**Krea 2 loads from the same ComfyUI single files**, with two constraints worth knowing. Only the
|
|
85
|
+
**bf16** builds in [`Comfy-Org/Krea-2`](https://huggingface.co/Comfy-Org/Krea-2) are loadable: the
|
|
86
|
+
fp8/int8/nvfp4 variants carry ComfyUI-specific scale tensors, and the loader refuses them by name
|
|
87
|
+
rather than failing mid-load. And the **VAE must be the diffusers-format** Qwen-Image file (the
|
|
88
|
+
node's popup fetches it from `Qwen/Qwen-Image`), because ComfyUI's copy of the same weights uses a
|
|
89
|
+
module layout diffusers has no converter for. `Krea2Transformer2DModel` has no `from_single_file`, so
|
|
90
|
+
the transformer checkpoint is renamed into diffusers naming on load (`models/krea2/convert.py`) and
|
|
91
|
+
streamed tensor by tensor onto the GPU.
|
|
78
92
|
|
|
79
93
|
## Nodes
|
|
80
94
|
|
|
@@ -82,15 +96,15 @@ start; a node's model pickers list what is present.
|
|
|
82
96
|
renders any node generically - adding a node type needs no UI release.
|
|
83
97
|
|
|
84
98
|
**High-level model nodes are what the user sees.** Generation is one-click: you drop a single
|
|
85
|
-
**Z-Image Turbo** node, wire a Prompt into it, and hit Run. The
|
|
86
|
-
and text-encoder behind the scenes - no loader/sampler
|
|
99
|
+
**Z-Image Turbo**, **Krea 2 Turbo** or **Krea 2 RAW** node, wire a Prompt into it, and hit Run. The
|
|
100
|
+
node hooks up the diffusion model, VAE, and text-encoder behind the scenes - no loader/sampler
|
|
101
|
+
wiring.
|
|
87
102
|
|
|
88
103
|
Underneath, a **low-level primitive vocabulary** exists (`load/diffusion-model`, `load/vae`,
|
|
89
104
|
`load/text-encoder`, `encode/text`, `latent/empty`, `sample`, `vae/decode`, `vae/encode`) - the
|
|
90
105
|
ComfyUI-equivalent decomposed graph, kept for validation/execution - but these are marked **`hidden`**
|
|
91
106
|
and never appear in the add-node menu. Engine handles (`model`, `vae`, `text-encoder`, `conditioning`,
|
|
92
|
-
`latent`) are typed sockets between nodes; only media outputs become Frames with take history.
|
|
93
|
-
best-effort ComfyUI importer maps existing workflows onto the primitives.
|
|
107
|
+
`latent`) are typed sockets between nodes; only media outputs become Frames with take history.
|
|
94
108
|
|
|
95
109
|
## Multi-GPU: split one image across GPUs
|
|
96
110
|
|
|
@@ -173,7 +187,7 @@ The easy path is `webui.sh`, which maps friendly flags onto the engine's `INLINE
|
|
|
173
187
|
./webui.sh --listen --port 9000 # bind all interfaces on 9000
|
|
174
188
|
./webui.sh --multi-gpu # split one image across GPUs (auto with 2+ GPUs)
|
|
175
189
|
./webui.sh --lowvram # tight-VRAM profile
|
|
176
|
-
./webui.sh --install --extra
|
|
190
|
+
./webui.sh --install --extra runtime # set up ./.venv with the model runtime, then exit
|
|
177
191
|
```
|
|
178
192
|
|
|
179
193
|
`./webui.sh --help` lists every flag (networking, multi-GPU, device/memory profile, paths). The same
|
|
@@ -1,9 +1,10 @@
|
|
|
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.5"
|
|
5
5
|
description = "The generation engine behind Inline Studio."
|
|
6
6
|
readme = "README.md"
|
|
7
|
+
license = "GPL-3.0-or-later"
|
|
7
8
|
requires-python = ">=3.11"
|
|
8
9
|
# Deliberately torch-free so the engine boots and serves source nodes with no ML stack installed.
|
|
9
10
|
dependencies = [
|
|
@@ -16,8 +17,8 @@ dependencies = [
|
|
|
16
17
|
runtime = [
|
|
17
18
|
# A bare `torch` is CPU-only on Windows; see [tool.uv] below and requirements.txt.
|
|
18
19
|
"torch>=2.2",
|
|
19
|
-
# Z-Image needs
|
|
20
|
-
"diffusers>=0.
|
|
20
|
+
# Krea 2 needs diffusers >= 0.39 (Krea2Pipeline); Z-Image needs >= 0.36 (ZImagePipeline).
|
|
21
|
+
"diffusers>=0.39",
|
|
21
22
|
"transformers>=4.44",
|
|
22
23
|
"accelerate>=0.30",
|
|
23
24
|
"safetensors>=0.4",
|
|
@@ -39,6 +40,20 @@ parallel = [
|
|
|
39
40
|
"xfuser>=0.4",
|
|
40
41
|
"nvidia-ml-py>=12",
|
|
41
42
|
]
|
|
43
|
+
# LoRA training (the Trainer tab): PEFT adapter training + local auto-caption + host/GPU telemetry.
|
|
44
|
+
# Install alongside `runtime`: `.[runtime,training]`. Reuses runtime's torch/diffusers/accelerate.
|
|
45
|
+
training = [
|
|
46
|
+
"peft>=0.11",
|
|
47
|
+
# 8-bit Adam keeps optimizer state small; no macOS wheels, so skip it there (AdamW fallback).
|
|
48
|
+
"bitsandbytes>=0.43; platform_system != 'Darwin'",
|
|
49
|
+
"Pillow>=10",
|
|
50
|
+
# Telemetry (CPU/RAM + per-GPU NVML) for the Trainer tab.
|
|
51
|
+
"psutil>=5.9",
|
|
52
|
+
"nvidia-ml-py>=12",
|
|
53
|
+
# Florence-2 auto-captioner is loaded via trust_remote_code and needs these.
|
|
54
|
+
"timm>=1.0",
|
|
55
|
+
"einops>=0.7",
|
|
56
|
+
]
|
|
42
57
|
dev = [
|
|
43
58
|
"pytest>=8",
|
|
44
59
|
"ruff>=0.6",
|
|
@@ -48,7 +63,7 @@ dev = [
|
|
|
48
63
|
# Everything needed to run locally. Lists concrete packages, never `inline-core[...]` self-refs.
|
|
49
64
|
all = [
|
|
50
65
|
"torch>=2.2",
|
|
51
|
-
"diffusers>=0.
|
|
66
|
+
"diffusers>=0.39",
|
|
52
67
|
"transformers>=4.44",
|
|
53
68
|
"accelerate>=0.30",
|
|
54
69
|
"safetensors>=0.4",
|
|
@@ -49,12 +49,18 @@ def remote_sha(url: str, ref: str) -> str | None:
|
|
|
49
49
|
an update check must never fail the dialog."""
|
|
50
50
|
try:
|
|
51
51
|
_validate(url, ref)
|
|
52
|
-
|
|
52
|
+
# Also request the peeled ref: an exact-match pattern alone won't return the `^{}` line.
|
|
53
|
+
done = _git("ls-remote", url, ref, f"{ref}^{{}}", check=False)
|
|
53
54
|
except (FetchError, OSError, subprocess.SubprocessError):
|
|
54
55
|
return None
|
|
55
56
|
if done.returncode != 0:
|
|
56
57
|
return None
|
|
57
|
-
|
|
58
|
+
lines = [ln for ln in done.stdout.splitlines() if ln.strip()]
|
|
59
|
+
# An annotated tag lists its own object sha first and the commit it points to on a `^{}` line.
|
|
60
|
+
# Prefer the peeled commit, since the installed sha is a commit (`_resolve` uses `^{commit}`) -
|
|
61
|
+
# otherwise every annotated-tag release reads as perpetually "update available".
|
|
62
|
+
peeled = next((ln for ln in lines if ln.rstrip().endswith("^{}")), None)
|
|
63
|
+
line = peeled or (lines[0] if lines else "")
|
|
58
64
|
sha = line.split()[0] if line else ""
|
|
59
65
|
return sha or None
|
|
60
66
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Locate the ffmpeg/ffprobe binaries. Prefers a bundled ``imageio-ffmpeg``, else PATH.
|
|
2
|
+
|
|
3
|
+
Lives at the top level rather than under ``studio/`` because both the timeline (studio) and the
|
|
4
|
+
take store (runtime) need it, and runtime must not import studio.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import shutil
|
|
10
|
+
from functools import lru_cache
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@lru_cache(maxsize=1)
|
|
14
|
+
def ffmpeg_exe() -> str | None:
|
|
15
|
+
try:
|
|
16
|
+
import imageio_ffmpeg
|
|
17
|
+
|
|
18
|
+
return imageio_ffmpeg.get_ffmpeg_exe()
|
|
19
|
+
except Exception: # noqa: BLE001
|
|
20
|
+
return shutil.which("ffmpeg")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@lru_cache(maxsize=1)
|
|
24
|
+
def ffprobe_exe() -> str | None:
|
|
25
|
+
"""PATH only - imageio bundles ffmpeg alone, so probing degrades gracefully when absent."""
|
|
26
|
+
return shutil.which("ffprobe")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def ffmpeg_available() -> bool:
|
|
30
|
+
return ffmpeg_exe() is not None
|
|
@@ -20,7 +20,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
20
20
|
|
|
21
21
|
from ..config import models_dir
|
|
22
22
|
from ..errors import ComponentError
|
|
23
|
-
from .primitives import LOAD_DIFFUSION_MODEL, LOAD_TEXT_ENCODER, LOAD_VAE
|
|
23
|
+
from .primitives import LOAD_DIFFUSION_MODEL, LOAD_LORA, LOAD_TEXT_ENCODER, LOAD_VAE
|
|
24
24
|
from .runners import NodeResult, NodeRunner
|
|
25
25
|
from .schema import Node
|
|
26
26
|
|
|
@@ -44,6 +44,16 @@ class ComponentRef:
|
|
|
44
44
|
file: str
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class LoraRef:
|
|
49
|
+
"""One LoRA in a stack: its absolute file path and blend strength. A ``load/lora`` node emits a
|
|
50
|
+
tuple of these (its own ref appended to any upstream stack); the model runner fuses them into
|
|
51
|
+
the diffusion transformer in order. Frozen + hashable so it can key the loader cache."""
|
|
52
|
+
|
|
53
|
+
file: str
|
|
54
|
+
strength: float
|
|
55
|
+
|
|
56
|
+
|
|
47
57
|
def _resolve_file(category: str, chosen: str) -> Path:
|
|
48
58
|
"""The single weight file a Load node points at: the explicit dropdown pick, else the first
|
|
49
59
|
weight file in ``models/<category>/`` (mirrors the model node's "auto"). Raises if none."""
|
|
@@ -81,6 +91,26 @@ class LoadComponentRunner(NodeRunner):
|
|
|
81
91
|
return NodeResult(outputs={self._output: ref})
|
|
82
92
|
|
|
83
93
|
|
|
94
|
+
class LoadLoraRunner(NodeRunner):
|
|
95
|
+
"""Resolve this node's ``file``/``strength`` into a ``LoraRef`` and append it to any upstream
|
|
96
|
+
stack on the ``lora`` input - so chaining ``load/lora`` nodes stacks them in wiring order. The
|
|
97
|
+
stack rides its own ``lora`` edge into the model runner, which fuses it into the transformer."""
|
|
98
|
+
|
|
99
|
+
produces_takes = False
|
|
100
|
+
|
|
101
|
+
def run(self, node: Node, inputs: dict[str, list[Any]], ctx: ExecutionContext) -> NodeResult:
|
|
102
|
+
upstream = _first(inputs.get("lora")) or ()
|
|
103
|
+
file = _resolve_file("loras", str(node.params.get("file", "")))
|
|
104
|
+
strength = float(node.params.get("strength", 1.0))
|
|
105
|
+
stack = (*upstream, LoraRef(file=str(file), strength=strength))
|
|
106
|
+
return NodeResult(outputs={"lora": stack})
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _first(values: list[Any] | None) -> Any:
|
|
110
|
+
"""The first wired value on a port, or None (an optional input may be absent/unconnected)."""
|
|
111
|
+
return values[0] if values else None
|
|
112
|
+
|
|
113
|
+
|
|
84
114
|
def register_loaders(registry: Registry) -> None:
|
|
85
115
|
"""Register the ``load/*`` nodes **visible** (unhidden) with their runners, so they appear in
|
|
86
116
|
the add-node menu and can feed a model node's component inputs. Torch-free - always on."""
|
|
@@ -98,3 +128,4 @@ def register_loaders(registry: Registry) -> None:
|
|
|
98
128
|
kind="text_encoder", category="text_encoders", output_port="text_encoder"
|
|
99
129
|
),
|
|
100
130
|
)
|
|
131
|
+
registry.register(replace(LOAD_LORA, hidden=False), LoadLoraRunner())
|
|
@@ -47,6 +47,20 @@ LOAD_TEXT_ENCODER = NodeDescriptor(
|
|
|
47
47
|
outputs=(Port("text_encoder", "Text encoder", PortKind.TEXT_ENCODER),),
|
|
48
48
|
)
|
|
49
49
|
|
|
50
|
+
LOAD_LORA = NodeDescriptor(
|
|
51
|
+
type="load/lora",
|
|
52
|
+
title="Load LoRA",
|
|
53
|
+
category="Loaders",
|
|
54
|
+
icon="box",
|
|
55
|
+
# Optional upstream lora: chain load/lora -> load/lora to stack several in order.
|
|
56
|
+
inputs=(Port("lora", "LoRA", PortKind.LORA, required=False),),
|
|
57
|
+
params=(
|
|
58
|
+
ParamField("file", "LoRA", Widget.SELECT, "", options_from="loras"),
|
|
59
|
+
ParamField("strength", "Strength", Widget.NUMBER, 1.0, min=-2.0, max=2.0, step=0.05),
|
|
60
|
+
),
|
|
61
|
+
outputs=(Port("lora", "LoRA", PortKind.LORA),),
|
|
62
|
+
)
|
|
63
|
+
|
|
50
64
|
ENCODE_TEXT = NodeDescriptor(
|
|
51
65
|
type="encode/text",
|
|
52
66
|
title="Encode Text",
|
|
@@ -122,6 +136,7 @@ PRIMITIVES: tuple[NodeDescriptor, ...] = (
|
|
|
122
136
|
LOAD_DIFFUSION_MODEL,
|
|
123
137
|
LOAD_VAE,
|
|
124
138
|
LOAD_TEXT_ENCODER,
|
|
139
|
+
LOAD_LORA,
|
|
125
140
|
ENCODE_TEXT,
|
|
126
141
|
EMPTY_LATENT,
|
|
127
142
|
SAMPLE,
|
|
@@ -132,7 +147,7 @@ PRIMITIVES: tuple[NodeDescriptor, ...] = (
|
|
|
132
147
|
|
|
133
148
|
#: The loader primitives now have runners and are offered in the add-node menu - registered
|
|
134
149
|
#: (unhidden) with their runners by ``graph/loader_runners.py``, so they are skipped here.
|
|
135
|
-
_HAS_RUNNER = {LOAD_DIFFUSION_MODEL.type, LOAD_VAE.type, LOAD_TEXT_ENCODER.type}
|
|
150
|
+
_HAS_RUNNER = {LOAD_DIFFUSION_MODEL.type, LOAD_VAE.type, LOAD_TEXT_ENCODER.type, LOAD_LORA.type}
|
|
136
151
|
|
|
137
152
|
|
|
138
153
|
def register_primitives(registry: Registry) -> None:
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Read a safetensors checkpoint tensor by tensor, without mapping the whole file.
|
|
2
|
+
|
|
3
|
+
``safetensors.safe_open`` maps the entire file at once, and Linux refuses a mapping larger than
|
|
4
|
+
physical RAM when there is no swap (the default heuristic overcommit mode). A 26GB Krea 2 checkpoint
|
|
5
|
+
is therefore unreadable on a 16GB machine - it fails with ``Cannot allocate memory`` before any GPU
|
|
6
|
+
work, no matter how small the model would be once quantized.
|
|
7
|
+
|
|
8
|
+
Reading each tensor's byte range instead keeps peak host RAM at one tensor and works for any file
|
|
9
|
+
size. Only used for the big single-file checkpoints; small files (LoRAs, VAEs) still go through
|
|
10
|
+
safetensors directly.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import struct
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from ..errors import ComponentError
|
|
21
|
+
|
|
22
|
+
#: safetensors dtype names -> torch dtypes, resolved lazily so importing this module stays cheap.
|
|
23
|
+
_DTYPE_NAMES = {
|
|
24
|
+
"F64": "float64",
|
|
25
|
+
"F32": "float32",
|
|
26
|
+
"F16": "float16",
|
|
27
|
+
"BF16": "bfloat16",
|
|
28
|
+
"I64": "int64",
|
|
29
|
+
"I32": "int32",
|
|
30
|
+
"I16": "int16",
|
|
31
|
+
"I8": "int8",
|
|
32
|
+
"U8": "uint8",
|
|
33
|
+
"BOOL": "bool",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CheckpointReader:
|
|
38
|
+
"""Random access to one safetensors file, one tensor at a time."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, path: str | Path) -> None:
|
|
41
|
+
self._path = Path(path)
|
|
42
|
+
try:
|
|
43
|
+
size = self._path.stat().st_size
|
|
44
|
+
with self._path.open("rb") as handle:
|
|
45
|
+
(header_len,) = struct.unpack("<Q", handle.read(8))
|
|
46
|
+
# Garbage bytes decode to an enormous length; reject it rather than trying to
|
|
47
|
+
# allocate it, which would raise MemoryError instead of a usable message.
|
|
48
|
+
if not 0 < header_len <= size - 8:
|
|
49
|
+
raise ValueError("header length is not plausible for this file")
|
|
50
|
+
header = json.loads(handle.read(header_len))
|
|
51
|
+
if not isinstance(header, dict):
|
|
52
|
+
raise ValueError("header is not a JSON object")
|
|
53
|
+
except (OSError, ValueError, struct.error) as error:
|
|
54
|
+
raise ComponentError(f"Could not read checkpoint {self._path.name}: {error}") from error
|
|
55
|
+
self._start = 8 + header_len
|
|
56
|
+
self._index: dict[str, Any] = {k: v for k, v in header.items() if k != "__metadata__"}
|
|
57
|
+
self.metadata: dict[str, Any] = header.get("__metadata__") or {}
|
|
58
|
+
|
|
59
|
+
def keys(self) -> list[str]:
|
|
60
|
+
return list(self._index)
|
|
61
|
+
|
|
62
|
+
def get_tensor(self, key: str, device: str | None = None) -> Any:
|
|
63
|
+
"""One tensor, read straight from its byte range into a fresh buffer."""
|
|
64
|
+
import torch
|
|
65
|
+
|
|
66
|
+
entry = self._index[key]
|
|
67
|
+
dtype_name = _DTYPE_NAMES.get(entry["dtype"])
|
|
68
|
+
if dtype_name is None:
|
|
69
|
+
raise ComponentError(
|
|
70
|
+
f"Checkpoint {self._path.name} uses the unsupported dtype {entry['dtype']!r} "
|
|
71
|
+
f"for {key!r}."
|
|
72
|
+
)
|
|
73
|
+
start, end = entry["data_offsets"]
|
|
74
|
+
buffer = bytearray(end - start)
|
|
75
|
+
with self._path.open("rb") as handle:
|
|
76
|
+
handle.seek(self._start + start)
|
|
77
|
+
if handle.readinto(buffer) != len(buffer):
|
|
78
|
+
raise ComponentError(f"Checkpoint {self._path.name} is truncated at {key!r}.")
|
|
79
|
+
tensor = torch.frombuffer(buffer, dtype=getattr(torch, dtype_name))
|
|
80
|
+
tensor = tensor.reshape(entry["shape"]) if entry["shape"] else tensor.reshape(())
|
|
81
|
+
return tensor.to(device) if device else tensor
|
|
82
|
+
|
|
83
|
+
def __enter__(self) -> CheckpointReader:
|
|
84
|
+
return self
|
|
85
|
+
|
|
86
|
+
def __exit__(self, *_exc: object) -> None:
|
|
87
|
+
return None
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""The Krea 2 (Krea AI) runtime: a diffusers-backed text-to-image / img2img runner.
|
|
2
|
+
|
|
3
|
+
Optional subpackage. `server.bootstrap` imports `register_krea2` best-effort, so a core install
|
|
4
|
+
without the ``runtime`` extra (torch + diffusers) still boots and serves the source nodes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .runner import register_krea2
|
|
8
|
+
|
|
9
|
+
__all__ = ["register_krea2"]
|