Spaces:
Running on Zero
Running on Zero
Download text_encoder_loader.py from amisima/Flux-3-Image: direct link, hf CLI and curl.
- Browser
- Download file 4.84 kB
-
https://huggingface.co/spaces/amisima/Flux-3-Image/resolve/main/text_encoder_loader.py
- Command line
-
hf download hf://spaces/amisima/Flux-3-Image/text_encoder_loader.py
-
curl -L -o text_encoder_loader.py https://huggingface.co/spaces/amisima/Flux-3-Image/resolve/main/text_encoder_loader.py
4.84 kB
| """Pinned Sakura Qwen3-VL encoder with the official FLUX.3 processor/config. | |
| Only the shared text/vision encoder changes. The FLUX image transformers and | |
| VAE remain official. Set FLUX3_TEXT_ENCODER=official to restore that encoder. | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| import os | |
| from pathlib import Path | |
| import tempfile | |
| OFFICIAL_REPO = "black-forest-labs/flux-3-image-klein-base" | |
| OFFICIAL_REVISION = "3f7fa5ee22ca1d5a5cc095ac7cc96e6a173b4c86" | |
| SAKURA_REPO = "webmp3/Sakura-FLUX.3-Image-Klein-TextEncoder-Uncensored-GGUF" | |
| SAKURA_REVISION = "fe49271a47b61efe0d96b476306c25934d943472" | |
| SAKURA_FILE = "Sakura-TextEncoder-Qwen3VL-4B-Uncensored-bf16-8.27GiB.safetensors" | |
| SAKURA_BYTES = 8_875_719_408 | |
| # All 713 names, shapes and dtypes independently match the official encoder. | |
| ENCODER_SCHEMA_SHA256 = "f4619891efac96ad653344f1b195a51427b5cc9637769f58f435d20af26c93dc" | |
| ENCODER_MODE = os.environ.get("FLUX3_TEXT_ENCODER", "sakura").strip().lower() | |
| if ENCODER_MODE not in {"sakura", "official"}: | |
| raise ValueError("FLUX3_TEXT_ENCODER must be 'sakura' or 'official'.") | |
| ENCODER_LABEL = "Sakura text encoder" if ENCODER_MODE == "sakura" else "Official text encoder" | |
| def text_encoder_info(): | |
| return { | |
| "variant": ENCODER_MODE, | |
| "label": ENCODER_LABEL, | |
| "repo": SAKURA_REPO if ENCODER_MODE == "sakura" else OFFICIAL_REPO, | |
| "revision": SAKURA_REVISION if ENCODER_MODE == "sakura" else OFFICIAL_REVISION, | |
| "processor_repo": OFFICIAL_REPO, | |
| "processor_revision": OFFICIAL_REVISION, | |
| } | |
| def _validate_checkpoint(checkpoint): | |
| """Read only the safetensors header; never allocate the 8.27 GiB tensors.""" | |
| path = Path(checkpoint) | |
| if path.stat().st_size != SAKURA_BYTES: | |
| raise RuntimeError("The Sakura text encoder checkpoint has an unexpected size.") | |
| with path.open("rb") as stream: | |
| header_size = int.from_bytes(stream.read(8), "little") | |
| if not 2 <= header_size <= 1_000_000: | |
| raise RuntimeError("The Sakura text encoder checkpoint has an invalid header.") | |
| try: | |
| header = json.loads(stream.read(header_size)) | |
| schema = {name: {"dtype": value["dtype"], "shape": value["shape"]} | |
| for name, value in header.items() if name != "__metadata__"} | |
| except (ValueError, TypeError, KeyError, AttributeError) as exc: | |
| raise RuntimeError("The Sakura text encoder checkpoint header is unreadable.") from exc | |
| digest = hashlib.sha256(json.dumps(schema, sort_keys=True, separators=(",", ":")).encode()).hexdigest() | |
| if digest != ENCODER_SCHEMA_SHA256: | |
| raise RuntimeError("The Sakura text encoder tensor layout differs from the verified Qwen3-VL encoder.") | |
| def _check_loading_info(info, config): | |
| # Transformers can report an omitted output head even when it is correctly | |
| # tied to the input embeddings. The checkpoint schema is checked separately. | |
| allowed_missing = {"lm_head.weight"} if getattr(config, "tie_word_embeddings", False) else set() | |
| missing = set(info.get("missing_keys", [])) - allowed_missing | |
| if missing or any(info.get(key) for key in ("unexpected_keys", "mismatched_keys", "error_msgs")): | |
| raise RuntimeError("Text encoder loading was incomplete; refusing to run with missing or mismatched weights.") | |
| def load_text_encoder(dtype, token=None): | |
| from huggingface_hub import hf_hub_download | |
| from transformers import AutoConfig, Qwen3VLForConditionalGeneration | |
| config = AutoConfig.from_pretrained( | |
| OFFICIAL_REPO, subfolder="text_encoder", revision=OFFICIAL_REVISION, token=token, | |
| ) | |
| if ENCODER_MODE == "official": | |
| model, info = Qwen3VLForConditionalGeneration.from_pretrained( | |
| OFFICIAL_REPO, subfolder="text_encoder", revision=OFFICIAL_REVISION, | |
| config=config, dtype=dtype, token=token, output_loading_info=True, | |
| ) | |
| else: | |
| checkpoint = hf_hub_download( | |
| SAKURA_REPO, SAKURA_FILE, revision=SAKURA_REVISION, token=token, | |
| ) | |
| _validate_checkpoint(checkpoint) | |
| # Transformers expects model.safetensors beside config.json. A temporary | |
| # symlink keeps the cached weight file in place without an 8 GiB copy. | |
| # The Space runs Linux; only this tiny directory is temporary. | |
| with tempfile.TemporaryDirectory(prefix="flux3-sakura-encoder-") as directory: | |
| config.save_pretrained(directory) | |
| os.symlink(str(Path(checkpoint).resolve()), str(Path(directory) / "model.safetensors")) | |
| model, info = Qwen3VLForConditionalGeneration.from_pretrained( | |
| directory, config=config, dtype=dtype, local_files_only=True, | |
| output_loading_info=True, | |
| ) | |
| _check_loading_info(info, config) | |
| model.tie_weights() | |
| return model.eval() | |