GLM 5.2 commited on
Commit
e22cb7b
·
1 Parent(s): cbc1340

Use two-Space architecture: workflow frontend calls akaliq backend

Browse files

mrfakename/Z-Image-Turbo becomes a pure gr.Workflow frontend that calls
the akaliq/Z-Image-Turbo Blocks backend via its /generate_image
endpoint. The canvas's call_space server function forwards the visitor's
HF OAuth token through gradio_client's call_space path, so signed-in
PRO users on mrfakename get their PRO quota metered against the
backend Space rather than being throttled as anonymous on a saturated
shared single-Space pool.

- workflow.json: operator kind converted fn -> space, target
akaliq/Z-Image-Turbo with endpoint /generate_image. Same 6 inputs
(prompt, height, width, num_inference_steps, seed, randomize_seed)
and 2 outputs (image, seed_used); same references, subjects, edges.
- app.py: dropped the local pipeline, the bound generate_image, the
PR-91 LocalContext wrapper, and the duration/error helpers. Pure
frontend: gr.Workflow(graph=workflow.json).launch().

The bound-fn path with @spaces.GPU + LocalContext.request.set was
unable to recover PRO metering on a shared ZeroGPU Space because the
session-to-X-IP-Token binding does not propagate account context on the
/component_server route. The cross-Space call_space path does, because
mrfakename's canvas forwards auth.token (the visitor's HF OAuth access
token) via gradio_client.Client(token=...). The backend's @spaces.GPU
reads the resulting account-bound token through its standard
per-request flow and applies PRO's 40-min / highest queue priority.

Files changed (2) hide show
  1. app.py +7 -203
  2. workflow.json +248 -46
app.py CHANGED
@@ -1,208 +1,12 @@
1
- import os
2
- import tempfile
3
- from contextlib import contextmanager
4
-
5
- import spaces
6
- import torch
7
  import gradio as gr
8
- from gradio.context import LocalContext
9
- from diffusers import DiffusionPipeline
10
-
11
- # Load the pipeline once at startup. The Space is a ZeroGPU space, so the
12
- # model weights stay resident and `@spaces.GPU` allocates a worker per call.
13
- print("Loading Z-Image-Turbo pipeline...")
14
- pipe = DiffusionPipeline.from_pretrained(
15
- "Tongyi-MAI/Z-Image-Turbo",
16
- torch_dtype=torch.bfloat16,
17
- low_cpu_mem_usage=False,
18
- )
19
- pipe.to("cuda")
20
- print("Pipeline loaded!")
21
-
22
-
23
- def _save_image(image) -> dict:
24
- """Mirror of `gradio.workflow._save_tmp`: serialize a PIL.Image as a JSON
25
- pointer the canvas can render. `Workflow.launch()` already adds the
26
- tempdir to `allowed_paths`, so the /gradio_api/file=… URL resolves."""
27
- path = os.path.join(
28
- tempfile.gettempdir(), f"zimage_{os.urandom(8).hex()}.png"
29
- )
30
- image.save(path)
31
- return {
32
- "path": path,
33
- "url": f"/gradio_api/file={path}",
34
- "orig_name": "zimage.png",
35
- "mime_type": "image/png",
36
- }
37
-
38
-
39
- def _estimate_duration(prompt, height, width, num_inference_steps, seed, randomize_seed) -> int:
40
- """Rough wall-clock estimate (seconds) for one Z-Image-Turbo call.
41
-
42
- ZeroGPU's default per-call duration is 60s. Requesting less than you need
43
- *raises* queue priority (shorter tasks get scheduled sooner) and — crucially
44
- for a busy shared Space — frees the GPU slot for the next visitor far
45
- sooner than holding it for a full minute, so far fewer users hit the
46
- Space's "reached its GPU limit" rejection. Scaled by pixel count and steps;
47
- clamped to a small floor/ceiling so a runaway slider can't starve the queue
48
- or under-budget a big call.
49
-
50
- Signature mirrors the GPU function exactly because @spaces.GPU passes the
51
- decorated function's inputs straight through to the duration callable.
52
-
53
- See: https://huggingface.co/docs/hub/en/spaces-zerogpu#duration-management
54
- """
55
- pixels = max(int(height), 1) * max(int(width), 1)
56
- # ~0.4s/step at 1024^2, linear-ish in pixels. Big calls still need headroom.
57
- per_step = 0.4 * (pixels / (1024 * 1024))
58
- seconds = int(num_inference_steps) * per_step
59
- return max(20, min(int(seconds) + 15, 120))
60
-
61
-
62
- def _friendly_gpu_error(err: Exception) -> str:
63
- """Turn ZeroGPU's terse allocator rejections into a clear, honest message.
64
-
65
- 'Space app has reached its GPU limit' is a *Space-level* capacity rejection
66
- (the shared ZeroGPU pool is saturated), not a per-user quota wall — it
67
- reproduces regardless of inputs, account tier, or sign-in state. Don't make
68
- an upgrade claim whose truth we can't pin down, so the message is neutral:
69
- shared GPU at capacity, retry shortly.
70
- """
71
- msg = (str(err) or "").lower()
72
- capacity_hints = (
73
- "gpu limit", "reached its gpu limit", "gpu quota", "out of quota",
74
- "quota", "no gpu", "could not allocate", "gpu is busy", "too many",
75
- "concurrent",
76
- )
77
- if any(h in msg for h in capacity_hints):
78
- return (
79
- "⛔ This demo's shared GPU is at capacity right now — it's not a "
80
- "problem with your prompt or your account. The GPU pool is fully "
81
- "booked by other users at the moment. Please wait a minute and "
82
- "retry; demand clears between bursts."
83
- )
84
- if "out of memory" in msg or "oom" in msg or "cuda" in msg:
85
- return (
86
- "💥 Image generation ran out of GPU memory. Try a smaller "
87
- "Height/Width or fewer Inference Steps, then retry."
88
- )
89
- return (
90
- "⚠️ Image generation failed. Please try again in a moment — if it "
91
- "keeps happening, simplify your prompt or lower the resolution."
92
- )
93
-
94
-
95
- @contextmanager
96
- def _with_request_context(request):
97
- """Publish the incoming request to Gradio's LocalContext for the duration
98
- of the GPU call so the ZeroGPU allocator can read the caller's X-IP-Token.
99
-
100
- ZeroGPU meters per-user quota via the X-IP-Token header the HF Hub injects
101
- on every request to a Space; the allocator/gradio_client reads it from
102
- LocalContext.request (see gradio_client.add_zero_gpu_headers). Normal event
103
- handlers get this for free because Gradio sets LocalContext.request around
104
- the handler. The workflow's call_fn route hands us the request via
105
- _special_args (when we type-hint gr.Request) but does *not* publish it to
106
- LocalContext, so without this wrap every workflow call runs with the token
107
- missing and is metered as unauthenticated (2 min/day, lowest priority) —
108
- which saturates a busy Space for everyone, signed-in PRO users included.
109
- This restores per-user metering. Idempotent: set()/reset() restores prior.
110
- """
111
- if request is None:
112
- yield
113
- return
114
- token = LocalContext.request.set(request)
115
- try:
116
- yield
117
- finally:
118
- LocalContext.request.reset(token)
119
-
120
-
121
- @spaces.GPU(duration=_estimate_duration)
122
- def _generate_image_gpu(
123
- prompt: str,
124
- height: int,
125
- width: int,
126
- num_inference_steps: int,
127
- seed: int,
128
- randomize_seed: bool,
129
- ):
130
- """The GPU-decorated worker. Runs only under a ZeroGPU allocation; the
131
- allocator raises *before* this body if no GPU can be granted, which is why
132
- the rewording lives in the plain `generate_image` wrapper below, not here.
133
- """
134
- if not prompt or not prompt.strip():
135
- raise gr.Error("Please enter a prompt.")
136
-
137
- if randomize_seed:
138
- seed = torch.randint(0, 2**32 - 1, (1,)).item()
139
-
140
- generator = torch.Generator("cuda").manual_seed(int(seed))
141
- image = pipe(
142
- prompt=prompt,
143
- height=int(height),
144
- width=int(width),
145
- num_inference_steps=int(num_inference_steps),
146
- guidance_scale=0.0,
147
- generator=generator,
148
- ).images[0]
149
-
150
- return _save_image(image), int(seed)
151
-
152
-
153
- def generate_image(
154
- prompt: str,
155
- height: int,
156
- width: int,
157
- num_inference_steps: int,
158
- seed: int,
159
- randomize_seed: bool,
160
- request: gr.Request | None = None,
161
- ):
162
- """Workflow-facing wrapper around the GPU worker.
163
-
164
- Bound to the canvas as a `fn` operator node — the workflow calls this
165
- Python function directly server-side, so the entire pipeline (frontend +
166
- ZeroGPU) lives in a single Space. The `request` param is injected by Gradio
167
- (`_special_args`) and is *not* exposed as a canvas port (injected params are
168
- excluded from the workflow port schema), so workflow.json topology is
169
- unchanged.
170
-
171
- This wrapper does two jobs:
172
- 1. Publish `request` to LocalContext (via `_with_request_context`) so the
173
- @spaces.GPU allocator sees the caller's X-IP-Token and meters the call
174
- to the actual user — restoring per-user ZeroGPU quota/priority.
175
- 2. Catch rejections from the allocator (which fire before the worker body
176
- runs) and reword them into a clear, honest user-facing message.
177
-
178
- Returns (image_dict, seed_used). The image is serialized to a /gradio_api
179
- file URL so JSON serialization across the fn bridge succeeds; the
180
- executor's `fromGradioOutput` turns the dict back into an image port value.
181
- """
182
- try:
183
- with _with_request_context(request):
184
- return _generate_image_gpu(
185
- prompt, height, width, num_inference_steps, seed, randomize_seed
186
- )
187
- except gr.Error:
188
- # Already a user-facing validation message (e.g. empty prompt) — pass
189
- # it through unchanged.
190
- raise
191
- except Exception as e:
192
- # Allocator rejection (GPU limit / quota / OOM / etc.) — reword.
193
- raise gr.Error(_friendly_gpu_error(e)) from e
194
 
 
 
 
 
 
195
 
196
- # The workflow (workflow.json) wires `generate_image` as a `fn` operator:
197
- # Prompt, Height, Width, Inference Steps, Seed, Randomize Seed ─▶
198
- # generate_image (fn operator, kind="fn") ─▶ Output Image, Seed Used
199
- #
200
- # On a Space with `hf_oauth: true`, visiting the canvas runs this function
201
- # under a ZeroGPU worker using each visitor's own HF token.
202
- demo = gr.Workflow(
203
- graph="workflow.json",
204
- bind={"generate_image": generate_image},
205
- )
206
 
207
  if __name__ == "__main__":
208
- demo.launch()
 
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ # Pure-frontend workflow. The canvas's `space` operator calls
4
+ # akhaliq/Z-Image-Turbo's /generate_image via gradio_client's call_space
5
+ # server function, forwarding the visitor's HF OAuth token. That proxy
6
+ # identity flows into the backend @spaces.GPU, where ZeroGPU budgets the
7
+ # call against the visitor's / PRO account.
8
 
9
+ demo = gr.Workflow(graph="workflow.json")
 
 
 
 
 
 
 
 
 
10
 
11
  if __name__ == "__main__":
12
+ demo.launch()
workflow.json CHANGED
@@ -1,17 +1,33 @@
1
  {
2
  "schema_version": "2",
3
  "name": "Z-Image-Turbo",
4
- "description": "Ultra-fast AI image generation with Z-Image-Turbo on ZeroGPU, driven by a gr.Workflow fn-bound @spaces.GPU function. Single Space: the workflow frontend and the GPU worker share one process.",
5
- "runtime": { "default": "client" },
6
- "view": { "default": "canvas" },
 
 
 
 
7
  "references": [
8
  {
9
  "id": "ref_prompt",
10
  "label": "Prompt",
11
  "role": "reference",
12
  "asset_type": "text",
13
- "inputs": [{ "id": "in", "label": "Prompt", "type": "text" }],
14
- "outputs": [{ "id": "out", "label": "Prompt", "type": "text" }],
 
 
 
 
 
 
 
 
 
 
 
 
15
  "x": 60,
16
  "y": 160,
17
  "width": 240,
@@ -25,92 +41,198 @@
25
  "label": "Height",
26
  "role": "reference",
27
  "asset_type": "number",
28
- "inputs": [{ "id": "in", "label": "Height", "type": "number" }],
29
- "outputs": [{ "id": "out", "label": "Height", "type": "number" }],
 
 
 
 
 
 
 
 
 
 
 
 
30
  "x": 60,
31
  "y": 300,
32
  "width": 200,
33
  "height": 90,
34
- "data": { "out": 1024 }
 
 
35
  },
36
  {
37
  "id": "ref_width",
38
  "label": "Width",
39
  "role": "reference",
40
  "asset_type": "number",
41
- "inputs": [{ "id": "in", "label": "Width", "type": "number" }],
42
- "outputs": [{ "id": "out", "label": "Width", "type": "number" }],
 
 
 
 
 
 
 
 
 
 
 
 
43
  "x": 60,
44
  "y": 410,
45
  "width": 200,
46
  "height": 90,
47
- "data": { "out": 1024 }
 
 
48
  },
49
  {
50
  "id": "ref_steps",
51
  "label": "Inference Steps",
52
  "role": "reference",
53
  "asset_type": "number",
54
- "inputs": [{ "id": "in", "label": "Steps", "type": "number" }],
55
- "outputs": [{ "id": "out", "label": "Steps", "type": "number" }],
 
 
 
 
 
 
 
 
 
 
 
 
56
  "x": 60,
57
  "y": 520,
58
  "width": 200,
59
  "height": 90,
60
- "data": { "out": 9 }
 
 
61
  },
62
  {
63
  "id": "ref_seed",
64
  "label": "Seed",
65
  "role": "reference",
66
  "asset_type": "number",
67
- "inputs": [{ "id": "in", "label": "Seed", "type": "number" }],
68
- "outputs": [{ "id": "out", "label": "Seed", "type": "number" }],
 
 
 
 
 
 
 
 
 
 
 
 
69
  "x": 60,
70
  "y": 630,
71
  "width": 200,
72
  "height": 90,
73
- "data": { "out": 42 }
 
 
74
  },
75
  {
76
  "id": "ref_randomize",
77
  "label": "Randomize Seed",
78
  "role": "reference",
79
  "asset_type": "boolean",
80
- "inputs": [{ "id": "in", "label": "Randomize", "type": "boolean" }],
81
- "outputs": [{ "id": "out", "label": "Randomize", "type": "boolean" }],
 
 
 
 
 
 
 
 
 
 
 
 
82
  "x": 60,
83
  "y": 740,
84
  "width": 200,
85
  "height": 90,
86
- "data": { "out": true }
 
 
87
  }
88
  ],
89
  "operators": [
90
  {
91
  "id": "op_generate",
92
- "label": "generate_image",
93
  "role": "operator",
94
- "kind": "fn",
95
- "source": "fn",
96
- "fn": "generate_image",
97
  "inputs": [
98
- { "id": "in_0", "label": "prompt", "type": "text", "required": true },
99
- { "id": "in_1", "label": "height", "type": "number" },
100
- { "id": "in_2", "label": "width", "type": "number" },
101
- { "id": "in_3", "label": "num_inference_steps", "type": "number" },
102
- { "id": "in_4", "label": "seed", "type": "number" },
103
- { "id": "in_5", "label": "randomize_seed", "type": "boolean" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  ],
105
  "outputs": [
106
- { "id": "out_0", "label": "image", "type": "image", "output_index": 0 },
107
- { "id": "out_1", "label": "seed_used", "type": "number", "output_index": 1 }
 
 
 
 
 
 
 
 
 
 
108
  ],
109
  "x": 420,
110
  "y": 320,
111
  "width": 280,
112
  "height": 220,
113
- "data": {}
 
 
114
  }
115
  ],
116
  "subjects": [
@@ -119,8 +241,20 @@
119
  "label": "Output Image",
120
  "role": "subject",
121
  "asset_type": "image",
122
- "inputs": [{ "id": "in", "label": "Image", "type": "image" }],
123
- "outputs": [{ "id": "out", "label": "Image", "type": "image" }],
 
 
 
 
 
 
 
 
 
 
 
 
124
  "x": 820,
125
  "y": 280,
126
  "width": 240,
@@ -132,8 +266,20 @@
132
  "label": "Seed Used",
133
  "role": "subject",
134
  "asset_type": "number",
135
- "inputs": [{ "id": "in", "label": "Seed", "type": "number" }],
136
- "outputs": [{ "id": "out", "label": "Seed", "type": "number" }],
 
 
 
 
 
 
 
 
 
 
 
 
137
  "x": 820,
138
  "y": 460,
139
  "width": 240,
@@ -142,13 +288,69 @@
142
  }
143
  ],
144
  "edges": [
145
- { "id": "e_prompt", "from_node_id": "ref_prompt", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "in_0", "type": "text" },
146
- { "id": "e_height", "from_node_id": "ref_height", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "in_1", "type": "number" },
147
- { "id": "e_width", "from_node_id": "ref_width", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "in_2", "type": "number" },
148
- { "id": "e_steps", "from_node_id": "ref_steps", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "in_3", "type": "number" },
149
- { "id": "e_seed", "from_node_id": "ref_seed", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "in_4", "type": "number" },
150
- { "id": "e_randomize", "from_node_id": "ref_randomize", "from_port_id": "out", "to_node_id": "op_generate", "to_port_id": "in_5", "type": "boolean" },
151
- { "id": "e_image_out", "from_node_id": "op_generate", "from_port_id": "out_0", "to_node_id": "sub_image", "to_port_id": "in", "type": "image" },
152
- { "id": "e_seed_out", "from_node_id": "op_generate", "from_port_id": "out_1", "to_node_id": "sub_seed", "to_port_id": "in", "type": "number" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  ]
154
- }
 
1
  {
2
  "schema_version": "2",
3
  "name": "Z-Image-Turbo",
4
+ "description": "Two-Space architecture: this Space is a gr.Workflow frontend that calls the akhaliq/Z-Image-Turbo Blocks backend via its /generate_image endpoint. The canvas forwards the visitor's HF OAuth token through gradio_client's call_space path, so signed-in PRO users get their proper quota metered against the backend Space.",
5
+ "runtime": {
6
+ "default": "client"
7
+ },
8
+ "view": {
9
+ "default": "canvas"
10
+ },
11
  "references": [
12
  {
13
  "id": "ref_prompt",
14
  "label": "Prompt",
15
  "role": "reference",
16
  "asset_type": "text",
17
+ "inputs": [
18
+ {
19
+ "id": "in",
20
+ "label": "Prompt",
21
+ "type": "text"
22
+ }
23
+ ],
24
+ "outputs": [
25
+ {
26
+ "id": "out",
27
+ "label": "Prompt",
28
+ "type": "text"
29
+ }
30
+ ],
31
  "x": 60,
32
  "y": 160,
33
  "width": 240,
 
41
  "label": "Height",
42
  "role": "reference",
43
  "asset_type": "number",
44
+ "inputs": [
45
+ {
46
+ "id": "in",
47
+ "label": "Height",
48
+ "type": "number"
49
+ }
50
+ ],
51
+ "outputs": [
52
+ {
53
+ "id": "out",
54
+ "label": "Height",
55
+ "type": "number"
56
+ }
57
+ ],
58
  "x": 60,
59
  "y": 300,
60
  "width": 200,
61
  "height": 90,
62
+ "data": {
63
+ "out": 1024
64
+ }
65
  },
66
  {
67
  "id": "ref_width",
68
  "label": "Width",
69
  "role": "reference",
70
  "asset_type": "number",
71
+ "inputs": [
72
+ {
73
+ "id": "in",
74
+ "label": "Width",
75
+ "type": "number"
76
+ }
77
+ ],
78
+ "outputs": [
79
+ {
80
+ "id": "out",
81
+ "label": "Width",
82
+ "type": "number"
83
+ }
84
+ ],
85
  "x": 60,
86
  "y": 410,
87
  "width": 200,
88
  "height": 90,
89
+ "data": {
90
+ "out": 1024
91
+ }
92
  },
93
  {
94
  "id": "ref_steps",
95
  "label": "Inference Steps",
96
  "role": "reference",
97
  "asset_type": "number",
98
+ "inputs": [
99
+ {
100
+ "id": "in",
101
+ "label": "Steps",
102
+ "type": "number"
103
+ }
104
+ ],
105
+ "outputs": [
106
+ {
107
+ "id": "out",
108
+ "label": "Steps",
109
+ "type": "number"
110
+ }
111
+ ],
112
  "x": 60,
113
  "y": 520,
114
  "width": 200,
115
  "height": 90,
116
+ "data": {
117
+ "out": 9
118
+ }
119
  },
120
  {
121
  "id": "ref_seed",
122
  "label": "Seed",
123
  "role": "reference",
124
  "asset_type": "number",
125
+ "inputs": [
126
+ {
127
+ "id": "in",
128
+ "label": "Seed",
129
+ "type": "number"
130
+ }
131
+ ],
132
+ "outputs": [
133
+ {
134
+ "id": "out",
135
+ "label": "Seed",
136
+ "type": "number"
137
+ }
138
+ ],
139
  "x": 60,
140
  "y": 630,
141
  "width": 200,
142
  "height": 90,
143
+ "data": {
144
+ "out": 42
145
+ }
146
  },
147
  {
148
  "id": "ref_randomize",
149
  "label": "Randomize Seed",
150
  "role": "reference",
151
  "asset_type": "boolean",
152
+ "inputs": [
153
+ {
154
+ "id": "in",
155
+ "label": "Randomize",
156
+ "type": "boolean"
157
+ }
158
+ ],
159
+ "outputs": [
160
+ {
161
+ "id": "out",
162
+ "label": "Randomize",
163
+ "type": "boolean"
164
+ }
165
+ ],
166
  "x": 60,
167
  "y": 740,
168
  "width": 200,
169
  "height": 90,
170
+ "data": {
171
+ "out": true
172
+ }
173
  }
174
  ],
175
  "operators": [
176
  {
177
  "id": "op_generate",
178
+ "label": "Z-Image-Turbo (backend)",
179
  "role": "operator",
180
+ "kind": "space",
181
+ "source": "hf://spaces/akhaliq/Z-Image-Turbo",
 
182
  "inputs": [
183
+ {
184
+ "id": "in_0",
185
+ "label": "prompt",
186
+ "type": "text",
187
+ "required": true
188
+ },
189
+ {
190
+ "id": "in_1",
191
+ "label": "height",
192
+ "type": "number"
193
+ },
194
+ {
195
+ "id": "in_2",
196
+ "label": "width",
197
+ "type": "number"
198
+ },
199
+ {
200
+ "id": "in_3",
201
+ "label": "num_inference_steps",
202
+ "type": "number"
203
+ },
204
+ {
205
+ "id": "in_4",
206
+ "label": "seed",
207
+ "type": "number"
208
+ },
209
+ {
210
+ "id": "in_5",
211
+ "label": "randomize_seed",
212
+ "type": "boolean"
213
+ }
214
  ],
215
  "outputs": [
216
+ {
217
+ "id": "out_0",
218
+ "label": "image",
219
+ "type": "image",
220
+ "output_index": 0
221
+ },
222
+ {
223
+ "id": "out_1",
224
+ "label": "seed_used",
225
+ "type": "number",
226
+ "output_index": 1
227
+ }
228
  ],
229
  "x": 420,
230
  "y": 320,
231
  "width": 280,
232
  "height": 220,
233
+ "data": {},
234
+ "space_id": "akhaliq/Z-Image-Turbo",
235
+ "endpoint": "/generate_image"
236
  }
237
  ],
238
  "subjects": [
 
241
  "label": "Output Image",
242
  "role": "subject",
243
  "asset_type": "image",
244
+ "inputs": [
245
+ {
246
+ "id": "in",
247
+ "label": "Image",
248
+ "type": "image"
249
+ }
250
+ ],
251
+ "outputs": [
252
+ {
253
+ "id": "out",
254
+ "label": "Image",
255
+ "type": "image"
256
+ }
257
+ ],
258
  "x": 820,
259
  "y": 280,
260
  "width": 240,
 
266
  "label": "Seed Used",
267
  "role": "subject",
268
  "asset_type": "number",
269
+ "inputs": [
270
+ {
271
+ "id": "in",
272
+ "label": "Seed",
273
+ "type": "number"
274
+ }
275
+ ],
276
+ "outputs": [
277
+ {
278
+ "id": "out",
279
+ "label": "Seed",
280
+ "type": "number"
281
+ }
282
+ ],
283
  "x": 820,
284
  "y": 460,
285
  "width": 240,
 
288
  }
289
  ],
290
  "edges": [
291
+ {
292
+ "id": "e_prompt",
293
+ "from_node_id": "ref_prompt",
294
+ "from_port_id": "out",
295
+ "to_node_id": "op_generate",
296
+ "to_port_id": "in_0",
297
+ "type": "text"
298
+ },
299
+ {
300
+ "id": "e_height",
301
+ "from_node_id": "ref_height",
302
+ "from_port_id": "out",
303
+ "to_node_id": "op_generate",
304
+ "to_port_id": "in_1",
305
+ "type": "number"
306
+ },
307
+ {
308
+ "id": "e_width",
309
+ "from_node_id": "ref_width",
310
+ "from_port_id": "out",
311
+ "to_node_id": "op_generate",
312
+ "to_port_id": "in_2",
313
+ "type": "number"
314
+ },
315
+ {
316
+ "id": "e_steps",
317
+ "from_node_id": "ref_steps",
318
+ "from_port_id": "out",
319
+ "to_node_id": "op_generate",
320
+ "to_port_id": "in_3",
321
+ "type": "number"
322
+ },
323
+ {
324
+ "id": "e_seed",
325
+ "from_node_id": "ref_seed",
326
+ "from_port_id": "out",
327
+ "to_node_id": "op_generate",
328
+ "to_port_id": "in_4",
329
+ "type": "number"
330
+ },
331
+ {
332
+ "id": "e_randomize",
333
+ "from_node_id": "ref_randomize",
334
+ "from_port_id": "out",
335
+ "to_node_id": "op_generate",
336
+ "to_port_id": "in_5",
337
+ "type": "boolean"
338
+ },
339
+ {
340
+ "id": "e_image_out",
341
+ "from_node_id": "op_generate",
342
+ "from_port_id": "out_0",
343
+ "to_node_id": "sub_image",
344
+ "to_port_id": "in",
345
+ "type": "image"
346
+ },
347
+ {
348
+ "id": "e_seed_out",
349
+ "from_node_id": "op_generate",
350
+ "from_port_id": "out_1",
351
+ "to_node_id": "sub_seed",
352
+ "to_port_id": "in",
353
+ "type": "number"
354
+ }
355
  ]
356
+ }