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.
Files changed (194) hide show
  1. {inline_core-1.2.52 → inline_core-1.2.53}/PKG-INFO +5 -1
  2. {inline_core-1.2.52 → inline_core-1.2.53}/pyproject.toml +7 -1
  3. inline_core-1.2.53/src/inline_core/__init__.py +14 -0
  4. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/memory.py +4 -3
  5. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/policy.py +5 -1
  6. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/cache.py +35 -2
  7. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/executor.py +16 -4
  8. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/schema.py +5 -1
  9. inline_core-1.2.53/src/inline_core/models/controlspace.py +26 -0
  10. inline_core-1.2.53/src/inline_core/models/krea2/depth_control.py +149 -0
  11. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/img2img.py +2 -2
  12. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/requirements.py +75 -1
  13. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/runner.py +62 -3
  14. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/loaders.py +108 -3
  15. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/pipeline_runtime.py +60 -19
  16. inline_core-1.2.53/src/inline_core/models/preprocess/__init__.py +5 -0
  17. inline_core-1.2.53/src/inline_core/models/preprocess/requirements.py +59 -0
  18. inline_core-1.2.53/src/inline_core/models/preprocess/runner.py +174 -0
  19. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/requirements.py +3 -0
  20. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/requirements.py +90 -4
  21. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/runner.py +69 -5
  22. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/bootstrap.py +16 -0
  23. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/fal.py +33 -4
  24. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/generation.py +26 -5
  25. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/graph_build.py +85 -2
  26. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/handlers.py +12 -2
  27. inline_core-1.2.53/src/inline_core/studio/image_meta.py +36 -0
  28. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/models.py +6 -2
  29. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/moodboard.py +37 -0
  30. inline_core-1.2.53/src/inline_core/studio/recipe.py +109 -0
  31. {inline_core-1.2.52 → inline_core-1.2.53}/tests/helpers.py +1 -0
  32. inline_core-1.2.53/tests/test_cache.py +116 -0
  33. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_executor.py +26 -1
  34. inline_core-1.2.53/tests/test_krea2_depth_control.py +104 -0
  35. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_krea2_requirements.py +35 -2
  36. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_krea2_runner.py +4 -3
  37. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_loaders.py +35 -0
  38. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_model_requirements.py +25 -0
  39. inline_core-1.2.53/tests/test_pipeline_cache.py +82 -0
  40. inline_core-1.2.53/tests/test_recipe.py +100 -0
  41. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_generation.py +38 -0
  42. inline_core-1.2.53/tests/test_studio_graph_build.py +111 -0
  43. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_models.py +20 -0
  44. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_rpc.py +12 -0
  45. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_zimage_resolve.py +6 -2
  46. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_zimage_runner.py +109 -13
  47. {inline_core-1.2.52 → inline_core-1.2.53}/uv.lock +258 -1
  48. {inline_core-1.2.52 → inline_core-1.2.53}/webui.bat +3 -1
  49. {inline_core-1.2.52 → inline_core-1.2.53}/webui.sh +4 -1
  50. inline_core-1.2.52/src/inline_core/__init__.py +0 -7
  51. inline_core-1.2.52/tests/test_cache.py +0 -48
  52. {inline_core-1.2.52 → inline_core-1.2.53}/.gitignore +0 -0
  53. {inline_core-1.2.52 → inline_core-1.2.53}/.python-version +0 -0
  54. {inline_core-1.2.52 → inline_core-1.2.53}/CLAUDE.md +0 -0
  55. {inline_core-1.2.52 → inline_core-1.2.53}/README.md +0 -0
  56. {inline_core-1.2.52 → inline_core-1.2.53}/main.py +0 -0
  57. {inline_core-1.2.52 → inline_core-1.2.53}/scripts/reference.py +0 -0
  58. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/components/__init__.py +0 -0
  59. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/components/conditioning.py +0 -0
  60. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/components/interfaces.py +0 -0
  61. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/config.py +0 -0
  62. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/__init__.py +0 -0
  63. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/auto.py +0 -0
  64. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/detect.py +0 -0
  65. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/device/types.py +0 -0
  66. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/errors.py +0 -0
  67. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/__init__.py +0 -0
  68. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/api.py +0 -0
  69. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/constraints.py +0 -0
  70. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/fetch.py +0 -0
  71. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/handlers.py +0 -0
  72. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/importer.py +0 -0
  73. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/install.py +0 -0
  74. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/loader.py +0 -0
  75. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/manifest.py +0 -0
  76. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/models.py +0 -0
  77. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/paths.py +0 -0
  78. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/resolve.py +0 -0
  79. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/scanner.py +0 -0
  80. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/state.py +0 -0
  81. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/extensions/tools.py +0 -0
  82. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/ffmpeg.py +0 -0
  83. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/__init__.py +0 -0
  84. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/descriptor.py +0 -0
  85. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/loader_runners.py +0 -0
  86. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/primitives.py +0 -0
  87. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/registry.py +0 -0
  88. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/runners.py +0 -0
  89. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/topo.py +0 -0
  90. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/graph/validate.py +0 -0
  91. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/media.py +0 -0
  92. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/__init__.py +0 -0
  93. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/catalog.py +0 -0
  94. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/checkpoint.py +0 -0
  95. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/__init__.py +0 -0
  96. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/convert.py +0 -0
  97. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/krea2/provider.py +0 -0
  98. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/lora.py +0 -0
  99. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/sampling.py +0 -0
  100. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/__init__.py +0 -0
  101. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/primitives.py +0 -0
  102. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/models/zimage/provider.py +0 -0
  103. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/__init__.py +0 -0
  104. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/config.py +0 -0
  105. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/group.py +0 -0
  106. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/launch.py +0 -0
  107. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/protocol.py +0 -0
  108. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/registry.py +0 -0
  109. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/parallel/worker.py +0 -0
  110. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/__init__.py +0 -0
  111. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/context.py +0 -0
  112. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/file_store.py +0 -0
  113. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/progress.py +0 -0
  114. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/run.py +0 -0
  115. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/runtime/store.py +0 -0
  116. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/sampling/__init__.py +0 -0
  117. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/sampling/batch.py +0 -0
  118. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/__init__.py +0 -0
  119. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/__main__.py +0 -0
  120. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/app.py +0 -0
  121. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/assets.py +0 -0
  122. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/frontend.py +0 -0
  123. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/manager.py +0 -0
  124. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/rpc.py +0 -0
  125. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/run_store.py +0 -0
  126. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/server/serialize.py +0 -0
  127. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/__init__.py +0 -0
  128. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/assets.py +0 -0
  129. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/config.py +0 -0
  130. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/frames.py +0 -0
  131. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/peaks.py +0 -0
  132. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/schema.py +0 -0
  133. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/store.py +0 -0
  134. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/system_stats.py +0 -0
  135. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/__init__.py +0 -0
  136. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/compose.py +0 -0
  137. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/ffmpeg.py +0 -0
  138. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/render.py +0 -0
  139. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/timeline/resolve.py +0 -0
  140. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/training.py +0 -0
  141. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/studio/training_store.py +0 -0
  142. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/takes.py +0 -0
  143. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/__init__.py +0 -0
  144. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/__main__.py +0 -0
  145. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/arch.py +0 -0
  146. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/caption.py +0 -0
  147. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/dataset.py +0 -0
  148. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/models.py +0 -0
  149. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/protocol.py +0 -0
  150. {inline_core-1.2.52 → inline_core-1.2.53}/src/inline_core/training/trainer.py +0 -0
  151. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_catalog.py +0 -0
  152. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_checkpoint.py +0 -0
  153. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_config.py +0 -0
  154. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_device_detect.py +0 -0
  155. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_api.py +0 -0
  156. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_install.py +0 -0
  157. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_manifest.py +0 -0
  158. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_resolve.py +0 -0
  159. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_scanner.py +0 -0
  160. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_spine.py +0 -0
  161. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_extension_state.py +0 -0
  162. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_file_store.py +0 -0
  163. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_frontend_serving.py +0 -0
  164. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_hidden_nodes.py +0 -0
  165. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_krea2_convert.py +0 -0
  166. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_loader_runners.py +0 -0
  167. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_lora.py +0 -0
  168. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_lora_download.py +0 -0
  169. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_memory_policy.py +0 -0
  170. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_parallel_group.py +0 -0
  171. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_primitives.py +0 -0
  172. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_rpc_bridge.py +0 -0
  173. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_run_store.py +0 -0
  174. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_sampling.py +0 -0
  175. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_schema.py +0 -0
  176. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_server.py +0 -0
  177. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_assets.py +0 -0
  178. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_fal.py +0 -0
  179. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_frames.py +0 -0
  180. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_moodboard.py +0 -0
  181. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_peaks.py +0 -0
  182. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_schema.py +0 -0
  183. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_store.py +0 -0
  184. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_timeline.py +0 -0
  185. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_studio_training.py +0 -0
  186. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_take_bytes.py +0 -0
  187. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_topo.py +0 -0
  188. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_arch.py +0 -0
  189. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_dataset.py +0 -0
  190. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_models.py +0 -0
  191. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_training_resolve.py +0 -0
  192. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_validate.py +0 -0
  193. {inline_core-1.2.52 → inline_core-1.2.53}/tests/test_xfuser_sampler.py +0 -0
  194. {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.52
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.52"
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
- vae = fp.vae_bytes / 1e9
206
- full = big + vae
207
- int8 = big * _INT8_FACTOR + vae
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 self.diffusion_bytes + self.text_encoder_bytes + self.vae_bytes
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
- # TODO(phase1): pass real asset content hashes so identity is content-addressed.
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
- return source is PortKind.IMAGE and target is PortKind.IMAGE_LIST
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 = _encode_image(pipe, image, width, height, device, generator)
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 _encode_image(
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, vae: object = None, text_encoder: 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
  }