horde-engine 6.3.2__tar.gz → 6.3.3__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 (275) hide show
  1. {horde_engine-6.3.2 → horde_engine-6.3.3}/PKG-INFO +1 -1
  2. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/_version.py +2 -2
  3. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/civitai_adhoc.py +270 -64
  4. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/lora.py +165 -57
  5. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/ti.py +101 -14
  6. {horde_engine-6.3.2 → horde_engine-6.3.3}/.gitignore +0 -0
  7. {horde_engine-6.3.2 → horde_engine-6.3.3}/LICENSE +0 -0
  8. {horde_engine-6.3.2 → horde_engine-6.3.3}/README.md +0 -0
  9. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/__init__.py +0 -0
  10. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/__pyinstaller.py +0 -0
  11. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/api.py +0 -0
  12. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/benchmark.py +0 -0
  13. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/beta_models.py +0 -0
  14. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/comfy_horde.py +0 -0
  15. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/config_path.py +0 -0
  16. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/consts.py +0 -0
  17. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/exceptions.py +0 -0
  18. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/__init__.py +0 -0
  19. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/comfy_events.py +0 -0
  20. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/comfy_patches.py +0 -0
  21. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/component_materialize.py +0 -0
  22. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/component_stability_probe.py +0 -0
  23. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/graph_utils.py +0 -0
  24. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/in_process.py +0 -0
  25. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/interface.py +0 -0
  26. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/model_dirs.py +0 -0
  27. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/phase_timing.py +0 -0
  28. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/progress_hook.py +0 -0
  29. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/results.py +0 -0
  30. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/sampling_lease.py +0 -0
  31. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/server_shim.py +0 -0
  32. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/shared_components.py +0 -0
  33. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/stage_graph.py +0 -0
  34. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/stage_payloads.py +0 -0
  35. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/zero_copy_load.py +0 -0
  36. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/feature_impact.py +0 -0
  37. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/feature_requirements.py +0 -0
  38. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/horde.py +0 -0
  39. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/initialisation.py +0 -0
  40. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/__init__.py +0 -0
  41. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/__main__.py +0 -0
  42. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/installer.py +0 -0
  43. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/manifest.json +0 -0
  44. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/manifest.py +0 -0
  45. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/integrations/__init__.py +0 -0
  46. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/integrations/logfire_comfy_internals.py +0 -0
  47. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/integrations/logfire_setup.py +0 -0
  48. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/metrics.py +0 -0
  49. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/db_dep.json +0 -0
  50. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/db_embeds.json +0 -0
  51. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/diffusers.json +0 -0
  52. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/med_config.json +0 -0
  53. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/__init__.py +0 -0
  54. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/annotator_provider.py +0 -0
  55. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/base.py +0 -0
  56. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/civitai_provider.py +0 -0
  57. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/civitai_records.py +0 -0
  58. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/codeformer.py +0 -0
  59. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/compvis.py +0 -0
  60. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/controlnet.py +0 -0
  61. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/controlnet_annotator.py +0 -0
  62. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/diffusers.py +0 -0
  63. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/esrgan.py +0 -0
  64. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/gfpgan.py +0 -0
  65. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/hyper.py +0 -0
  66. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/miscellaneous.py +0 -0
  67. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/safety_checker.py +0 -0
  68. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/__init__.py +0 -0
  69. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/LICENSE +0 -0
  70. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/README.md +0 -0
  71. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/__init__.py +0 -0
  72. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_example.json +0 -0
  73. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_fg_all.json +0 -0
  74. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_joint_bg.json +0 -0
  75. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_joint_fg.json +0 -0
  76. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_bg.json +0 -0
  77. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_bg_stop_at.json +0 -0
  78. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_fg.json +0 -0
  79. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_fg_example.json +0 -0
  80. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_fg_example_rgba.json +0 -0
  81. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_joint.json +0 -0
  82. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/layered_diffusion.py +0 -0
  83. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/__init__.py +0 -0
  84. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/attention_sharing.py +0 -0
  85. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/enums.py +0 -0
  86. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/models.py +0 -0
  87. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/utils.py +0 -0
  88. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/pyproject.toml +0 -0
  89. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/requirements.txt +0 -0
  90. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/LICENSE +0 -0
  91. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/README.md +0 -0
  92. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/__init__.py +0 -0
  93. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/VERSION +0 -0
  94. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/__init__.py +0 -0
  95. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/__init__.py +0 -0
  96. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/arcface_arch.py +0 -0
  97. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/arch_util.py +0 -0
  98. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/codeformer_arch.py +0 -0
  99. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/rrdbnet_arch.py +0 -0
  100. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/vgg_arch.py +0 -0
  101. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/vqgan_arch.py +0 -0
  102. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/__init__.py +0 -0
  103. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/data_sampler.py +0 -0
  104. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/data_util.py +0 -0
  105. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/prefetch_dataloader.py +0 -0
  106. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/transforms.py +0 -0
  107. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/losses/__init__.py +0 -0
  108. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/losses/loss_util.py +0 -0
  109. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/losses/losses.py +0 -0
  110. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/metrics/__init__.py +0 -0
  111. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/metrics/metric_util.py +0 -0
  112. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/metrics/psnr_ssim.py +0 -0
  113. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/models/__init__.py +0 -0
  114. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/__init__.py +0 -0
  115. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/__init__.py +0 -0
  116. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/deform_conv.py +0 -0
  117. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_cuda.cpp +0 -0
  118. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu +0 -0
  119. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_ext.cpp +0 -0
  120. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/__init__.py +0 -0
  121. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/fused_act.py +0 -0
  122. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/src/fused_bias_act.cpp +0 -0
  123. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu +0 -0
  124. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/__init__.py +0 -0
  125. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp +0 -0
  126. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu +0 -0
  127. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/upfirdn2d.py +0 -0
  128. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/setup.py +0 -0
  129. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/train.py +0 -0
  130. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/__init__.py +0 -0
  131. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/dist_util.py +0 -0
  132. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/download_util.py +0 -0
  133. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/file_client.py +0 -0
  134. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/img_util.py +0 -0
  135. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/lmdb_util.py +0 -0
  136. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/logger.py +0 -0
  137. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/matlab_functions.py +0 -0
  138. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/misc.py +0 -0
  139. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/options.py +0 -0
  140. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/realesrgan_utils.py +0 -0
  141. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/registry.py +0 -0
  142. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/version.py +0 -0
  143. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/__init__.py +0 -0
  144. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/__init__.py +0 -0
  145. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/align_trans.py +0 -0
  146. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/matlab_cp2tform.py +0 -0
  147. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface.py +0 -0
  148. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface_net.py +0 -0
  149. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface_utils.py +0 -0
  150. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/__init__.py +0 -0
  151. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/face_detector.py +0 -0
  152. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/__init__.py +0 -0
  153. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/common.py +0 -0
  154. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/experimental.py +0 -0
  155. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolo.py +0 -0
  156. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolov5l.yaml +0 -0
  157. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolov5n.yaml +0 -0
  158. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/__init__.py +0 -0
  159. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/autoanchor.py +0 -0
  160. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/datasets.py +0 -0
  161. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/extract_ckpt.py +0 -0
  162. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/general.py +0 -0
  163. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/torch_utils.py +0 -0
  164. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/__init__.py +0 -0
  165. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/bisenet.py +0 -0
  166. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/parsenet.py +0 -0
  167. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/resnet.py +0 -0
  168. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/__init__.py +0 -0
  169. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/face_restoration_helper.py +0 -0
  170. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/face_utils.py +0 -0
  171. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/misc.py +0 -0
  172. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/README.md +0 -0
  173. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/__init__.py +0 -0
  174. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/gfpganv1_clean_arch.py +0 -0
  175. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/model_loading.py +0 -0
  176. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/stylegan2_clean_arch.py +0 -0
  177. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/types.py +0 -0
  178. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_controlnet_model_loader.py +0 -0
  179. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_image_loader.py +0 -0
  180. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_image_output.py +0 -0
  181. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_lora_loader.py +0 -0
  182. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_model_loader.py +0 -0
  183. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_stage_io.py +0 -0
  184. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_upscale_model_loader.py +0 -0
  185. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_v3_canary.py +0 -0
  186. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/__init__.py +0 -0
  187. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/comfy_node_inputs.json +0 -0
  188. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/constants.py +0 -0
  189. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/context.py +0 -0
  190. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/definition.py +0 -0
  191. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/__init__.py +0 -0
  192. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image.py +0 -0
  193. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/__init__.py +0 -0
  194. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/baselines.py +0 -0
  195. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/bindings.py +0 -0
  196. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/cascade.py +0 -0
  197. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/controlnet.py +0 -0
  198. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/creative_upscale.py +0 -0
  199. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/features.py +0 -0
  200. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/flux.py +0 -0
  201. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/qr_code.py +0 -0
  202. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/qwen.py +0 -0
  203. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/stable_diffusion.py +0 -0
  204. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/steps.py +0 -0
  205. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/z_image.py +0 -0
  206. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/post_processing.py +0 -0
  207. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/graph.py +0 -0
  208. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/horde_compat.py +0 -0
  209. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/identifiers.py +0 -0
  210. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/node_schemas.py +0 -0
  211. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/patches.py +0 -0
  212. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/payload.py +0 -0
  213. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/payload_pp.py +0 -0
  214. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/registry.py +0 -0
  215. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/resolution.py +0 -0
  216. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/sdk_adapter.py +0 -0
  217. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_controlnet.json +0 -0
  218. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_controlnet_annotator.json +0 -0
  219. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_controlnet_hires_fix.json +0 -0
  220. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_flux.json +0 -0
  221. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_image_facefix.json +0 -0
  222. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_image_upscale.json +0 -0
  223. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_qr_code.json +0 -0
  224. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_qwen.json +0 -0
  225. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_cascade.json +0 -0
  226. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_cascade_2pass.json +0 -0
  227. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_cascade_remix.json +0 -0
  228. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion.json +0 -0
  229. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion_hires_fix.json +0 -0
  230. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion_img2img_mask.json +0 -0
  231. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion_paint.json +0 -0
  232. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_controlnet.json +0 -0
  233. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_controlnet_annotator.json +0 -0
  234. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_controlnet_hires_fix.json +0 -0
  235. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_creative_upscale.json +0 -0
  236. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_flux.json +0 -0
  237. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_image_facefix.json +0 -0
  238. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_image_upscale.json +0 -0
  239. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_qr_code.json +0 -0
  240. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_qwen.json +0 -0
  241. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_cascade.json +0 -0
  242. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_cascade_2pass.json +0 -0
  243. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_cascade_remix.json +0 -0
  244. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion.json +0 -0
  245. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion_hires_fix.json +0 -0
  246. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion_img2img_mask.json +0 -0
  247. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion_paint.json +0 -0
  248. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_z_image.json +0 -0
  249. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/preload.py +0 -0
  250. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/profiling.py +0 -0
  251. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/py.typed +0 -0
  252. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pyinstaller_hooks/__init__.py +0 -0
  253. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pyinstaller_hooks/hook-hordelib.horde.py +0 -0
  254. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/safety.py +0 -0
  255. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/safety_checker.py +0 -0
  256. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/settings.py +0 -0
  257. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/shared_model_manager.py +0 -0
  258. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/train.py +0 -0
  259. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/__init__.py +0 -0
  260. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/device_pinning.py +0 -0
  261. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/distance.py +0 -0
  262. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/dynamicprompt.py +0 -0
  263. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/fd_limits.py +0 -0
  264. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/gpuinfo.py +0 -0
  265. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/image_utils.py +0 -0
  266. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/ioredirect.py +0 -0
  267. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/logger.py +0 -0
  268. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/nvml.py +0 -0
  269. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/optional_deps.py +0 -0
  270. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/sanitizer.py +0 -0
  271. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/switch.py +0 -0
  272. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/torch_build.py +0 -0
  273. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/torch_memory.py +0 -0
  274. {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/vram_planning.py +0 -0
  275. {horde_engine-6.3.2 → horde_engine-6.3.3}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: horde-engine
3
- Version: 6.3.2
3
+ Version: 6.3.3
4
4
  Summary: A wrapper around ComfyUI to allow use by AI Horde.
5
5
  Project-URL: Homepage, https://github.com/Haidra-Org/hordelib
6
6
  Project-URL: Bug Tracker, https://github.com/Haidra-Org/hordelib/issues
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '6.3.2'
22
- __version_tuple__ = version_tuple = (6, 3, 2)
21
+ __version__ = version = '6.3.3'
22
+ __version_tuple__ = version_tuple = (6, 3, 3)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -30,7 +30,7 @@ import time
30
30
  import uuid
31
31
  from abc import abstractmethod
32
32
  from collections import deque
33
- from collections.abc import Iterable
33
+ from collections.abc import Callable, Iterable
34
34
  from contextlib import AbstractContextManager, nullcontext
35
35
  from dataclasses import dataclass, field
36
36
  from datetime import datetime
@@ -48,6 +48,12 @@ from loguru import logger
48
48
  from hordelib.metrics import DownloadCategory, DownloadEvent, get_metrics_collector
49
49
  from hordelib.model_manager.base import BaseModelManager
50
50
 
51
+ try:
52
+ from horde_model_reference.download_engine import DownloadOutcome, _segmented_download
53
+ except ImportError: # pragma: no cover - the engine ships with horde_model_reference; degrade to single-stream
54
+ _segmented_download = None # type: ignore[assignment]
55
+ DownloadOutcome = None # type: ignore[assignment,misc]
56
+
51
57
  TESTS_ONGOING = os.getenv("TESTS_ONGOING", "0") == "1"
52
58
 
53
59
  TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
@@ -86,6 +92,24 @@ fails (its own cleanup) well inside this window, so only a crashed writer's temp
86
92
  Age-gating the prune reclaims leaked disk without ever deleting an in-flight write.
87
93
  """
88
94
 
95
+ DOWNLOAD_CHUNK_SIZE = 1024 * 1024
96
+ """Bytes pulled from the network per streamed read of a weight download.
97
+
98
+ A weight file is transferred a chunk at a time rather than buffered whole in process memory (one LoRA
99
+ can be hundreds of MB), so only one chunk is resident at once. The chunk boundary is also where an
100
+ in-flight transfer observes a cancellation and reports progress, so it doubles as the responsiveness
101
+ granularity: large enough to keep per-chunk overhead negligible, small enough that a cancelled or
102
+ throttled stream reacts promptly.
103
+ """
104
+
105
+ DEFAULT_ADHOC_DOWNLOAD_CONNECTIONS = 4
106
+ """Concurrent ranged connections for a single large ad-hoc LoRA, matching the worker's checkpoint default.
107
+
108
+ A single TCP stream to Cloudflare-fronted CivitAI is capped near the per-connection rate limit, so a large
109
+ LoRA is fetched over several ranged connections to raise throughput. Small LoRAs and range-refusing servers
110
+ fall through to the single-stream path unchanged. Overridable via ``AIWORKER_LORA_DOWNLOAD_CONNECTIONS``."""
111
+
112
+
89
113
  DEFAULT_MIN_FREE_DISK_MB = 1024
90
114
  """Keep at least this many MB free on the cache volume; ad-hoc downloads evict (then refuse) below it.
91
115
 
@@ -110,6 +134,17 @@ def timestamp_to_datetime(value: str | None) -> datetime | None:
110
134
  return None
111
135
 
112
136
 
137
+ def _adhoc_connections_from_env() -> int:
138
+ """The configured concurrent-connection count for large ad-hoc LoRA downloads (>= 1)."""
139
+ raw = os.getenv("AIWORKER_LORA_DOWNLOAD_CONNECTIONS")
140
+ if raw is None:
141
+ return DEFAULT_ADHOC_DOWNLOAD_CONNECTIONS
142
+ try:
143
+ return max(1, int(raw))
144
+ except ValueError:
145
+ return DEFAULT_ADHOC_DOWNLOAD_CONNECTIONS
146
+
147
+
113
148
  def normalise_a1111_triggers(triggers: list[str]) -> list[str]:
114
149
  """Return *triggers* with any A1111-style ``<lora:name:weight>`` entries reduced to ``name``."""
115
150
  return [
@@ -163,6 +198,16 @@ class SkipDownload(Exception):
163
198
  """
164
199
 
165
200
 
201
+ class _DownloadCancelled(Exception):
202
+ """Internal signal that an in-flight streamed transfer was abandoned because its job was cancelled.
203
+
204
+ Raised from the streaming loop when the manager's cancellation generation moves past the value the
205
+ download was enqueued under, so a caller that has given up frees the shared worker without the
206
+ transfer running to completion. Handled entirely within :meth:`CivitaiAdhocModelManager._run_download`;
207
+ never surfaces to callers.
208
+ """
209
+
210
+
166
211
  class ReadOnlyModelManagerError(RuntimeError):
167
212
  """Raised when a write, download, or eviction is attempted on a read-only manager.
168
213
 
@@ -184,6 +229,22 @@ class _QueuedDownload[R]:
184
229
  A worker thread abandons an in-flight or queued download once the manager's current generation has
185
230
  moved past the value stamped here (see :meth:`CivitaiAdhocModelManager.cancel_active_downloads`),
186
231
  so a caller that has given up on a job can free the shared pool without killing its threads."""
232
+ progress_callback: Callable[[int, int], None] | None = None
233
+ """Optional per-chunk progress hook invoked as ``(downloaded_bytes, total_bytes)``.
234
+
235
+ ``total_bytes`` is the ``Content-Length`` of the transfer, or ``0`` when the server does not report
236
+ one. Invoked once per streamed chunk while the weight file is being written."""
237
+ completion_event: threading.Event = field(default_factory=threading.Event)
238
+ """Set exactly once when this download reaches any terminal outcome.
239
+
240
+ Lets a caller wait on this one record finishing rather than on the whole pool going idle, so
241
+ unrelated queue traffic (the default-model seed, another job's ad-hoc fetch) cannot blow a bounded
242
+ per-record wait. Read :attr:`success` after the event is set to learn the outcome."""
243
+ success: bool | None = None
244
+ """The terminal outcome, readable once :attr:`completion_event` is set.
245
+
246
+ ``True`` when the weight file is present and committed (freshly downloaded or already on disk and
247
+ verified), ``False`` on any failure/skip/cancellation, and ``None`` while still in flight."""
187
248
 
188
249
 
189
250
  class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
@@ -298,6 +359,7 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
298
359
  # freeing the (shared, daemon) pool for the next job without tearing the threads down. Guarded by
299
360
  # _download_mutex for writes; read locklessly in the worker loop (a plain int read is atomic).
300
361
  self._download_generation = 0
362
+ self._download_connections = _adhoc_connections_from_env()
301
363
  self._index_ids: dict[int, str] = {}
302
364
  self._index_orig_names: dict[str, str] = {}
303
365
  self.total_retries_attempted = 0
@@ -644,9 +706,19 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
644
706
  # Download queue and worker threads
645
707
  # ------------------------------------------------------------------
646
708
 
647
- def _enqueue_download(self, record: RecordT, context: dict | None = None) -> None:
709
+ def _enqueue_download(
710
+ self,
711
+ record: RecordT,
712
+ context: dict | None = None,
713
+ *,
714
+ progress_callback: Callable[[int, int], None] | None = None,
715
+ ) -> _QueuedDownload[RecordT]:
648
716
  """Ensure the worker pool is running and append *record* to the download queue.
649
717
 
718
+ Returns:
719
+ The enqueued :class:`_QueuedDownload`, whose ``completion_event`` a caller can wait on to
720
+ block until this specific record finishes (rather than the whole pool going idle).
721
+
650
722
  Raises:
651
723
  ReadOnlyModelManagerError: If the manager is read-only.
652
724
  """
@@ -660,10 +732,15 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
660
732
  worker.start()
661
733
  self._metric_active_threads.set(len(self._download_threads))
662
734
 
663
- self._download_queue.append(
664
- _QueuedDownload(record=record, context=context or {}, generation=self._download_generation),
735
+ queued = _QueuedDownload(
736
+ record=record,
737
+ context=context or {},
738
+ generation=self._download_generation,
739
+ progress_callback=progress_callback,
665
740
  )
741
+ self._download_queue.append(queued)
666
742
  self._metric_queue_size.set(len(self._download_queue))
743
+ return queued
667
744
 
668
745
  def _download_thread(self, thread_number: int) -> None:
669
746
  """Worker loop: dequeue records and download their weight files until stopped.
@@ -743,7 +820,31 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
743
820
  return True
744
821
 
745
822
  def _process_download(self, queued: _QueuedDownload, thread_logger) -> None:
746
- """Download a single queued record's weight file, retrying transient failures."""
823
+ """Download a queued record, then signal its completion event exactly once.
824
+
825
+ Delegates the transfer to :meth:`_run_download` and, whatever the outcome, records the result on
826
+ the queued item (:attr:`_QueuedDownload.success`) and sets its completion event so a caller
827
+ waiting on this one record is released regardless of which exit path was taken.
828
+ """
829
+ try:
830
+ queued.success = self._run_download(queued, thread_logger)
831
+ finally:
832
+ queued.completion_event.set()
833
+
834
+ def _content_length(self, response: requests.Response) -> int:
835
+ """Return the response's ``Content-Length`` in bytes, or ``0`` when it is absent or malformed."""
836
+ try:
837
+ return int(response.headers.get("Content-Length") or 0)
838
+ except (TypeError, ValueError):
839
+ return 0
840
+
841
+ def _run_download(self, queued: _QueuedDownload, thread_logger) -> bool:
842
+ """Download a single queued record's weight file, retrying transient failures.
843
+
844
+ Returns:
845
+ ``True`` when the weight file is present and committed (freshly downloaded, or already on
846
+ disk and verified); ``False`` on any skip, cancellation, or terminal/exhausted failure.
847
+ """
747
848
  record = queued.record
748
849
  download_logger = thread_logger.bind(model_name=record.name)
749
850
 
@@ -752,10 +853,10 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
752
853
  notfound_attempts = 0
753
854
  cache_key = record.name # Refined to the version id once the target is prepared.
754
855
  while retries <= self.MAX_RETRIES:
755
- # Abandon a download whose job has been cancelled (the caller gave up and freed the slot): a
756
- # single-shot requests.get cannot be interrupted mid-transfer, but checking here bails before
757
- # the next attempt or retry sleep, so a wedged retry ladder stops within ~one request timeout
758
- # and the shared pool is free for the next job instead of pinning a thread on dead work.
856
+ # Abandon a download whose job has been cancelled (the caller gave up and freed the slot):
857
+ # checking here bails before the next attempt or retry sleep, so a wedged retry ladder stops
858
+ # within ~one request timeout and the shared pool is free for the next job instead of pinning
859
+ # a thread on dead work. An in-flight stream also honours this at each chunk boundary below.
759
860
  if queued.generation != self._download_generation:
760
861
  download_logger.info("adhoc.download_cancelled", retries=retries)
761
862
  self._record_download_event(
@@ -765,11 +866,11 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
765
866
  duration_seconds=time.perf_counter() - overall_start,
766
867
  retries=retries,
767
868
  )
768
- return
869
+ return False
769
870
  try:
770
871
  target = self._prepare_download(record)
771
872
  if target is None:
772
- return
873
+ return False
773
874
 
774
875
  cache_key = self._download_cache_key(record, target)
775
876
  if self._is_version_bad(cache_key):
@@ -781,7 +882,7 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
781
882
  duration_seconds=time.perf_counter() - overall_start,
782
883
  retries=retries,
783
884
  )
784
- return
885
+ return False
785
886
  filepath = os.path.join(self.model_folder_path, target.filename)
786
887
  hashpath = f"{os.path.splitext(filepath)[0]}.sha256"
787
888
 
@@ -790,7 +891,7 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
790
891
  if self.is_default_cache_full():
791
892
  self.stop_downloading = True
792
893
  self.save_reference_to_disk()
793
- return
894
+ return True
794
895
 
795
896
  # Refuse to fetch a fresh weight when the volume can't hold it above the floor, even
796
897
  # after evicting every ad-hoc entry. Writing anyway risks an ENOSPC that takes the
@@ -812,72 +913,134 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
812
913
  duration_seconds=time.perf_counter() - overall_start,
813
914
  retries=retries,
814
915
  )
815
- return
916
+ return False
816
917
 
817
918
  download_start = time.perf_counter()
818
919
  download_url = target.url
819
920
  if self._civitai_api_token and self.is_model_url_from_civitai(download_url):
820
921
  download_url += f"{'&' if '?' in download_url else '?'}token={self._civitai_api_token}"
821
922
 
822
- response = requests.get(download_url, timeout=self.REQUEST_DOWNLOAD_TIMEOUT)
823
- response.raise_for_status()
824
- if "reason=download-auth" in response.url:
825
- download_logger.error("adhoc.download_auth_redirect", response_url=response.url)
826
- self._mark_version_bad(cache_key)
827
- self._record_download_event(
828
- record,
829
- success=False,
830
- size_bytes=0,
831
- duration_seconds=time.perf_counter() - overall_start,
832
- retries=retries,
833
- )
834
- return
835
-
836
- sha256 = hashlib.sha256(response.content).hexdigest()
837
- if target.sha256 and sha256.lower() != target.sha256.lower():
838
- download_logger.warning(
839
- "adhoc.download_hash_mismatch",
840
- expected=target.sha256[:16],
841
- actual=sha256[:16],
842
- attempt=retries + 1,
843
- )
844
- retries += 1
845
- self.total_retries_attempted += 1
846
- if retries > self.MAX_RETRIES:
847
- self._record_download_event(
848
- record,
849
- success=False,
850
- size_bytes=0,
851
- duration_seconds=time.perf_counter() - overall_start,
852
- retries=retries,
853
- )
854
- return
855
- time.sleep(self.RETRY_DELAY)
856
- continue
857
-
858
- # Stage the weight bytes to a temp file and rename into place so a concurrent reader
859
- # (another process loading this model) never observes a torn or truncated file at the
860
- # final path. The sidecar lands only after the weight file is fully placed, so a present
861
- # sidecar always describes a complete weight file; a weight file without a sidecar is
862
- # harmlessly re-verified or re-downloaded.
863
- self._atomic_write_bytes(filepath, response.content)
864
- self._atomic_write_bytes(hashpath, f"{sha256} *{target.filename}".encode())
923
+ segmented = self._try_segmented_download(download_url, filepath, target, queued)
924
+ if segmented is not None:
925
+ if not segmented.success:
926
+ # The segmented transfer landed a file whose sha256 does not match the record: discard it
927
+ # and retry (or, on the final attempt, fail) exactly as the single-stream hash-mismatch
928
+ # path does.
929
+ self._discard_partial(filepath, hashpath)
930
+ retries += 1
931
+ self.total_retries_attempted += 1
932
+ if retries > self.MAX_RETRIES:
933
+ self._record_download_event(
934
+ record,
935
+ success=False,
936
+ size_bytes=0,
937
+ duration_seconds=time.perf_counter() - overall_start,
938
+ retries=retries,
939
+ )
940
+ return False
941
+ time.sleep(self.RETRY_DELAY)
942
+ continue
943
+ downloaded_bytes = segmented.bytes_written
944
+ else:
945
+ # Stream the weight a chunk at a time into a sibling temp file, hashing as we go, rather
946
+ # than buffering the whole (often hundreds-of-MB) file in memory. The read timeout applies
947
+ # per chunk when streaming, so it bounds a stalled stream without capping a legitimately
948
+ # long transfer, and the temp file is renamed into place only after the checksum passes so
949
+ # a concurrent reader never observes a torn or partially written weight at the final path.
950
+ with requests.get(download_url, stream=True, timeout=self.REQUEST_DOWNLOAD_TIMEOUT) as response:
951
+ response.raise_for_status()
952
+ if "reason=download-auth" in response.url:
953
+ download_logger.error("adhoc.download_auth_redirect", response_url=response.url)
954
+ self._mark_version_bad(cache_key)
955
+ self._record_download_event(
956
+ record,
957
+ success=False,
958
+ size_bytes=0,
959
+ duration_seconds=time.perf_counter() - overall_start,
960
+ retries=retries,
961
+ )
962
+ return False
963
+
964
+ total_bytes = self._content_length(response)
965
+ hasher = hashlib.sha256()
966
+ downloaded_bytes = 0
967
+ tmp_path = f"{filepath}.tmp-{uuid.uuid4().hex[:8]}"
968
+ try:
969
+ with open(tmp_path, "wb") as tmp_file:
970
+ for chunk in response.iter_content(chunk_size=DOWNLOAD_CHUNK_SIZE):
971
+ # A generation bump means the owning job gave up; stop at this chunk
972
+ # boundary so a cancelled stream frees the shared pool promptly instead
973
+ # of transferring to completion.
974
+ if queued.generation != self._download_generation:
975
+ raise _DownloadCancelled
976
+ if not chunk:
977
+ continue
978
+ tmp_file.write(chunk)
979
+ hasher.update(chunk)
980
+ downloaded_bytes += len(chunk)
981
+ if queued.progress_callback is not None:
982
+ queued.progress_callback(downloaded_bytes, total_bytes)
983
+
984
+ sha256 = hasher.hexdigest()
985
+ if target.sha256 and sha256.lower() != target.sha256.lower():
986
+ download_logger.warning(
987
+ "adhoc.download_hash_mismatch",
988
+ expected=target.sha256[:16],
989
+ actual=sha256[:16],
990
+ attempt=retries + 1,
991
+ )
992
+ retries += 1
993
+ self.total_retries_attempted += 1
994
+ if retries > self.MAX_RETRIES:
995
+ self._record_download_event(
996
+ record,
997
+ success=False,
998
+ size_bytes=0,
999
+ duration_seconds=time.perf_counter() - overall_start,
1000
+ retries=retries,
1001
+ )
1002
+ return False
1003
+ time.sleep(self.RETRY_DELAY)
1004
+ continue
1005
+
1006
+ self._replace_file_with_retry(tmp_path, filepath)
1007
+ finally:
1008
+ if os.path.exists(tmp_path):
1009
+ try:
1010
+ os.remove(tmp_path)
1011
+ except OSError:
1012
+ pass
1013
+
1014
+ # The sidecar lands only after the weight file is fully placed, so a present sidecar
1015
+ # always describes a complete weight file; a weight file without a sidecar is
1016
+ # harmlessly re-verified or re-downloaded.
1017
+ self._atomic_write_bytes(hashpath, f"{sha256} *{target.filename}".encode())
865
1018
 
866
1019
  self._commit_download(record, target, downloaded=True)
867
1020
  self._metric_download_duration.record(time.perf_counter() - download_start)
868
1021
  self._record_download_event(
869
1022
  record,
870
1023
  success=True,
871
- size_bytes=len(response.content),
1024
+ size_bytes=downloaded_bytes,
872
1025
  duration_seconds=time.perf_counter() - download_start,
873
1026
  retries=retries,
874
1027
  )
875
1028
  self._evict_adhoc_over_limit()
876
1029
  self.save_reference_to_disk()
877
1030
  download_logger.info("adhoc.download_success", filename=target.filename)
878
- return
1031
+ return True
1032
+ except _DownloadCancelled:
1033
+ download_logger.info("adhoc.download_cancelled", retries=retries)
1034
+ self._record_download_event(
1035
+ record,
1036
+ success=False,
1037
+ size_bytes=0,
1038
+ duration_seconds=time.perf_counter() - overall_start,
1039
+ retries=retries,
1040
+ )
1041
+ return False
879
1042
  except SkipDownload:
880
- return
1043
+ return False
881
1044
  except (requests.HTTPError, requests.ConnectionError, requests.Timeout, json.JSONDecodeError) as net_error:
882
1045
  error_type = type(net_error).__name__
883
1046
  self._metric_network_errors.add(1, {"error_type": error_type})
@@ -893,7 +1056,7 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
893
1056
  duration_seconds=time.perf_counter() - overall_start,
894
1057
  retries=retries,
895
1058
  )
896
- return
1059
+ return False
897
1060
  if isinstance(net_error, requests.HTTPError) and status_code == 404:
898
1061
  # A 404 on the signed download URL is deterministic: the file (or its
899
1062
  # token-scoped resource) is gone and the identical URL will not heal. Retrying the
@@ -910,7 +1073,7 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
910
1073
  duration_seconds=time.perf_counter() - overall_start,
911
1074
  retries=retries,
912
1075
  )
913
- return
1076
+ return False
914
1077
  time.sleep(self.NOTFOUND_CONFIRM_DELAY)
915
1078
  continue
916
1079
  except OSError as disk_error:
@@ -928,7 +1091,7 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
928
1091
  duration_seconds=time.perf_counter() - overall_start,
929
1092
  retries=retries,
930
1093
  )
931
- return
1094
+ return False
932
1095
  # Any other OS error may be transient; fall through to the shared retry path below.
933
1096
  self._metric_network_errors.add(1, {"error_type": "fatal"})
934
1097
  download_logger.error("adhoc.download_fatal_error", error=str(disk_error))
@@ -947,8 +1110,51 @@ class CivitaiAdhocModelManager[RecordT: GenericModelRecord](
947
1110
  duration_seconds=time.perf_counter() - overall_start,
948
1111
  retries=retries,
949
1112
  )
950
- return
1113
+ return False
951
1114
  time.sleep(self.RETRY_DELAY)
1115
+ return False
1116
+
1117
+ def _try_segmented_download(
1118
+ self,
1119
+ download_url: str,
1120
+ filepath: str,
1121
+ target: DownloadTarget,
1122
+ queued: _QueuedDownload,
1123
+ ) -> DownloadOutcome | None:
1124
+ """Attempt a multi-connection ranged download of a large LoRA; return the outcome or None to fall back.
1125
+
1126
+ Returns None (so the caller uses the single-stream path) when segmentation is disabled, the engine
1127
+ helper is unavailable, or the engine reports the file is unsuitable (small, no range support, or a
1128
+ transient segment failure). A generation bump during transfer raises :class:`_DownloadCancelled`
1129
+ through the progress hook, aborting every segment, exactly as the single-stream path cancels at a
1130
+ chunk boundary.
1131
+ """
1132
+ if _segmented_download is None or self._download_connections <= 1:
1133
+ return None
1134
+
1135
+ def _progress(downloaded_bytes: int, total_bytes: int) -> None:
1136
+ if queued.generation != self._download_generation:
1137
+ raise _DownloadCancelled
1138
+ if queued.progress_callback is not None:
1139
+ queued.progress_callback(downloaded_bytes, total_bytes)
1140
+
1141
+ return _segmented_download(
1142
+ download_url,
1143
+ Path(filepath),
1144
+ expected_sha256=target.sha256 or None,
1145
+ progress_callback=_progress,
1146
+ extra_headers=None,
1147
+ connections=self._download_connections,
1148
+ max_retries=self.MAX_RETRIES,
1149
+ )
1150
+
1151
+ def _discard_partial(self, filepath: str, hashpath: str) -> None:
1152
+ """Remove a hash-mismatched weight file and its sidecar so a retry re-downloads cleanly."""
1153
+ for path in (filepath, hashpath):
1154
+ try:
1155
+ os.remove(path)
1156
+ except OSError:
1157
+ pass
952
1158
 
953
1159
  def _existing_file_matches(self, filepath: str, hashpath: str, expected_sha256: str | None) -> bool:
954
1160
  """Return whether a previously downloaded file is present and matches *expected_sha256*."""