Files
aigen/scripts/setup-qwen21.ps1
T
TowstyandCursor 1f10082087 Fix Qwen 2.1 GGUF load: promote Q8 norms to F32 and wire TextEncode latent.
abenzerps Q8_0 ships 1D RMSNorms as packed Q8 (136 vs 128), which breaks
Comfy rms_rope; tagger now dequantizes small tensors and the graph uses
TextEncodeQwenImage21's 64-ch latent plus AuraFlow shift.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-20 16:22:55 -05:00

226 lines
8.3 KiB
PowerShell

# Download Qwen Image 2.1 GGUF + companions into the live Comfy Desktop Shared models tree.
# Also ensures city96/ComfyUI-GGUF is present on the ComfyUI (1) install.
#
# Example:
# powershell -ExecutionPolicy Bypass -File scripts\setup-qwen21.ps1
# powershell -File scripts\setup-qwen21.ps1 -SkipDownload
# powershell -File scripts\setup-qwen21.ps1 -ComfyRoot "C:\...\ComfyUI (1)\ComfyUI"
[CmdletBinding()]
param(
[string]$ComfyRoot = '',
[string]$ModelsRoot = '',
[switch]$SkipDownload,
[switch]$SkipNode,
[switch]$Force
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'Continue'
function Write-Step([string]$Message) {
Write-Host ""
Write-Host "==> $Message" -ForegroundColor Cyan
}
function Write-Ok([string]$Message) {
Write-Host " $Message" -ForegroundColor Green
}
function Write-Warn([string]$Message) {
Write-Host " warn $Message" -ForegroundColor Yellow
}
function Resolve-ComfyRoot([string]$Hint) {
$candidates = @(
$Hint,
$env:COMFY_ROOT,
$env:COMFYUI_ROOT,
(Join-Path $env:LOCALAPPDATA 'Comfy-Desktop\ComfyUI-Installs\ComfyUI (1)\ComfyUI'),
(Join-Path $env:LOCALAPPDATA 'Comfy-Desktop\ComfyUI-Installs\ComfyUI\ComfyUI'),
'C:\ComfyUI',
(Join-Path $env:USERPROFILE 'ComfyUI')
) | Where-Object { $_ } | ForEach-Object { $_.TrimEnd('\', '/') }
foreach ($path in $candidates) {
if (Test-Path (Join-Path $path 'main.py')) { return (Resolve-Path $path).Path }
if (Test-Path (Join-Path $path 'ComfyUI\main.py')) { return (Resolve-Path (Join-Path $path 'ComfyUI')).Path }
}
throw "Could not find ComfyUI root (main.py). Pass -ComfyRoot."
}
function Resolve-ModelsRoot([string]$Hint, [string]$ComfyRootPath) {
$shared = Join-Path $env:LOCALAPPDATA 'Comfy-Desktop\ComfyUI-Shared\models'
$candidates = @(
$Hint,
$env:COMFY_MODELS_ROOT,
$shared,
(Join-Path $ComfyRootPath 'models')
) | Where-Object { $_ } | ForEach-Object { $_.TrimEnd('\', '/') }
foreach ($path in $candidates) {
if (Test-Path $path) { return (Resolve-Path $path).Path }
}
New-Item -ItemType Directory -Force -Path $shared | Out-Null
return (Resolve-Path $shared).Path
}
function Find-HfCli {
foreach ($name in @('hf', 'huggingface-cli')) {
$cmd = Get-Command $name -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
}
throw "Hugging Face CLI not found (hf / huggingface-cli). Install with: pip install -U huggingface_hub"
}
function Ensure-Dir([string]$Path) {
if (-not (Test-Path $Path)) {
New-Item -ItemType Directory -Force -Path $Path | Out-Null
}
}
function Assert-ModelFile([string]$Path, [long]$MinBytes) {
if (-not (Test-Path $Path)) { throw "Missing model file: $Path" }
$item = Get-Item $Path
if ($item.Length -lt $MinBytes) {
throw "Suspicious size for $Path ($($item.Length) bytes). Expected >= $MinBytes. Delete and re-run."
}
Write-Ok ("{0} {1:N0} bytes" -f $item.FullName, $item.Length)
}
function Flatten-IfNested([string]$DestDir, [string]$FileName) {
$nested = Get-ChildItem $DestDir -Recurse -File -Filter $FileName -ErrorAction SilentlyContinue |
Where-Object { $_.DirectoryName -ne $DestDir } |
Select-Object -First 1
if ($nested) {
$target = Join-Path $DestDir $FileName
if ((Test-Path $target) -and -not $Force) {
Write-Warn "Nested copy found but destination already exists: $target"
return
}
Move-Item -Force $nested.FullName $target
Write-Ok "Flattened $($nested.FullName) -> $target"
}
}
function Invoke-HfFile {
param(
[string]$Cli,
[string]$Repo,
[string]$File,
[string]$LocalDir
)
Ensure-Dir $LocalDir
$dest = Join-Path $LocalDir (Split-Path $File -Leaf)
if ((Test-Path $dest) -and -not $Force) {
$len = (Get-Item $dest).Length
if ($len -gt 1MB) {
Write-Ok "Already present: $dest ($len bytes)"
return
}
}
Write-Step "hf download $Repo $File"
& $Cli download $Repo $File --local-dir $LocalDir
if ($LASTEXITCODE -ne 0) { throw "hf download failed for $Repo / $File (exit $LASTEXITCODE)" }
Flatten-IfNested -DestDir $LocalDir -FileName (Split-Path $File -Leaf)
}
# --- resolve paths ---
Write-Step "Resolve ComfyUI root"
$root = Resolve-ComfyRoot $ComfyRoot
Write-Ok $root
Write-Step "Resolve models root (Desktop Shared preferred)"
$models = Resolve-ModelsRoot $ModelsRoot $root
Write-Ok $models
$Diff = Join-Path $models 'diffusion_models'
$TE = Join-Path $models 'text_encoders'
$VAE = Join-Path $models 'vae'
Ensure-Dir $Diff
Ensure-Dir $TE
Ensure-Dir $VAE
if (-not $SkipDownload) {
$cli = Find-HfCli
Write-Ok "HF CLI: $cli"
# DiT GGUF only (no Q6/Q5/Q4, no full bf16 DiT)
# Exact invocation required on this host:
# hf download hf://abenzerps/Qwen-Image-2.1-Uncensored-GGUF/qwen-image-2.1-Q8_0.gguf
# That form writes into the Hugging Face hub cache (not --local-dir).
Write-Step "hf download hf://abenzerps/Qwen-Image-2.1-Uncensored-GGUF/qwen-image-2.1-Q8_0.gguf"
$ditName = 'qwen-image-2.1-Q8_0.gguf'
$ditDest = Join-Path $Diff $ditName
if ((Test-Path $ditDest) -and -not $Force -and (Get-Item $ditDest).Length -gt 1MB) {
Write-Ok "Already present: $ditDest ($((Get-Item $ditDest).Length) bytes)"
} else {
& $cli download "hf://abenzerps/Qwen-Image-2.1-Uncensored-GGUF/$ditName"
if ($LASTEXITCODE -ne 0) { throw "hf download failed for DiT GGUF (exit $LASTEXITCODE)" }
$cacheRoot = Join-Path $env:USERPROFILE '.cache\huggingface\hub\models--abenzerps--Qwen-Image-2.1-Uncensored-GGUF'
$found = Get-ChildItem $cacheRoot -Recurse -File -Filter $ditName -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if (-not $found) { throw "hf download finished but $ditName was not found under $cacheRoot" }
Ensure-Dir $Diff
Copy-Item -Force $found.FullName $ditDest
Write-Ok "Copied $($found.FullName) -> $ditDest ($((Get-Item $ditDest).Length) bytes)"
}
# abenzerps ships kv_count=0 + Q8_0 1D RMSNorms (packed 136 vs logical 128).
# Tag architecture=qwen_image and promote small/1D tensors to F32 for ComfyUI-GGUF.
Write-Step "Tag DiT GGUF (architecture + F32 norms)"
$tagger = Join-Path $PSScriptRoot 'tag-qwen21-gguf.py'
$pyCandidates = @(
(Join-Path $root '.venv\Scripts\python.exe'),
(Join-Path (Split-Path $root) 'standalone-env\python.exe'),
(Get-Command python -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source)
) | Where-Object { $_ -and (Test-Path $_) }
$py = $pyCandidates | Select-Object -First 1
if (-not $py) { throw "No Python found to run $tagger" }
$fixed = Join-Path $Diff 'qwen-image-2.1-Q8_0.fixed.gguf'
& $py $tagger $ditDest $fixed
if ($LASTEXITCODE -ne 0) { throw "tag-qwen21-gguf.py failed (exit $LASTEXITCODE)" }
Copy-Item -Force $fixed $ditDest
Write-Ok "Installed tagged GGUF at $ditDest"
# TE + VAE companions from Comfy-Org (INT8 TE for 16 GB VRAM)
Invoke-HfFile -Cli $cli -Repo 'Comfy-Org/Qwen-Image-2.1' -File 'text_encoders/qwen3vl_8b_int8_convrot.safetensors' -LocalDir $TE
Invoke-HfFile -Cli $cli -Repo 'Comfy-Org/Qwen-Image-2.1' -File 'vae/qwen_image_2.1_vae_bf16.safetensors' -LocalDir $VAE
}
Write-Step "Verify weights"
Assert-ModelFile (Join-Path $Diff 'qwen-image-2.1-Q8_0.gguf') 7000000000
Assert-ModelFile (Join-Path $TE 'qwen3vl_8b_int8_convrot.safetensors') 1000000000
Assert-ModelFile (Join-Path $VAE 'qwen_image_2.1_vae_bf16.safetensors') 100000000
if (-not $SkipNode) {
Write-Step "city96 ComfyUI-GGUF"
$nodes = Join-Path $root 'custom_nodes\ComfyUI-GGUF'
if (-not (Test-Path $nodes)) {
git clone https://github.com/city96/ComfyUI-GGUF $nodes
if ($LASTEXITCODE -ne 0) { throw "git clone ComfyUI-GGUF failed" }
Write-Ok "Cloned $nodes"
} else {
Write-Ok "Already present: $nodes"
}
$py = Join-Path $root '.venv\Scripts\python.exe'
if (-not (Test-Path $py)) {
$py = Join-Path (Split-Path $root) 'standalone-env\python.exe'
}
if (Test-Path $py) {
Write-Step "Ensure gguf package in Comfy python ($py)"
& $py -m pip install -q gguf
if ($LASTEXITCODE -ne 0) { Write-Warn "pip install gguf returned $LASTEXITCODE" }
else { Write-Ok "gguf importable" }
} else {
Write-Warn "Comfy python not found; install gguf into the interpreter that runs main.py"
}
}
Write-Host ""
Write-Host "Done. Restart Comfy only when /status shows gpu.busy=false." -ForegroundColor Cyan
Write-Host "ComfyRoot=$root"
Write-Host "ModelsRoot=$models"