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.
- {horde_engine-6.3.2 → horde_engine-6.3.3}/PKG-INFO +1 -1
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/_version.py +2 -2
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/civitai_adhoc.py +270 -64
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/lora.py +165 -57
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/ti.py +101 -14
- {horde_engine-6.3.2 → horde_engine-6.3.3}/.gitignore +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/LICENSE +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/README.md +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/__pyinstaller.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/api.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/benchmark.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/beta_models.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/comfy_horde.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/config_path.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/consts.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/exceptions.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/comfy_events.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/comfy_patches.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/component_materialize.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/component_stability_probe.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/graph_utils.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/in_process.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/interface.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/model_dirs.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/phase_timing.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/progress_hook.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/results.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/sampling_lease.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/server_shim.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/shared_components.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/stage_graph.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/stage_payloads.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/execution/zero_copy_load.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/feature_impact.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/feature_requirements.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/horde.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/initialisation.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/__main__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/installer.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/manifest.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/installation/manifest.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/integrations/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/integrations/logfire_comfy_internals.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/integrations/logfire_setup.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/metrics.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/db_dep.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/db_embeds.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/diffusers.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_database/med_config.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/annotator_provider.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/base.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/civitai_provider.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/civitai_records.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/codeformer.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/compvis.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/controlnet.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/controlnet_annotator.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/diffusers.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/esrgan.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/gfpgan.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/hyper.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/miscellaneous.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/model_manager/safety_checker.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/LICENSE +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/README.md +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_example.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_fg_all.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_joint_bg.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_joint_fg.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_bg.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_bg_stop_at.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_fg.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_fg_example.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_fg_example_rgba.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_joint.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/layered_diffusion.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/attention_sharing.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/enums.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/models.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/utils.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/pyproject.toml +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/comfyui_layerdiffuse/requirements.txt +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/LICENSE +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/README.md +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/VERSION +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/arcface_arch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/arch_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/codeformer_arch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/rrdbnet_arch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/vgg_arch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/archs/vqgan_arch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/data_sampler.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/data_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/prefetch_dataloader.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/data/transforms.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/losses/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/losses/loss_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/losses/losses.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/metrics/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/metrics/metric_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/metrics/psnr_ssim.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/models/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/deform_conv.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_cuda.cpp +0 -0
- {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
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_ext.cpp +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/fused_act.py +0 -0
- {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
- {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
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/upfirdn2d.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/setup.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/train.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/dist_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/download_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/file_client.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/img_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/lmdb_util.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/logger.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/matlab_functions.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/misc.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/options.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/realesrgan_utils.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/utils/registry.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/basicsr/version.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/align_trans.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/matlab_cp2tform.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface_net.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface_utils.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/face_detector.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/common.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/experimental.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolo.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolov5l.yaml +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolov5n.yaml +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/autoanchor.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/datasets.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/extract_ckpt.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/general.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/torch_utils.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/bisenet.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/parsenet.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/parsing/resnet.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/face_restoration_helper.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/face_utils.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/facelib/utils/misc.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/README.md +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/gfpganv1_clean_arch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/model_loading.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/stylegan2_clean_arch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/facerestore_cf/r_chainner/types.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_controlnet_model_loader.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_image_loader.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_image_output.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_lora_loader.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_model_loader.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_stage_io.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_upscale_model_loader.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/nodes/node_v3_canary.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/comfy_node_inputs.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/constants.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/context.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/definition.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/baselines.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/bindings.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/cascade.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/controlnet.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/creative_upscale.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/features.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/flux.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/qr_code.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/qwen.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/stable_diffusion.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/steps.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/image_gen/z_image.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/families/post_processing.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/graph.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/horde_compat.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/identifiers.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/node_schemas.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/patches.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/payload.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/payload_pp.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/registry.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/resolution.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline/sdk_adapter.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_controlnet.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_controlnet_annotator.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_controlnet_hires_fix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_flux.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_image_facefix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_image_upscale.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_qr_code.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_qwen.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_cascade.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_cascade_2pass.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_cascade_remix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion_hires_fix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion_img2img_mask.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipeline_designs/pipeline_stable_diffusion_paint.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_controlnet.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_controlnet_annotator.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_controlnet_hires_fix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_creative_upscale.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_flux.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_image_facefix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_image_upscale.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_qr_code.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_qwen.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_cascade.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_cascade_2pass.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_cascade_remix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion_hires_fix.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion_img2img_mask.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_stable_diffusion_paint.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pipelines/pipeline_z_image.json +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/preload.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/profiling.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/py.typed +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pyinstaller_hooks/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/pyinstaller_hooks/hook-hordelib.horde.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/safety.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/safety_checker.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/settings.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/shared_model_manager.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/train.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/__init__.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/device_pinning.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/distance.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/dynamicprompt.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/fd_limits.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/gpuinfo.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/image_utils.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/ioredirect.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/logger.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/nvml.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/optional_deps.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/sanitizer.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/switch.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/torch_build.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/utils/torch_memory.py +0 -0
- {horde_engine-6.3.2 → horde_engine-6.3.3}/hordelib/vram_planning.py +0 -0
- {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.
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (6, 3,
|
|
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(
|
|
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
|
-
|
|
664
|
-
|
|
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
|
|
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):
|
|
756
|
-
#
|
|
757
|
-
#
|
|
758
|
-
#
|
|
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
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
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=
|
|
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*."""
|