Spaces:
Running on Zero
gradio workflow app (#1)
Browse files- Add workflow graph: 16 reference inputs -> generate -> gallery + status subjects, example 007 prefill (6efb787f49d4130c4e959ef839e0f95ad4035551)
- Replace demo entry point with gr.Workflow app (node canvas + styled App view) (f1539b30af4e45f532d9ab4b3d7e256eb4b5ef02)
- Add gradio_client dependency for media serialization helpers (aeba2d2e89cfb82127985c5317c8976f216ea2ec)
- README for the Workflow version: parity table, design notes, hf_oauth scopes (4dbd7c78cde6952af9bc81b548c0c2fa34b369e6)
- Add PR description (257f01db4ea8dbe92a27adee1f6f727ea8c5beee)
- Carry over upstream README badge row (homepage, discord, QQ, feishu) so the merge doesn't drop it (82c6b4f536fe04e02e046d0be552f4d8f2024c70)
Co-authored-by: AK <akhaliq@users.noreply.huggingface.co>
- README.md +66 -73
- app.py +576 -36
- requirements.txt +1 -0
- workflow.json +191 -0
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: MOSS
|
| 3 |
emoji: 🖼
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
|
@@ -7,12 +7,20 @@ sdk: gradio
|
|
| 7 |
sdk_version: 6.30.0
|
| 8 |
python_version: "3.12"
|
| 9 |
app_file: app.py
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
pinned: false
|
| 11 |
license: apache-2.0
|
| 12 |
-
|
|
|
|
|
|
|
| 13 |
---
|
| 14 |
|
| 15 |
-
|
|
|
|
|
|
|
| 16 |
|
| 17 |
<p align="center">
|
| 18 |
<a href="https://huggingface.co/OpenMOSS-Team/MOSS-Image-Base"><img alt="Hugging Face · MOSS-Image-Base" src="https://img.shields.io/badge/🤗%20Hugging%20Face-MOSS--Image--Base-ffc107?style=flat-square"></a>
|
|
@@ -38,82 +46,67 @@ short_description: Official demo of MOSS-Image.
|
|
| 38 |
<a href="https://discord.gg/nXFWEyvbb"><img alt="MOSS-Image Discord Community" src="https://img.shields.io/badge/Discord-Community-5865F2?style=flat-square&logo=discord&logoColor=white"></a>
|
| 39 |
</p>
|
| 40 |
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
## Model loading
|
| 46 |
-
|
| 47 |
-
The Space preloads **MOSS-Image-Base** and **MOSS-Image-SFT** with
|
| 48 |
-
`snapshot_download(repo_id, revision=resolved_commit)`, followed by
|
| 49 |
-
`MOSSImagePipeline.from_pretrained(snapshot_path, local_files_only=True, ...)`.
|
| 50 |
-
The Space explicitly loads its bundled pipeline, transformer and scheduler classes
|
| 51 |
-
from `pipeline_moss_image.py` for the ZeroGPU AOT lifecycle. The bundled inference
|
| 52 |
-
implementation follows the current Base pipeline, including negative-branch skipping
|
| 53 |
-
and standard SLG. Progress is reported using the same bridge as the standalone app.
|
| 54 |
-
Weights are downloaded into the Hugging Face cache; no local model directory
|
| 55 |
-
or model weights in this repository are required.
|
| 56 |
-
|
| 57 |
-
Select **MOSS-Image-Base** or **MOSS-Image-SFT** in the Model dropdown before
|
| 58 |
-
clicking Generate. Base is selected by default. The optional `MOSS_MODEL_ID`
|
| 59 |
-
Space variable changes this initial selection.
|
| 60 |
-
|
| 61 |
-
Both transformers are loaded at startup. The text encoder, tokenizer and VAE
|
| 62 |
-
are each shared only when their Hub file hashes match at the resolved revisions.
|
| 63 |
-
Components that differ, such as model-specific VAEs, are loaded separately. Each model retains its own
|
| 64 |
-
scheduler, prompt cache and revision metadata. Generation requests run serially;
|
| 65 |
-
changing the dropdown does not change an already submitted request.
|
| 66 |
-
|
| 67 |
-
While the model repositories are private, configure an `HF_TOKEN` **Space secret**
|
| 68 |
-
with read access to both models. Do not put a token in source files or
|
| 69 |
-
ordinary Space variables. Local development uses the same environment variable
|
| 70 |
-
or your cached Hugging Face login.
|
| 71 |
-
|
| 72 |
-
## ZeroGPU
|
| 73 |
-
|
| 74 |
-
Both pipelines are loaded and placed on CUDA before the UI starts. Generation runs
|
| 75 |
-
under `@spaces.GPU(duration=80)`. At startup, a separate GPU allocation exports
|
| 76 |
-
and AOT-compiles the repeated DiT and text-projection blocks with dynamic sequence
|
| 77 |
-
lengths. Both models reuse the compiled graphs with their own weights. Compilation
|
| 78 |
-
is validated against eager block outputs before the UI starts. This uses ZeroGPU
|
| 79 |
-
AOTInductor support, not runtime `torch.compile`; GPU residency is fixed.
|
| 80 |
-
Set the Space variable `MOSS_AOTI=0` to run the eager baseline. Compilation happens
|
| 81 |
-
once per container start and is not part of image generation latency.
|
| 82 |
-
The 80-second budget is scaled to 120 seconds by the current RTX PRO 6000
|
| 83 |
-
ZeroGPU SDK configuration; it is not a measured latency guarantee.
|
| 84 |
-
Large images or batches may require more time or memory than the allocation allows.
|
| 85 |
-
|
| 86 |
-
Outputs and version-2 parameter JSON files are stored under `outputs/gradio/`. Records
|
| 87 |
-
include the model repository and resolved revision, actual seed, sampling
|
| 88 |
-
parameters, component configuration hashes, execution backend, and software versions. Space storage
|
| 89 |
-
is ephemeral; save generated images you want to keep.
|
| 90 |
-
|
| 91 |
-
## Local development
|
| 92 |
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
``
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
```bash
|
| 101 |
-
|
|
|
|
|
|
|
| 102 |
```
|
| 103 |
|
| 104 |
-
The
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
| 109 |
|
| 110 |
-
|
| 111 |
-
sharing and AOT compilation. Memory mode and runtime Compile DiT controls are not
|
| 112 |
-
shown because GPU residency and compilation are managed at startup. The generation
|
| 113 |
-
API returns the gallery and status, matching the standalone interface.
|
| 114 |
|
| 115 |
-
|
|
|
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
|
|
|
| 1 |
---
|
| 2 |
+
title: MOSS-Image Workflow
|
| 3 |
emoji: 🖼
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
|
|
|
| 7 |
sdk_version: 6.30.0
|
| 8 |
python_version: "3.12"
|
| 9 |
app_file: app.py
|
| 10 |
+
hf_oauth: true
|
| 11 |
+
hf_oauth_scopes:
|
| 12 |
+
- inference-api
|
| 13 |
+
- write-repos
|
| 14 |
pinned: false
|
| 15 |
license: apache-2.0
|
| 16 |
+
tags:
|
| 17 |
+
- ml-intern
|
| 18 |
+
short_description: MOSS-Image text-to-image as a gr.Workflow node canvas.
|
| 19 |
---
|
| 20 |
|
| 21 |
+
[](https://hf.co/chat?mode=ml-intern)
|
| 22 |
+
|
| 23 |
+
# MOSS-Image Workflow
|
| 24 |
|
| 25 |
<p align="center">
|
| 26 |
<a href="https://huggingface.co/OpenMOSS-Team/MOSS-Image-Base"><img alt="Hugging Face · MOSS-Image-Base" src="https://img.shields.io/badge/🤗%20Hugging%20Face-MOSS--Image--Base-ffc107?style=flat-square"></a>
|
|
|
|
| 46 |
<a href="https://discord.gg/nXFWEyvbb"><img alt="MOSS-Image Discord Community" src="https://img.shields.io/badge/Discord-Community-5865F2?style=flat-square&logo=discord&logoColor=white"></a>
|
| 47 |
</p>
|
| 48 |
|
| 49 |
+
A [Gradio Workflow](https://www.gradio.app/guides/workflows) (node-canvas) version of the official
|
| 50 |
+
[OpenMOSS-Team/MOSS-Image](https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image) demo, built on
|
| 51 |
+
`gr.Workflow` in gradio 6.30.0, with the App view styled after the MOSS-Image landing page.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
## Feature parity with the official demo
|
| 54 |
+
|
| 55 |
+
Every sampling control of the original Space is a reference node wired into the
|
| 56 |
+
`MOSS-Image Generate` function node:
|
| 57 |
+
|
| 58 |
+
| Original demo | Here |
|
| 59 |
+
|---|---|
|
| 60 |
+
| Model dropdown (Base / SFT) | **Model** reference (`base` or `sft`); both checkpoints loaded at startup |
|
| 61 |
+
| Prompt / Negative Prompt | **Prompt** / **Negative Prompt** references (same default text) |
|
| 62 |
+
| Width × Height (presets) | **Width** / **Height** references (multiples of 16) |
|
| 63 |
+
| Steps / CFG / Shift / Seed | **Steps** / **CFG** / **Shift** / **Seed** references (`-1` = random) |
|
| 64 |
+
| Images per prompt | **Images** reference → gallery output |
|
| 65 |
+
| Max text length | **Max Text Length** reference (1–4096) |
|
| 66 |
+
| Skip-layer modes (Off / Negative-branch skipping / Standard SLG) | **Skip Layers** (`22` = negative-branch skipping, empty = off), **SLG Layers**, **SLG Scale**, **Skip Start**, **Skip Stop** references |
|
| 67 |
+
| Example gallery with one-click restore | **Examples strip** under the hero: the original Space's 20 showcase images, hot-linked; clicking one fetches its `parameters.json` and restores prompt, model, size, sampling and SLG settings into the form |
|
| 68 |
+
|
| 69 |
+
Not ported: showcase add/delete with live refresh (the Workflow App view is
|
| 70 |
+
generated from the graph and has no upload-to-gallery mechanism), and ZeroGPU AOT
|
| 71 |
+
compilation (this app runs the eager backend — set it up like the original if you
|
| 72 |
+
need the extra speed).
|
| 73 |
|
| 74 |
+
## Design
|
| 75 |
+
|
| 76 |
+
The App view is auto-generated by `gr.Workflow`, so the landing-page look is applied
|
| 77 |
+
through `launch(theme=…, css=…, head=…)`: Inter type, a light gradient canvas, the
|
| 78 |
+
official MOSS-Image lockup (light/dark aware) above the
|
| 79 |
+
"Text-to-Image Generation with Native Layout Control" headline, a top nav with
|
| 80 |
+
model/code links, and a Soft blue theme. The canvas view is untouched.
|
| 81 |
+
|
| 82 |
+
## Running it
|
| 83 |
+
|
| 84 |
+
Generation runs on GPU via `@spaces.GPU(duration=80)` per node call, so this Space needs
|
| 85 |
+
**ZeroGPU hardware** (Settings → Hardware). `hf_oauth: true` is enabled so the owner can edit the
|
| 86 |
+
canvas and signed-in visitors spend their own ZeroGPU quota; anonymous visitors fall back to the
|
| 87 |
+
small shared allowance.
|
| 88 |
+
|
| 89 |
+
Local development:
|
| 90 |
|
| 91 |
```bash
|
| 92 |
+
pip install -r requirements.txt
|
| 93 |
+
python app.py # full run, needs a CUDA GPU
|
| 94 |
+
MOSS_WORKFLOW_PREVIEW=1 python app.py # canvas + styling only, no model download
|
| 95 |
```
|
| 96 |
|
| 97 |
+
The bundled `pipeline_moss_image.py` is copied verbatim from the
|
| 98 |
+
[original Space](https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image) (BSD 3-Clause); model
|
| 99 |
+
weights are Apache-2.0. The lockup artwork is hot-linked from the original Space's assets.
|
| 100 |
+
The form is prefilled with showcase example 007 (the photorealistic Kazusa at
|
| 101 |
+
a Tokyo crosswalk: 1184×1776, 50 steps, CFG 3, shift 5, skip layer 22, seed
|
| 102 |
+
3537712559) so visitors can just hit **Run** and reproduce it exactly. Other
|
| 103 |
+
defaults match the official demo: Euler sampling and negative-branch skipping.
|
| 104 |
|
| 105 |
+
## API example
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
+
```python
|
| 108 |
+
from gradio_client import Client
|
| 109 |
|
| 110 |
+
client = Client("akhaliq/moss-image-workflow")
|
| 111 |
+
client.view_api() # exact parameters of /generated_images
|
| 112 |
+
```
|
|
@@ -1,47 +1,587 @@
|
|
| 1 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
import argparse
|
| 4 |
-
import logging
|
| 5 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
|
|
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
)
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
if __name__ == "__main__":
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""MOSS-Image as a gr.Workflow app — node-canvas text-to-image pipeline.
|
| 2 |
+
|
| 3 |
+
A reimagining of https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image as a
|
| 4 |
+
Gradio Workflow (gradio 6.30.0): generation is a bound Python-function node on
|
| 5 |
+
a drag-and-drop canvas, fed by reference nodes (prompt, size, and every
|
| 6 |
+
sampling knob of the original demo) and writing into an output-gallery subject.
|
| 7 |
+
Edit the graph on the canvas or in workflow.json; the App view and REST API
|
| 8 |
+
stay in sync automatically.
|
| 9 |
+
|
| 10 |
+
Both checkpoints are loaded once at module scope (ZeroGPU packs and transfers
|
| 11 |
+
them when a GPU is actually allocated) and shared by every run of the node.
|
| 12 |
+
"""
|
| 13 |
|
|
|
|
|
|
|
| 14 |
import os
|
| 15 |
+
import secrets
|
| 16 |
+
import tempfile
|
| 17 |
+
import time
|
| 18 |
+
from collections import OrderedDict
|
| 19 |
+
from pathlib import Path
|
| 20 |
|
| 21 |
+
# `spaces` must be imported before anything that initializes CUDA.
|
| 22 |
+
import spaces # ty: ignore[unresolved-import]
|
| 23 |
+
from gradio_client import utils as client_utils
|
| 24 |
|
| 25 |
+
import gradio as gr
|
| 26 |
+
from gradio import workflow_api as _wf_api
|
| 27 |
+
|
| 28 |
+
# Workaround for gradio 6.30.0: the App view hands a subject's value to its
|
| 29 |
+
# output component via `_from_output`, which unwraps a top-level file dict to
|
| 30 |
+
# its path but leaves dicts nested in a LIST untouched — and gr.Gallery only
|
| 31 |
+
# accepts path strings, not file dicts. A gallery subject fed by an fn node
|
| 32 |
+
# (which must return file dicts for the canvas) therefore crashes the App
|
| 33 |
+
# view. Unwrap list items the same way so both views work.
|
| 34 |
+
_orig_from_output = _wf_api._from_output
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def _from_output_unwrapping_lists(value):
|
| 38 |
+
if isinstance(value, list):
|
| 39 |
+
return [_orig_from_output(item) for item in value]
|
| 40 |
+
return _orig_from_output(value)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
_wf_api._from_output = _from_output_unwrapping_lists
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _install_component_server_keepalive():
|
| 47 |
+
"""Keep `/gradio_api/component_server` connections alive during long fn calls.
|
| 48 |
+
|
| 49 |
+
Canvas fn nodes call back through the component_server route — a plain
|
| 50 |
+
POST that awaits the bound function synchronously, unlike App-view
|
| 51 |
+
endpoints, which run through the queue with SSE heartbeats. A MOSS-Image
|
| 52 |
+
generation takes 45-130s, and the silent connection is dropped by the
|
| 53 |
+
proxy chain, surfacing on the canvas as "Connection errored out" (the
|
| 54 |
+
App view is unaffected). Wrap the route so it streams whitespace
|
| 55 |
+
heartbeat bytes — legal leading whitespace in JSON — until the real
|
| 56 |
+
result is ready.
|
| 57 |
+
"""
|
| 58 |
+
import asyncio
|
| 59 |
+
import logging
|
| 60 |
+
|
| 61 |
+
from fastapi import Request
|
| 62 |
+
from fastapi.dependencies.utils import get_dependant
|
| 63 |
+
from fastapi.routing import APIRoute
|
| 64 |
+
from starlette.responses import StreamingResponse
|
| 65 |
+
from starlette.routing import request_response
|
| 66 |
+
from gradio.routes import App as _RoutesApp
|
| 67 |
+
|
| 68 |
+
original_create_app = _RoutesApp.create_app
|
| 69 |
+
|
| 70 |
+
def _wrap(endpoint):
|
| 71 |
+
async def component_server_with_keepalive(request: Request):
|
| 72 |
+
task = asyncio.create_task(endpoint(request))
|
| 73 |
+
|
| 74 |
+
async def body():
|
| 75 |
+
try:
|
| 76 |
+
while not task.done():
|
| 77 |
+
yield b" "
|
| 78 |
+
await asyncio.sleep(10)
|
| 79 |
+
payload = json.dumps(task.result(), default=str).encode()
|
| 80 |
+
except Exception as exc: # keep the canvas error path readable
|
| 81 |
+
payload = json.dumps({"error": str(exc)}).encode()
|
| 82 |
+
yield payload
|
| 83 |
+
|
| 84 |
+
return StreamingResponse(body(), media_type="application/json")
|
| 85 |
|
| 86 |
+
return component_server_with_keepalive
|
| 87 |
|
| 88 |
+
@staticmethod
|
| 89 |
+
def create_app(blocks, *args, **kwargs):
|
| 90 |
+
app = original_create_app(blocks, *args, **kwargs)
|
| 91 |
+
for route in app.routes:
|
| 92 |
+
if isinstance(route, APIRoute) and route.path.rstrip("/").endswith(
|
| 93 |
+
"/component_server"
|
| 94 |
+
):
|
| 95 |
+
route.endpoint = _wrap(route.endpoint)
|
| 96 |
+
dependant = get_dependant(
|
| 97 |
+
path=route.path_format, endpoint=route.endpoint
|
| 98 |
+
)
|
| 99 |
+
# Keep the route-level dependencies (e.g. Depends(login_check)).
|
| 100 |
+
dependant.dependencies = (
|
| 101 |
+
route.dependant.dependencies + dependant.dependencies
|
| 102 |
+
)
|
| 103 |
+
route.dependant = dependant
|
| 104 |
+
route.app = request_response(route.get_route_handler())
|
| 105 |
+
logging.getLogger(__name__).info(
|
| 106 |
+
"component_server keep-alive wrapper installed"
|
| 107 |
+
)
|
| 108 |
+
return app
|
| 109 |
+
|
| 110 |
+
_RoutesApp.create_app = create_app
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
_install_component_server_keepalive()
|
| 114 |
+
|
| 115 |
+
MODEL_IDS = {
|
| 116 |
+
"base": "OpenMOSS-Team/MOSS-Image-Base",
|
| 117 |
+
"sft": "OpenMOSS-Team/MOSS-Image-SFT",
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
# Set MOSS_WORKFLOW_PREVIEW=1 to open the canvas without downloading weights
|
| 121 |
+
# (useful for local UI work on a CPU-only machine).
|
| 122 |
+
PREVIEW = os.environ.get("MOSS_WORKFLOW_PREVIEW") == "1"
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def _load_pipeline(model_id, shared_pipe=None):
|
| 126 |
+
"""Load one MOSS-Image checkpoint with the bundled inference classes.
|
| 127 |
+
|
| 128 |
+
Text encoder, tokenizer and VAE are shared with `shared_pipe` when their
|
| 129 |
+
Hub file hashes match — this is what keeps both checkpoints inside a
|
| 130 |
+
ZeroGPU worker's GPU memory (two full copies do not fit).
|
| 131 |
+
"""
|
| 132 |
+
import torch
|
| 133 |
+
from huggingface_hub import HfApi, snapshot_download
|
| 134 |
+
from pipeline_moss_image import (
|
| 135 |
+
MOSSImagePipeline,
|
| 136 |
+
MOSSImageScheduler,
|
| 137 |
+
MOSSImageTransformer,
|
| 138 |
)
|
| 139 |
+
|
| 140 |
+
info = HfApi().model_info(model_id, files_metadata=True)
|
| 141 |
+
revision = info.sha
|
| 142 |
+
shared_hashes = {
|
| 143 |
+
item.rfilename: item.lfs.sha256 if item.lfs else item.blob_id
|
| 144 |
+
for item in info.siblings
|
| 145 |
+
if item.rfilename.startswith(("text_encoder/", "tokenizer/", "vae/"))
|
| 146 |
+
}
|
| 147 |
+
components = {}
|
| 148 |
+
if shared_pipe is not None:
|
| 149 |
+
for name in ("text_encoder", "tokenizer", "vae"):
|
| 150 |
+
current = {k: v for k, v in shared_hashes.items() if k.startswith(f"{name}/")}
|
| 151 |
+
previous = {
|
| 152 |
+
k: v for k, v in shared_pipe._shared_component_hashes.items()
|
| 153 |
+
if k.startswith(f"{name}/")
|
| 154 |
+
}
|
| 155 |
+
if current and all(current.values()) and current == previous:
|
| 156 |
+
components[name] = getattr(shared_pipe, name)
|
| 157 |
+
snapshot = snapshot_download(
|
| 158 |
+
model_id, revision=revision, ignore_patterns=[f"{name}/*" for name in components]
|
| 159 |
)
|
| 160 |
+
pipe = MOSSImagePipeline.from_pretrained(
|
| 161 |
+
snapshot,
|
| 162 |
+
custom_pipeline=str(Path(__file__).with_name("pipeline_moss_image")),
|
| 163 |
+
trust_remote_code=True,
|
| 164 |
+
transformer=MOSSImageTransformer.from_pretrained(
|
| 165 |
+
snapshot, subfolder="transformer", torch_dtype=torch.bfloat16
|
| 166 |
+
),
|
| 167 |
+
scheduler=MOSSImageScheduler.from_pretrained(snapshot, subfolder="scheduler"),
|
| 168 |
+
local_files_only=True,
|
| 169 |
+
torch_dtype={"default": torch.bfloat16, "vae": torch.float32},
|
| 170 |
+
**components,
|
| 171 |
+
)
|
| 172 |
+
# On ZeroGPU the `spaces` package intercepts this move, packs the weights,
|
| 173 |
+
# and transfers them when a GPU is allocated — module scope costs no GPU time.
|
| 174 |
+
pipe.to("cuda")
|
| 175 |
+
pipe._prompt_cache = OrderedDict()
|
| 176 |
+
pipe._shared_component_hashes = shared_hashes
|
| 177 |
+
return pipe
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
PIPELINES = {}
|
| 181 |
+
if not PREVIEW:
|
| 182 |
+
PIPELINES["base"] = _load_pipeline(MODEL_IDS["base"])
|
| 183 |
+
PIPELINES["sft"] = _load_pipeline(MODEL_IDS["sft"], shared_pipe=PIPELINES["base"])
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def _as_media(path: str) -> dict:
|
| 187 |
+
"""The dict shape the workflow canvas renders media from."""
|
| 188 |
+
return {
|
| 189 |
+
"path": path,
|
| 190 |
+
"url": f"/gradio_api/file={client_utils.encode_file_path(path)}",
|
| 191 |
+
"is_file": True,
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
|
| 195 |
+
def _parse_layers(value: str, name: str) -> tuple:
|
| 196 |
+
"""'22' -> (22,); '' -> (). Same rules as the original demo."""
|
| 197 |
+
if not (value or "").strip():
|
| 198 |
+
return ()
|
| 199 |
+
try:
|
| 200 |
+
layers = tuple(int(item.strip()) for item in value.split(","))
|
| 201 |
+
except ValueError:
|
| 202 |
+
raise gr.Error(
|
| 203 |
+
f"{name} must be comma-separated integer indices, e.g. 22."
|
| 204 |
+
) from None
|
| 205 |
+
if any(layer < 0 for layer in layers) or len(set(layers)) != len(layers):
|
| 206 |
+
raise gr.Error(f"{name} indices must be nonnegative and unique.")
|
| 207 |
+
return layers
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
@spaces.GPU(duration=80)
|
| 211 |
+
def generate(
|
| 212 |
+
prompt: str,
|
| 213 |
+
negative_prompt: str,
|
| 214 |
+
model: str,
|
| 215 |
+
width: float,
|
| 216 |
+
height: float,
|
| 217 |
+
steps: float,
|
| 218 |
+
cfg: float,
|
| 219 |
+
shift: float,
|
| 220 |
+
seed: float,
|
| 221 |
+
num_images: float,
|
| 222 |
+
max_sequence_length: float,
|
| 223 |
+
uncond_skip_layers: str,
|
| 224 |
+
slg_layers: str,
|
| 225 |
+
slg_scale: float,
|
| 226 |
+
slg_start: float,
|
| 227 |
+
slg_stop: float,
|
| 228 |
+
) -> list:
|
| 229 |
+
"""Generate images with MOSS-Image (Base or SFT). Full sampling controls."""
|
| 230 |
+
import torch
|
| 231 |
+
|
| 232 |
+
if PREVIEW:
|
| 233 |
+
raise gr.Error("Preview mode: models are not loaded (MOSS_WORKFLOW_PREVIEW=1).")
|
| 234 |
+
if not (prompt or "").strip():
|
| 235 |
+
raise gr.Error("Enter a prompt.")
|
| 236 |
+
key = (model or "").strip().lower().replace("moss-image-", "")
|
| 237 |
+
if key not in PIPELINES:
|
| 238 |
+
raise gr.Error(f"Unknown model {model!r}. Choose 'base' or 'sft'.")
|
| 239 |
+
width, height, steps, num_images, max_sequence_length = (
|
| 240 |
+
round(width), round(height), round(steps), round(num_images),
|
| 241 |
+
round(max_sequence_length),
|
| 242 |
+
)
|
| 243 |
+
seed = round(seed)
|
| 244 |
+
if width <= 0 or height <= 0 or width % 16 or height % 16:
|
| 245 |
+
raise gr.Error("Width and height must be positive multiples of 16.")
|
| 246 |
+
if steps < 1 or num_images < 1:
|
| 247 |
+
raise gr.Error("Steps and image count must be positive.")
|
| 248 |
+
if not 0 <= cfg or shift <= 0:
|
| 249 |
+
raise gr.Error("CFG must be nonnegative; shift must be positive.")
|
| 250 |
+
if not 1 <= max_sequence_length <= 4096:
|
| 251 |
+
raise gr.Error("Maximum text length must be between 1 and 4096.")
|
| 252 |
+
if slg_scale < 0 or not 0 <= slg_start <= slg_stop <= 1:
|
| 253 |
+
raise gr.Error("Require SLG scale >= 0 and 0 <= SLG start <= SLG stop <= 1.")
|
| 254 |
+
if seed == -1:
|
| 255 |
+
seed = secrets.randbelow(2**32)
|
| 256 |
+
if not 0 <= seed < 2**53:
|
| 257 |
+
raise gr.Error("Seed must be -1 (random) or an integer from 0 to 2^53 - 1.")
|
| 258 |
+
|
| 259 |
+
pipe = PIPELINES[key]
|
| 260 |
+
depth = pipe.transformer.config.num_layers
|
| 261 |
+
skip = _parse_layers(uncond_skip_layers, "Skip layers")
|
| 262 |
+
slg = _parse_layers(slg_layers, "SLG layers")
|
| 263 |
+
if any(layer >= depth for layer in skip + slg):
|
| 264 |
+
raise gr.Error(f"Skip-layer indices must be below {depth}.")
|
| 265 |
+
|
| 266 |
+
pipe.scheduler = type(pipe.scheduler).from_config(pipe.scheduler.config, shift=shift)
|
| 267 |
+
start = time.monotonic()
|
| 268 |
+
try:
|
| 269 |
+
images = pipe(
|
| 270 |
+
prompt=prompt,
|
| 271 |
+
negative_prompt=negative_prompt or "",
|
| 272 |
+
width=width,
|
| 273 |
+
height=height,
|
| 274 |
+
num_inference_steps=steps,
|
| 275 |
+
guidance_scale=float(cfg),
|
| 276 |
+
num_images_per_prompt=num_images,
|
| 277 |
+
max_sequence_length=max_sequence_length,
|
| 278 |
+
uncond_skip_layers=skip,
|
| 279 |
+
slg_layers=slg,
|
| 280 |
+
slg_scale=float(slg_scale),
|
| 281 |
+
slg_start=float(slg_start),
|
| 282 |
+
slg_stop=float(slg_stop),
|
| 283 |
+
generator=torch.Generator("cpu").manual_seed(seed),
|
| 284 |
+
).images
|
| 285 |
+
except torch.cuda.OutOfMemoryError as exc:
|
| 286 |
+
torch.cuda.empty_cache()
|
| 287 |
+
raise gr.Error("GPU memory exhausted. Try fewer or smaller images.") from exc
|
| 288 |
+
|
| 289 |
+
results = []
|
| 290 |
+
batch = os.urandom(8).hex()
|
| 291 |
+
for i, image in enumerate(images):
|
| 292 |
+
path = os.path.join(tempfile.gettempdir(), f"moss_{batch}_{i + 1:02d}.png")
|
| 293 |
+
image.save(path)
|
| 294 |
+
results.append(_as_media(path))
|
| 295 |
+
elapsed = time.monotonic() - start
|
| 296 |
+
status = (
|
| 297 |
+
f"{len(results)} image(s) · {MODEL_IDS[key].rsplit('/', 1)[-1]} "
|
| 298 |
+
f"· seed {seed} · {width}×{height} · {steps} steps "
|
| 299 |
+
f"· CFG {cfg:g} · shift {shift:g} · {elapsed:.1f}s"
|
| 300 |
+
)
|
| 301 |
+
# call_fn maps a fn's returned list/tuple onto the node's output PORTS
|
| 302 |
+
# (one element per port), so the gallery — one port holding many images —
|
| 303 |
+
# must come back as a single element containing the list: [results].
|
| 304 |
+
# Returning `results` bare would hand the gallery port only image #1.
|
| 305 |
+
return [results, status]
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
demo = gr.Workflow(
|
| 309 |
+
graph=str(Path(__file__).with_name("workflow.json")),
|
| 310 |
+
bind={"generate": generate},
|
| 311 |
+
)
|
| 312 |
+
demo.title = "MOSS-Image"
|
| 313 |
+
|
| 314 |
+
# --- Landing-page styling for the auto-generated App view -------------------
|
| 315 |
+
# gr.Workflow builds its App view inside #workflow-app; launch() threads
|
| 316 |
+
# theme/css/head straight through to Blocks.launch(). The hero uses the
|
| 317 |
+
# official lockup SVGs from the original Space (hot-linked, Apache-2.0).
|
| 318 |
+
|
| 319 |
+
_LOCKUP_LIGHT = (
|
| 320 |
+
"https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image"
|
| 321 |
+
"/resolve/main/assets/lockup-light.svg"
|
| 322 |
+
)
|
| 323 |
+
_LOCKUP_DARK = (
|
| 324 |
+
"https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image"
|
| 325 |
+
"/resolve/main/assets/lockup-dark.svg"
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
HEAD = """
|
| 329 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 330 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 331 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 332 |
+
"""
|
| 333 |
+
|
| 334 |
+
# Custom JS for launch(js=...): the gradio frontend executes this function once
|
| 335 |
+
# the page is up. A <script> tag in head= is unreliable for this under gradio 6
|
| 336 |
+
# SSR — the frontend can inject head HTML after the window load event, and
|
| 337 |
+
# innerHTML-inserted <script> tags never execute.
|
| 338 |
+
#
|
| 339 |
+
# Two pieces: the top nav, and the examples strip — the original Space's
|
| 340 |
+
# showcase, hot-linked. Clicking a thumbnail fetches its parameters.json and
|
| 341 |
+
# restores every field of the generated App-view form, replicating the
|
| 342 |
+
# original's click-to-restore.
|
| 343 |
+
JS = """
|
| 344 |
+
() => {
|
| 345 |
+
const ORIGIN = "https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image/resolve/main/assets/images";
|
| 346 |
+
const IDS = Array.from({length: 20}, (_, i) => String(i + 1).padStart(3, "0"));
|
| 347 |
+
|
| 348 |
+
function setField(prefix, value) {
|
| 349 |
+
if (value === undefined || value === null) return;
|
| 350 |
+
const labels = Array.from(document.querySelectorAll('#workflow-app label'));
|
| 351 |
+
const match = labels.find(l => (l.textContent || "").trim().toLowerCase().startsWith(prefix));
|
| 352 |
+
if (!match) return;
|
| 353 |
+
let root = match;
|
| 354 |
+
for (let i = 0; i < 6 && root; i++) {
|
| 355 |
+
const input = root.querySelector('input:not([type="hidden"]), textarea');
|
| 356 |
+
if (input) {
|
| 357 |
+
input.value = value;
|
| 358 |
+
input.dispatchEvent(new Event("input", {bubbles: true}));
|
| 359 |
+
input.dispatchEvent(new Event("change", {bubbles: true}));
|
| 360 |
+
return;
|
| 361 |
+
}
|
| 362 |
+
root = root.parentElement;
|
| 363 |
+
}
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
function applyRecord(rec) {
|
| 367 |
+
const p = rec.parameters || {};
|
| 368 |
+
let uncond = p.uncond_skip_layers ?? "";
|
| 369 |
+
let slgLayers = p.slg_layers ?? "";
|
| 370 |
+
let slgScale = p.slg_scale ?? 0;
|
| 371 |
+
if ((rec.schema_version ?? 2) === 1) {
|
| 372 |
+
// v1 records called negative-branch skipping "slg_layers".
|
| 373 |
+
uncond = (p.slg ?? true) ? (p.slg_layers ?? "") : "";
|
| 374 |
+
slgLayers = "";
|
| 375 |
+
slgScale = 0;
|
| 376 |
+
}
|
| 377 |
+
const model = (rec.model_repo || rec.model || "").toLowerCase().includes("sft") ? "sft" : "base";
|
| 378 |
+
setField("negative", p.negative_prompt ?? "");
|
| 379 |
+
setField("prompt", p.prompt ?? "");
|
| 380 |
+
setField("model", model);
|
| 381 |
+
setField("width", p.width);
|
| 382 |
+
setField("height", p.height);
|
| 383 |
+
setField("steps", p.steps);
|
| 384 |
+
setField("cfg", p.cfg);
|
| 385 |
+
setField("shift", p.shift);
|
| 386 |
+
setField("seed", p.seed ?? -1);
|
| 387 |
+
setField("images", p.num_images ?? 1);
|
| 388 |
+
setField("max text", p.max_sequence_length ?? 4096);
|
| 389 |
+
setField("skip start", p.slg_start ?? 0);
|
| 390 |
+
setField("skip stop", p.slg_stop ?? 1);
|
| 391 |
+
setField("skip layers", uncond);
|
| 392 |
+
setField("slg layers", slgLayers);
|
| 393 |
+
setField("slg scale", slgScale);
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
const recordCache = {};
|
| 397 |
+
function fetchRecord(id) {
|
| 398 |
+
if (!recordCache[id]) {
|
| 399 |
+
recordCache[id] = fetch(`${ORIGIN}/${id}/parameters.json`)
|
| 400 |
+
.then(r => (r.ok ? r.json() : null))
|
| 401 |
+
.catch(() => null);
|
| 402 |
+
}
|
| 403 |
+
return recordCache[id];
|
| 404 |
+
}
|
| 405 |
+
|
| 406 |
+
function buildStrip(app) {
|
| 407 |
+
if (document.getElementById('moss-examples')) return true;
|
| 408 |
+
const wrap = document.createElement('div');
|
| 409 |
+
wrap.id = 'moss-examples';
|
| 410 |
+
const title = document.createElement('div');
|
| 411 |
+
title.className = 'mx-title';
|
| 412 |
+
title.textContent = 'Examples · click to restore prompt & settings';
|
| 413 |
+
wrap.appendChild(title);
|
| 414 |
+
const row = document.createElement('div');
|
| 415 |
+
row.className = 'mx-row';
|
| 416 |
+
IDS.forEach((id, i) => {
|
| 417 |
+
const fig = document.createElement('button');
|
| 418 |
+
fig.className = 'mx-item';
|
| 419 |
+
fig.type = 'button';
|
| 420 |
+
fig.title = 'Load this example into the form';
|
| 421 |
+
const img = document.createElement('img');
|
| 422 |
+
img.loading = 'lazy';
|
| 423 |
+
img.alt = `Example ${i + 1}`;
|
| 424 |
+
img.src = `${ORIGIN}/${id}/image.webp`;
|
| 425 |
+
img.onerror = () => fig.remove();
|
| 426 |
+
fig.appendChild(img);
|
| 427 |
+
fig.addEventListener('click', () => {
|
| 428 |
+
fetchRecord(id).then(rec => { if (rec) applyRecord(rec); });
|
| 429 |
+
});
|
| 430 |
+
row.appendChild(fig);
|
| 431 |
+
});
|
| 432 |
+
wrap.appendChild(row);
|
| 433 |
+
app.prepend(wrap);
|
| 434 |
+
return true;
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
if (!document.getElementById('moss-nav')) {
|
| 438 |
+
const nav = document.createElement('nav');
|
| 439 |
+
nav.id = 'moss-nav';
|
| 440 |
+
nav.innerHTML = `
|
| 441 |
+
<span class="moss-brand">MOSS-Image</span>
|
| 442 |
+
<span class="moss-links">
|
| 443 |
+
<a href="https://huggingface.co/OpenMOSS-Team/MOSS-Image-Base" target="_blank" rel="noopener">Base</a>
|
| 444 |
+
<a href="https://huggingface.co/OpenMOSS-Team/MOSS-Image-SFT" target="_blank" rel="noopener">SFT</a>
|
| 445 |
+
<a href="https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image" target="_blank" rel="noopener">Original Demo</a>
|
| 446 |
+
<a href="https://github.com/OpenMOSS/MOSS-Image" target="_blank" rel="noopener">GitHub ↗</a>
|
| 447 |
+
</span>`;
|
| 448 |
+
document.body.prepend(nav);
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
// #workflow-app appears when the frontend mounts; poll briefly for it.
|
| 452 |
+
let tries = 0;
|
| 453 |
+
const timer = setInterval(() => {
|
| 454 |
+
const app = document.getElementById('workflow-app');
|
| 455 |
+
if ((app && buildStrip(app)) || ++tries > 75) clearInterval(timer);
|
| 456 |
+
}, 400);
|
| 457 |
+
}
|
| 458 |
+
"""
|
| 459 |
+
|
| 460 |
+
CSS = f"""
|
| 461 |
+
body, .gradio-container {{
|
| 462 |
+
font-family: 'Inter', ui-sans-serif, system-ui, sans-serif !important;
|
| 463 |
+
}}
|
| 464 |
+
.gradio-container {{
|
| 465 |
+
background: linear-gradient(180deg, #eef3fc 0%, #ffffff 460px) !important;
|
| 466 |
+
}}
|
| 467 |
+
.dark .gradio-container {{
|
| 468 |
+
background: linear-gradient(180deg, #0d1526 0%, #0b0f19 460px) !important;
|
| 469 |
+
}}
|
| 470 |
+
|
| 471 |
+
#moss-nav {{
|
| 472 |
+
display: flex;
|
| 473 |
+
align-items: center;
|
| 474 |
+
justify-content: space-between;
|
| 475 |
+
padding: 14px 24px 0;
|
| 476 |
+
max-width: 1200px;
|
| 477 |
+
margin: 0 auto;
|
| 478 |
+
font-size: 14px;
|
| 479 |
+
}}
|
| 480 |
+
#moss-nav .moss-brand {{
|
| 481 |
+
font-weight: 800;
|
| 482 |
+
font-size: 16px;
|
| 483 |
+
letter-spacing: -0.01em;
|
| 484 |
+
background: linear-gradient(90deg, #3b7fe0, #4aa5f2, #7fe6fb);
|
| 485 |
+
-webkit-background-clip: text;
|
| 486 |
+
background-clip: text;
|
| 487 |
+
color: transparent;
|
| 488 |
+
}}
|
| 489 |
+
#moss-nav .moss-links a {{
|
| 490 |
+
color: #475569;
|
| 491 |
+
text-decoration: none;
|
| 492 |
+
font-weight: 500;
|
| 493 |
+
margin-left: 22px;
|
| 494 |
+
}}
|
| 495 |
+
#moss-nav .moss-links a:hover {{ color: #2563eb; }}
|
| 496 |
+
.dark #moss-nav .moss-links a {{ color: #94a3b8; }}
|
| 497 |
+
.dark #moss-nav .moss-links a:hover {{ color: #7fe6fb; }}
|
| 498 |
+
|
| 499 |
+
#workflow-app::before {{
|
| 500 |
+
content: "MOSS-Image: Text-to-Image Generation\\Awith Native Layout Control";
|
| 501 |
+
white-space: pre-wrap;
|
| 502 |
+
display: block;
|
| 503 |
+
text-align: center;
|
| 504 |
+
font-weight: 800;
|
| 505 |
+
font-size: clamp(24px, 4.2vw, 40px);
|
| 506 |
+
line-height: 1.15;
|
| 507 |
+
letter-spacing: -0.02em;
|
| 508 |
+
color: #0b1220;
|
| 509 |
+
max-width: 860px;
|
| 510 |
+
margin: 0 auto 12px;
|
| 511 |
+
padding: 158px 16px 26px;
|
| 512 |
+
background: url("{_LOCKUP_LIGHT}") no-repeat top 26px center / min(480px, 78%) auto;
|
| 513 |
+
}}
|
| 514 |
+
.dark #workflow-app::before {{
|
| 515 |
+
color: #f1f5f9;
|
| 516 |
+
background-image: url("{_LOCKUP_DARK}");
|
| 517 |
+
}}
|
| 518 |
+
|
| 519 |
+
#workflow-app::after {{
|
| 520 |
+
content: "Models by the OpenMOSS Team · MOSS-Image-Base & MOSS-Image-SFT (Apache-2.0) · Rebuilt as a Gradio Workflow";
|
| 521 |
+
display: block;
|
| 522 |
+
text-align: center;
|
| 523 |
+
font-size: 13px;
|
| 524 |
+
color: #64748b;
|
| 525 |
+
padding: 8px 16px 36px;
|
| 526 |
+
}}
|
| 527 |
+
.dark #workflow-app::after {{ color: #94a3b8; }}
|
| 528 |
+
|
| 529 |
+
/* Examples strip (injected by the head script; hot-linked from the original Space). */
|
| 530 |
+
#moss-examples {{
|
| 531 |
+
max-width: 960px;
|
| 532 |
+
margin: 0 auto 18px;
|
| 533 |
+
padding: 0 16px;
|
| 534 |
+
}}
|
| 535 |
+
#moss-examples .mx-title {{
|
| 536 |
+
text-align: center;
|
| 537 |
+
font-size: 13px;
|
| 538 |
+
font-weight: 600;
|
| 539 |
+
letter-spacing: 0.02em;
|
| 540 |
+
color: #64748b;
|
| 541 |
+
margin-bottom: 10px;
|
| 542 |
+
}}
|
| 543 |
+
.dark #moss-examples .mx-title {{ color: #94a3b8; }}
|
| 544 |
+
#moss-examples .mx-row {{
|
| 545 |
+
display: flex;
|
| 546 |
+
gap: 10px;
|
| 547 |
+
overflow-x: auto;
|
| 548 |
+
padding-bottom: 6px;
|
| 549 |
+
scrollbar-width: thin;
|
| 550 |
+
}}
|
| 551 |
+
#moss-examples .mx-item {{
|
| 552 |
+
flex: 0 0 auto;
|
| 553 |
+
height: 128px;
|
| 554 |
+
padding: 0;
|
| 555 |
+
border: 2px solid transparent;
|
| 556 |
+
border-radius: 12px;
|
| 557 |
+
overflow: hidden;
|
| 558 |
+
cursor: pointer;
|
| 559 |
+
background: #e2e8f0;
|
| 560 |
+
transition: border-color 0.15s ease, transform 0.15s ease;
|
| 561 |
+
}}
|
| 562 |
+
#moss-examples .mx-item:hover {{
|
| 563 |
+
border-color: #2563eb;
|
| 564 |
+
transform: translateY(-2px);
|
| 565 |
+
}}
|
| 566 |
+
#moss-examples .mx-item img {{
|
| 567 |
+
height: 128px;
|
| 568 |
+
width: auto;
|
| 569 |
+
display: block;
|
| 570 |
+
}}
|
| 571 |
+
|
| 572 |
+
/* Soften the generated form into landing-page cards. */
|
| 573 |
+
#workflow-app .form, #workflow-app .panel, #workflow-app .block {{
|
| 574 |
+
border-radius: 14px !important;
|
| 575 |
+
}}
|
| 576 |
+
#workflow-app button.primary, #workflow-app button.lg {{
|
| 577 |
+
border-radius: 10px !important;
|
| 578 |
+
}}
|
| 579 |
+
"""
|
| 580 |
|
| 581 |
if __name__ == "__main__":
|
| 582 |
+
demo.launch(
|
| 583 |
+
theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"),
|
| 584 |
+
css=CSS,
|
| 585 |
+
head=HEAD,
|
| 586 |
+
js=JS,
|
| 587 |
+
)
|
|
@@ -6,3 +6,4 @@ safetensors>=0.6
|
|
| 6 |
Pillow>=10
|
| 7 |
spaces>=0.51.3,<0.52
|
| 8 |
gradio==6.30.0
|
|
|
|
|
|
| 6 |
Pillow>=10
|
| 7 |
spaces>=0.51.3,<0.52
|
| 8 |
gradio==6.30.0
|
| 9 |
+
gradio_client>=1.13
|
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema_version": "2",
|
| 3 |
+
"name": "MOSS-Image Text-to-Image",
|
| 4 |
+
"references": [
|
| 5 |
+
{
|
| 6 |
+
"id": "ref_prompt", "label": "Prompt", "role": "reference", "asset_type": "text",
|
| 7 |
+
"inputs": [{"id": "in", "label": "Text", "type": "text"}],
|
| 8 |
+
"outputs": [{"id": "out", "label": "Text", "type": "text"}],
|
| 9 |
+
"x": 60, "y": 60, "width": 280,
|
| 10 |
+
"data": {"out": "A young anime woman, Kazusa from Blue Archive, stands still at the edge of a crowded Tokyo crosswalk outside a busy train station on a damp, overcast dusk. Her short dark hair with soft magenta inner highlights frames her face, cat ears perked slightly beneath her distinctive glowing pink halo. She wears an oversized charcoal wool coat over a beige knit hoodie, a short pleated black skirt, black crew socks, and scuffed loafers, with a canvas tote bag slung over her shoulder. She holds a wired earphone in one hand as if pausing her music, looking slightly sideways with a quiet, unguarded expression while the evening rush hour flows around her.\n\nIn the immediate foreground, a heavily motion-blurred commuter in a dark coat walks right past the lens, partially framing the edge of the shot and giving it the raw, accidental feel of a candid street photograph. Around and behind Kazusa, a bustling crowd of out-of-focus pedestrians moves across the wet asphalt crosswalk under the muted glow of neon department store signs, traffic signals, and concrete office buildings. The image blends Kazusa’s anime appearance seamlessly into a gritty, photorealistic modern city environment, using natural ambient dusk lighting, shallow depth of field, motion blur on the passing crowd, muted cool tones, and a cinematic sense of stillness amid urban chaos."}
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"id": "ref_negative", "label": "Negative Prompt", "role": "reference", "asset_type": "text",
|
| 14 |
+
"inputs": [{"id": "in", "label": "Text", "type": "text"}],
|
| 15 |
+
"outputs": [{"id": "out", "label": "Text", "type": "text"}],
|
| 16 |
+
"x": 60, "y": 250, "width": 280,
|
| 17 |
+
"data": {"out": "watermark, bad anatomy"}
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"id": "ref_model", "label": "Model (base or sft)", "role": "reference", "asset_type": "text",
|
| 21 |
+
"inputs": [{"id": "in", "label": "Text", "type": "text"}],
|
| 22 |
+
"outputs": [{"id": "out", "label": "Text", "type": "text"}],
|
| 23 |
+
"x": 60, "y": 440, "width": 280,
|
| 24 |
+
"data": {"out": "base"}
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"id": "ref_width", "label": "Width", "role": "reference", "asset_type": "number",
|
| 28 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 29 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 30 |
+
"x": 400, "y": 60, "width": 200,
|
| 31 |
+
"data": {"out": 1184}
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
"id": "ref_height", "label": "Height", "role": "reference", "asset_type": "number",
|
| 35 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 36 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 37 |
+
"x": 400, "y": 210, "width": 200,
|
| 38 |
+
"data": {"out": 1776}
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"id": "ref_steps", "label": "Steps", "role": "reference", "asset_type": "number",
|
| 42 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 43 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 44 |
+
"x": 400, "y": 360, "width": 200,
|
| 45 |
+
"data": {"out": 50}
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"id": "ref_cfg", "label": "CFG", "role": "reference", "asset_type": "number",
|
| 49 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 50 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 51 |
+
"x": 400, "y": 510, "width": 200,
|
| 52 |
+
"data": {"out": 3}
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"id": "ref_shift", "label": "Shift", "role": "reference", "asset_type": "number",
|
| 56 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 57 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 58 |
+
"x": 400, "y": 660, "width": 200,
|
| 59 |
+
"data": {"out": 5}
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
"id": "ref_seed", "label": "Seed (-1 = random)", "role": "reference", "asset_type": "number",
|
| 63 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 64 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 65 |
+
"x": 60, "y": 630, "width": 200,
|
| 66 |
+
"data": {"out": 3537712559}
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"id": "ref_num_images", "label": "Images", "role": "reference", "asset_type": "number",
|
| 70 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 71 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 72 |
+
"x": 60, "y": 790, "width": 200,
|
| 73 |
+
"data": {"out": 1}
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"id": "ref_max_seq", "label": "Max Text Length", "role": "reference", "asset_type": "number",
|
| 77 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 78 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 79 |
+
"x": 660, "y": 60, "width": 200,
|
| 80 |
+
"data": {"out": 4096}
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"id": "ref_uncond_skip", "label": "Skip Layers (uncond, empty = off)", "role": "reference", "asset_type": "text",
|
| 84 |
+
"inputs": [{"id": "in", "label": "Text", "type": "text"}],
|
| 85 |
+
"outputs": [{"id": "out", "label": "Text", "type": "text"}],
|
| 86 |
+
"x": 660, "y": 210, "width": 200,
|
| 87 |
+
"data": {"out": "22"}
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"id": "ref_slg_layers", "label": "SLG Layers (empty = off)", "role": "reference", "asset_type": "text",
|
| 91 |
+
"inputs": [{"id": "in", "label": "Text", "type": "text"}],
|
| 92 |
+
"outputs": [{"id": "out", "label": "Text", "type": "text"}],
|
| 93 |
+
"x": 660, "y": 360, "width": 200,
|
| 94 |
+
"data": {"out": ""}
|
| 95 |
+
},
|
| 96 |
+
{
|
| 97 |
+
"id": "ref_slg_scale", "label": "SLG Scale", "role": "reference", "asset_type": "number",
|
| 98 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 99 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 100 |
+
"x": 660, "y": 510, "width": 200,
|
| 101 |
+
"data": {"out": 0}
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"id": "ref_slg_start", "label": "Skip Start", "role": "reference", "asset_type": "number",
|
| 105 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 106 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 107 |
+
"x": 660, "y": 660, "width": 200,
|
| 108 |
+
"data": {"out": 0}
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"id": "ref_slg_stop", "label": "Skip Stop", "role": "reference", "asset_type": "number",
|
| 112 |
+
"inputs": [{"id": "in", "label": "Number", "type": "number"}],
|
| 113 |
+
"outputs": [{"id": "out", "label": "Number", "type": "number"}],
|
| 114 |
+
"x": 660, "y": 810, "width": 200,
|
| 115 |
+
"data": {"out": 1}
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"id": "ref_example_image", "label": "Example 007 Result (prefill target)", "role": "reference", "asset_type": "image",
|
| 119 |
+
"inputs": [{"id": "in", "label": "Image", "type": "image"}],
|
| 120 |
+
"outputs": [{"id": "out", "label": "Image", "type": "image"}],
|
| 121 |
+
"x": 1300, "y": 60, "width": 340,
|
| 122 |
+
"data": {"out": {"url": "https://huggingface.co/spaces/OpenMOSS-Team/MOSS-Image/resolve/main/assets/images/007/image.webp", "name": "image.webp", "mime": "image/webp"}}
|
| 123 |
+
}
|
| 124 |
+
],
|
| 125 |
+
"operators": [
|
| 126 |
+
{
|
| 127 |
+
"id": "op_generate", "label": "MOSS-Image Generate", "role": "operator",
|
| 128 |
+
"kind": "fn", "fn": "generate",
|
| 129 |
+
"inputs": [
|
| 130 |
+
{"id": "prompt", "label": "Prompt", "type": "text", "required": true},
|
| 131 |
+
{"id": "negative_prompt", "label": "Negative Prompt", "type": "text", "required": false},
|
| 132 |
+
{"id": "model", "label": "Model", "type": "text", "required": true},
|
| 133 |
+
{"id": "width", "label": "Width", "type": "number", "required": true},
|
| 134 |
+
{"id": "height", "label": "Height", "type": "number", "required": true},
|
| 135 |
+
{"id": "steps", "label": "Steps", "type": "number", "required": true},
|
| 136 |
+
{"id": "cfg", "label": "CFG", "type": "number", "required": true},
|
| 137 |
+
{"id": "shift", "label": "Shift", "type": "number", "required": true},
|
| 138 |
+
{"id": "seed", "label": "Seed", "type": "number", "required": true},
|
| 139 |
+
{"id": "num_images", "label": "Images", "type": "number", "required": true},
|
| 140 |
+
{"id": "max_sequence_length", "label": "Max Text Length", "type": "number", "required": true},
|
| 141 |
+
{"id": "uncond_skip_layers", "label": "Skip Layers", "type": "text", "required": false},
|
| 142 |
+
{"id": "slg_layers", "label": "SLG Layers", "type": "text", "required": false},
|
| 143 |
+
{"id": "slg_scale", "label": "SLG Scale", "type": "number", "required": true},
|
| 144 |
+
{"id": "slg_start", "label": "Skip Start", "type": "number", "required": true},
|
| 145 |
+
{"id": "slg_stop", "label": "Skip Stop", "type": "number", "required": true}
|
| 146 |
+
],
|
| 147 |
+
"outputs": [
|
| 148 |
+
{"id": "out_0", "label": "Images", "type": "gallery"},
|
| 149 |
+
{"id": "out_1", "label": "Status", "type": "text"}
|
| 150 |
+
],
|
| 151 |
+
"x": 940, "y": 380, "width": 280,
|
| 152 |
+
"data": {}
|
| 153 |
+
}
|
| 154 |
+
],
|
| 155 |
+
"subjects": [
|
| 156 |
+
{
|
| 157 |
+
"id": "sub_gallery", "label": "Generated Images", "role": "subject", "asset_type": "gallery",
|
| 158 |
+
"inputs": [{"id": "in", "label": "Images", "type": "gallery"}],
|
| 159 |
+
"outputs": [{"id": "out", "label": "Images", "type": "gallery"}],
|
| 160 |
+
"x": 1300, "y": 380, "width": 340,
|
| 161 |
+
"data": {}
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"id": "sub_status", "label": "Status", "role": "subject", "asset_type": "text",
|
| 165 |
+
"inputs": [{"id": "in", "label": "Status", "type": "text"}],
|
| 166 |
+
"outputs": [{"id": "out", "label": "Status", "type": "text"}],
|
| 167 |
+
"x": 1300, "y": 620, "width": 340,
|
| 168 |
+
"data": {}
|
| 169 |
+
}
|
| 170 |
+
],
|
| 171 |
+
"edges": [
|
| 172 |
+
{"id": "e_prompt", "from_node_id": "ref_prompt", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "prompt", "type": "text"},
|
| 173 |
+
{"id": "e_negative", "from_node_id": "ref_negative", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "negative_prompt", "type": "text"},
|
| 174 |
+
{"id": "e_model", "from_node_id": "ref_model", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "model", "type": "text"},
|
| 175 |
+
{"id": "e_width", "from_node_id": "ref_width", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "width", "type": "number"},
|
| 176 |
+
{"id": "e_height", "from_node_id": "ref_height", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "height", "type": "number"},
|
| 177 |
+
{"id": "e_steps", "from_node_id": "ref_steps", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "steps", "type": "number"},
|
| 178 |
+
{"id": "e_cfg", "from_node_id": "ref_cfg", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "cfg", "type": "number"},
|
| 179 |
+
{"id": "e_shift", "from_node_id": "ref_shift", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "shift", "type": "number"},
|
| 180 |
+
{"id": "e_seed", "from_node_id": "ref_seed", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "seed", "type": "number"},
|
| 181 |
+
{"id": "e_num_images", "from_node_id": "ref_num_images", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "num_images", "type": "number"},
|
| 182 |
+
{"id": "e_max_seq", "from_node_id": "ref_max_seq", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "max_sequence_length", "type": "number"},
|
| 183 |
+
{"id": "e_uncond_skip", "from_node_id": "ref_uncond_skip", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "uncond_skip_layers", "type": "text"},
|
| 184 |
+
{"id": "e_slg_layers", "from_node_id": "ref_slg_layers", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "slg_layers", "type": "text"},
|
| 185 |
+
{"id": "e_slg_scale", "from_node_id": "ref_slg_scale", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "slg_scale", "type": "number"},
|
| 186 |
+
{"id": "e_slg_start", "from_node_id": "ref_slg_start", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "slg_start", "type": "number"},
|
| 187 |
+
{"id": "e_slg_stop", "from_node_id": "ref_slg_stop", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "slg_stop", "type": "number"},
|
| 188 |
+
{"id": "e_gallery", "from_node_id": "op_generate", "from_port_id": "out_0", "to_node_id": "sub_gallery", "to_port_id": "in", "type": "gallery"},
|
| 189 |
+
{"id": "e_status", "from_node_id": "op_generate", "from_port_id": "out_1", "to_node_id": "sub_status", "to_port_id": "in", "type": "text"}
|
| 190 |
+
]
|
| 191 |
+
}
|