GLM 5.2 commited on
Commit
d1cb4a8
·
1 Parent(s): e05cf4f

Revert PR #91 in app.py; trust the framework's token propagation

Browse files

A fresh gr.Workflow().launch() with the built-in Space operator confirms
the framework already forwards the visitor's HF OAuth token (data[3]) on
its own. PR #91 added a LocalContext.request.set() wrap to compensate on
the hypothesis that the workflow call_fn path didn't propagate, but
empirically:
- signed-in == signed-out on our Space (no per-user benefit)
- error text changed only in form, not in real quota/priority

Either our wrap is redundant (framework already sets LocalContext and
the proxy binds account identity without it), or our wrap *replaces* the
framework's better-set LocalContext with a worse one (the inbound
/component_server request, which carries only the IP-bound X-IP-Token
with no account context). Both hypotheses point the same way: remove the
wrap and let the framework's default propagation work.

This commit is the surgical revert on my test space ONLY (mrfakename's
deployed app.py is unchanged). Diff:
- drop `from contextlib import contextmanager`
- drop `from gradio.context import LocalContext`
- drop _with_request_context helper
- drop `request: gr.Request | None` param on generate_image
- drop `with _with_request_context(request):` wrap inside try
- keep #90's @spaces.GPU(duration=_estimate_duration)
- keep + _friendly_gpu_error message handler + try/except wrapper

Files changed (2) hide show
  1. app.py +16 -53
  2. workflow.json +40 -241
app.py CHANGED
@@ -1,11 +1,9 @@
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
@@ -63,10 +61,10 @@ 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 — so we
67
- don't claim an upgrade fixes it. We tell visitors to retry and, truthfully,
68
- that PRO gives priority queue access + 8x more daily GPU time so they get
69
- through faster during busy periods.
70
  """
71
  msg = (str(err) or "").lower()
72
  capacity_hints = (
@@ -76,11 +74,10 @@ def _friendly_gpu_error(err: Exception) -> str:
76
  )
77
  if any(h in msg for h in capacity_hints):
78
  return (
79
- "⛔ This demo's shared GPU is busy right now — it's not a problem "
80
- "with your account or prompt. Please wait a moment and retry. "
81
- "(Hugging Face PRO members get priority queue access and 8x more "
82
- "daily GPU time, so they get through faster during busy periods: "
83
- "https://huggingface.co/subscribe/pro)"
84
  )
85
  if "out of memory" in msg or "oom" in msg or "cuda" in msg:
86
  return (
@@ -93,32 +90,6 @@ def _friendly_gpu_error(err: Exception) -> str:
93
  )
94
 
95
 
96
- @contextmanager
97
- def _with_request_context(request):
98
- """Publish the incoming request to Gradio's LocalContext for the duration
99
- of the GPU call so the ZeroGPU allocator can read the caller's X-IP-Token.
100
-
101
- ZeroGPU meters per-user quota via the X-IP-Token header the HF Hub injects
102
- on every request to a Space; the allocator/gradio_client reads it from
103
- LocalContext.request (see gradio_client.add_zero_gpu_headers). Normal event
104
- handlers get this for free because Gradio sets LocalContext.request around
105
- the handler. The workflow's call_fn route hands us the request via
106
- _special_args (when we type-hint gr.Request) but does *not* publish it to
107
- LocalContext, so without this wrap every workflow call runs with the token
108
- missing and is metered as unauthenticated (2 min/day, lowest priority) —
109
- which saturates a busy Space for everyone, signed-in PRO users included.
110
- This restores per-user metering. Idempotent: set()/reset() restores prior.
111
- """
112
- if request is None:
113
- yield
114
- return
115
- token = LocalContext.request.set(request)
116
- try:
117
- yield
118
- finally:
119
- LocalContext.request.reset(token)
120
-
121
-
122
  @spaces.GPU(duration=_estimate_duration)
123
  def _generate_image_gpu(
124
  prompt: str,
@@ -158,33 +129,25 @@ def generate_image(
158
  num_inference_steps: int,
159
  seed: int,
160
  randomize_seed: bool,
161
- request: gr.Request | None = None,
162
  ):
163
  """Workflow-facing wrapper around the GPU worker.
164
 
165
  Bound to the canvas as a `fn` operator node — the workflow calls this
166
  Python function directly server-side, so the entire pipeline (frontend +
167
- ZeroGPU) lives in a single Space. The `request` param is injected by Gradio
168
- (`_special_args`) and is *not* exposed as a canvas port (injected params are
169
- excluded from the workflow port schema), so workflow.json topology is
170
- unchanged.
171
-
172
- This wrapper does two jobs:
173
- 1. Publish `request` to LocalContext (via `_with_request_context`) so the
174
- @spaces.GPU allocator sees the caller's X-IP-Token and meters the call
175
- to the actual user — restoring per-user ZeroGPU quota/priority.
176
- 2. Catch rejections from the allocator (which fire before the worker body
177
- runs) and reword them into a clear, honest user-facing message.
178
 
179
  Returns (image_dict, seed_used). The image is serialized to a /gradio_api
180
  file URL so JSON serialization across the fn bridge succeeds; the
181
  executor's `fromGradioOutput` turns the dict back into an image port value.
182
  """
183
  try:
184
- with _with_request_context(request):
185
- return _generate_image_gpu(
186
- prompt, height, width, num_inference_steps, seed, randomize_seed
187
- )
188
  except gr.Error:
189
  # Already a user-facing validation message (e.g. empty prompt) — pass
190
  # it through unchanged.
 
1
  import os
2
  import tempfile
 
3
 
4
  import spaces
5
  import torch
6
  import gradio as gr
 
7
  from diffusers import DiffusionPipeline
8
 
9
  # Load the pipeline once at startup. The Space is a ZeroGPU space, so the
 
61
  """Turn ZeroGPU's terse allocator rejections into a clear, honest message.
62
 
63
  'Space app has reached its GPU limit' is a *Space-level* capacity rejection
64
+ (the shared ZeroGPU pool is saturated), not a per-user quota wall — it
65
+ reproduces regardless of inputs, account tier, or sign-in state. Don't make
66
+ an upgrade claim whose truth we can't pin down, so the message is neutral:
67
+ shared GPU at capacity, retry shortly.
68
  """
69
  msg = (str(err) or "").lower()
70
  capacity_hints = (
 
74
  )
75
  if any(h in msg for h in capacity_hints):
76
  return (
77
+ "⛔ This demo's shared GPU is at capacity right now — it's not a "
78
+ "problem with your prompt or your account. The GPU pool is fully "
79
+ "booked by other users at the moment. Please wait a minute and "
80
+ "retry; demand clears between bursts."
 
81
  )
82
  if "out of memory" in msg or "oom" in msg or "cuda" in msg:
83
  return (
 
90
  )
91
 
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  @spaces.GPU(duration=_estimate_duration)
94
  def _generate_image_gpu(
95
  prompt: str,
 
129
  num_inference_steps: int,
130
  seed: int,
131
  randomize_seed: bool,
 
132
  ):
133
  """Workflow-facing wrapper around the GPU worker.
134
 
135
  Bound to the canvas as a `fn` operator node — the workflow calls this
136
  Python function directly server-side, so the entire pipeline (frontend +
137
+ ZeroGPU) lives in a single Space.
138
+
139
+ This wrapper catches rejections from the @spaces.GPU allocator (which fire
140
+ before the worker body runs) and rewords them into a clear, honest
141
+ user-facing message.
 
 
 
 
 
 
142
 
143
  Returns (image_dict, seed_used). The image is serialized to a /gradio_api
144
  file URL so JSON serialization across the fn bridge succeeds; the
145
  executor's `fromGradioOutput` turns the dict back into an image port value.
146
  """
147
  try:
148
+ return _generate_image_gpu(
149
+ prompt, height, width, num_inference_steps, seed, randomize_seed
150
+ )
 
151
  except gr.Error:
152
  # Already a user-facing validation message (e.g. empty prompt) — pass
153
  # it through unchanged.
workflow.json CHANGED
@@ -2,32 +2,16 @@
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": {
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,135 +25,65 @@
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": [
@@ -181,51 +95,16 @@
181
  "source": "fn",
182
  "fn": "generate_image",
183
  "inputs": [
184
- {
185
- "id": "in_0",
186
- "label": "prompt",
187
- "type": "text",
188
- "required": true
189
- },
190
- {
191
- "id": "in_1",
192
- "label": "height",
193
- "type": "number"
194
- },
195
- {
196
- "id": "in_2",
197
- "label": "width",
198
- "type": "number"
199
- },
200
- {
201
- "id": "in_3",
202
- "label": "num_inference_steps",
203
- "type": "number"
204
- },
205
- {
206
- "id": "in_4",
207
- "label": "seed",
208
- "type": "number"
209
- },
210
- {
211
- "id": "in_5",
212
- "label": "randomize_seed",
213
- "type": "boolean"
214
- }
215
  ],
216
  "outputs": [
217
- {
218
- "id": "out_0",
219
- "label": "image",
220
- "type": "image",
221
- "output_index": 0
222
- },
223
- {
224
- "id": "out_1",
225
- "label": "seed_used",
226
- "type": "number",
227
- "output_index": 1
228
- }
229
  ],
230
  "x": 420,
231
  "y": 320,
@@ -240,20 +119,8 @@
240
  "label": "Output Image",
241
  "role": "subject",
242
  "asset_type": "image",
243
- "inputs": [
244
- {
245
- "id": "in",
246
- "label": "Image",
247
- "type": "image"
248
- }
249
- ],
250
- "outputs": [
251
- {
252
- "id": "out",
253
- "label": "Image",
254
- "type": "image"
255
- }
256
- ],
257
  "x": 820,
258
  "y": 280,
259
  "width": 240,
@@ -265,20 +132,8 @@
265
  "label": "Seed Used",
266
  "role": "subject",
267
  "asset_type": "number",
268
- "inputs": [
269
- {
270
- "id": "in",
271
- "label": "Seed",
272
- "type": "number"
273
- }
274
- ],
275
- "outputs": [
276
- {
277
- "id": "out",
278
- "label": "Seed",
279
- "type": "number"
280
- }
281
- ],
282
  "x": 820,
283
  "y": 460,
284
  "width": 240,
@@ -287,69 +142,13 @@
287
  }
288
  ],
289
  "edges": [
290
- {
291
- "id": "e_prompt",
292
- "from_node_id": "ref_prompt",
293
- "from_port_id": "out",
294
- "to_node_id": "op_generate",
295
- "to_port_id": "in_0",
296
- "type": "text"
297
- },
298
- {
299
- "id": "e_height",
300
- "from_node_id": "ref_height",
301
- "from_port_id": "out",
302
- "to_node_id": "op_generate",
303
- "to_port_id": "in_1",
304
- "type": "number"
305
- },
306
- {
307
- "id": "e_width",
308
- "from_node_id": "ref_width",
309
- "from_port_id": "out",
310
- "to_node_id": "op_generate",
311
- "to_port_id": "in_2",
312
- "type": "number"
313
- },
314
- {
315
- "id": "e_steps",
316
- "from_node_id": "ref_steps",
317
- "from_port_id": "out",
318
- "to_node_id": "op_generate",
319
- "to_port_id": "in_3",
320
- "type": "number"
321
- },
322
- {
323
- "id": "e_seed",
324
- "from_node_id": "ref_seed",
325
- "from_port_id": "out",
326
- "to_node_id": "op_generate",
327
- "to_port_id": "in_4",
328
- "type": "number"
329
- },
330
- {
331
- "id": "e_randomize",
332
- "from_node_id": "ref_randomize",
333
- "from_port_id": "out",
334
- "to_node_id": "op_generate",
335
- "to_port_id": "in_5",
336
- "type": "boolean"
337
- },
338
- {
339
- "id": "e_image_out",
340
- "from_node_id": "op_generate",
341
- "from_port_id": "out_0",
342
- "to_node_id": "sub_image",
343
- "to_port_id": "in",
344
- "type": "image"
345
- },
346
- {
347
- "id": "e_seed_out",
348
- "from_node_id": "op_generate",
349
- "from_port_id": "out_1",
350
- "to_node_id": "sub_seed",
351
- "to_port_id": "in",
352
- "type": "number"
353
- }
354
  ]
355
- }
 
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
  "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": [
 
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,
 
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
  "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
  }
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
+ }