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.
Files changed (184) hide show
  1. {inline_core-1.2.2 → inline_core-1.2.5}/CLAUDE.md +3 -6
  2. {inline_core-1.2.2 → inline_core-1.2.5}/PKG-INFO +39 -16
  3. {inline_core-1.2.2 → inline_core-1.2.5}/README.md +27 -13
  4. {inline_core-1.2.2 → inline_core-1.2.5}/pyproject.toml +19 -4
  5. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/fetch.py +8 -2
  6. inline_core-1.2.5/src/inline_core/ffmpeg.py +30 -0
  7. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/loader_runners.py +32 -1
  8. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/primitives.py +16 -1
  9. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/schema.py +1 -0
  10. inline_core-1.2.5/src/inline_core/models/checkpoint.py +87 -0
  11. inline_core-1.2.5/src/inline_core/models/krea2/__init__.py +9 -0
  12. inline_core-1.2.5/src/inline_core/models/krea2/convert.py +138 -0
  13. inline_core-1.2.5/src/inline_core/models/krea2/img2img.py +89 -0
  14. inline_core-1.2.5/src/inline_core/models/krea2/provider.py +57 -0
  15. inline_core-1.2.5/src/inline_core/models/krea2/requirements.py +180 -0
  16. inline_core-1.2.5/src/inline_core/models/krea2/runner.py +407 -0
  17. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/loaders.py +381 -34
  18. inline_core-1.2.5/src/inline_core/models/lora.py +190 -0
  19. inline_core-1.2.5/src/inline_core/models/pipeline_runtime.py +522 -0
  20. inline_core-1.2.5/src/inline_core/models/zimage/runner.py +465 -0
  21. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/__main__.py +6 -1
  22. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/app.py +9 -0
  23. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/bootstrap.py +10 -0
  24. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/rpc.py +5 -0
  25. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/config.py +0 -1
  26. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/frames.py +4 -17
  27. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/graph_build.py +13 -2
  28. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/handlers.py +39 -10
  29. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/models.py +42 -59
  30. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/moodboard.py +114 -23
  31. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/schema.py +59 -3
  32. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/store.py +0 -8
  33. inline_core-1.2.5/src/inline_core/studio/system_stats.py +92 -0
  34. inline_core-1.2.5/src/inline_core/studio/training.py +477 -0
  35. inline_core-1.2.5/src/inline_core/studio/training_store.py +274 -0
  36. inline_core-1.2.5/src/inline_core/training/__init__.py +10 -0
  37. inline_core-1.2.5/src/inline_core/training/__main__.py +74 -0
  38. inline_core-1.2.5/src/inline_core/training/arch.py +184 -0
  39. inline_core-1.2.5/src/inline_core/training/caption.py +127 -0
  40. inline_core-1.2.5/src/inline_core/training/dataset.py +158 -0
  41. inline_core-1.2.5/src/inline_core/training/models.py +288 -0
  42. inline_core-1.2.5/src/inline_core/training/protocol.py +56 -0
  43. inline_core-1.2.5/src/inline_core/training/trainer.py +236 -0
  44. inline_core-1.2.5/tests/test_checkpoint.py +79 -0
  45. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_install.py +27 -0
  46. inline_core-1.2.5/tests/test_krea2_convert.py +157 -0
  47. inline_core-1.2.5/tests/test_krea2_requirements.py +115 -0
  48. inline_core-1.2.5/tests/test_krea2_runner.py +163 -0
  49. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_loaders.py +15 -3
  50. inline_core-1.2.5/tests/test_lora.py +218 -0
  51. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_frames.py +3 -2
  52. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_generation.py +25 -1
  53. inline_core-1.2.5/tests/test_studio_models.py +90 -0
  54. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_rpc.py +3 -3
  55. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_store.py +2 -5
  56. inline_core-1.2.5/tests/test_studio_training.py +81 -0
  57. inline_core-1.2.5/tests/test_training_arch.py +113 -0
  58. inline_core-1.2.5/tests/test_training_dataset.py +103 -0
  59. inline_core-1.2.5/tests/test_training_models.py +106 -0
  60. inline_core-1.2.5/tests/test_training_resolve.py +90 -0
  61. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_zimage_runner.py +27 -24
  62. {inline_core-1.2.2 → inline_core-1.2.5}/uv.lock +130 -4
  63. {inline_core-1.2.2 → inline_core-1.2.5}/webui.sh +1 -1
  64. inline_core-1.2.2/src/inline_core/importer/__init__.py +0 -1
  65. inline_core-1.2.2/src/inline_core/importer/comfy.py +0 -162
  66. inline_core-1.2.2/src/inline_core/models/zimage/runner.py +0 -945
  67. inline_core-1.2.2/tests/test_comfy_import.py +0 -80
  68. inline_core-1.2.2/tests/test_studio_models.py +0 -84
  69. {inline_core-1.2.2 → inline_core-1.2.5}/.gitignore +0 -0
  70. {inline_core-1.2.2 → inline_core-1.2.5}/.python-version +0 -0
  71. {inline_core-1.2.2 → inline_core-1.2.5}/main.py +0 -0
  72. {inline_core-1.2.2 → inline_core-1.2.5}/scripts/reference.py +0 -0
  73. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/__init__.py +0 -0
  74. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/components/__init__.py +0 -0
  75. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/components/conditioning.py +0 -0
  76. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/components/interfaces.py +0 -0
  77. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/config.py +0 -0
  78. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/__init__.py +0 -0
  79. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/auto.py +0 -0
  80. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/detect.py +0 -0
  81. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/memory.py +0 -0
  82. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/policy.py +0 -0
  83. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/device/types.py +0 -0
  84. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/errors.py +0 -0
  85. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/__init__.py +0 -0
  86. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/api.py +0 -0
  87. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/constraints.py +0 -0
  88. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/handlers.py +0 -0
  89. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/importer.py +0 -0
  90. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/install.py +0 -0
  91. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/loader.py +0 -0
  92. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/manifest.py +0 -0
  93. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/models.py +0 -0
  94. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/paths.py +0 -0
  95. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/resolve.py +0 -0
  96. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/scanner.py +0 -0
  97. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/state.py +0 -0
  98. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/extensions/tools.py +0 -0
  99. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/__init__.py +0 -0
  100. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/cache.py +0 -0
  101. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/descriptor.py +0 -0
  102. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/executor.py +0 -0
  103. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/registry.py +0 -0
  104. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/runners.py +0 -0
  105. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/topo.py +0 -0
  106. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/graph/validate.py +0 -0
  107. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/media.py +0 -0
  108. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/__init__.py +0 -0
  109. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/catalog.py +0 -0
  110. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/requirements.py +0 -0
  111. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/sampling.py +0 -0
  112. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/__init__.py +0 -0
  113. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/primitives.py +0 -0
  114. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/provider.py +0 -0
  115. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/models/zimage/requirements.py +0 -0
  116. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/__init__.py +0 -0
  117. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/config.py +0 -0
  118. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/group.py +0 -0
  119. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/launch.py +0 -0
  120. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/protocol.py +0 -0
  121. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/registry.py +0 -0
  122. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/parallel/worker.py +0 -0
  123. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/__init__.py +0 -0
  124. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/context.py +0 -0
  125. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/file_store.py +0 -0
  126. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/progress.py +0 -0
  127. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/run.py +0 -0
  128. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/runtime/store.py +0 -0
  129. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/sampling/__init__.py +0 -0
  130. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/sampling/batch.py +0 -0
  131. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/__init__.py +0 -0
  132. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/assets.py +0 -0
  133. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/frontend.py +0 -0
  134. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/manager.py +0 -0
  135. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/run_store.py +0 -0
  136. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/server/serialize.py +0 -0
  137. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/__init__.py +0 -0
  138. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/assets.py +0 -0
  139. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/fal.py +0 -0
  140. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/generation.py +0 -0
  141. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/peaks.py +0 -0
  142. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/__init__.py +0 -0
  143. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/compose.py +0 -0
  144. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/ffmpeg.py +0 -0
  145. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/render.py +0 -0
  146. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/studio/timeline/resolve.py +0 -0
  147. {inline_core-1.2.2 → inline_core-1.2.5}/src/inline_core/takes.py +0 -0
  148. {inline_core-1.2.2 → inline_core-1.2.5}/tests/helpers.py +0 -0
  149. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_cache.py +0 -0
  150. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_catalog.py +0 -0
  151. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_config.py +0 -0
  152. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_device_detect.py +0 -0
  153. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_executor.py +0 -0
  154. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_api.py +0 -0
  155. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_manifest.py +0 -0
  156. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_resolve.py +0 -0
  157. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_scanner.py +0 -0
  158. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_spine.py +0 -0
  159. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_extension_state.py +0 -0
  160. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_file_store.py +0 -0
  161. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_frontend_serving.py +0 -0
  162. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_hidden_nodes.py +0 -0
  163. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_loader_runners.py +0 -0
  164. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_memory_policy.py +0 -0
  165. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_model_requirements.py +0 -0
  166. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_parallel_group.py +0 -0
  167. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_primitives.py +0 -0
  168. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_rpc_bridge.py +0 -0
  169. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_run_store.py +0 -0
  170. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_sampling.py +0 -0
  171. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_schema.py +0 -0
  172. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_server.py +0 -0
  173. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_assets.py +0 -0
  174. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_fal.py +0 -0
  175. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_moodboard.py +0 -0
  176. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_peaks.py +0 -0
  177. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_schema.py +0 -0
  178. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_studio_timeline.py +0 -0
  179. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_take_bytes.py +0 -0
  180. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_topo.py +0 -0
  181. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_validate.py +0 -0
  182. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_xfuser_sampler.py +0 -0
  183. {inline_core-1.2.2 → inline_core-1.2.5}/tests/test_zimage_primitives.py +0 -0
  184. {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 ComfyUI-format knowledge lives in `importer/comfy.py`; all xDiT/worker
204
- knowledge behind `parallel/` and the sampler seam. Don't scatter it.
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
- the comfy importer, and each model runner (import-guarded, no GPU needed). See `tests/`.
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.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.36; extra == 'all'
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.36; extra == 'runtime'
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
- First model: Z-Image (Alibaba Tongyi), a 6B rectified-flow diffusion transformer (and a model xDiT
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, and a ComfyUI workflow importer. The Z-Image loader is written and validates on a GPU.
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 <- the one file you need for Z-Image
98
- vae/ ae.safetensors <- optional (see below)
99
- text_encoders/ qwen/ (an HF-format folder: config + tokenizer + weights) <- optional
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`. The engine scans the models dir on
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 node hooks up the diffusion model, VAE,
128
- and text-encoder behind the scenes - no loader/sampler wiring.
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. A
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 zimage # set up ./.venv with the Z-Image runtime, then exit
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
- First model: Z-Image (Alibaba Tongyi), a 6B rectified-flow diffusion transformer (and a model xDiT
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, and a ComfyUI workflow importer. The Z-Image loader is written and validates on a GPU.
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 <- the one file you need for Z-Image
56
- vae/ ae.safetensors <- optional (see below)
57
- text_encoders/ qwen/ (an HF-format folder: config + tokenizer + weights) <- optional
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`. The engine scans the models dir on
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 node hooks up the diffusion model, VAE,
86
- and text-encoder behind the scenes - no loader/sampler wiring.
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. A
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 zimage # set up ./.venv with the Z-Image runtime, then exit
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.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 diffusers >= 0.36 (ZImagePipeline).
20
- "diffusers>=0.36",
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.36",
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
- done = _git("ls-remote", url, ref, check=False)
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
- line = done.stdout.strip().split("\n")[0]
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:
@@ -23,6 +23,7 @@ class PortKind(str, Enum):
23
23
  MODEL = "model"
24
24
  VAE = "vae"
25
25
  TEXT_ENCODER = "text-encoder"
26
+ LORA = "lora"
26
27
  CONDITIONING = "conditioning"
27
28
  LATENT = "latent"
28
29
 
@@ -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"]