Nodes/comfyui-lensturbo/Lens / Lens-Turbo Loader
ComfyUI Node

Lens / Lens-Turbo Loader

The Lens loader that squeezes a 40 GB text encoder onto a 24 GB card

By aadebuger·Created 3 months ago·Updated 3 months ago· 0
Lens / Lens-Turbo Loader
    • pipeline
    repo_idmicrosoft/Lens-Turbo
    devicecuda:0
    dtypebf16
    offload_modecpu_text_encoder
    cpu_constructtrue
    force_reloadfalse
    evict_otherstrue
    hf_cache_dir

    Microsoft's Lens-Turbo is a weird beast, and this loader exists for one reason: getting its enormous text encoder to fit on a normal consumer GPU. Lens itself is a modest 3.8B diffusion transformer, but its text encoder - GPT-OSS-20B - dequantizes to roughly 40 GB of bf16, more than most cards hold. This node's party trick is a hybrid placement mode that parks that encoder on CPU, runs the transformer and VAE on the GPU, and monkey-patches the pipeline so the two halves can talk across the gap. Measured on an RTX 3090: ~11 GB peak VRAM and ~11–15 s per 1024² image at 4 steps. That headline is the whole reason you're reading this page.

    LensTurboLoader is the first half of a two-node pack - load it, then hand the result to LensTurboSampler for the actual T2I. It builds the pipeline once, holds it in a module-level cache, and returns a single pipeline output (a custom LENS_PIPELINE type). Wire that straight into the sampler. Classic ComfyUI loader→sampler pattern, just with an unusually heavy load.

    The inputs that matter

    Most of the required fields have sane defaults and you should leave them alone. The two worth your attention:

    • repo_id - defaults to microsoft/Lens-Turbo, and also accepts microsoft/Lens (the RL-tuned variant, 20 steps) or microsoft/Lens-Base (the 50-step research baseline), or an absolute local path to a snapshot you already downloaded. Given Microsoft's record of pulling releases, a local path is arguably the safest choice - more on that in a second.
    • offload_mode - the one knob that matters. cpu_text_encoder (the default) is the whole point: ~11 GB peak VRAM. model and none expect 48 GB+ of VRAM because the full dequantized encoder has to fit while active. sequential is listed in the dropdown and is known broken with Lens on accelerate ≥1.x (a meta-tensor bug in layer-level offload). It's there for completeness. Don't touch it.

    The rest is cache plumbing: force_reload bypasses the cache and rebuilds, evict_others (on by default) frees any other cached pipeline first, and cpu_construct (also on) builds under torch.device('cpu') so giant RoPE caches and position tables don't OOM you at init. device (cuda:0) and dtype (bf16) are fine as-is. hf_cache_dir only matters if you want a different HF_HOME for this load.

    The first load is slow - around two minutes on a 3090 - and it prints a running commentary into comfy.log while it works. After that, the cache keys on (repo, dtype, device, offload, cpu_construct), so subsequent loads are instant. This is exactly why the batch CLI exists: amortize the cold load once, then cruise at ~12 s/image.

    How to install it - the honest version

    This is not a drop-in custom node. ComfyUI Manager can clone the repo, but the README's real install runs four bash scripts:

    cd $COMFY_ROOT/custom_nodes
    git clone https://github.com/aadebuger/ComfyUI-LensTurbo
    cd ComfyUI-LensTurbo
    
    # 1. Vendor the upstream `lens` Python package (required - LensPipeline won't import without it)
    bash scripts/fetch_upstream.sh
    
    # 2. Install Python deps (uv pip; handles Py3.13 overrides, uninstalls broken `kernels`)
    bash scripts/install_deps.sh
    
    # 3. Check HF gated access
    bash scripts/hf_login_check.sh
    hf auth login   # if needed
    
    # 4. Pre-download weights (~58 GB, resumable - run in tmux)
    bash scripts/download_models.sh
    

    Then restart ComfyUI and confirm the banner:

    grep ComfyUI-LensTurbo comfy.log
    

    Non-negotiables before you start: the model repos are gated, so click "Agree" on the HF pages for microsoft/Lens-Turbo and black-forest-labs/FLUX.2-dev (the VAE). The pipeline wants ~48 GB of system RAM even though it only needs 11 GB of VRAM - the encoder lives in host memory. Plus 80 GB of disk for the HF cache. Beefy setup, full stop.

    The availability caveat

    Microsoft published Lens and Lens-Turbo on Hugging Face in May 2026, gated, and then deleted them days later - the same move it pulled with VibeVoice the year before. The community's standing rule applies here: download interesting Microsoft releases immediately and mirror them. The pack's scripts point at the official repos, so if you hit a 404 or a "repo not found," find a community mirror and point repo_id at the local snapshot instead. It's the most likely thing to trip up a new user - and it has nothing to do with the node.

    Troubleshooting

    The README's pitfall table is worth a skim, but the top three:

    • [ComfyUI-LensTurbo/nodes] ... lens_source=MISSING in the log - fetch_upstream.sh didn't run. The lens package must sit in lens_source/ for the import to work.
    • httpx.LocalProtocolError: Illegal header value b'kernels/0.14.0; ...' - the kernels package is broken and unused on Ampere anyway (it needs sm_90+ for MXFP4). install_deps.sh uninstalls it; if you skipped the scripts, do it manually.
    • OOM during sampling - you're on the wrong offload_mode. Use cpu_text_encoder and keep evict_others on; a 24 GB card holds exactly one of these pipelines.
    Categoryloaders/Lens

    Inputs (8)

    NameTypeDefaultDescription
    repo_idSTRINGmicrosoft/Lens-TurboHF repo id (e.g. microsoft/Lens-Turbo) OR an absolute local path to a downloaded snapshot.
    deviceCOMBOcuda:03 options: cuda:0, cuda:1, cpu
    dtypeCOMBObf163 options: bf16, fp16, fp32
    offload_modeCOMBOcpu_text_encoderVRAM strategy: cpu_text_encoder = text_encoder on CPU, transformer+vae on GPU. ~11 GB peak VRAM, ~10-15 s/image @ 1024² on RTX 3090. DEFAULT, recommended for 24 GB cards. sequential = layer-level (KNOWN BROKEN with Lens on accelerate >=1.x meta tensor bug). model = sub-model offload (needs ≥48 GB). none = pipe.to(device), needs ≥48 GB VRAM.
    cpu_constructoptBOOLEANtrueConstruct under `with torch.device('cpu')` to avoid OOM at __init__ time when RoPE caches / position tables would otherwise allocate on GPU.
    force_reloadoptBOOLEANfalseBypass cache, rebuild.
    evict_othersoptBOOLEANtrueFree other cached pipelines before loading.
    hf_cache_diroptSTRINGOverride HF_HOME for this load. Empty = use env.

    Outputs (1)

    NameTypeDescription
    pipelineLENS_PIPELINE