Nymbo commited on
Commit
06ff1bc
·
verified ·
1 Parent(s): 45fc100

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -21
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
- This tool generates a single image from a text prompt using the
29
- black-forest-labs/FLUX.1-Krea-dev model on Hugging Face Inference.
 
 
30
 
31
  Args:
32
- prompt: Text description of the image to generate.
33
- negative_prompt: What should NOT appear in the image.
34
- steps: Number of denoising steps (1-100). Higher is slower but can improve quality.
35
- cfg_scale: Classifier-free guidance scale (1-20). Higher = follow the prompt more closely.
36
- sampler: Sampling method to use. One of: "DPM++ 2M Karras", "DPM++ SDE Karras", "Euler", "Euler a", "Heun", "DDIM".
37
- seed: Random seed for reproducible results. Use -1 for a random seed per call.
38
- strength: Generation strength (0-1). Kept for parity; not an input image strength.
39
- width: Output width in pixels (64-1216, multiple of 32 recommended).
40
- height: Output height in pixels (64-1216, multiple of 32 recommended).
41
 
42
  Returns:
43
- A PIL.Image of the generated result. No input image is expected or required.
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 avoids clients misinterpreting the UI event as requiring an input image.
 
156
  gr.api(
157
- flux_krea_generate,
158
  api_name="generate_image",
159
  api_description=(
160
  "Generate an image from a text prompt using FLUX.1-Krea-dev. "
161
- "Inputs are text and numeric parameters only; no input image is required."
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