Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -2,7 +2,7 @@ import gradio as gr
|
|
2 |
import random
|
3 |
import os
|
4 |
from PIL import Image
|
5 |
-
from typing import Optional
|
6 |
from huggingface_hub import InferenceClient
|
7 |
|
8 |
# Project by Nymbo
|
@@ -10,9 +10,8 @@ from huggingface_hub import InferenceClient
|
|
10 |
API_TOKEN = os.getenv("HF_READ_TOKEN")
|
11 |
timeout = 100
|
12 |
|
13 |
-
# Function to query the API and return the generated image
|
14 |
def flux_krea_generate(
|
15 |
-
prompt: str,
|
16 |
negative_prompt: str = "(deformed, distorted, disfigured), poorly drawn, bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, (mutated hands and fingers), disconnected limbs, mutation, mutated, ugly, disgusting, blurry, amputation, misspellings, typos",
|
17 |
steps: int = 35,
|
18 |
cfg_scale: float = 7.0,
|
@@ -20,27 +19,28 @@ def flux_krea_generate(
|
|
20 |
seed: int = -1,
|
21 |
strength: float = 0.7,
|
22 |
width: int = 1024,
|
23 |
-
height: int = 1024
|
24 |
) -> Optional[Image.Image]:
|
25 |
-
"""
|
26 |
-
Text-to-image generation with FLUX.1-Krea-dev (no input image required).
|
27 |
|
28 |
-
|
29 |
-
|
|
|
|
|
30 |
|
31 |
Args:
|
32 |
-
prompt:
|
33 |
-
negative_prompt:
|
34 |
-
steps: Number of
|
35 |
-
cfg_scale: Classifier-free guidance scale (1-20). Higher
|
36 |
-
sampler:
|
37 |
-
seed:
|
38 |
-
strength:
|
39 |
-
width: Output width in pixels (64-1216
|
40 |
-
height: Output height in pixels (64-1216
|
41 |
|
42 |
Returns:
|
43 |
-
|
44 |
"""
|
45 |
if prompt == "" or prompt is None:
|
46 |
return None
|
@@ -96,6 +96,120 @@ def flux_krea_generate(
|
|
96 |
raise gr.Error(f"Image generation failed: {str(e)}")
|
97 |
return None
|
98 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
# CSS to style the app
|
100 |
css = """
|
101 |
#app-container {
|
@@ -152,13 +266,14 @@ with gr.Blocks(theme='Nymbo/Nymbo_Theme', css=css) as app:
|
|
152 |
)
|
153 |
|
154 |
# Expose a dedicated MCP/API endpoint with a clear schema (text-to-image only)
|
155 |
-
# This
|
|
|
156 |
gr.api(
|
157 |
-
|
158 |
api_name="generate_image",
|
159 |
api_description=(
|
160 |
"Generate an image from a text prompt using FLUX.1-Krea-dev. "
|
161 |
-
"
|
162 |
),
|
163 |
)
|
164 |
|
|
|
2 |
import random
|
3 |
import os
|
4 |
from PIL import Image
|
5 |
+
from typing import Optional, Dict, Tuple
|
6 |
from huggingface_hub import InferenceClient
|
7 |
|
8 |
# Project by Nymbo
|
|
|
10 |
API_TOKEN = os.getenv("HF_READ_TOKEN")
|
11 |
timeout = 100
|
12 |
|
|
|
13 |
def flux_krea_generate(
|
14 |
+
prompt: str,
|
15 |
negative_prompt: str = "(deformed, distorted, disfigured), poorly drawn, bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, (mutated hands and fingers), disconnected limbs, mutation, mutated, ugly, disgusting, blurry, amputation, misspellings, typos",
|
16 |
steps: int = 35,
|
17 |
cfg_scale: float = 7.0,
|
|
|
19 |
seed: int = -1,
|
20 |
strength: float = 0.7,
|
21 |
width: int = 1024,
|
22 |
+
height: int = 1024,
|
23 |
) -> Optional[Image.Image]:
|
24 |
+
"""Generate a single image from a text prompt using FLUX.1-Krea-dev.
|
|
|
25 |
|
26 |
+
Contract (for UI):
|
27 |
+
- Inputs: prompt (required), optional tuning params below. No input image used.
|
28 |
+
- Output: a PIL.Image for Gradio image component wiring.
|
29 |
+
- Errors: raises gr.Error on auth/model/service issues.
|
30 |
|
31 |
Args:
|
32 |
+
prompt: Required. Describes what to create. Keep it specific and concise.
|
33 |
+
negative_prompt: Phrases/objects to avoid in the output.
|
34 |
+
steps: Number of diffusion steps (1-100). Higher may improve detail but is slower.
|
35 |
+
cfg_scale: Classifier-free guidance scale (1-20). Higher forces closer adherence to prompt.
|
36 |
+
sampler: Sampler algorithm label (UI only; provider may ignore or auto-select).
|
37 |
+
seed: Set a deterministic seed (>=0). Use -1 to randomize per call.
|
38 |
+
strength: Kept for parity; has no effect without an input image (0-1).
|
39 |
+
width: Output width in pixels (64-1216). Prefer multiples of 32.
|
40 |
+
height: Output height in pixels (64-1216). Prefer multiples of 32.
|
41 |
|
42 |
Returns:
|
43 |
+
PIL.Image or None if prompt is empty.
|
44 |
"""
|
45 |
if prompt == "" or prompt is None:
|
46 |
return None
|
|
|
96 |
raise gr.Error(f"Image generation failed: {str(e)}")
|
97 |
return None
|
98 |
|
99 |
+
|
100 |
+
def _space_base_url() -> Optional[str]:
|
101 |
+
"""Return the public base URL of this Space if available.
|
102 |
+
|
103 |
+
Looks up SPACE_ID (e.g. "username/space-name") and converts it to
|
104 |
+
the public subdomain "https://username-space-name.hf.space".
|
105 |
+
If SPACE_ID is not set, optionally respects HF_SPACE_BASE_URL.
|
106 |
+
"""
|
107 |
+
# Explicit override if provided
|
108 |
+
explicit = os.getenv("HF_SPACE_BASE_URL")
|
109 |
+
if explicit:
|
110 |
+
return explicit.rstrip("/")
|
111 |
+
|
112 |
+
space_id = os.getenv("SPACE_ID")
|
113 |
+
if not space_id:
|
114 |
+
return None
|
115 |
+
sub = space_id.replace("/", "-")
|
116 |
+
return f"https://{sub}.hf.space"
|
117 |
+
|
118 |
+
|
119 |
+
def _save_image_and_url(image: Image.Image, key: int) -> Tuple[str, Optional[str]]:
|
120 |
+
"""Save the image to a temporary path and construct a public URL if running on Spaces.
|
121 |
+
|
122 |
+
Returns (local_path, public_url_or_None).
|
123 |
+
"""
|
124 |
+
# Ensure POSIX-like temp dir (Spaces is Linux). Still works locally.
|
125 |
+
out_dir = os.path.join("/tmp", "flux-krea-outputs")
|
126 |
+
os.makedirs(out_dir, exist_ok=True)
|
127 |
+
file_path = os.path.join(out_dir, f"flux_{key}.png")
|
128 |
+
image.save(file_path)
|
129 |
+
|
130 |
+
base_url = _space_base_url()
|
131 |
+
public_url = None
|
132 |
+
if base_url:
|
133 |
+
# Gradio serves local files via /file=<absolute-path>
|
134 |
+
# Normalize backslashes to forward slashes in case of local dev on Windows
|
135 |
+
posix_path = file_path.replace("\\", "/")
|
136 |
+
public_url = f"{base_url}/file={posix_path}"
|
137 |
+
|
138 |
+
return file_path, public_url
|
139 |
+
|
140 |
+
|
141 |
+
def flux_krea_generate_mcp(
|
142 |
+
prompt: str,
|
143 |
+
negative_prompt: str = "(deformed, distorted, disfigured), poorly drawn, bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, (mutated hands and fingers), disconnected limbs, mutation, mutated, ugly, disgusting, blurry, amputation, misspellings, typos",
|
144 |
+
steps: int = 35,
|
145 |
+
cfg_scale: float = 7,
|
146 |
+
sampler: str = "DPM++ 2M Karras",
|
147 |
+
seed: int = -1,
|
148 |
+
strength: float = 0.7,
|
149 |
+
width: int = 1024,
|
150 |
+
height: int = 1024,
|
151 |
+
) -> Dict[str, object]:
|
152 |
+
"""Generate an image (MCP tool) and return a JSON payload with a public URL.
|
153 |
+
|
154 |
+
This endpoint is tailored for Model Context Protocol (MCP) clients per the
|
155 |
+
latest Hugging Face MCP Space guidance (see hf-docs-search: "Spaces as MCP servers").
|
156 |
+
|
157 |
+
Inputs:
|
158 |
+
- prompt (str, required): Description of the desired image.
|
159 |
+
- negative_prompt (str): Items to avoid in the generation.
|
160 |
+
- steps (int, 1-100): Denoising steps. Higher is slower and may add detail.
|
161 |
+
- cfg_scale (float, 1-20): Guidance strength. Higher adheres more to the prompt.
|
162 |
+
- sampler (str): Sampler label (informational; provider may auto-select).
|
163 |
+
- seed (int): -1 for random per call; otherwise a deterministic seed >= 0.
|
164 |
+
- strength (float, 0-1): No-op in pure text-to-image; kept for cross-app parity.
|
165 |
+
- width (int, 64-1216): Output width (prefer multiples of 32).
|
166 |
+
- height (int, 64-1216): Output height (prefer multiples of 32).
|
167 |
+
|
168 |
+
Returns (JSON):
|
169 |
+
- image_path (str): Absolute path to the saved image on the Space VM.
|
170 |
+
- image_url (str|None): Publicly accessible URL to the image on the Space
|
171 |
+
(present when running on Spaces; None when running locally).
|
172 |
+
- seed (int): The seed used for this run (randomized if input was -1).
|
173 |
+
- width (int), height (int): Echo of output dimensions.
|
174 |
+
- sampler (str): Echo of requested sampler.
|
175 |
+
|
176 |
+
Error Modes:
|
177 |
+
- Raises a Gradio-friendly error with a concise message for common HTTP
|
178 |
+
failure codes (401/403 auth; 404 model; 503 warmup).
|
179 |
+
"""
|
180 |
+
if not prompt:
|
181 |
+
raise gr.Error("'prompt' is required and cannot be empty.")
|
182 |
+
|
183 |
+
# Reuse core generator for image creation
|
184 |
+
image = flux_krea_generate(
|
185 |
+
prompt=prompt,
|
186 |
+
negative_prompt=negative_prompt,
|
187 |
+
steps=steps,
|
188 |
+
cfg_scale=cfg_scale,
|
189 |
+
sampler=sampler,
|
190 |
+
seed=seed,
|
191 |
+
strength=strength,
|
192 |
+
width=width,
|
193 |
+
height=height,
|
194 |
+
)
|
195 |
+
|
196 |
+
if image is None:
|
197 |
+
raise gr.Error("No image generated.")
|
198 |
+
|
199 |
+
# Save and build URLs
|
200 |
+
key = random.randint(0, 999)
|
201 |
+
file_path, public_url = _save_image_and_url(image, key)
|
202 |
+
|
203 |
+
# Expose URL explicitly for MCP clients (LLMs need a resolvable URL)
|
204 |
+
return {
|
205 |
+
"image_path": file_path,
|
206 |
+
"image_url": public_url,
|
207 |
+
"seed": seed,
|
208 |
+
"width": width,
|
209 |
+
"height": height,
|
210 |
+
"sampler": sampler,
|
211 |
+
}
|
212 |
+
|
213 |
# CSS to style the app
|
214 |
css = """
|
215 |
#app-container {
|
|
|
266 |
)
|
267 |
|
268 |
# Expose a dedicated MCP/API endpoint with a clear schema (text-to-image only)
|
269 |
+
# This wrapper returns both a local file path and a fully-qualified public URL
|
270 |
+
# when running on Spaces so LLMs can access the finished image.
|
271 |
gr.api(
|
272 |
+
flux_krea_generate_mcp,
|
273 |
api_name="generate_image",
|
274 |
api_description=(
|
275 |
"Generate an image from a text prompt using FLUX.1-Krea-dev. "
|
276 |
+
"Returns JSON with image_path and image_url (public URL when on Spaces)."
|
277 |
),
|
278 |
)
|
279 |
|