202 lines
8.8 KiB
Markdown
202 lines
8.8 KiB
Markdown
# ComfyUI-LensTurbo
|
|
|
|
[](LICENSE)
|
|
[](https://www.python.org/downloads/)
|
|
[](https://github.com/comfyanonymous/ComfyUI)
|
|
[](https://huggingface.co/microsoft/Lens-Turbo)
|
|
|
|
> ComfyUI nodes + standalone CLI for **Microsoft Lens / Lens-Turbo / Lens-Base** — a 3.8B-parameter text-to-image diffusion model that pairs a GPT-OSS-20B text encoder with a FLUX.2 VAE.
|
|
|
|
The killer feature: a **hybrid CPU/GPU placement** strategy that fits the whole pipeline on a single 24 GB consumer GPU. The 40 GB dequantized text encoder stays on CPU; the transformer and VAE run on GPU. Measured **11.6 s per 1024² image at 4 steps**, peak **11.11 GB VRAM** on an RTX 3090.
|
|
|
|
## Table of contents
|
|
|
|
- [Features](#features)
|
|
- [Variants](#variants)
|
|
- [Hardware requirements](#hardware-requirements)
|
|
- [Install](#install)
|
|
- [Usage — ComfyUI](#usage--comfyui)
|
|
- [Usage — CLI](#usage--cli)
|
|
- [Usage — Batch CLI](#usage--batch-cli)
|
|
- [Offload modes](#offload-modes)
|
|
- [Performance](#performance)
|
|
- [Troubleshooting](#troubleshooting)
|
|
- [Acknowledgments](#acknowledgments)
|
|
- [License](#license)
|
|
|
|
## Features
|
|
|
|
- **Two nodes**: `LensTurboLoader` (with cache + evict + force-reload) and `LensTurboSampler` (T2I, all the usual knobs).
|
|
- **Single-prompt CLI** (`cli/lens_turbo_cli.py`) — submit one prompt to a running ComfyUI server.
|
|
- **Batch CLI** (`cli/lens_turbo_batch.py`) — JSON-driven, resumable, retry-aware. Drop a `[{word, prompt}, ...]` list, get a directory of named PNGs.
|
|
- **24 GB single-card support** out of the box via the `cpu_text_encoder` offload mode (default).
|
|
- **Three monkey-patches** quietly handle the cross-device communication that vanilla LensPipeline doesn't.
|
|
- **Self-verifying banner** on import — `grep ComfyUI-LensTurbo comfy.log` tells you which copy is loaded.
|
|
- **Bash scripts** for upstream vendoring, dep install, HF login check, and model download.
|
|
|
|
## Variants
|
|
|
|
| Model | `--repo-id` | Steps | CFG | Use case |
|
|
|---|---|---|---|---|
|
|
| Lens-Turbo *(default)* | `microsoft/Lens-Turbo` | 4 | 1.0 | Fast preview, 4-step distilled |
|
|
| Lens (RL) | `microsoft/Lens` | 20 | 5.0 | Best quality, RL-tuned |
|
|
| Lens-Base | `microsoft/Lens-Base` | 50 | 5.0 | Research baseline, no RL/distill |
|
|
|
|
## Hardware requirements
|
|
|
|
| Resource | Minimum | Recommended |
|
|
|---|---|---|
|
|
| GPU | 24 GB VRAM (RTX 3090 / 4090) | 48 GB+ (A100 / H100) |
|
|
| System RAM | **48 GB** | 64 GB+ |
|
|
| Disk | 80 GB free (for HF cache) | NVMe SSD |
|
|
| Python | 3.10+ | 3.13 |
|
|
| CUDA | 12.0+ | 12.6 |
|
|
|
|
**Why 48 GB RAM**: GPT-OSS-20B's MoE layers are natively MXFP4-quantized, but Ampere (sm_86) cards can't run MXFP4 kernels — transformers dequantizes to bf16 (~40 GB), which lives in host memory when the encoder isn't actively running.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
cd $COMFY_ROOT/custom_nodes
|
|
git clone https://github.com/aadebuger/ComfyUI-LensTurbo
|
|
cd ComfyUI-LensTurbo
|
|
|
|
# 1. Vendor the upstream `lens` Python package
|
|
bash scripts/fetch_upstream.sh
|
|
|
|
# 2. Install Python deps (handles Py3.13 override + kernels uninstall)
|
|
bash scripts/install_deps.sh
|
|
|
|
# 3. Check HF gated access
|
|
bash scripts/hf_login_check.sh
|
|
hf auth login # if needed
|
|
```
|
|
|
|
**Agree to the licenses** on the HF web pages before downloading:
|
|
|
|
- <https://huggingface.co/microsoft/Lens-Turbo> (gated, research-only)
|
|
- <https://huggingface.co/black-forest-labs/FLUX.2-dev> (gated, FLUX Non-Commercial)
|
|
- `openai/gpt-oss-20b` is Apache-2.0, no agreement needed.
|
|
|
|
```bash
|
|
# 4. Pre-download model weights (~58 GB, sequential, resumable).
|
|
# Run inside tmux/screen if you want to detach.
|
|
bash scripts/download_models.sh
|
|
|
|
# 5. Restart ComfyUI
|
|
pkill -9 -f main.py; sleep 5
|
|
export HF_HOME=$HOME/hf_cache
|
|
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
|
|
python main.py --listen 0.0.0.0 --port 8188
|
|
```
|
|
|
|
You should see two banner lines in the ComfyUI log:
|
|
|
|
```
|
|
[ComfyUI-LensTurbo] v0.1.6 loaded from .../custom_nodes/ComfyUI-LensTurbo/__init__.py
|
|
[ComfyUI-LensTurbo/nodes] module loaded from .../nodes (lens_source=OK)
|
|
```
|
|
|
|
If `lens_source=MISSING`, `scripts/fetch_upstream.sh` didn't run.
|
|
|
|
## Usage — ComfyUI
|
|
|
|
Load `example_workflows/lens_turbo_t2i.json` in the ComfyUI UI:
|
|
|
|
```
|
|
LensTurboLoader → LensTurboSampler → SaveImage
|
|
```
|
|
|
|
Default sampler knobs match Lens-Turbo's recipe: `base_resolution=1024`, `aspect_ratio=1:1`, `num_inference_steps=4`, `guidance_scale=1.0`.
|
|
|
|
## Usage — CLI
|
|
|
|
Submit a single prompt to a running ComfyUI server:
|
|
|
|
```bash
|
|
python cli/lens_turbo_cli.py \
|
|
"a photorealistic photo of an astronaut riding a horse on mars" \
|
|
--ip 192.168.1.10 --gpu 0 \
|
|
--base-resolution 1024 --aspect-ratio 1:1 \
|
|
--steps 4 --cfg 1.0 --seed 42 \
|
|
--out ./out
|
|
```
|
|
|
|
See [`cli/README.md`](cli/README.md) for all flags.
|
|
|
|
## Usage — Batch CLI
|
|
|
|
Drop a JSON file like:
|
|
|
|
```json
|
|
[
|
|
{"word": "apple", "prompt": "a fresh red apple, studio light, 4k"},
|
|
{"word": "sunset", "prompt": "warm sunset over the ocean, cinematic"}
|
|
]
|
|
```
|
|
|
|
Then:
|
|
|
|
```bash
|
|
python cli/lens_turbo_batch.py vocab.json \
|
|
--ip 192.168.1.10 --gpu 0 \
|
|
--out-dir ./vocab_images
|
|
```
|
|
|
|
Outputs → `<out-dir>/<sanitized-word>.png`. Existing files are skipped on re-run (use `--force` to overwrite). CJK preserved in filenames; spaces and punctuation become `_`.
|
|
|
|
The JSON root may also be `{"entries": [...]}`, `{"data": [...]}`, or `{"items": [...]}` — auto-detected.
|
|
|
|
## Offload modes
|
|
|
|
The `offload_mode` knob on `LensTurboLoader` (also `--offload-mode` on both CLIs):
|
|
|
|
| Mode | VRAM | Speed | Notes |
|
|
|---|---|---|---|
|
|
| `cpu_text_encoder` *(default)* | ~11 GB | ~12 s/image | **24 GB-safe**. Text encoder permanently on CPU, transformer + VAE on GPU. Three monkey-patches bridge device crossings. |
|
|
| `model` | ~50 GB | ~6 s/image | `enable_model_cpu_offload()`. Needs ≥48 GB VRAM to hold the dequantized text encoder when active. |
|
|
| `none` | ~50 GB | ~5 s/image | `pipe.to(device)`, no offload. ≥48 GB VRAM. Fastest. |
|
|
| `sequential` | — | — | **KNOWN BROKEN** on accelerate ≥1.x — meta tensor bug in `pre_forward` + custom Lens TextEncoder forward. Kept for completeness, don't use. See INSTALL_NOTES L6. |
|
|
|
|
## Performance
|
|
|
|
**Measured on RTX 3090 (24 GB), `cpu_text_encoder` mode, Lens-Turbo:**
|
|
|
|
| Metric | Value |
|
|
|---|---|
|
|
| Peak VRAM | **11.11 GB** (46% of card) |
|
|
| Cold load (`from_pretrained`) | ~117 s |
|
|
| Warm sample @ 1024² / 4 steps | **11.6 s** total |
|
|
| - text encoding (CPU, bf16) | ~5-7 s |
|
|
| - transformer 4 steps | 4 s (≈1 s/step) |
|
|
| - VAE decode | <1 s |
|
|
|
|
After the cold load, the `LensTurboLoader` cache makes subsequent images hit instantly. Batch jobs amortize the cold load over the whole run — 16 images take ~3-4 minutes including cold start.
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Likely cause | Fix |
|
|
|---|---|---|
|
|
| `Illegal header value b'kernels/0.14.0; ...'` | `kernels==0.14.0` has a user_agent header bug | `uv pip uninstall kernels`. RTX 3090 can't run MXFP4 native anyway. |
|
|
| `OutOfMemoryError` during sampling | Wrong offload mode for your VRAM | Use `offload_mode="cpu_text_encoder"` (default) |
|
|
| `Cannot copy out of meta tensor; no data!` | `enable_sequential_cpu_offload` + Lens custom forward | Switch to `offload_mode="cpu_text_encoder"` |
|
|
| `Cannot generate cpu tensor from cuda generator` | `_execution_device` resolved to CPU because text_encoder is there | Use `cpu_text_encoder` mode (auto-patches the property) |
|
|
| `Expected all tensors to be on the same device` | encoder outputs not moved to GPU | Use `cpu_text_encoder` mode (auto-wraps encode_layers) |
|
|
| Nodes don't appear in the UI | `__init__.py` errored at import, or `.bak` directory is shadowing | `grep ComfyUI-LensTurbo comfy.log`, check `IMPORT FAILED`, move any `.bak` directories out of `custom_nodes/` |
|
|
| First sample times out via CLI | Cold load > 600 s default WS timeout | `--ws-recv-timeout 1800` |
|
|
|
|
See [INSTALL_NOTES.md](INSTALL_NOTES.md) for the full deployment story including 10 pitfalls discovered while bringing this online.
|
|
|
|
## Acknowledgments
|
|
|
|
- **Lens / Lens-Turbo / Lens-Base** — research by Microsoft. Model weights at [microsoft/Lens-Turbo](https://huggingface.co/microsoft/Lens-Turbo). Architecture pairs a large LLM-based text encoder (GPT-OSS-20B) with a diffusion transformer and FLUX.2 VAE.
|
|
- **GPT-OSS-20B** — text encoder by OpenAI ([openai/gpt-oss-20b](https://huggingface.co/openai/gpt-oss-20b), Apache-2.0).
|
|
- **FLUX.2 VAE** — by Black Forest Labs ([black-forest-labs/FLUX.2-dev](https://huggingface.co/black-forest-labs/FLUX.2-dev)).
|
|
- **ComfyUI** — the host runtime by comfyanonymous.
|
|
|
|
## License
|
|
|
|
MIT for this wrapper — see [LICENSE](LICENSE).
|
|
|
|
Model weights are released by Microsoft for **research purposes only**, not for product or service deployment. Consult the upstream HF model card for the full terms. This repository does not redistribute weights.
|