Downloads · 30 days
55
2% of all-time downloads
hoidhxd/SenseNova-U1.5-8B-GGUF
SenseNova-U1.5-8B-GGUF is a text-to-image model from hoidhxd. Use it when you need an image from a text prompt. It is set up for gguf.
Q40 GGUF build of SenseNova-U1.5-8B-MoT-Preview, for running the model locally in ComfyUI on a consumer GPU.
Downloads · 30 days
55
2% of all-time downloads
All-time downloads
2.3K
Public
Repo size
9.9 GB
Likes
0
Public
Click a slice to open those files.
.gguf9.9 GB · 100%
From the Hugging Face model README
Q4_0 GGUF build of SenseNova-U1.5-8B-MoT-Preview, for running the model locally in ComfyUI on a consumer GPU.
| File | SenseNova-U1.5-8B-MoT-Preview-Q4_0.gguf |
| Size | 9,929,596,256 bytes — 9.25 GiB (9.93 GB) |
| Source | 16-shard BF16 release (~50 GB) |
| Fits | 16 GB VRAM fully resident; 12 GB with layer offload |
The file must go in
ComfyUI/models/gguf/, notComfyUI/models/unet/— see step 3.
SenseNova U1 is a native multimodal model — one graph handles text and pixels end to end.
So the ComfyUI graph is just two nodes: a loader and a sampler. There is nothing else to wire up.
Converted directly from the BF16 safetensors with smart filtering:
Five tensors that should have been excluded were not; see Known issue below. The workaround is a 30-line file and takes one minute to install.
Tested on Windows 11 + RTX 5060 Ti 16 GB, ComfyUI with a Python 3.13 venv. Linux is the same apart from paths.
Throughout, <ComfyUI> is your ComfyUI root (e.g. D:\ComfyUI) and
<python> is the interpreter ComfyUI itself runs on — not your system
Python. For a portable build that is <ComfyUI>\..\python_embeded\python.exe;
for a venv install, <ComfyUI>\venv\Scripts\python.exe (Windows) or
<ComfyUI>/venv/bin/python (Linux).
Install ComfyUI-SenseNova-U1 through ComfyUI Manager, or clone it:
git clone https://github.com/OpenSenseNova/ComfyUI-SenseNova-U1 <ComfyUI>/custom_nodes/ComfyUI-SenseNova-U1
The nodes need the sensenova-u1 runtime package plus the GGUF dependencies.
Install both into ComfyUI's Python:
<python> -m pip install -r <ComfyUI>/custom_nodes/ComfyUI-SenseNova-U1/requirements.txt
<python> -m pip install "gguf>=0.10.0" "diffusers>=0.30.0" accelerate transformers
requirements.txt pulls sensenova-u1 from a GitHub release tarball, which
is intentional — a git+https install would drag in hundreds of MB of
evaluation submodules.
ComfyUI/models/unet/ does not work. The SenseNova U1 Local Loader
scans exactly two folder names, gguf and diffusion_models, and
diffusion_models filters on ComfyUI's supported_pt_extensions, which does
not include .gguf. Anything in unet/ is invisible to the node and the
dropdown comes up empty.
Download into <ComfyUI>/models/gguf/:
hf download hoidhxd/SenseNova-U1.5-8B-GGUF SenseNova-U1.5-8B-MoT-Preview-Q4_0.gguf --local-dir <ComfyUI>/models/gguf
If you already have the file elsewhere (a different drive, say) don't copy
9.25 GiB around — register the directory in <ComfyUI>/extra_model_paths.yaml
under the key gguf:
ai_models:
base_path: C:/Users/Admin/ai/models
gguf: SenseNova-U1.5-8B-GGUF
The key name is what matters. ComfyUI gives an unrecognised folder name an
empty extension set, and an empty set means "no filter" — which is why .gguf
files surface under gguf but not under diffusion_models.
Restart ComfyUI after adding files; the dropdown is built at startup.
The GGUF holds weights only. The loader still needs the config and tokenizer from the base repo — but not the 50 GB of safetensors:
hf download sensenova/SenseNova-U1.5-8B-MoT-Preview --local-dir <somewhere>/SenseNova-U1.5-8B-MoT-Preview --include "*.json" "*.txt"
That yields ~5 MB:
config.json added_tokens.json special_tokens_map.json
tokenizer_config.json vocab.json merges.txt
model.safetensors.index.json
This directory is what you type into the loader's model_path.
Five tensors in this GGUF crash the loader as published. Create
<ComfyUI>/custom_nodes/sensenova_u1_embed_fix/__init__.py with the file below
and restart ComfyUI. It registers no nodes — it patches the GGUF loader at
import time and dequantizes those five tensors back to bf16 (~1.3 GB extra
weight memory).
"""Hand a few SenseNova-U1 tensors to the GGUF loader as plain floats.
Two tensor groups in `hoidhxd/SenseNova-U1.5-8B-GGUF` cannot survive the
diffusers GGUF quantizer, for two different reasons.
1. ``language_model.model.embed_tokens.weight`` (Q4_0)
The quantizer only swaps ``nn.Linear`` for ``GGUFLinear``; every other module
keeps whatever parameter it was handed. An ``nn.Embedding`` therefore looks up
raw Q4_0 *block bytes*, and the first RMSNorm dies with
RuntimeError: The size of tensor a (4096) must match the size of
tensor b (2304) at non-singleton dimension 2
2304 is exactly ``4096 // 32 * 18`` - the Q4_0 byte width of a 4096-wide row.
Costs ~1.2 GB of extra weight memory in bfloat16.
2. ``fm_modules.timestep_embedder`` / ``fm_modules.noise_scale_embedder`` (Q4_0)
``modeling_fm_modules.TimestepEmbedder.forward`` casts its input with
``t_freq.to(self.mlp[0].weight.dtype)``. On a ``GGUFLinear`` that dtype is the
*storage* dtype, ``torch.uint8``, so the activations are cast to Byte and the
matmul dies with
RuntimeError: mat1 and mat2 must have the same dtype, but got Byte and BFloat16
Only 35.7M params live here (~71 MB in bfloat16), so keeping them dense is
basically free.
The proper fix is to leave both groups in F16/F32 when producing the GGUF; this
shim exists so an already-published checkpoint still runs.
Set SENSENOVA_GGUF_EMBED_KEYS to a comma-separated list of name fragments to
override which tensors get dequantized.
"""
import logging
import os
LOGGER = logging.getLogger(__name__)
DEFAULT_KEYS = (
"embed_tokens.weight",
"fm_modules.timestep_embedder.",
"fm_modules.noise_scale_embedder.",
)
def _embedding_keys() -> tuple[str, ...]:
raw = os.environ.get("SENSENOVA_GGUF_EMBED_KEYS", "").strip()
if not raw:
return DEFAULT_KEYS
return tuple(part.strip() for part in raw.split(",") if part.strip())
def _apply_patch() -> None:
from sensenova_u1.utils import gguf_loader
if getattr(gguf_loader, "_embed_fix_applied", False):
return
original = gguf_loader.load_gguf_checkpoint
keys = _embedding_keys()
def load_gguf_checkpoint(path: str, *args, **kwargs) -> dict:
import torch
from diffusers.quantizers.gguf.utils import GGUFParameter, dequantize_gguf_tensor
state_dict = original(path, *args, **kwargs)
for name, tensor in list(state_dict.items()):
if not isinstance(tensor, GGUFParameter) or not any(k in name for k in keys):
continue
dequantized = dequantize_gguf_tensor(tensor).as_subclass(torch.Tensor)
state_dict[name] = dequantized
LOGGER.info(
"SenseNova GGUF embed fix: dequantized %s %s -> %s",
name,
tuple(tensor.shape),
tuple(dequantized.shape),
)
return state_dict
gguf_loader.load_gguf_checkpoint = load_gguf_checkpoint
gguf_loader._embed_fix_applied = True
LOGGER.info("SenseNova GGUF embed fix installed for keys: %s", ", ".join(keys))
try:
_apply_patch()
except Exception as exc: # noqa: BLE001 - never block ComfyUI startup
LOGGER.warning("SenseNova GGUF embed fix not installed: %s", exc)
NODE_CLASS_MAPPINGS: dict = {}
NODE_DISPLAY_NAME_MAPPINGS: dict = {}
On startup the ComfyUI console should print:
SenseNova GGUF embed fix installed for keys: embed_tokens.weight, fm_modules.timestep_embedder., fm_modules.noise_scale_embedder.
If it prints not installed: No module named 'sensenova_u1' instead, step 2
did not land in the interpreter ComfyUI is actually using.
Two nodes, one link:
[SenseNova U1 Local Loader] --u1_model--> [SenseNova U1 Local Text to Image] --images--> [Save Image]
SenseNova U1 Local Loader
| Input | Value |
|---|---|
model_path | the config/tokenizer directory from step 4 |
sensenova_u1_src | leave as-is (auto-resolved) |
device | cuda |
dtype | bfloat16 |
attn_backend | auto |
device_map | none — must be none when a GGUF is selected |
max_memory | empty |
vram_mode | full on 16 GB, balanced on 12 GB |
gguf_checkpoint | SenseNova-U1.5-8B-MoT-Preview-Q4_0.gguf |
vram_mode replaced the old prefetch_count input:
full — every weight stays on the GPU. Fastest, ~2× the offload modes.balanced — asynchronous layer prefetch, overlaps host→device copies with
compute. Use this on 12 GB.low — synchronous one-layer-at-a-time swap. Smallest footprint, slowest.device_map is for splitting across multiple GPUs and is mutually exclusive
with vram_mode; leave it none for single-GPU use.
SenseNova U1 Local Text to Image
| Input | Default | Notes |
|---|---|---|
prompt | — | plain text, no encoder node |
resolution | 2048x2048|1:1 | native sizes only, see below |
cfg_scale | 4.0 | |
cfg_norm | none | global / channel / cfg_zero_star |
timestep_shift | 3.0 | sampler schedule shift |
cfg_interval_start / _end | 0.0 / 1.0 | window where CFG applies |
num_steps | 50 | 16 is fine for drafts |
batch_size | 1 | |
seed | — | |
think_mode | false | model reasons before drawing; text on the think_text output |
U1.5 samples only at its own native resolutions. Pick the aspect ratio you want and downscale afterwards if you need a specific pixel size:
| Ratio | Pixels | Ratio | Pixels |
|---|---|---|---|
| 1:1 | 2048×2048 | 2:1 | 2880×1440 |
| 16:9 | 2720×1536 | 1:2 | 1440×2880 |
| 9:16 | 1536×2720 | 3:1 | 3456×1152 |
| 3:2 | 2496×1664 | 1:3 | 1152×3456 |
| 2:3 | 1664×2496 | 4:3 | 2368×1760 |
| 3:4 | 1760×2368 |
Example prompt:
A cinematic, dynamic shot of a terrified old man frantically running away from a massive, shadowy monster in a dark, foggy forest, high contrast, 8k resolution, photorealistic.
Also available: SenseNova U1 Local Image Edit (image + instruction) and
SenseNova U1 Local Interleave (alternating text and images). Ready-made
graphs ship in the node's example_workflows/ folder.
Measured on an RTX 5060 Ti 16 GB with this Q4_0 file:
| Run | Steps | Size | Wall time |
|---|---|---|---|
t2i, full, includes loading the 9.25 GiB file | 4 | 2048×2048 | 106 s |
t2i, balanced (layer offload) | 20 | 2720×1536 | 200 s |
t2i, full, batch_size=2 | 8 | 2048×2048 | 106 s |
edit, balanced, 2.1 MP | 8 | 1440×1440 | 119 s |
At vram_mode=full the weights sit at 10.2 GiB resident and peak around
10.9 GiB while sampling 2048². batch_size=2 at 2048² peaks at 14.15 GiB,
about as far as a 16 GB card goes — go balanced beyond that.
Image editing needs more room than generation. The edit node runs the source
image and the generated one through the model together; at full with the
node's stock 4.19 MP target it OOMs on 16 GB (11.81 GiB weights plus a 2.27 GiB
allocation). Use vram_mode=balanced and lower the megapixel target to ~2.1 for
editing.
Every run above includes a model reload, because each changed something in the
loader's cache key. Changing vram_mode, model_path, dtype, device_map
or the GGUF selection forces a full reload — keep them stable between
generations and only the first run pays the load cost.
Without the shim from step 5, this checkpoint fails at load or on the first
step. Both failures come from the same place: diffusers' GGUF quantizer only
swaps nn.Linear for GGUFLinear, so every other module keeps the raw
quantized bytes.
1. The token embedding. language_model.model.embed_tokens.weight is an
nn.Embedding, so the lookup returns Q4_0 block bytes — 4096 // 32 * 18 =
2304 wide instead of 4096 — and the first RMSNorm fails:
RuntimeError: The size of tensor a (4096) must match the size of tensor b (2304)
at non-singleton dimension 2
2. The two embedders. fm_modules.timestep_embedder and
fm_modules.noise_scale_embedder are nn.Linear, but
modeling_fm_modules.py casts activations with
t_freq.to(self.mlp[0].weight.dtype). On a GGUFLinear that dtype is the
storage dtype torch.uint8, so the activations become Byte:
RuntimeError: mat1 and mat2 must have the same dtype, but got Byte and BFloat16
| Symptom | Cause | Fix |
|---|---|---|
gguf_checkpoint dropdown is empty | file is in models/unet/ | move it to models/gguf/ (step 3), restart |
tensor a (4096) ... tensor b (2304) | quantized embedding | install the shim (step 5) |
got Byte and BFloat16 | quantized timestep embedder | install the shim (step 5) |
not installed: No module named 'sensenova_u1' | deps in the wrong Python | reinstall with ComfyUI's interpreter (step 2) |
| OOM while editing | vram_mode=full + 4.19 MP | balanced, ~2.1 MP |
The proper fix is upstream, in the quantization step: keep those five tensors in F16/F32 when producing the GGUF. The embedding costs ~1.2 GB and the two embedders only 35.7M params (~71 MB), so a re-quantized upload would need no shim and would work with the stock node. That is planned for the next revision of this repo.
hf download hoidhxd/SenseNova-U1.5-8B-GGUF SenseNova-U1.5-8B-MoT-Preview-Q4_0.gguf --local-dir .
Inherits the license of the base model, sensenova/SenseNova-U1.5-8B-MoT-Preview.