Spaces:
Running on Zero
Running on Zero
Commit History
-
test: empty signed commit to verify HF GPG verification Running
Verified signature4412288 -
feat(t2i): raise Width/Height cap to 2048 and switch step to 16
Unverified signatureZ-Image-Base's model card explicitly documents the supported range as "512x512 to 2048x2048 (total pixel area, any aspect ratio)". The previous cap of 1536 with step=64 clipped valid configurations and contradicted the actual mod-16 constraint (8x VAE + 2x patch). - Width / Height sliders: 384..1536 step 64 -> 384..2048 step 16. - Tooltips: "Multiples of 64" was wrong; correct constraint is 16. Call out the 1024-1280 sweet spot and the documented 2048x2048 area max so users know what's supported vs untrained territory. Turbo is a distillation of Base and inherits the same dim constraints.3aed2df -
refactor(upscale): drop LoRA support per spec
Unverified signatureCLAUDE.md and docs/superpowers/specs marked LoRA on the Upscale refinement pass as out-of-scope for v1, but the UI + handler still exposed it. In practice the upscale flow is RealESRGAN x2 -> 5 step Z-Image-Turbo img2img at 0.33 denoising — a window so tight that style LoRAs (Toon5, DarkGhibly, etc.) have almost no purchase on the output. Worse, users expect "Use a LoRA" to actually do something visible. Remove: - LoRA checkbox / file / strength group from ui.build_upscale_tab - lora_enabled/lora_path/lora_strength params + _coerce_lora call in app.on_upscale_generate - The lora_enabled.change visibility wiring in build_app - applied_lora wrapper + lora/lora_strength meta fields in modes.call_upscale Test coverage: - Update existing call_upscale tests to drop the no-longer-accepted lora_path / lora_strength params. - Add test_build_upscale_tab_has_no_lora_components to lock the UI surface against regression. The lora module + the T2I/ControlNet LoRA paths are untouched.7953225 -
fix(controlnet): crop control image to multiple-of-16 dims
Unverified signatureSame modulus-of-16 bug pattern as the upscale fix in 9514256, hit on the ControlNet path instead. With an input image whose dims aren't already multiples of 16, DiffSynth's VAE encode rounds the latent shape *down* while the noise allocator for inpaint_mask rounds *up*, so the torch.concat([control_latents, inpaint_mask, inpaint_latent], dim=1) in the pipeline raises a shape mismatch (e.g. 45 vs 46 on dim 3). Crop the control image (or fallback raw input) to (W//16*16, H//16*16) before handing it to the pipe, mirroring the upscale fix. Verified live with cn-input.webp (618x367) + Toon5_E10 LoRA on Z-Image Turbo locally.2035fc8 -
fix(lora): accept diffusion_model.* prefix and use pipe.load_lora hotload
Unverified signatureThe sniff allowlist only had transformer./dit./model.transformer., so any CivitAI / Kohya LoRA whose keys started with diffusion_model.* (Toontastic, DarkGhibly, etc.) was rejected before reaching the pipeline. The bigger issue: _apply_lora_impl was calling diffsynth.utils.lora.merge_lora (loras: List[Dict], alpha) as if it were a state_dict-to-module fuser. It isn't — it's an image2lora list-merger — so even the LoRAs we did accept never actually patched the DiT. Swapped to pipe.load_lora(module=pipe.dit, ...) which routes through GeneralLoRALoader.convert_state_dict (strips the diffusion_model. prefix natively) and hotloads into AutoWrappedLinear, reverted at exit via pipe.clear_lora(). Verified live with Toon5_E10 (Z-Image-Turbo) and DarkGhiblyZBase (Z-Image Base) on local MPS.2e18e13
-
fix(upscale): crop upscaled image to multiple-of-16 dims before refinement
Unverified signatureDiffSynth allocates the noise tensor at ceil-multiple-of-16 but its VAE encodes at floor-multiple-of-16, so a 1240x728 ESRGAN output produced a 156-wide noise latent against a 154-wide input latent and add_noise crashed with 'tensor a (154) must match tensor b (156) at dim 3'. Floor-crop to mod 16 in modes.call_upscale so both paths agree.9514256 -
docs: polish README + write AGENTS.md + SKILLS.md, refresh CLAUDE.md
Unverified signatureFour meta-docs aligned for a real open-source project: - README.md: badges (HF Space / GitHub stars / MIT / Python 3.11 / DiffSynth-Studio), demo link, features table, quick-start (local + HF), architecture diagram, project layout, tech stack, design philosophy (Soft Dark Restraint), license + credits. - CLAUDE.md: refreshed with current architecture facts (one pipeline, two transformers, swap via pool index, MPS vram_limit=None, PYTORCH_ENABLE_MPS_FALLBACK, hf cache -> ./models/<repo>/ symlink). Adds 'Gotchas we already paid for' covering every footgun caught during the 40+ commit iteration (model_pool discard, visible=False DOM removal, transformers<5 pin, torchaudio + basicsr shims, model slug mismatches, depth_midas vs midas, etc.). - AGENTS.md (new): tool-agnostic version of CLAUDE.md. TL;DR five rules, locked architecture decisions table, commit + verify + testing conventions, v1 scope cap. - SKILLS.md (new): process rules. Investigation before fix, HF log …dc32ce0 -
feat(ui): support CTA bar — '♥ at the top to support · follow for what's next'
Unverified signatureSingle-line banner between the brand header and the tab strip. Two CTAs: - '♥ at the top' points at HF Space's native heart-widget so users can support without leaving the page - 'Follow @techfreakworm' links to the HF profile for future-project signal Visual restraint: dim text on a faint amber wash, hairline border, soft 2.4s pulse on the heart. Wraps to two lines under 600 px so phones get a clean stacked layout. Inspired by LTX2.3 Studio's tipbar pattern but adapted to the Soft Dark Restraint palette.3801f4d -
test: update _on_model_change asserts for new 3-tuple return shape
Unverified signaturee3764fc -
fix(upscale): pass post-upscale dims to pipe + LoRA label hints model compat
Unverified signatureTwo coupled UX fixes from live testing: 1. Upscale generation crashed with 'size of tensor a (256) must match size of tensor b (128) at non-singleton dimension 3' in DiffSynth's add_noise. RealESRGAN x4 + 0.5 resize yields a 2x image (1024 -> 2048). The VAE- encoded input latents were 256x256 but DiffSynth's noise initializer used the default 1024 height/width, producing 128x128 noise. call_upscale now passes height=upscaled.size[1], width=upscaled.size[0] — mirrors what call_controlnet already did for the preprocessed control image. 2. The LoRA toggle didn't say which model's LoRA it accepts. Now reads 'Use a LoRA (compatible with Z-Image-Turbo)' by default. On the T2I tab, _on_model_change updates the label to 'Use a LoRA (compatible with Z-Image)' when the user picks Base. ControlNet + Upscale stay Turbo-locked.296faa9 -
fix: depth preprocessor name + basicsr/torchvision import shim
Unverified signatureTwo bugs caught after the HF Space redesign push: 1. ControlNet Depth preview always showed the raw image. controlnet_aux's Processor registers depth under 'depth_midas', not 'midas'. The plain 'midas' name raised KeyError, which _preview_cn silently swallowed (returning the raw input). Fix: use 'depth_midas' in preprocessors._run_depth. 2. Upscale crashed with ModuleNotFoundError on torchvision.transforms. functional_tensor — basicsr (a realesrgan dep) imports that module, but torchvision >=0.17 removed it. Fix: alias torchvision.transforms.functional into sys.modules['torchvision.transforms.functional_tensor'] inside upscale._realesrgan_4x before basicsr loads. 3. Hardened _preview_cn to log the underlying exception to stderr instead of swallowing silently. The next preprocessor bug surfaces in HF logs.6d862f4 -
perf(preload): broaden globs to capture configs + index.jsons (~47 GB total)
Unverified signatureOld preload listed only *.safetensors and tokenizer/*, which missed: - text_encoder/config.json + model.safetensors.index.json (shard map) - transformer/config.json + .index.json - vae/config.json - scheduler/scheduler_config.json - model_index.json (diffusers top-level) Result: every cold-start inference fired 5-8 small hf_hub_download calls to fill those gaps before the pipeline could load. Annoying first-call latency on the Space. Broaden to transformer/* etc. so HF preload pulls each subdir wholesale. Skip the shared encoder/vae/tokenizer from Z-Image-Turbo (identical to Base's). Keep alibaba-pai and lllyasviel/Annotators tight — their repos contain other models we don't want. Sizes (from HF API): Tongyi-MAI/Z-Image: ~20.6 GB Tongyi-MAI/Z-Image-Turbo: ~24.6 GB (transformer subdir only) alibaba-pai/...Union-2.1: ~2 GB (one file) lllyasviel/Annotators: 67 MB (one file) ────────────────────────────────── Total: ~47 GB (cap is 150 …6bd8e31 -
chore(ui): display brand as 'Z-Image Studio' (title-case) in header + browser tab
Unverified signatureRepo / directory / Python package stay kebab-case z-image-studio. Only the user-facing brand mark and gr.Blocks title= switch to title-case.0e7a9cf -
feat(ui): soft dark restraint redesign — native gr.Radio + accordion + lora toggle
Unverified signatureDrops the custom model-card grid, the popover tooltip pattern, the JS shim, and the decorative CSS. Uses Gradio-native shapes throughout (gr.Radio for Base/Turbo, gr.Checkbox to toggle LoRA visibility, gr.Accordion for advanced width/height/seed, info= subtitle text in place of the (i) icon). Adds the ControlNet preprocessor preview slot (Canny / Depth / Pose render live as the user changes the dropdown). Single output meta under the image (the prior mockup duplicated it inside Advanced).af844a1 -
fix: actually use Base transformer when user picks Base
Unverified signatureGradio 5 does not render components with visible=False to the DOM at all — the element is entirely absent. The JS shim in _HEAD_JS called document.querySelector('#zis-model-state textarea, #zis-model-state input') which returned null, so hidden.value and the input event dispatch were silently skipped. Gradio's internal state for model_state therefore stayed "Turbo" on every Generate click regardless of which card the user had selected. Fix: drop visible=False and instead keep the textbox in the DOM with elem_classes=["zis-hidden"] backed by a new CSS rule `.zis-hidden { display: none !important; }` in theme.py. The element is now invisible to users but present in the DOM so the JS selector resolves, the value update propagates, and Gradio routes the correct model name ("Base" or "Turbo") into on_t2i_generate → modes.call_t2i → _swap_transformer.663f937 -
feat(progress): gradio progress(track_tqdm=True) on all three handlers
Unverified signatureDiffSynth uses tqdm for the diffusion step loop and RealESRGAN uses it for the tile pass. Adding gr.Progress(track_tqdm=True) as a keyword parameter on the three on_*_generate handlers lets Gradio auto-capture both without threading a progress object through backend/modes.aa2a834 -
fix: pool-stashed transformer swap + MPS-safe vram + corrected model-zoo anchor
Unverified signatureThree coupled fixes uncovered by the first real Generate click locally: 1. AttributeError: 'ZImagePipeline' object has no attribute 'model_pool' DiffSynth's from_pretrained builds a fresh ModelPool, attaches pipe.dit/ pipe.text_encoder/etc. from it, then discards the pool. My _swap_transformer in modes.py assumed pipe.model_pool persisted. Replace from_pretrained call with a manual replication that stashes the pool on pipe._zis_pool, then index into pool.model for the two z_image_dit entries (Base loaded first, Turbo second per MODEL_CONFIGS order). fetch_model can't distinguish them since both register under the same name. 2. AttributeError: module 'torch.mps' has no attribute 'mem_get_info' DiffSynth's AutoWrappedModule.forward gates module load on check_free_vram (vram/layers.py:195), which calls torch.<device>.mem_get_info — CUDA-only. The escape hatch is vram_limit=None, which short-circuits the gate. Update models.vram_limit_for('mps') -> None (…0cf8ffc -
fix(deps): add torchaudio + pin transformers<5
Unverified signatureDiffSynth 2.0.7 imports: - torchaudio (blanket import in diffsynth.core.data.operators) - SiglipVisionTransformer from transformers.models.siglip (renamed/removed in transformers 5.x; works fine in 4.45 - 4.57) Without these pins, both local venv setup and the HF Space build pulled transformers 5.x via DiffSynth's own pyproject (which lists 'transformers' unbounded), and the import chain broke at app boot.5ab6428 -
fix(deploy): switch model lookups to HF + fix ZeroGPU build
Unverified signatureMultiple issues caught in the HF Space build cycle: 1. DiffSynth defaults to ModelScope, not HF. Set DIFFSYNTH_DOWNLOAD_SOURCE= huggingface in app.py before imports so preload_from_hub + HF cache work. 2. PAI/Z-Image-Turbo-Fun-Controlnet-Union-2.1 is the ModelScope slug. On HF the mirror is alibaba-pai/... — update models.py + README + spec + test. 3. ZeroGPU build appends spaces==0.50.0 to the install line, conflicting with our spaces==0.30.0 pin. Drop the pin entirely — HF provides the right one. 4. DiffSynth resolves models at ./models/<repo>/, not ~/.cache/huggingface/hub. Add models.symlink_hf_cache_to_diffsynth_layout() that creates symlinks from cache snapshots to that layout. Wire into _bootstrap() so the preload weights are actually findable at runtime (otherwise the Space re-downloads all 30 GB on first call). 5. Set DIFFSYNTH_MODEL_BASE_PATH=<repo>/models in _bootstrap(). 6. Add models/ to .gitignore. 7. _bootstrap() now also runs locally (just th…99302bc -
fix(realesrgan): switch repo from xinntao/Real-ESRGAN to lllyasviel/Annotators
Unverified signaturexinntao/Real-ESRGAN doesn't exist on HF (returns 401) — the canonical weights live in GitHub releases. lllyasviel/Annotators mirrors the same 67 MB RealESRGAN_x4plus.pth and serves it cleanly via HF Hub. Caught when the HF Space build hit RUNTIME_ERROR on preload. Updates: README preload_from_hub, app._esrgan_path(), spec, plan.213bf15 -
fix(deps): install diffsynth from github (not on pypi)
Unverified signatureThe package 'diffsynth-studio' does not exist on pypi. The actual package name in modelscope/DiffSynth-Studio's pyproject.toml is 'diffsynth' (v2.0.7), and the only install path is from the git repo. HF Space build failed: ERROR: Could not find a version that satisfies the requirement diffsynth-studio>=0.5.0 (from versions: none) Switch to: diffsynth @ git+https://github.com/modelscope/DiffSynth-Studio.gitb800376 -
fix: post-review polish — slider defaults, error handling, comment
Unverified signature- I5: t2i steps/cfg sliders update via gr.change() when model selector toggles base/turbo (was: stuck at turbo defaults, base generations silently used cfg=1 steps=8 → bad output). - I3: controlnet preprocessor wraps in try/except with raw-input fallback + stderr warning. - I1: zerogpu 'gpu task aborted' retries once with 2x duration via __retry_multiplier__ in params. duration_for honors it. - I4: 'Eager backend boot' comment renamed to 'Lazy backend singleton' to match the actual lazy get_backend() behavior. - bonus: fix pre-existing ruff failures in test_smoke_gpu.py (unused meta vars, unsorted local imports).76862de -
test: l3 gpu smoke (t2i base/turbo + controlnet + upscale)
Unverified signatureAdd pytest.mark.gpu smoke tests for real-weight end-to-end validation. Also add addopts=-m 'not gpu' to pyproject.toml so CI auto-deselects these tests; run with `pytest -m gpu` to opt in on a GPU box.5ac741c -
ci: ruff + pytest on push/pr (l1+l2, no gpu deps)
Unverified signatureAdd .github/workflows/ci.yml running ruff format, ruff check, and pytest against a minimal dep set (no torch/diffsynth/realesrgan/controlnet_aux). Fix all ruff format + lint violations found during CI replication (import order, unused imports, EN-dash RUF001, RUF059 unused unpack variables). Skip auto_device test when torch is absent so CI passes without a GPU.9a5065c -
docs: hf space frontmatter + readme
Unverified signatureceadaef -
feat(app): gradio blocks entrypoint with bootstrap + event wiring + js shim
Unverified signature5d4511a -
feat(ui): per-tab gradio builders with labeled_label + custom model selector
Unverified signature14b904e -
feat(backend): ZImageStudioBackend with @spaces.GPU and mode dispatch
Unverified signature3b83775 -
feat(backend): zerogpu duration estimator (clamped 60-180s)
Unverified signature8894ed9 -
feat(modes): upscale handler (realesrgan + z-image-turbo refinement)
Unverified signature84d00fe -
feat(modes): controlnet handler (turbo + union 2.1 + preprocessor)
Unverified signature3762756 -
feat(modes): t2i handler (base + turbo) with transformer swap and lora ctx
Unverified signature8f6ce7f -
feat(upscale): realesrgan x4 wrapper with 0.5-resize bridge
Unverified signatureb855333 -
feat(preprocessors): canny/depth/pose via controlnet_aux (lazy imports)
Unverified signature036940b -
feat(lora): applied_lora ctx manager — validate, apply, revert on exit
Unverified signature8759971 -
feat(lora): safetensors header sniff + zimage key validation
Unverified signaturebc2513c -
feat(models): hf cache mirror (hardlink blobs, preserve snapshot symlinks, copy refs)
Unverified signature261639d -
feat(ui): labeled_label and model_selector_html helpers
Unverified signaturea7d0a44 -
feat(theme): css rules for param info tooltips and custom model selector
Unverified signature1a3b62d -
refactor(tooltips): rename copy.py → tooltips.py to avoid stdlib shadow
Unverified signatureThe original copy.py shadowed Python's stdlib copy module, requiring a fragile conftest hack to pre-import torch/pydantic/fastapi/gradio so they captured the real stdlib before sys.modules['copy'] was swapped. That coupling — every new dep with a lazy 'from copy import deepcopy' would silently break tests — is bad enough to avoid entirely. Rename to tooltips.py. conftest.py reverts to a simple sys.path insert. Spec and plan amendments updated.0592951 -
feat(copy): tooltip strings dict — one source of truth for param descriptions
Unverified signatureAlso fix conftest.py to pre-load torch/gradio/pydantic before swapping sys.modules["copy"] with the project module, preventing stdlib shadow breakage in tests that import those packages.232f8d6 -
docs: amend spec + plan with param tooltips and coming-soon model placeholders
Unverified signature- Spec § 4.4 updated: every component uses show_label=False with a labeled_label() HTML prefix. T2I model selector becomes custom HTML with a hidden gr.Textbox carrying the state. - Spec § 4.6 (new): tooltip pattern (subtle circled (i) superscript; hover desktop / tap-pin mobile; copy lives in copy.py). - Spec § 4.7 (new): coming-soon model placeholders for Z-Image-Edit and Z-Image-Omni-Base; disabled cards are <a> elements opening github.com/Tongyi-MAI/Z-Image#model-zoo. - Spec § 5: copy.py added to file layout. - Spec § 11: 3 new implicit decisions + 1 new out-of-scope item. - Plan: amendment section with Tasks A (copy.py), B (theme CSS extensions), C (ui.py helpers) plus modifications to existing Task 14 and Task 15.86c22f6 -
feat(models): device autodetect, vram-limit helpers, model config registry
Unverified signature613dab3 -
fix(theme): preserve gradio's --font / --font-mono css vars
Unverified signatureThe _OnyxAmberBase override redirected self.font into font_str, which broke _get_theme_css emission of --font / --font-mono. Expose iterable access via .font_list / .font_mono_list instead, and add a regression assertion on the generated CSS.fd0ad15 -
feat(theme): onyx amber palette + gr.themes.Base + glow CSS
Unverified signature170a904 -
chore: project scaffolding (pyproject, requirements, license, claude.md, tests)
Unverified signature322b245 -
docs: implementation plan for z-image-studio (19 tasks)
Unverified signatureTDD-driven plan covering: scaffolding, onyx amber theme, device/model config registry, hf cache mirror, lora sniff + apply/revert ctx, controlnet preprocessors, realesrgan upscale wrapper, three mode handlers (t2i/controlnet/upscale), zerogpu duration estimator, backend dispatch, gradio ui builders, app entrypoint, readme + hf space yaml, ci workflow, l3 gpu smoke, hf space deploy.0aed297 -
docs: initial design spec for z-image-studio
Unverified signatureGradio app for Z-Image + Z-Image-Turbo via DiffSynth-Studio (no ComfyUI). Three tabs (T2I dual-model / ControlNet Union / Upscale RealESRGAN+refine), per-tab LoRA loader, MPS+CUDA dual-device, eager preload, Onyx Amber theme.9ee5274