horde-engine 3.2.7__tar.gz → 3.2.9__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-3.2.7 → horde_engine-3.2.9}/PKG-INFO +2 -2
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/_version.py +2 -2
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/api.py +4 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/preload.py +49 -3
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/settings.py +7 -4
- horde_engine-3.2.9/hordelib/utils/gpuinfo.py +155 -0
- horde_engine-3.2.9/hordelib/utils/nvml.py +190 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/torch_memory.py +18 -2
- {horde_engine-3.2.7 → horde_engine-3.2.9}/pyproject.toml +1 -1
- horde_engine-3.2.7/hordelib/utils/gpuinfo.py +0 -205
- {horde_engine-3.2.7 → horde_engine-3.2.9}/.gitignore +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/LICENSE +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/README.md +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/__pyinstaller.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/benchmark.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/beta_models.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/comfy_horde.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/config_path.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/consts.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/exceptions.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/comfy_patches.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/graph_utils.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/in_process.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/interface.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/phase_timing.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/progress_hook.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/execution/sampling_lease.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/feature_impact.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/feature_requirements.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/horde.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/initialisation.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/installation/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/installation/__main__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/installation/installer.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/installation/manifest.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/installation/manifest.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/integrations/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/integrations/logfire_comfy_internals.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/integrations/logfire_setup.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/metrics.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_database/db_dep.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_database/db_embeds.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_database/diffusers.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_database/med_config.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/base.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/civitai_adhoc.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/civitai_provider.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/civitai_records.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/codeformer.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/compvis.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/controlnet.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/diffusers.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/esrgan.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/gfpgan.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/hyper.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/lora.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/miscellaneous.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/safety_checker.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/model_manager/ti.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/LICENSE +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/README.md +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_example.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_fg_all.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_joint_bg.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_cond_joint_fg.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_bg.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_bg_stop_at.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_diff_fg.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_fg_example.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_fg_example_rgba.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/examples/layer_diffusion_joint.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/layered_diffusion.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/attention_sharing.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/enums.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/models.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/lib_layerdiffusion/utils.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/pyproject.toml +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/comfyui_layerdiffuse/requirements.txt +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/LICENSE +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/README.md +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/VERSION +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/archs/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/archs/arcface_arch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/archs/arch_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/archs/codeformer_arch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/archs/rrdbnet_arch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/archs/vgg_arch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/archs/vqgan_arch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/data/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/data/data_sampler.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/data/data_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/data/prefetch_dataloader.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/data/transforms.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/losses/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/losses/loss_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/losses/losses.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/metrics/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/metrics/metric_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/metrics/psnr_ssim.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/models/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/deform_conv.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_cuda.cpp +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/dcn/src/deform_conv_ext.cpp +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/fused_act.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/src/fused_bias_act.cpp +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/ops/upfirdn2d/upfirdn2d.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/setup.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/train.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/dist_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/download_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/file_client.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/img_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/lmdb_util.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/logger.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/matlab_functions.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/misc.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/options.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/realesrgan_utils.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/utils/registry.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/basicsr/version.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/align_trans.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/matlab_cp2tform.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface_net.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/retinaface/retinaface_utils.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/face_detector.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/common.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/experimental.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolo.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolov5l.yaml +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/models/yolov5n.yaml +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/autoanchor.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/datasets.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/extract_ckpt.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/general.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/detection/yolov5face/utils/torch_utils.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/parsing/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/parsing/bisenet.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/parsing/parsenet.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/parsing/resnet.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/utils/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/utils/face_restoration_helper.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/utils/face_utils.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/facelib/utils/misc.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/r_chainner/README.md +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/r_chainner/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/r_chainner/gfpganv1_clean_arch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/r_chainner/model_loading.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/r_chainner/stylegan2_clean_arch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/facerestore_cf/r_chainner/types.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/node_controlnet_model_loader.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/node_image_loader.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/node_image_output.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/node_lora_loader.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/node_model_loader.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/nodes/node_upscale_model_loader.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/constants.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/context.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/families/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/families/image.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/families/post_processing.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/graph.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/horde_compat.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/patches.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/payload.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/payload_pp.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/registry.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/resolution.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline/template.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_controlnet.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_controlnet_annotator.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_controlnet_hires_fix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_flux.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_image_facefix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_image_upscale.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_qr_code.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_qwen.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_stable_cascade.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_stable_cascade_2pass.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_stable_cascade_remix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_stable_diffusion.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_stable_diffusion_hires_fix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_stable_diffusion_img2img_mask.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipeline_designs/pipeline_stable_diffusion_paint.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_controlnet.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_controlnet_annotator.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_controlnet_hires_fix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_flux.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_image_facefix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_image_upscale.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_qr_code.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_qwen.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_stable_cascade.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_stable_cascade_2pass.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_stable_cascade_remix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_stable_diffusion.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_stable_diffusion_hires_fix.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_stable_diffusion_img2img_mask.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pipelines/pipeline_stable_diffusion_paint.json +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/py.typed +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pyinstaller_hooks/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/pyinstaller_hooks/hook-hordelib.horde.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/safety.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/safety_checker.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/shared_model_manager.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/train.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/__init__.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/distance.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/dynamicprompt.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/image_utils.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/ioredirect.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/logger.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/optional_deps.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/sanitizer.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/switch.py +0 -0
- {horde_engine-3.2.7 → horde_engine-3.2.9}/hordelib/utils/torch_build.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: horde-engine
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.9
|
|
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
|
|
@@ -698,6 +698,7 @@ Requires-Dist: loguru>=0.7.3
|
|
|
698
698
|
Requires-Dist: lpips
|
|
699
699
|
Requires-Dist: matplotlib>=3.10.9
|
|
700
700
|
Requires-Dist: numpy>=1.25.0
|
|
701
|
+
Requires-Dist: nvidia-ml-py>=12.0.0
|
|
701
702
|
Requires-Dist: omegaconf>=2.3.0
|
|
702
703
|
Requires-Dist: opencv-contrib-python>=4.11.0.86
|
|
703
704
|
Requires-Dist: opencv-python>=4.11.0.86
|
|
@@ -707,7 +708,6 @@ Requires-Dist: pillow
|
|
|
707
708
|
Requires-Dist: psutil
|
|
708
709
|
Requires-Dist: pydantic-settings~=2.0
|
|
709
710
|
Requires-Dist: pydantic~=2.0
|
|
710
|
-
Requires-Dist: pynvml>=13.0.1
|
|
711
711
|
Requires-Dist: python-dotenv>=1.2.1
|
|
712
712
|
Requires-Dist: pyyaml
|
|
713
713
|
Requires-Dist: qrcode==7.4.2
|
|
@@ -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 = '3.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (3, 2,
|
|
21
|
+
__version__ = version = '3.2.9'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 2, 9)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -66,6 +66,7 @@ from hordelib.pipeline.payload_pp import (
|
|
|
66
66
|
UpscalePayload,
|
|
67
67
|
classify_post_processor,
|
|
68
68
|
)
|
|
69
|
+
from hordelib.preload import controlnet_annotators_present
|
|
69
70
|
from hordelib.shared_model_manager import SharedModelManager
|
|
70
71
|
from hordelib.utils.ioredirect import ComfyUIProgress, ComfyUIProgressUnit
|
|
71
72
|
from hordelib.utils.logger import HordeLog
|
|
@@ -74,6 +75,7 @@ from hordelib.utils.torch_memory import (
|
|
|
74
75
|
AcceleratorKind,
|
|
75
76
|
clear_accelerator_cache,
|
|
76
77
|
enumerate_accelerators,
|
|
78
|
+
get_accelerator_utilization_percent,
|
|
77
79
|
get_free_ram_mb,
|
|
78
80
|
get_torch_free_vram_mb,
|
|
79
81
|
get_torch_total_vram_mb,
|
|
@@ -120,6 +122,7 @@ __all__ = [
|
|
|
120
122
|
"classify_post_processor",
|
|
121
123
|
"clear_accelerator_cache",
|
|
122
124
|
"controlnet_annotator_download_bytes",
|
|
125
|
+
"controlnet_annotators_present",
|
|
123
126
|
"enumerate_accelerators",
|
|
124
127
|
"estimate_job_burden",
|
|
125
128
|
"feature_available",
|
|
@@ -128,6 +131,7 @@ __all__ = [
|
|
|
128
131
|
"get_feature_requirement",
|
|
129
132
|
"get_feature_requirement_registry",
|
|
130
133
|
"missing_packages",
|
|
134
|
+
"get_accelerator_utilization_percent",
|
|
131
135
|
"get_free_ram_mb",
|
|
132
136
|
"get_metrics_collector",
|
|
133
137
|
"get_torch_free_vram_mb",
|
|
@@ -88,12 +88,32 @@ def _pinned_annotator_ref() -> str | None:
|
|
|
88
88
|
return None
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
def _annotator_ckpts_dir() -> Path | None:
|
|
92
|
+
"""Return the directory annotator checkpoints live in, or None if it cannot be determined.
|
|
93
|
+
|
|
94
|
+
``hordelib.initialise()`` exports ``AUX_ANNOTATOR_CKPTS_PATH`` and is the authority once a run is
|
|
95
|
+
underway. When it is unset (e.g. a no-boot caller that only wants to *check* presence) the same value
|
|
96
|
+
is derived from :class:`~hordelib.settings.UserSettings`, which needs neither ComfyUI nor a GPU, so
|
|
97
|
+
presence can be read before (or without) initialisation.
|
|
98
|
+
"""
|
|
99
|
+
ckpts_dir = os.environ.get("AUX_ANNOTATOR_CKPTS_PATH")
|
|
100
|
+
if ckpts_dir:
|
|
101
|
+
return Path(ckpts_dir)
|
|
102
|
+
try:
|
|
103
|
+
from hordelib.settings import UserSettings
|
|
104
|
+
|
|
105
|
+
return UserSettings.get_model_directory() / "controlnet" / "annotators"
|
|
106
|
+
except Exception as e:
|
|
107
|
+
logger.debug("Could not derive the annotator checkpoint directory: error={}", e)
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
91
111
|
def _preload_marker_path() -> Path | None:
|
|
92
112
|
"""Return the on-disk marker path, or None if the annotator directory is unknown."""
|
|
93
|
-
ckpts_dir =
|
|
94
|
-
if
|
|
113
|
+
ckpts_dir = _annotator_ckpts_dir()
|
|
114
|
+
if ckpts_dir is None:
|
|
95
115
|
return None
|
|
96
|
-
return
|
|
116
|
+
return ckpts_dir / _PRELOAD_MARKER_NAME
|
|
97
117
|
|
|
98
118
|
|
|
99
119
|
def _annotators_already_verified(ref: str) -> bool:
|
|
@@ -107,6 +127,32 @@ def _annotators_already_verified(ref: str) -> bool:
|
|
|
107
127
|
return False
|
|
108
128
|
|
|
109
129
|
|
|
130
|
+
def controlnet_annotators_present() -> bool | None:
|
|
131
|
+
"""Whether the controlnet annotators are already downloaded and verified on this machine.
|
|
132
|
+
|
|
133
|
+
Reads the on-disk preload marker (keyed to the pinned ``comfyui_controlnet_aux`` commit) exactly the
|
|
134
|
+
way :func:`download_all_controlnet_annotators` reads it for its fast-path skip, so a caller can decide
|
|
135
|
+
whether a (slow, one-time) annotator download is still pending *before* paying for it. Import-safe:
|
|
136
|
+
needs neither :func:`hordelib.initialise` nor a GPU, because the directory is derived from
|
|
137
|
+
:class:`~hordelib.settings.UserSettings` when the runtime env var is unset.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
``True`` when the marker matches the pinned ref (a full set was downloaded and verified),
|
|
141
|
+
``False`` when it is absent or stale (a download is still pending), or ``None`` when presence
|
|
142
|
+
cannot be determined (the pinned ref or the annotator directory is unknown).
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
ref = _pinned_annotator_ref()
|
|
146
|
+
if ref is None:
|
|
147
|
+
return None
|
|
148
|
+
if _preload_marker_path() is None:
|
|
149
|
+
return None
|
|
150
|
+
return _annotators_already_verified(ref)
|
|
151
|
+
except Exception as e: # pragma: no cover - presence is best-effort; never raise into a caller
|
|
152
|
+
logger.debug("Could not determine controlnet annotator presence: error={}", e)
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
|
|
110
156
|
def _record_annotators_verified(ref: str) -> None:
|
|
111
157
|
"""Persist that annotators for ``ref`` are downloaded and verified on this machine.
|
|
112
158
|
|
|
@@ -5,6 +5,7 @@ from typing import Self
|
|
|
5
5
|
|
|
6
6
|
import psutil
|
|
7
7
|
from horde_model_reference import resolve_weights_root
|
|
8
|
+
from loguru import logger
|
|
8
9
|
|
|
9
10
|
from hordelib import is_initialised
|
|
10
11
|
from hordelib.utils.switch import Switch
|
|
@@ -43,12 +44,14 @@ class UserSettings:
|
|
|
43
44
|
if not is_initialised():
|
|
44
45
|
return 0
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
# Route through the backend-agnostic accelerator layer (CUDA/ROCm/XPU/MPS/CPU), not NVIDIA-only
|
|
48
|
+
# NVML, so VRAM-based mode/threshold selection is correct on non-NVIDIA cards too.
|
|
49
|
+
from hordelib.utils.torch_memory import get_torch_total_vram_mb
|
|
47
50
|
|
|
48
51
|
try:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
return get_torch_total_vram_mb()
|
|
53
|
+
except Exception as vram_error:
|
|
54
|
+
logger.debug(f"Could not read total VRAM for settings: {type(vram_error).__name__}: {vram_error}")
|
|
52
55
|
return 0
|
|
53
56
|
|
|
54
57
|
@staticmethod
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Human-facing GPU information for diagnostics and the benchmark.
|
|
2
|
+
|
|
3
|
+
VRAM totals/frees come from the backend-agnostic :mod:`hordelib.utils.torch_memory` (correct on every
|
|
4
|
+
backend ComfyUI supports); NVIDIA-only enrichment (utilization, temperature, power, fan, PCIe) comes from
|
|
5
|
+
:mod:`hordelib.utils.nvml`. On non-NVIDIA backends the rich stats are simply marked unsupported rather than
|
|
6
|
+
erroring.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
import torch.version
|
|
14
|
+
from loguru import logger
|
|
15
|
+
from pydantic import BaseModel
|
|
16
|
+
from strenum import StrEnum
|
|
17
|
+
|
|
18
|
+
from hordelib.utils import nvml
|
|
19
|
+
from hordelib.utils.torch_memory import get_torch_free_vram_mb, get_torch_total_vram_mb
|
|
20
|
+
|
|
21
|
+
_AVERAGE_SAMPLES_PER_SECOND = 10
|
|
22
|
+
_AVERAGE_WINDOW_SECONDS = 60 * 5
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GPUInfo:
|
|
26
|
+
"""Collects per-device GPU statistics, keeping rolling averages of load, temperature, and power."""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
"""Initialize the collector for the device named by ``CUDA_VISIBLE_DEVICES`` (default 0)."""
|
|
30
|
+
self.avg_load: list[int] = []
|
|
31
|
+
self.avg_temp: list[int] = []
|
|
32
|
+
self.avg_power: list[int] = []
|
|
33
|
+
self._average_window_samples = _AVERAGE_SAMPLES_PER_SECOND * _AVERAGE_WINDOW_SECONDS
|
|
34
|
+
if not self.is_nvidia():
|
|
35
|
+
logger.warning("Detailed GPU info (load/temp/power) is only available on NVIDIA GPUs")
|
|
36
|
+
self.device = int(os.getenv("CUDA_VISIBLE_DEVICES", "0"))
|
|
37
|
+
|
|
38
|
+
def is_nvidia(self) -> bool:
|
|
39
|
+
"""Return whether the active torch build targets NVIDIA CUDA (not ROCm/HIP)."""
|
|
40
|
+
return torch.version.cuda is not None and torch.version.hip is None
|
|
41
|
+
|
|
42
|
+
def is_amd(self) -> bool:
|
|
43
|
+
"""Return whether the active torch build targets AMD ROCm/HIP."""
|
|
44
|
+
return torch.version.hip is not None
|
|
45
|
+
|
|
46
|
+
def get_total_vram_mb(self) -> int:
|
|
47
|
+
"""Return the device's total VRAM in MB (backend-agnostic), or 0 when unavailable."""
|
|
48
|
+
return get_torch_total_vram_mb()
|
|
49
|
+
|
|
50
|
+
def get_free_vram_mb(self) -> int:
|
|
51
|
+
"""Return the device's free VRAM in MB (backend-agnostic), or 0 when unavailable."""
|
|
52
|
+
return get_torch_free_vram_mb()
|
|
53
|
+
|
|
54
|
+
def _append_rolling(self, samples: list[int], value: int) -> list[int]:
|
|
55
|
+
"""Append ``value`` to ``samples`` and trim it to the averaging window; return the trimmed list."""
|
|
56
|
+
samples.append(value)
|
|
57
|
+
return samples[-self._average_window_samples :]
|
|
58
|
+
|
|
59
|
+
def get_info(self) -> GPUInfoResult | None:
|
|
60
|
+
"""Return a snapshot of this device's stats, or None when even total VRAM cannot be read.
|
|
61
|
+
|
|
62
|
+
On non-NVIDIA backends the snapshot carries backend-agnostic VRAM figures with the rich sensors
|
|
63
|
+
marked unsupported. On NVIDIA, sensors come from NVML (accurate v2 memory).
|
|
64
|
+
"""
|
|
65
|
+
total_vram_mb = self.get_total_vram_mb()
|
|
66
|
+
free_vram_mb = self.get_free_vram_mb()
|
|
67
|
+
|
|
68
|
+
stats = nvml.get_device_stats(self.device) if self.is_nvidia() else None
|
|
69
|
+
if stats is None:
|
|
70
|
+
return GPUInfoResult.get_empty_info(vram_total_mb=total_vram_mb, vram_free_mb=free_vram_mb)
|
|
71
|
+
|
|
72
|
+
avg_load = self._append_rolling(self.avg_load, stats.utilization_gpu_percent)
|
|
73
|
+
avg_temp = self._append_rolling(self.avg_temp, stats.temperature_celsius)
|
|
74
|
+
avg_power = self._append_rolling(self.avg_power, stats.power_watts)
|
|
75
|
+
|
|
76
|
+
# NVML's v2 memory is more accurate than the comfy/torch totals here; prefer it when present.
|
|
77
|
+
vram_total = stats.memory.total_mb or total_vram_mb
|
|
78
|
+
vram_free = stats.memory.free_mb or free_vram_mb
|
|
79
|
+
vram_used = stats.memory.used_mb or max(vram_total - vram_free, 0)
|
|
80
|
+
|
|
81
|
+
return GPUInfoResult(
|
|
82
|
+
supported=True,
|
|
83
|
+
product=stats.name,
|
|
84
|
+
pci_gen=str(stats.pcie_link_generation),
|
|
85
|
+
pci_width=str(stats.pcie_link_width),
|
|
86
|
+
fan_speed=(str(stats.fan_speed_percent), Unit.percent),
|
|
87
|
+
vram_total=(vram_total, Unit.megabytes),
|
|
88
|
+
vram_used=(vram_used, Unit.megabytes),
|
|
89
|
+
vram_free=(vram_free, Unit.megabytes),
|
|
90
|
+
load=(stats.utilization_gpu_percent, Unit.percent),
|
|
91
|
+
temp=(stats.temperature_celsius, Unit.degrees_celsius),
|
|
92
|
+
power=(stats.power_watts, Unit.watts),
|
|
93
|
+
avg_load=(round(sum(avg_load) / len(avg_load)), Unit.percent),
|
|
94
|
+
avg_temp=(round(sum(avg_temp) / len(avg_temp)), Unit.degrees_celsius),
|
|
95
|
+
avg_power=(round(sum(avg_power) / len(avg_power)), Unit.watts),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Unit(StrEnum):
|
|
100
|
+
"""A display unit for a GPU statistic."""
|
|
101
|
+
|
|
102
|
+
unitless = ""
|
|
103
|
+
percent = "%"
|
|
104
|
+
degrees_celsius = "C"
|
|
105
|
+
megabytes = "MiB"
|
|
106
|
+
gigabytes = "GiB"
|
|
107
|
+
watts = "W"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class GPUInfoResult(BaseModel):
|
|
111
|
+
"""Represents a single snapshot of a GPU's statistics, each value paired with its display unit."""
|
|
112
|
+
|
|
113
|
+
supported: bool
|
|
114
|
+
product: str
|
|
115
|
+
pci_gen: str
|
|
116
|
+
pci_width: str
|
|
117
|
+
fan_speed: tuple[str, Unit]
|
|
118
|
+
vram_total: tuple[int, Unit]
|
|
119
|
+
vram_used: tuple[int, Unit]
|
|
120
|
+
vram_free: tuple[int, Unit]
|
|
121
|
+
load: tuple[int, Unit]
|
|
122
|
+
temp: tuple[int, Unit]
|
|
123
|
+
power: tuple[int, Unit]
|
|
124
|
+
avg_load: tuple[int, Unit]
|
|
125
|
+
avg_temp: tuple[int, Unit]
|
|
126
|
+
avg_power: tuple[int, Unit]
|
|
127
|
+
|
|
128
|
+
def __str__(self) -> str:
|
|
129
|
+
"""Return a newline-separated ``key: value unit`` rendering of every field."""
|
|
130
|
+
final_string = ""
|
|
131
|
+
for key, value in self.model_dump().items():
|
|
132
|
+
rendered_value = f"{value[0]} {value[1]}" if isinstance(value, tuple) else value
|
|
133
|
+
final_string += f"{key}: {rendered_value}\n"
|
|
134
|
+
return final_string
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
def get_empty_info(cls, vram_total_mb: int = 0, vram_free_mb: int = 0) -> GPUInfoResult:
|
|
138
|
+
"""Create a result carrying only backend-agnostic VRAM, with the NVIDIA-only sensors zeroed."""
|
|
139
|
+
vram_used_mb = max(vram_total_mb - vram_free_mb, 0)
|
|
140
|
+
return GPUInfoResult(
|
|
141
|
+
supported=False,
|
|
142
|
+
product="unknown (detailed stats unavailable on this backend)",
|
|
143
|
+
pci_gen="?",
|
|
144
|
+
pci_width="?",
|
|
145
|
+
fan_speed=("0", Unit.percent),
|
|
146
|
+
vram_total=(vram_total_mb, Unit.megabytes),
|
|
147
|
+
vram_used=(vram_used_mb, Unit.megabytes),
|
|
148
|
+
vram_free=(vram_free_mb, Unit.megabytes),
|
|
149
|
+
load=(0, Unit.percent),
|
|
150
|
+
temp=(0, Unit.degrees_celsius),
|
|
151
|
+
power=(0, Unit.watts),
|
|
152
|
+
avg_load=(0, Unit.percent),
|
|
153
|
+
avg_temp=(0, Unit.degrees_celsius),
|
|
154
|
+
avg_power=(0, Unit.watts),
|
|
155
|
+
)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Optional NVIDIA-only GPU telemetry via NVML, the single home for direct NVML access in hordelib.
|
|
2
|
+
|
|
3
|
+
NVML is NVIDIA-specific *enrichment* (core utilization, temperature, power, and the most accurate memory
|
|
4
|
+
figures); the backend-agnostic metrics that never assume NVIDIA live in
|
|
5
|
+
:mod:`hordelib.utils.torch_memory`. Every function here is best-effort: when NVML is missing or the machine
|
|
6
|
+
has no NVIDIA driver, each returns ``None`` so non-NVIDIA backends simply report no enrichment rather than
|
|
7
|
+
erroring.
|
|
8
|
+
|
|
9
|
+
Uses ``nvidia-ml-py`` (imported as ``pynvml``), the maintained official binding. Memory is read through
|
|
10
|
+
``nvmlDeviceGetMemoryInfo`` with the ``nvmlMemory_v2`` version, which accounts for driver-reserved memory;
|
|
11
|
+
the deprecated standalone ``pynvml`` package and its ``pynvml.smi`` interface under-report *used* VRAM
|
|
12
|
+
(over-reporting *free*), which is exactly the wrong direction for a VRAM budget.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import warnings
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
|
|
20
|
+
from loguru import logger
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from ctypes import c_void_p
|
|
25
|
+
|
|
26
|
+
_BYTES_PER_MB = 1024 * 1024
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NvmlMemory(BaseModel):
|
|
30
|
+
"""Represents a device's NVML memory figures, in MB (from ``nvmlDeviceGetMemoryInfo`` v2)."""
|
|
31
|
+
|
|
32
|
+
total_mb: int
|
|
33
|
+
free_mb: int
|
|
34
|
+
used_mb: int
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class NvmlDeviceStats(BaseModel):
|
|
38
|
+
"""Represents a single NVIDIA device's NVML telemetry snapshot (enrichment for display/diagnostics)."""
|
|
39
|
+
|
|
40
|
+
name: str
|
|
41
|
+
memory: NvmlMemory
|
|
42
|
+
utilization_gpu_percent: int
|
|
43
|
+
temperature_celsius: int
|
|
44
|
+
power_watts: int
|
|
45
|
+
fan_speed_percent: int
|
|
46
|
+
pcie_link_generation: int
|
|
47
|
+
pcie_link_width: int
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
_nvml_unavailable = False
|
|
51
|
+
"""Latches True after the first failed NVML init so the worker does not retry NVML every call."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _nvml_module() -> object | None:
|
|
55
|
+
"""Return the initialised ``nvidia-ml-py`` module, or None when NVML is unavailable.
|
|
56
|
+
|
|
57
|
+
Initialisation is attempted once; any failure (no driver, non-NVIDIA host, missing binding) latches
|
|
58
|
+
NVML off for the process and logs the reason at debug. ``nvmlInit`` is idempotent, so calling this
|
|
59
|
+
repeatedly is cheap once initialised.
|
|
60
|
+
"""
|
|
61
|
+
global _nvml_unavailable
|
|
62
|
+
if _nvml_unavailable:
|
|
63
|
+
return None
|
|
64
|
+
try:
|
|
65
|
+
with warnings.catch_warnings():
|
|
66
|
+
warnings.simplefilter("ignore") # silence any deprecation warning from a shim install
|
|
67
|
+
import pynvml
|
|
68
|
+
pynvml.nvmlInit()
|
|
69
|
+
return pynvml
|
|
70
|
+
except Exception as nvml_error: # noqa: BLE001 - any NVML failure means "no NVIDIA telemetry", not a crash
|
|
71
|
+
logger.debug(f"NVML unavailable ({type(nvml_error).__name__}: {nvml_error}); NVIDIA telemetry disabled")
|
|
72
|
+
_nvml_unavailable = True
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_nvml_available() -> bool:
|
|
77
|
+
"""Return whether NVML telemetry can be read on this machine."""
|
|
78
|
+
return _nvml_module() is not None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _device_handle(pynvml: object, index: int) -> c_void_p | None:
|
|
82
|
+
"""Return the NVML handle for device ``index``, or None when it cannot be resolved."""
|
|
83
|
+
try:
|
|
84
|
+
return pynvml.nvmlDeviceGetHandleByIndex(index) # type: ignore[attr-defined]
|
|
85
|
+
except Exception as handle_error: # noqa: BLE001 - a missing device is "no telemetry", not a crash
|
|
86
|
+
logger.debug(f"NVML handle for device {index} unavailable ({type(handle_error).__name__}: {handle_error})")
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_device_memory_mb(index: int = 0) -> NvmlMemory | None:
|
|
91
|
+
"""Return device ``index``'s memory (MB) via the accurate ``nvmlMemory_v2`` query, or None.
|
|
92
|
+
|
|
93
|
+
The v2 query includes driver-reserved memory in ``used``/``free``; older v1 (and the deprecated
|
|
94
|
+
``pynvml.smi``) omit it, under-reporting used VRAM. Falls back to v1 only if the binding lacks v2.
|
|
95
|
+
"""
|
|
96
|
+
pynvml = _nvml_module()
|
|
97
|
+
if pynvml is None:
|
|
98
|
+
return None
|
|
99
|
+
handle = _device_handle(pynvml, index)
|
|
100
|
+
if handle is None:
|
|
101
|
+
return None
|
|
102
|
+
try:
|
|
103
|
+
memory_v2_version = getattr(pynvml, "nvmlMemory_v2", None)
|
|
104
|
+
if memory_v2_version is not None:
|
|
105
|
+
info = pynvml.nvmlDeviceGetMemoryInfo(handle, version=memory_v2_version) # type: ignore[attr-defined]
|
|
106
|
+
else:
|
|
107
|
+
info = pynvml.nvmlDeviceGetMemoryInfo(handle) # type: ignore[attr-defined]
|
|
108
|
+
except Exception as memory_error: # noqa: BLE001 - best-effort enrichment
|
|
109
|
+
logger.debug(f"NVML memory read failed for device {index} ({type(memory_error).__name__}: {memory_error})")
|
|
110
|
+
return None
|
|
111
|
+
return NvmlMemory(
|
|
112
|
+
total_mb=round(info.total / _BYTES_PER_MB),
|
|
113
|
+
free_mb=round(info.free / _BYTES_PER_MB),
|
|
114
|
+
used_mb=round(info.used / _BYTES_PER_MB),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def get_device_utilization_percent(index: int = 0) -> int | None:
|
|
119
|
+
"""Return device ``index``'s core-utilization percentage (0-100), or None when unavailable."""
|
|
120
|
+
pynvml = _nvml_module()
|
|
121
|
+
if pynvml is None:
|
|
122
|
+
return None
|
|
123
|
+
handle = _device_handle(pynvml, index)
|
|
124
|
+
if handle is None:
|
|
125
|
+
return None
|
|
126
|
+
try:
|
|
127
|
+
return int(pynvml.nvmlDeviceGetUtilizationRates(handle).gpu) # type: ignore[attr-defined]
|
|
128
|
+
except Exception as utilization_error: # noqa: BLE001 - best-effort enrichment
|
|
129
|
+
logger.debug(f"NVML utilization read failed for device {index} ({type(utilization_error).__name__})")
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _device_name(pynvml: object, handle: c_void_p) -> str:
|
|
134
|
+
"""Return the device product name, decoding the bytes older bindings return."""
|
|
135
|
+
try:
|
|
136
|
+
name = pynvml.nvmlDeviceGetName(handle) # type: ignore[attr-defined]
|
|
137
|
+
except Exception: # noqa: BLE001 - name is cosmetic
|
|
138
|
+
return "unknown"
|
|
139
|
+
return name.decode() if isinstance(name, bytes) else str(name)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _best_effort_int(read: object, *, field: str, index: int) -> int:
|
|
143
|
+
"""Return ``read()`` coerced to int, or 0 when the card does not support that sensor."""
|
|
144
|
+
try:
|
|
145
|
+
return int(read()) # type: ignore[operator]
|
|
146
|
+
except Exception as sensor_error: # noqa: BLE001 - many sensors are unsupported per-card
|
|
147
|
+
logger.debug(f"NVML {field} unsupported for device {index} ({type(sensor_error).__name__})")
|
|
148
|
+
return 0
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_device_stats(index: int = 0) -> NvmlDeviceStats | None:
|
|
152
|
+
"""Return a full NVML telemetry snapshot for device ``index``, or None when NVML is unavailable.
|
|
153
|
+
|
|
154
|
+
Memory uses the accurate v2 query; each enrichment sensor (temperature, power, fan, PCIe) is read
|
|
155
|
+
best-effort and defaults to 0 when the card does not expose it.
|
|
156
|
+
"""
|
|
157
|
+
pynvml = _nvml_module()
|
|
158
|
+
if pynvml is None:
|
|
159
|
+
return None
|
|
160
|
+
handle = _device_handle(pynvml, index)
|
|
161
|
+
if handle is None:
|
|
162
|
+
return None
|
|
163
|
+
memory = get_device_memory_mb(index)
|
|
164
|
+
if memory is None:
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
temperature_sensor = getattr(pynvml, "NVML_TEMPERATURE_GPU", 0)
|
|
168
|
+
power_milliwatts = _best_effort_int(lambda: pynvml.nvmlDeviceGetPowerUsage(handle), field="power", index=index)
|
|
169
|
+
return NvmlDeviceStats(
|
|
170
|
+
name=_device_name(pynvml, handle),
|
|
171
|
+
memory=memory,
|
|
172
|
+
utilization_gpu_percent=get_device_utilization_percent(index) or 0,
|
|
173
|
+
temperature_celsius=_best_effort_int(
|
|
174
|
+
lambda: pynvml.nvmlDeviceGetTemperature(handle, temperature_sensor),
|
|
175
|
+
field="temperature",
|
|
176
|
+
index=index,
|
|
177
|
+
),
|
|
178
|
+
power_watts=round(power_milliwatts / 1000),
|
|
179
|
+
fan_speed_percent=_best_effort_int(lambda: pynvml.nvmlDeviceGetFanSpeed(handle), field="fan", index=index),
|
|
180
|
+
pcie_link_generation=_best_effort_int(
|
|
181
|
+
lambda: pynvml.nvmlDeviceGetCurrPcieLinkGeneration(handle),
|
|
182
|
+
field="pcie_gen",
|
|
183
|
+
index=index,
|
|
184
|
+
),
|
|
185
|
+
pcie_link_width=_best_effort_int(
|
|
186
|
+
lambda: pynvml.nvmlDeviceGetCurrPcieLinkWidth(handle),
|
|
187
|
+
field="pcie_width",
|
|
188
|
+
index=index,
|
|
189
|
+
),
|
|
190
|
+
)
|
|
@@ -8,8 +8,9 @@ what the execution backend sees; otherwise they fall back to plain torch device
|
|
|
8
8
|
cover every torch backend (CUDA/ROCm, Intel XPU, Apple MPS, CPU), not just CUDA.
|
|
9
9
|
|
|
10
10
|
This module is the single source of accelerator truth for the worker: it never assumes
|
|
11
|
-
NVIDIA. NVML
|
|
12
|
-
|
|
11
|
+
NVIDIA. NVML is treated as optional NVIDIA-only enrichment (see :mod:`hordelib.utils.nvml`),
|
|
12
|
+
never as a hard requirement here; :func:`get_accelerator_utilization_percent` is the one place
|
|
13
|
+
that consults it, and only for the CUDA/NVIDIA backend.
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
16
|
from __future__ import annotations
|
|
@@ -140,6 +141,21 @@ def log_free_ram() -> None:
|
|
|
140
141
|
logger.debug(f"Free VRAM: {get_torch_free_vram_mb():0.0f} MB, Free RAM: {get_free_ram_mb():0.0f} MB")
|
|
141
142
|
|
|
142
143
|
|
|
144
|
+
def get_accelerator_utilization_percent(index: int = 0) -> int | None:
|
|
145
|
+
"""Return device ``index``'s core-utilization percentage (0-100), or None when unavailable.
|
|
146
|
+
|
|
147
|
+
Utilization is vendor telemetry with no portable torch/ComfyUI equivalent, so this is best-effort and
|
|
148
|
+
backend-specific: the NVIDIA CUDA backend is read via NVML (:mod:`hordelib.utils.nvml`); every other
|
|
149
|
+
backend (ROCm, XPU, MPS, CPU) returns None until a vendor source is wired, so callers simply report no
|
|
150
|
+
GPU duty cycle there. ROCm presents through ``torch.cuda`` but is not an NVML device, so it returns None.
|
|
151
|
+
"""
|
|
152
|
+
if _active_torch_kind() is not AcceleratorKind.cuda:
|
|
153
|
+
return None
|
|
154
|
+
from hordelib.utils import nvml
|
|
155
|
+
|
|
156
|
+
return nvml.get_device_utilization_percent(index)
|
|
157
|
+
|
|
158
|
+
|
|
143
159
|
def _enumerate_via_comfy(mm: ModuleType) -> list[AcceleratorInfo]:
|
|
144
160
|
"""Enumerate devices through ComfyUI's backend-agnostic device APIs."""
|
|
145
161
|
devices = mm.get_all_torch_devices()
|