Skip to content

Feature(backend): Add user toggle to run encoder models on CPU - #8777

Merged
lstein merged 5 commits into
invoke-ai:mainfrom
lstein:lstein/feature/text-encoder-cpu-toggle
Feb 4, 2026
Merged

lstein merged 5 commits into
invoke-ai:mainfrom
lstein:lstein/feature/text-encoder-cpu-toggle

Conversation

@lstein

@lstein lstein commented Jan 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds the ability to configure standalone text encoder models to run on CPU exclusively, thereby freeing up VRAM that might otherwise compete with the denoiser and other large models. Users can set a text encoder to run in CPU from the Model Manager, by clicking on a new toggle in the details area shown below:

image

All the text encoders are supported, including CLIPEmbed, T5Encoder, Qwen3Encoder, CLIPVision, SigLIP, and LlavaOnevision. However, Invoke only offers the option of changing the text encoder for some of the more recent main models, chiefly Flux.1, Flux.2 and Z-Image.

In most cases it does not make sense to run the text encoder on CPU, as execution speed suffers greatly (up to 5x slower for Qwen3 encoders). However, for users who have very low VRAM (e.g. 8 GB), this may allow them to run encoder models that would otherwise be inaccessible.

Related Issues / Discussions

Brief discussion on Discord regarding Comfy's use of a similar strategy: https://discord.com/channels/1020123559063990373/1020123559831539744/1462795385469796591

QA Instructions

CPU mode on

  1. Go to the model manager and select one of the standalone encoders, e.g. Z-Image Qwen3 Text Encoder (for Z-Image).
  2. The details panel will show a new setting, "Run text encoder model on CPU only". Turn it on and click "Save".
  3. Go to a generation pane (linear, canvas or workflow) and select a main model that uses this encoder, and then under Advanced select the text encoder you modified.
  4. Run a generation and look at the log messages. You should not see any messages about the text encoder being loaded into the cuda device.
  5. When the generation is finished, examine the performance statistics. The text encoder should have taken an unusually long time to run.

CPU mode off

  1. Repeat the instructions above, but this time turn the CPU toggle off.
  2. You should see log messages about the text encoder loading into cuda.
  3. Execution speed should be fast.

Repeat this with other text encoders and main models.

Merge Plan

Simple merge.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Tests added / updated (if applicable)
  • ❗Changes to a redux slice have a corresponding migration
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

Co-authored-by: lstein <111189+lstein@users.noreply.github.com>

Add frontend UI for CPU-only model execution toggle

Co-authored-by: lstein <111189+lstein@users.noreply.github.com>
@github-actions github-actions Bot added python PRs that change python files invocations PRs that change invocations backend PRs that change backend files services PRs that change app services frontend PRs that change frontend files labels Jan 21, 2026
@JPPhoto

JPPhoto commented Jan 21, 2026

Copy link
Copy Markdown
Collaborator

@lstein This is failing a frontend check. Once you resolve that, I'll do a deeper dive.

I think you need import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings'; at the top of invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/EncoderModelSettings/EncoderModelSettings.tsx. Put that right before import { toast } from 'features/toast/toast'; to not get an ordering error.

@JPPhoto JPPhoto left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After making the changes above, I was able to build and run and it worked as advertised. Approval is pending that fix and successful tests.

@blessedcoolant blessedcoolant left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package-lock.json is being uploaded from your end. We use pnpm by default and ship the pnpm lock file. This file should not be committed from your end as it is generated by npm mostly.

@dunkeroni dunkeroni left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works well for me.

As Blessed mentioned, package-lock.json is coming from an npm build. I've noticed a lot of coding AI's like to run npm commands in terminal when checking for errors unless you give them context for pnpm. Just delete it.

@lstein we should look into making an AGENTS.md to try and improve that sort of behavior, and hope that more tools support checking it in the future. Start keeping track of times you see a bot go off the rails and touch things it shouldn't.

@lstein lstein added the v6.12.0 Intended for 6.12.0 release label Jan 29, 2026
@lstein

lstein commented Jan 29, 2026

Copy link
Copy Markdown
Collaborator Author

Works well for me.

As Blessed mentioned, package-lock.json is coming from an npm build. I've noticed a lot of coding AI's like to run npm commands in terminal when checking for errors unless you give them context for pnpm. Just delete it.

@lstein we should look into making an AGENTS.md to try and improve that sort of behavior, and hope that more tools support checking it in the future. Start keeping track of times you see a bot go off the rails and touch things it shouldn't.

Thanks for picking up on package-lock.json. I have removed it. I'll look into creating an appropriate agents guide.

@lstein
lstein merged commit 76b0838 into invoke-ai:main Feb 4, 2026
13 checks passed
@lstein
lstein deleted the lstein/feature/text-encoder-cpu-toggle branch February 4, 2026 20:13
danielteeny added a commit to danielteeny/InvokeAI that referenced this pull request Feb 4, 2026
Merged upstream features:
- Gradient tools (invoke-ai#8774)
- Paged gallery view (invoke-ai#8772)
- Encoder CPU toggle (invoke-ai#8777)
- Various bug fixes

Removed downstream pagination implementation in favor of upstream:
- Deleted PaginatedGrid.tsx
- Deleted GalleryModeCombobox.tsx
- Deleted PageSizeCombobox.tsx
- Removed galleryMode, paginationPageSize, currentPage from gallery state
- Updated GallerySettingsPopover to use upstream's UsePagedGalleryViewCheckbox

Kept downstream features:
- LoRA category system
- Folder hierarchy (recursiveFolderView)
- Layout mode

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
lstein added a commit that referenced this pull request Jul 17, 2026
* feat(vae): support running VAEs on CPU via cpu_only setting

Extends the cpu_only mechanism from #8777 (text encoders) to VAE decode.
Adds a cpu_only field to all standalone VAE configs; the loader already
forces standalone configs with cpu_only=True onto the CPU. The 7 decode
invocations now move latents to the VAE's effective device instead of
hard-coding CUDA, and the SD/SDXL path falls back to fp32 on CPU (fp16
conv is unsupported there). Adds a "Run on CPU" toggle to the VAE model
settings panel and regenerates the API schema.

Decode-only for now; encode and main-model VAE submodels are unchanged.

Closes #7276 (VAE part)

* Chore Openapi + Fix logging

* fix(vae): address cpu_only VAE review feedback (#9293)

Item 1 — VAE settings panel showed text-encoder-specific copy:
- Add `runVaeOnCpu` label and a `cpuOnlyVae` popover feature with
  VAE-accurate wording instead of reusing the encoder's `runOnCpu` /
  `cpuOnly` strings (which describe the text encoder and conditioning
  outputs, wrong on the VAE panel).
- Register `cpuOnlyVae` in the InformationalPopover feature union.

Item 2 — mislabeled INFO log on GPU-less installs:
- ModelCache.lock only logs the "(cpu_only)" cause at INFO when the
  cache's default execution device is a GPU (a genuine per-model
  choice). On a CPU-only install every model has a CPU compute_device,
  so that case now logs at DEBUG without the misleading wording.

Item 3 — stale l2i comment:
- Replace the false "fp16 VAE ops are not supported on CPU" rationale
  with the real reason: fp16 conv runs on CPU with the pinned torch but
  is much slower, and SD/SDXL VAE has known fp16 overflow (black
  images). The fp32 force is unchanged.

Item 4 — frontend duplication:
- Extract the shared CPU-only settings panel into CpuOnlyModelSettings
  and a single useCpuOnlyModelSettings hook, parameterized by label,
  popover feature, and toast id. EncoderModelSettings and
  VAEModelSettings become thin wrappers; the config unions stay
  separate and each panel keeps its own copy. Removes ~200 lines of
  duplicated code and the awkward cross-folder type import; deletes the
  now-redundant useEncoderModelSettings / useVAEModelSettings hooks.

SD3 / CogView4 left unchanged (out of scope):
- No standalone VAE config exists for these bases, and a main model's
  default_settings.cpu_only only applies to text-encoder submodels
  (load_default.py), so cpu_only can never reach an SD3/CogView4 VAE —
  their l2i changes are harmless consistency, nothing to toggle.
- Adding standalone VAE configs for them is not worth it here: there is
  no real ecosystem of separate SD3/CogView4 VAE files, and their VAEs
  are 16-channel AutoencoderKL that can't be reliably told apart from
  each other (or Flux) at import-probe time — which is exactly why
  standalone VAE configs are limited to the distinguishable bases
  (SD1/SD2/SDXL, Flux). The runtime is already cpu_only-ready, so if
  such a config is ever added the toggle works with no further change.

---------

Co-authored-by: Lincoln Stein <lincoln.stein@gmail.com>
@lstein lstein mentioned this pull request Jul 19, 2026
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend PRs that change backend files frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files services PRs that change app services v6.12.0 Intended for 6.12.0 release

Projects

Status: 6.12.x

Development

Successfully merging this pull request may close these issues.

5 participants