Skip to content

Dependencies

dependencies

Python dependency management — Steps 7-9.

Installs all Python packages required by ComfyUI:

  • Core (Step 7): PyTorch with CUDA index, ComfyUI requirements.txt.
  • Standard + Wheels (Step 8): additional packages and pre-built .whl files (e.g. Nunchaku, InsightFace).
  • Custom Nodes (Step 9): delegates to :mod:src.installer.nodes for Git-clone-based node installation.

All installs use uv — no raw pip.

install_core_dependencies(python_exe, comfy_path, deps, log, *, cuda_tag)

Install PyTorch and ComfyUI requirements.

Performs two sub-steps:

  1. Install PyTorch packages from the CUDA index URL.
  2. Install ComfyUI's own requirements.txt.

Parameters:

Name Type Description Default
python_exe Path

Path to the venv Python executable.

required
comfy_path Path

ComfyUI repository directory.

required
deps DependenciesConfig

Parsed dependencies.json.

required
log InstallerLogger

Installer logger for user-facing messages.

required
cuda_tag str | None

CUDA tag to select PyTorch variant, e.g. "cu130".

required
Source code in src/installer/dependencies.py
def install_core_dependencies(
    python_exe: Path,
    comfy_path: Path,
    deps: DependenciesConfig,
    log: InstallerLogger,
    *,
    cuda_tag: str | None,
) -> None:
    """Install PyTorch and ComfyUI requirements.

    Performs two sub-steps:

    1. Install PyTorch packages from the CUDA index URL.
    2. Install ComfyUI's own ``requirements.txt``.

    Args:
        python_exe: Path to the venv Python executable.
        comfy_path: ComfyUI repository directory.
        deps: Parsed ``dependencies.json``.
        log: Installer logger for user-facing messages.
        cuda_tag: CUDA tag to select PyTorch variant, e.g. ``"cu130"``.
    """

    # For macOS (MPS/CPU), install standard torch from PyPI without index_url
    if cuda_tag is None:
        # Derive package names from any available torch config, stripping +cuXXX suffixes
        any_torch = next(
            (deps.pip_packages.get_torch(t) for t in deps.pip_packages.supported_cuda_tags
             if deps.pip_packages.get_torch(t) is not None),
            None,
        )
        if any_torch is None:
            log.warning("No PyTorch configuration found in dependencies.json.", level=1)
            return
        torch_pkgs = [p.split("+")[0].split("==")[0] for p in any_torch.packages.split()]
        # xformers is NVIDIA-only — skip on macOS/CPU
        torch_pkgs = [p for p in torch_pkgs if p != "xformers"]
        log.item(f"Installing PyTorch ({', '.join(torch_pkgs)}) [macOS/CPU]...")
        uv_install(python_exe, torch_pkgs)

    # AMD ROCm on Windows — uses direct wheel URLs from repo.radeon.com
    elif cuda_tag == "rocm_windows":
        rocm_cfg = deps.pip_packages.rocm_windows
        if rocm_cfg is None:
            log.error("No ROCm Windows configuration found in dependencies.json.")
            log.item("Please update dependencies.json with the 'rocm_windows' section.")
            return

        # Detect venv Python version to select correct wheels
        from src.utils.python_info import detect_venv_python_version
        py_version = detect_venv_python_version(python_exe)

        torch_urls = rocm_cfg.get_torch_urls(py_version)
        if torch_urls is None:
            log.error(
                f"No ROCm Windows wheels for Python {py_version[0]}.{py_version[1]}. "
                f"ROCm on Windows requires Python {rocm_cfg.requires_python}."
            )
            return

        # Phase 1: Install ROCm SDK
        log.item(f"Installing AMD ROCm SDK v{rocm_cfg.rocm_version}...")
        uv_install(python_exe, rocm_cfg.rocm_sdk, no_cache=True)
        log.sub("ROCm SDK installed.", style="success")

        # Phase 2: Install PyTorch with ROCm support
        log.item("Installing PyTorch with ROCm support (this may take several minutes)...")
        uv_install(python_exe, torch_urls, no_cache=True)
        log.sub("PyTorch + ROCm installed.", style="success")

    # For Windows/Linux with CUDA
    else:
        torch_cfg = deps.pip_packages.get_torch(cuda_tag)
        if torch_cfg is None:
            log.warning(f"No PyTorch config found for CUDA tag '{cuda_tag}'. Skipping.", level=1)
            return

        torch_pkgs = torch_cfg.packages.split()
        log.item(f"Installing PyTorch ({', '.join(torch_pkgs)}) [{cuda_tag}]...")
        uv_install(
            python_exe,
            torch_pkgs,
            index_url=torch_cfg.index_url,
        )

    # ComfyUI requirements — filter out torch packages to avoid PyPI
    # overwriting the CUDA versions we just installed from the PyTorch index.
    req_file = comfy_path / deps.pip_packages.comfyui_requirements
    if req_file.exists():
        log.item("Installing ComfyUI requirements...")
        filtered = _filter_torch_from_requirements(req_file)
        uv_install(python_exe, requirements=filtered)

install_python_packages(python_exe, deps, log, *, cuda_tag=None)

Install additional standard packages listed in deps.

Parameters:

Name Type Description Default
python_exe Path

Path to the venv Python executable.

required
deps DependenciesConfig

Parsed dependencies.json.

required
log InstallerLogger

Installer logger for user-facing messages.

required
cuda_tag str | None

GPU configuration tag to conditionally filter packages.

None
Source code in src/installer/dependencies.py
def install_python_packages(
    python_exe: Path,
    deps: DependenciesConfig,
    log: InstallerLogger,
    *,
    cuda_tag: str | None = None,
) -> None:
    """Install additional standard packages listed in *deps*.

    Args:
        python_exe: Path to the venv Python executable.
        deps: Parsed ``dependencies.json``.
        log: Installer logger for user-facing messages.
        cuda_tag: GPU configuration tag to conditionally filter packages.
    """

    if deps.pip_packages.standard:
        pkgs = deps.pip_packages.standard.copy()

        # Filter out CUDA-specific logic if not using NVIDIA (macOS, AMD, or CPU)
        if cuda_tag is None or not cuda_tag.startswith("cu"):
            log.info("Filtering out CUDA-only standard packages (non-NVIDIA environment).")
            pkgs = [p for p in pkgs if not p.startswith("cupy-cuda") and p != "nvidia-ml-py"]
            if "onnxruntime-gpu" in pkgs:
                pkgs[pkgs.index("onnxruntime-gpu")] = "onnxruntime"

        # On Windows, insightface is installed via precompiled wheel (no C++ compiler needed)
        import sys
        if sys.platform == "win32" and "insightface" in pkgs:
            pkgs.remove("insightface")

        log.item(f"Installing {len(pkgs)} standard packages...")
        uv_install(python_exe, pkgs)

install_wheels(python_exe, install_path, deps, log, *, cuda_tag=None)

Download and install pre-built .whl packages.

Detects the Python version from the venv and picks the matching wheel for each entry. Checksums are looked up from the tools_manifest.json (downloaded at provision time) rather than being hardcoded in dependencies.json.

Parameters:

Name Type Description Default
python_exe Path

Path to the venv Python executable.

required
install_path Path

Root installation directory.

required
deps DependenciesConfig

Parsed dependencies.json.

required
log InstallerLogger

Installer logger for user-facing messages.

required
cuda_tag str | None

CUDA tag for CUDA-aware wheel selection.

None
Source code in src/installer/dependencies.py
def install_wheels(
    python_exe: Path,
    install_path: Path,
    deps: DependenciesConfig,
    log: InstallerLogger,
    *,
    cuda_tag: str | None = None,
) -> None:
    """Download and install pre-built ``.whl`` packages.

    Detects the Python version from the venv and picks the matching
    wheel for each entry.  Checksums are looked up from the
    ``tools_manifest.json`` (downloaded at provision time) rather than
    being hardcoded in ``dependencies.json``.

    Args:
        python_exe: Path to the venv Python executable.
        install_path: Root installation directory.
        deps: Parsed ``dependencies.json``.
        log: Installer logger for user-facing messages.
        cuda_tag: CUDA tag for CUDA-aware wheel selection.
    """
    if not deps.pip_packages.wheels:
        return

    # Detect Python version from the venv
    from src.utils.python_info import detect_venv_python_version
    py_version = detect_venv_python_version(python_exe)
    log.info(f"Python version detected: {py_version[0]}.{py_version[1]}")

    # Load tools manifest for checksum verification
    from src.installer.environment import load_tools_manifest, lookup_wheel_checksum
    manifest = load_tools_manifest(install_path)

    log.item(f"Installing {len(deps.pip_packages.wheels)} wheel packages...")
    scripts_dir = install_path / "scripts"

    for wheel in deps.pip_packages.wheels:
        if wheel.name == "nunchaku" and (cuda_tag is None or not cuda_tag.startswith("cu")):
            log.sub("Skipping nunchaku wheel (NVIDIA GPU required).", style="cyan")
            continue

        resolved = wheel.resolve(py_version, cuda_tag=cuda_tag)
        if resolved is None:
            log.warning(
                f"No wheel available for {wheel.name} on Python {py_version[0]}.{py_version[1]}, skipping.",
                level=2,
            )
            continue

        whl_name, whl_url, _legacy_checksum = resolved
        # Prefer manifest checksum; fall back to legacy hardcoded checksum
        whl_checksum = lookup_wheel_checksum(manifest, whl_url) or _legacy_checksum
        wheel_path = scripts_dir / f"{whl_name}.whl"
        log.sub(f"Installing {whl_name}...")

        try:
            download_file(whl_url, wheel_path, checksum=whl_checksum, mirrors=deps.mirrors)
            uv_install(python_exe, [str(wheel_path)], ignore_errors=True)
        except Exception as e:
            log.warning(f"Failed to install {whl_name}: {e}", level=3)
        finally:
            wheel_path.unlink(missing_ok=True)

install_custom_nodes(python_exe, comfy_path, install_path, log, *, node_tier='full', source_dir=None)

Install custom nodes from custom_nodes.json (additive-only).

Resolves the manifest from install_path/scripts/ or from the source scripts directory if not found locally. Delegates the actual clone + requirements install to :func:nodes.install_all_nodes.

Also provisions nunchaku_versions.json into the Nunchaku node directory if both files exist.

Parameters:

Name Type Description Default
python_exe Path

Path to the venv Python executable.

required
comfy_path Path

ComfyUI repository directory.

required
install_path Path

Root installation directory.

required
log InstallerLogger

Installer logger for user-facing messages.

required
node_tier str

Bundle tier — "minimal", "umeairt", or "full" (default).

'full'
source_dir Path | None

Pre-resolved source scripts directory. If None, falls back to calling :func:find_source_scripts.

None
Source code in src/installer/dependencies.py
def install_custom_nodes(
    python_exe: Path,
    comfy_path: Path,
    install_path: Path,
    log: InstallerLogger,
    *,
    node_tier: str = "full",
    source_dir: Path | None = None,
) -> None:
    """Install custom nodes from ``custom_nodes.json`` (additive-only).

    Resolves the manifest from ``install_path/scripts/`` or from the
    source scripts directory if not found locally. Delegates the actual
    clone + requirements install to :func:`nodes.install_all_nodes`.

    Also provisions ``nunchaku_versions.json`` into the Nunchaku node
    directory if both files exist.

    Args:
        python_exe: Path to the venv Python executable.
        comfy_path: ComfyUI repository directory.
        install_path: Root installation directory.
        log: Installer logger for user-facing messages.
        node_tier: Bundle tier — ``"minimal"``, ``"umeairt"``,
            or ``"full"`` (default).
        source_dir: Pre-resolved source scripts directory. If ``None``,
            falls back to calling :func:`find_source_scripts`.
    """
    from src.installer.nodes import filter_by_tier, install_all_nodes, load_manifest

    scripts_dir = install_path / "scripts"
    custom_nodes_dir = comfy_path / "custom_nodes"

    # Resolve source_dir lazily if not provided
    if source_dir is None:
        from src.installer.environment import find_source_scripts
        try:
            source_dir = find_source_scripts()
        except FileNotFoundError:
            source_dir = None

    # Try to load manifest: install_path/scripts/ first, then source scripts
    manifest_path = scripts_dir / "custom_nodes.json"
    if not manifest_path.exists() and source_dir:
        manifest_path = source_dir / "custom_nodes.json"

    if not manifest_path.exists():
        log.warning("custom_nodes.json not found. Skipping node installation.", level=1)
        return

    manifest = load_manifest(manifest_path)
    manifest = filter_by_tier(manifest, node_tier)
    install_all_nodes(manifest, custom_nodes_dir, python_exe, log)

    # Copy nunchaku_versions.json into the nunchaku node directory
    # (the node expects it in its own folder, not in scripts/)
    nunchaku_src = scripts_dir / "nunchaku_versions.json"
    if not nunchaku_src.exists() and source_dir:
        nunchaku_src = source_dir / "nunchaku_versions.json"

    nunchaku_dst = custom_nodes_dir / "ComfyUI-nunchaku" / "nunchaku_versions.json"
    if nunchaku_src.exists() and nunchaku_dst.parent.exists():
        import shutil
        shutil.copy2(nunchaku_src, nunchaku_dst)
        log.sub("  nunchaku_versions.json provisioned.", style="success")

    # Enable uv in ComfyUI-Manager config (mirrors PS installer's Set-ManagerUseUv)
    configure_manager_uv(install_path, log)

configure_manager_uv(install_path, log)

Ensure ComfyUI-Manager uses uv instead of raw pip.

Writes use_uv = True to user/__manager/config.ini. Creates the directory and file if they do not exist. If the file already exists, the setting is updated in-place (or appended if missing).

This aligns ComfyUI-Manager's runtime package management with the rest of the UmeAiRT system (uv-only), resulting in significantly faster and more stable installs when users add custom nodes via the Manager UI.

Parameters:

Name Type Description Default
install_path Path

Root installation directory.

required
log InstallerLogger

Installer logger for user-facing messages.

required
Source code in src/installer/dependencies.py
def configure_manager_uv(install_path: Path, log: InstallerLogger) -> None:
    """Ensure ComfyUI-Manager uses ``uv`` instead of raw ``pip``.

    Writes ``use_uv = True`` to ``user/__manager/config.ini``.
    Creates the directory and file if they do not exist.  If the
    file already exists, the setting is updated in-place (or
    appended if missing).

    This aligns ComfyUI-Manager's runtime package management with
    the rest of the UmeAiRT system (``uv``-only), resulting in
    significantly faster and more stable installs when users add
    custom nodes via the Manager UI.

    Args:
        install_path: Root installation directory.
        log: Installer logger for user-facing messages.
    """
    import re

    config_dir = install_path / "user" / "__manager"
    config_path = config_dir / "config.ini"
    config_dir.mkdir(parents=True, exist_ok=True)

    if config_path.exists():
        content = config_path.read_text(encoding="utf-8")
        if "use_uv" in content:
            content = re.sub(r"use_uv\s*=\s*\S+", "use_uv = True", content)
        else:
            content = content.rstrip() + "\nuse_uv = True\n"
        config_path.write_text(content, encoding="utf-8")
    else:
        config_path.write_text("[default]\nuse_uv = True\n", encoding="utf-8")

    log.sub("ComfyUI Manager: use_uv = True", style="success")