Spaces:
Running
Running
File size: 9,685 Bytes
183468a a6541eb 183468a cda4df7 07622a9 0b1be36 8ed828d 25b40da 07622a9 f8dd3ca cda4df7 a6541eb cda4df7 f8dd3ca cda4df7 07622a9 f9d0bb5 07622a9 392b2d6 07622a9 cda4df7 392b2d6 f8dd3ca 25b40da 8ed828d 25b40da 906aee4 25b40da 906aee4 25b40da 183468a 8ed828d 0b1be36 8ed828d 183468a 7d20112 e6fd579 7d20112 8ed828d cda4df7 8ed828d cda4df7 8ed828d cda4df7 25b40da 8ed828d f8dd3ca 8ed828d 392b2d6 8ed828d 07622a9 b79a512 0b1be36 b79a512 e6fd579 b79a512 cda4df7 b79a512 cda4df7 b79a512 cda4df7 b79a512 f8dd3ca b79a512 f8dd3ca b79a512 f8dd3ca 664e1fd 0b1be36 664e1fd e6fd579 664e1fd cda4df7 664e1fd cda4df7 664e1fd cda4df7 664e1fd f8dd3ca 664e1fd f8dd3ca 664e1fd f8dd3ca 2d323f9 f8dd3ca 8ed828d f8dd3ca 25b40da f8dd3ca 80974ab | 1 2 3 4 5 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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | import base64
import os
import tempfile
import urllib.request
import gradio as gr
import spaces
from gradio_client import Client, handle_file
from daggr import FnNode, GradioNode, Graph
# ==================== FREE TOOLS COLLECTION ====================
# These tools are free to use and don't consume credits
def _hf_token():
"""HF token so ZeroGPU quota is used (not 'unlogged user'). Prefer env, then huggingface_hub."""
token = os.environ.get("HF_TOKEN")
if token:
return token
try:
from huggingface_hub import get_token
return get_token()
except Exception:
return None
def _url_to_path(url):
"""Download image from URL to a temp file; Daggr needs file paths to display images."""
if not url or not isinstance(url, str) or not url.startswith("http"):
return url
try:
ext = "png"
if ".jpg" in url or ".jpeg" in url:
ext = "jpg"
elif ".webp" in url:
ext = "webp"
f = tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False)
req = urllib.request.Request(url, headers={"User-Agent": "Gradio-Daggr/1.0"})
with urllib.request.urlopen(req, timeout=60) as r:
f.write(r.read())
f.close()
return f.name
except Exception:
return url
# 1. Background Removal
# API may return (original, result); we only need the result for "final_image".
bg_remover = GradioNode(
"hf-applications/background-removal",
api_name="/image",
run_locally=False,
inputs={"image": gr.Image()},
postprocess=lambda *vals: vals[-1] if vals else None,
outputs={
"final_image": gr.Image(label="Background Removed"),
},
)
def _image_to_filepath(image):
"""Convert image input (list, dict, or path) to a single filepath string."""
if image is None:
return None
if isinstance(image, str):
return image
if isinstance(image, dict) and "path" in image and isinstance(image["path"], str):
return image["path"]
if isinstance(image, (list, tuple)) and len(image) > 0:
first = image[0]
if isinstance(first, str):
return first
if isinstance(first, dict) and "path" in first and isinstance(first["path"], str):
return first["path"]
return None
def _path_for_api(path):
"""Convert path (file path, URL, or data URL) to a path handle_file can use. Data URLs become temp files."""
if not path or not isinstance(path, str):
return None
if path.startswith("data:"):
# data:image/jpeg;base64,... -> decode and write to temp file (handle_file can't use data URL as filename)
try:
header, b64 = path.split(",", 1)
ext = "png"
if "jpeg" in header or "jpg" in header:
ext = "jpg"
elif "webp" in header:
ext = "webp"
data = base64.b64decode(b64)
f = tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False)
f.write(data)
f.close()
return f.name
except Exception as e:
raise RuntimeError(f"Upscaler: could not decode data URL: {e}") from e
return path
# 2. Image Upscaler (FnNode so we pass a single filepath string; GradioNode was receiving a list from Daggr)
@spaces.GPU
def run_upscaler(image, model_selection="4xBHI_dat2_real"):
"""Call Phips/Upscaler API with a single filepath string. Returns path to upscaled image."""
path = _image_to_filepath(image)
if not path:
return None
path = _path_for_api(path)
if not path:
return None
# Phips/Upscaler runs on a different server - send file content via handle_file so it can read the image.
try:
image_arg = handle_file(path)
except Exception as e:
raise RuntimeError(f"Upscaler: could not load image from path: {e}") from e
try:
client = Client("Phips/Upscaler", token=_hf_token())
result = client.predict(image_arg, model_selection, api_name="/upscale_image")
except Exception as e:
raise RuntimeError(f"Upscaler API error: {e}") from e
# Returns tuple of 2: [comparison, filepath]; we need the upscaled image path (second element)
out = None
if result and len(result) >= 2 and result[1]:
out = result[1]
elif result and len(result) >= 1 and result[0]:
r0 = result[0]
if isinstance(r0, (list, tuple)) and len(r0) >= 2 and r0[1]:
out = r0[1]
elif isinstance(r0, str):
out = r0
if out and isinstance(out, str) and out.startswith("http"):
return _url_to_path(out)
return out
upscaler = FnNode(
run_upscaler,
name="Upscaler",
inputs={
"image": gr.Image(label="Input Image"),
"model_selection": gr.Dropdown(
label="Model",
choices=["4xBHI_dat2_real", "4xNomos8kDAT", "4xHFA2k", "2xEvangelion_dat2"],
value="4xBHI_dat2_real",
),
},
outputs={"upscaled_image": gr.Image(label="Upscaled Image")},
)
# 3. Z-Image Turbo (FnNode so prompt is sent as prompt, not mistaken for a file path — avoids "File name too long")
@spaces.GPU
def run_z_image_turbo(prompt, height=1024, width=1024, seed=42):
"""Call Z-Image-Turbo API with prompt as string. Returns image URL or path."""
if not prompt or not isinstance(prompt, str) or not prompt.strip():
return None
try:
client = Client("hf-applications/Z-Image-Turbo", token=_hf_token())
result = client.predict(
prompt=prompt.strip(),
height=float(height),
width=float(width),
seed=int(seed),
api_name="/generate_image",
)
except Exception as e:
raise RuntimeError(f"Z-Image-Turbo API error: {e}") from e
# Returns tuple of 2: [0] dict(path=..., url=..., ...), [1] seed used. Daggr needs local path.
if result and len(result) >= 1 and result[0]:
img = result[0]
out = None
if isinstance(img, dict):
out = img.get("url") or img.get("path")
elif isinstance(img, str):
out = img
if out and isinstance(out, str) and out.startswith("http"):
return _url_to_path(out)
return out
return None
z_image_turbo = FnNode(
run_z_image_turbo,
name="Z-Image-Turbo",
inputs={
"prompt": gr.Textbox(label="Prompt", lines=3),
"height": gr.Slider(512, 1024, value=1024, step=64, label="Height"),
"width": gr.Slider(512, 1024, value=1024, step=64, label="Width"),
"seed": gr.Number(value=42, label="Seed", precision=0),
},
outputs={"generated_image": gr.Image(label="Generated Image")},
)
# 4. FLUX.2 Klein 9B (FnNode so prompt is sent as prompt, not mistaken for filename — avoids "File name too long")
@spaces.GPU
def run_flux_klein(
prompt,
mode_choice="Distilled (4 steps)",
seed=0,
randomize_seed=True,
width=1024,
height=1024,
):
"""Call FLUX.2-klein-9B API with prompt as string. Returns image URL or path."""
if not prompt or not isinstance(prompt, str) or not prompt.strip():
return None
try:
client = Client("black-forest-labs/FLUX.2-klein-9B", token=_hf_token())
result = client.predict(
prompt=prompt.strip(),
input_images=[],
mode_choice=mode_choice,
seed=float(seed),
randomize_seed=bool(randomize_seed),
width=float(width),
height=float(height),
num_inference_steps=4.0 if "Distilled" in mode_choice else 50.0,
guidance_scale=1.0 if "Distilled" in mode_choice else 3.5,
prompt_upsampling=False,
api_name="/generate",
)
except Exception as e:
raise RuntimeError(f"FLUX.2-klein-9B API error: {e}") from e
# Returns tuple of 2: [0] dict(path=..., url=...), [1] seed used. Daggr needs local path.
if result and len(result) >= 1 and result[0]:
img = result[0]
out = None
if isinstance(img, dict):
out = img.get("url") or img.get("path")
elif isinstance(img, str):
out = img
if out and isinstance(out, str) and out.startswith("http"):
return _url_to_path(out)
return out
return None
flux_klein = FnNode(
run_flux_klein,
name="FLUX.2-klein-9B",
inputs={
"prompt": gr.Textbox(label="Prompt", lines=3),
"mode_choice": gr.Radio(
choices=["Distilled (4 steps)", "Base (50 steps)"],
value="Distilled (4 steps)",
label="Mode",
),
"seed": gr.Number(value=0, label="Seed", precision=0),
"randomize_seed": gr.Checkbox(value=True, label="Randomize seed"),
"width": gr.Slider(512, 1024, value=1024, step=64, label="Width"),
"height": gr.Slider(512, 1024, value=1024, step=64, label="Height"),
},
outputs={"result": gr.Image(label="Generated Image")},
)
# Note: hysts-daggr/daggr-text-to-image-to-3d is a Daggr space itself,
# so it doesn't expose a standard Gradio API and cannot be connected via GradioNode.
# Users can access it directly at: https://huggingface.co/spaces/hysts-daggr/daggr-text-to-image-to-3d
# ==================== GRAPH SETUP ====================
# Create the workflow graph with all free tools
# Upscaler is an FnNode that calls Phips/Upscaler with a single path string (no list).
graph = Graph(
name="Imageat Workflow - Free Tools",
nodes=[
bg_remover,
upscaler,
z_image_turbo,
flux_klein,
],
)
# Launch the Space
graph.launch()
|