incendies commited on
Commit
444727d
·
1 Parent(s): 4bb3362

All tools in one Gradio Space (ZeroGPU, no Daggr)

Browse files
Files changed (2) hide show
  1. app.py +233 -25
  2. requirements.txt +1 -0
app.py CHANGED
@@ -1,43 +1,251 @@
1
  """
2
- Imageat Workflow Agent — ZeroGPU Gradio Space.
3
- Uses the exact pattern from https://huggingface.co/docs/hub/spaces-zerogpu:
4
- - import spaces
5
- - @spaces.GPU on GPU-dependent code
6
- - gr.Interface(..., fn=...).launch()
7
  """
 
 
 
 
8
  import gradio as gr
9
  import spaces
 
10
 
11
- # Full Daggr workflow runs on the sibling Space (same code as imageat-workflow).
12
- # Iframe embedding can break session/token — use "Open in new tab" so nodes work.
13
- WORKFLOW_SPACE_URL = "https://incendies-imageat-workflow.hf.space"
14
 
 
 
 
 
 
 
 
 
 
 
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  @spaces.GPU
17
- def _zero_gpu_ready():
18
- """Ensures HF detects @spaces.GPU at startup and enables ZeroGPU."""
19
- return "ZeroGPU ready. Use **Open workflow in new tab** below so the workflow runs correctly."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
 
22
- with gr.Blocks(title="Imageat Workflow Agent") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  with gr.Tabs():
24
- with gr.TabItem("Workflow"):
25
- gr.Markdown(
26
- "If the workflow below does not run (e.g. nodes show \"No image\"), "
27
- "**open it in a new tab** so your session and API calls work:"
 
 
28
  )
29
- gr.HTML(
30
- f'<p style="margin:0.5em 0"><a href="{WORKFLOW_SPACE_URL}" target="_blank" rel="noopener" style="font-weight:600">▶ Open workflow in new tab</a></p>'
 
 
 
 
 
 
 
 
 
 
 
31
  )
32
- gr.HTML(
33
- f'<iframe src="{WORKFLOW_SPACE_URL}" style="width:100%;height:75vh;border:none" title="Daggr workflow"></iframe>'
 
 
 
 
 
 
 
 
 
34
  )
35
- with gr.TabItem("GPU check"):
36
  gr.Interface(
37
- fn=_zero_gpu_ready,
38
- inputs=None,
39
- outputs=gr.Textbox(label="Status"),
40
- title="ZeroGPU",
 
 
 
 
 
 
 
 
 
 
 
41
  )
42
 
43
  demo.launch()
 
1
  """
2
+ Imageat Workflow Agent — All tools in one Gradio Space (ZeroGPU).
3
+ Single Space, single quota; no Daggr, no iframe. Pattern: spaces + @spaces.GPU + demo.launch().
 
 
 
4
  """
5
+ import base64
6
+ import os
7
+ import tempfile
8
+ import urllib.request
9
  import gradio as gr
10
  import spaces
11
+ from gradio_client import Client, handle_file
12
 
 
 
 
13
 
14
+ def _hf_token():
15
+ """HF token for ZeroGPU quota (Space's HF_TOKEN or huggingface_hub)."""
16
+ token = os.environ.get("HF_TOKEN")
17
+ if token:
18
+ return token
19
+ try:
20
+ from huggingface_hub import get_token
21
+ return get_token()
22
+ except Exception:
23
+ return None
24
 
25
+
26
+ def _url_to_path(url):
27
+ """Download image URL to temp file for Gradio display."""
28
+ if not url or not isinstance(url, str) or not url.startswith("http"):
29
+ return url
30
+ try:
31
+ ext = "png"
32
+ if ".jpg" in url or ".jpeg" in url:
33
+ ext = "jpg"
34
+ elif ".webp" in url:
35
+ ext = "webp"
36
+ f = tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False)
37
+ req = urllib.request.Request(url, headers={"User-Agent": "Gradio-Imageat/1.0"})
38
+ with urllib.request.urlopen(req, timeout=60) as r:
39
+ f.write(r.read())
40
+ f.close()
41
+ return f.name
42
+ except Exception:
43
+ return url
44
+
45
+
46
+ def _image_to_path(image):
47
+ """Gradio Image → single filepath string."""
48
+ if image is None:
49
+ return None
50
+ if isinstance(image, str):
51
+ return image
52
+ if isinstance(image, dict) and image.get("path"):
53
+ return image["path"]
54
+ if isinstance(image, (list, tuple)) and len(image) > 0:
55
+ first = image[0]
56
+ if isinstance(first, str):
57
+ return first
58
+ if isinstance(first, dict) and first.get("path"):
59
+ return first["path"]
60
+ return None
61
+
62
+
63
+ def _path_for_api(path):
64
+ """Data URL → temp file path; else return path."""
65
+ if not path or not isinstance(path, str):
66
+ return None
67
+ if path.startswith("data:"):
68
+ try:
69
+ header, b64 = path.split(",", 1)
70
+ ext = "png"
71
+ if "jpeg" in header or "jpg" in header:
72
+ ext = "jpg"
73
+ elif "webp" in header:
74
+ ext = "webp"
75
+ data = base64.b64decode(b64)
76
+ f = tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False)
77
+ f.write(data)
78
+ f.close()
79
+ return f.name
80
+ except Exception as e:
81
+ raise RuntimeError(f"Could not decode data URL: {e}") from e
82
+ return path
83
+
84
+
85
+ # ---------- 1. Background Removal ----------
86
+ def run_bg_removal(image):
87
+ if image is None:
88
+ return None
89
+ path = _image_to_path(image)
90
+ if not path:
91
+ return None
92
+ path = _path_for_api(path)
93
+ if not path:
94
+ return None
95
+ try:
96
+ client = Client("hf-applications/background-removal", token=_hf_token())
97
+ result = client.predict(handle_file(path), api_name="/image")
98
+ except Exception as e:
99
+ raise RuntimeError(f"Background removal error: {e}") from e
100
+ # API may return (original, result); we need the result.
101
+ out = result[-1] if isinstance(result, (list, tuple)) and result else result
102
+ if out and isinstance(out, str) and out.startswith("http"):
103
+ return _url_to_path(out)
104
+ return out
105
+
106
+
107
+ # ---------- 2. Upscaler ----------
108
  @spaces.GPU
109
+ def run_upscaler(image, model_selection="4xBHI_dat2_real"):
110
+ path = _image_to_path(image)
111
+ if not path:
112
+ return None
113
+ path = _path_for_api(path)
114
+ if not path:
115
+ return None
116
+ try:
117
+ image_arg = handle_file(path)
118
+ except Exception as e:
119
+ raise RuntimeError(f"Upscaler: could not load image: {e}") from e
120
+ try:
121
+ client = Client("Phips/Upscaler", token=_hf_token())
122
+ result = client.predict(image_arg, model_selection, api_name="/upscale_image")
123
+ except Exception as e:
124
+ raise RuntimeError(f"Upscaler API error: {e}") from e
125
+ out = None
126
+ if result and len(result) >= 2 and result[1]:
127
+ out = result[1]
128
+ elif result and len(result) >= 1 and result[0]:
129
+ r0 = result[0]
130
+ if isinstance(r0, (list, tuple)) and len(r0) >= 2 and r0[1]:
131
+ out = r0[1]
132
+ elif isinstance(r0, str):
133
+ out = r0
134
+ if out and isinstance(out, str) and out.startswith("http"):
135
+ return _url_to_path(out)
136
+ return out
137
 
138
 
139
+ # ---------- 3. Z-Image Turbo ----------
140
+ @spaces.GPU
141
+ def run_z_image_turbo(prompt, height=1024, width=1024, seed=42):
142
+ if not prompt or not str(prompt).strip():
143
+ return None
144
+ try:
145
+ client = Client("hf-applications/Z-Image-Turbo", token=_hf_token())
146
+ result = client.predict(
147
+ prompt=str(prompt).strip(),
148
+ height=float(height),
149
+ width=float(width),
150
+ seed=int(seed),
151
+ api_name="/generate_image",
152
+ )
153
+ except Exception as e:
154
+ raise RuntimeError(f"Z-Image-Turbo API error: {e}") from e
155
+ if result and len(result) >= 1 and result[0]:
156
+ img = result[0]
157
+ out = img.get("url") or img.get("path") if isinstance(img, dict) else (img if isinstance(img, str) else None)
158
+ if out and isinstance(out, str) and out.startswith("http"):
159
+ return _url_to_path(out)
160
+ return out
161
+ return None
162
+
163
+
164
+ # ---------- 4. FLUX.2 Klein 9B ----------
165
+ @spaces.GPU
166
+ def run_flux_klein(prompt, mode_choice="Distilled (4 steps)", seed=0, randomize_seed=True, width=1024, height=1024):
167
+ if not prompt or not str(prompt).strip():
168
+ return None
169
+ try:
170
+ client = Client("black-forest-labs/FLUX.2-klein-9B", token=_hf_token())
171
+ result = client.predict(
172
+ prompt=str(prompt).strip(),
173
+ input_images=[],
174
+ mode_choice=mode_choice,
175
+ seed=float(seed),
176
+ randomize_seed=bool(randomize_seed),
177
+ width=float(width),
178
+ height=float(height),
179
+ num_inference_steps=4.0 if "Distilled" in mode_choice else 50.0,
180
+ guidance_scale=1.0 if "Distilled" in mode_choice else 3.5,
181
+ prompt_upsampling=False,
182
+ api_name="/generate",
183
+ )
184
+ except Exception as e:
185
+ raise RuntimeError(f"FLUX.2-klein-9B API error: {e}") from e
186
+ if result and len(result) >= 1 and result[0]:
187
+ img = result[0]
188
+ out = img.get("url") or img.get("path") if isinstance(img, dict) else (img if isinstance(img, str) else None)
189
+ if out and isinstance(out, str) and out.startswith("http"):
190
+ return _url_to_path(out)
191
+ return out
192
+ return None
193
+
194
+
195
+ # ---------- Gradio UI: one app, all tools in tabs ----------
196
+ with gr.Blocks(title="Imageat Workflow Agent", theme=gr.themes.Soft()) as demo:
197
+ gr.Markdown("# Imageat Workflow — All tools on one Space (ZeroGPU)")
198
  with gr.Tabs():
199
+ with gr.TabItem("Background Removal"):
200
+ gr.Interface(
201
+ fn=run_bg_removal,
202
+ inputs=gr.Image(label="Input Image", type="filepath"),
203
+ outputs=gr.Image(label="Background Removed"),
204
+ title="Background Removal",
205
  )
206
+ with gr.TabItem("Upscaler"):
207
+ gr.Interface(
208
+ fn=run_upscaler,
209
+ inputs=[
210
+ gr.Image(label="Input Image", type="filepath"),
211
+ gr.Dropdown(
212
+ label="Model",
213
+ choices=["4xBHI_dat2_real", "4xNomos8kDAT", "4xHFA2k", "2xEvangelion_dat2"],
214
+ value="4xBHI_dat2_real",
215
+ ),
216
+ ],
217
+ outputs=gr.Image(label="Upscaled Image"),
218
+ title="Upscaler",
219
  )
220
+ with gr.TabItem("Z-Image Turbo"):
221
+ gr.Interface(
222
+ fn=run_z_image_turbo,
223
+ inputs=[
224
+ gr.Textbox(label="Prompt", lines=3),
225
+ gr.Slider(512, 1024, value=1024, step=64, label="Height"),
226
+ gr.Slider(512, 1024, value=1024, step=64, label="Width"),
227
+ gr.Number(value=42, label="Seed", precision=0),
228
+ ],
229
+ outputs=gr.Image(label="Generated Image"),
230
+ title="Z-Image Turbo",
231
  )
232
+ with gr.TabItem("FLUX.2 Klein 9B"):
233
  gr.Interface(
234
+ fn=run_flux_klein,
235
+ inputs=[
236
+ gr.Textbox(label="Prompt", lines=3),
237
+ gr.Radio(
238
+ choices=["Distilled (4 steps)", "Base (50 steps)"],
239
+ value="Distilled (4 steps)",
240
+ label="Mode",
241
+ ),
242
+ gr.Number(value=0, label="Seed", precision=0),
243
+ gr.Checkbox(value=True, label="Randomize seed"),
244
+ gr.Slider(512, 1024, value=1024, step=64, label="Width"),
245
+ gr.Slider(512, 1024, value=1024, step=64, label="Height"),
246
+ ],
247
+ outputs=gr.Image(label="Generated Image"),
248
+ title="FLUX.2 Klein 9B",
249
  )
250
 
251
  demo.launch()
requirements.txt CHANGED
@@ -1,2 +1,3 @@
1
  gradio
2
  spaces
 
 
1
  gradio
2
  spaces
3
+ gradio_client