Spaces:
Running
Running
File size: 12,231 Bytes
e547b24 06ff1bc 764029a e547b24 974dc33 06ff1bc 974dc33 dd21ab3 974dc33 dd21ab3 06ff1bc dd21ab3 06ff1bc b5176b4 06ff1bc b5176b4 dd21ab3 06ff1bc b5176b4 dd21ab3 06ff1bc dd21ab3 974dc33 e547b24 974dc33 e547b24 764029a e547b24 764029a e547b24 06ff1bc 4d6cbec e547b24 02f8cfa 4d6cbec 02f8cfa 73f7edc e547b24 4d6cbec 02f8cfa 4d6cbec 030414b 974dc33 4d6cbec 02f8cfa 4d6cbec 02f8cfa bc84ac0 4d6cbec 02f8cfa bc84ac0 4d6cbec 02f8cfa 4d6cbec e547b24 4d6cbec 02f8cfa 4d6cbec 02f8cfa b5176b4 e547b24 b5176b4 06ff1bc b5176b4 06ff1bc b5176b4 06ff1bc b5176b4 e547b24 974dc33 76ebcb5 |
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 274 275 276 277 278 279 280 281 |
import gradio as gr
import random
import os
from PIL import Image
from typing import Optional, Dict, Tuple
from huggingface_hub import InferenceClient
# Project by Nymbo
API_TOKEN = os.getenv("HF_READ_TOKEN")
timeout = 100
def flux_krea_generate(
prompt: str,
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",
steps: int = 35,
cfg_scale: float = 7.0,
sampler: str = "DPM++ 2M Karras",
seed: int = -1,
strength: float = 0.7,
width: int = 1024,
height: int = 1024,
) -> Optional[Image.Image]:
"""Generate a single image from a text prompt using FLUX.1-Krea-dev.
Contract (for UI):
- Inputs: prompt (required), optional tuning params below. No input image used.
- Output: a PIL.Image for Gradio image component wiring.
- Errors: raises gr.Error on auth/model/service issues.
Args:
prompt: Required. Describes what to create. Keep it specific and concise.
negative_prompt: Phrases/objects to avoid in the output.
steps: Number of diffusion steps (1-100). Higher may improve detail but is slower.
cfg_scale: Classifier-free guidance scale (1-20). Higher forces closer adherence to prompt.
sampler: Sampler algorithm label (UI only; provider may ignore or auto-select).
seed: Set a deterministic seed (>=0). Use -1 to randomize per call.
strength: Kept for parity; has no effect without an input image (0-1).
width: Output width in pixels (64-1216). Prefer multiples of 32.
height: Output height in pixels (64-1216). Prefer multiples of 32.
Returns:
PIL.Image or None if prompt is empty.
"""
if prompt == "" or prompt is None:
return None
key = random.randint(0, 999)
# Add some extra flair to the prompt
enhanced_prompt = f"{prompt} | ultra detail, ultra elaboration, ultra quality, perfect."
print(f'\033[1mGeneration {key}:\033[0m {enhanced_prompt}')
try:
# Initialize the Hugging Face Inference Client
# Try different providers in order of preference
providers = ["auto", "replicate", "fal-ai"]
for provider in providers:
try:
client = InferenceClient(
api_key=API_TOKEN,
provider=provider
)
# Generate the image using the proper client
image = client.text_to_image(
prompt=enhanced_prompt,
negative_prompt=negative_prompt,
model="black-forest-labs/FLUX.1-Krea-dev",
width=width,
height=height,
num_inference_steps=steps,
guidance_scale=cfg_scale,
seed=seed if seed != -1 else random.randint(1, 1000000000)
)
print(f'\033[1mGeneration {key} completed with {provider}!\033[0m ({enhanced_prompt})')
return image
except Exception as provider_error:
print(f"Provider {provider} failed: {provider_error}")
if provider == providers[-1]: # Last provider
raise provider_error
continue
except Exception as e:
print(f"Error during image generation: {e}")
if "404" in str(e):
raise gr.Error("Model not found. Please ensure the FLUX.1-Krea-dev model is accessible with your API token.")
elif "503" in str(e):
raise gr.Error("The model is currently being loaded. Please try again in a moment.")
elif "401" in str(e) or "403" in str(e):
raise gr.Error("Authentication failed. Please check your HF_READ_TOKEN environment variable.")
else:
raise gr.Error(f"Image generation failed: {str(e)}")
return None
def _space_base_url() -> Optional[str]:
"""Return the public base URL of this Space if available.
Looks up SPACE_ID (e.g. "username/space-name") and converts it to
the public subdomain "https://username-space-name.hf.space".
If SPACE_ID is not set, optionally respects HF_SPACE_BASE_URL.
"""
# Explicit override if provided
explicit = os.getenv("HF_SPACE_BASE_URL")
if explicit:
return explicit.rstrip("/")
space_id = os.getenv("SPACE_ID")
if not space_id:
return None
sub = space_id.replace("/", "-")
return f"https://{sub}.hf.space"
def _save_image_and_url(image: Image.Image, key: int) -> Tuple[str, Optional[str]]:
"""Save the image to a temporary path and construct a public URL if running on Spaces.
Returns (local_path, public_url_or_None).
"""
# Ensure POSIX-like temp dir (Spaces is Linux). Still works locally.
out_dir = os.path.join("/tmp", "flux-krea-outputs")
os.makedirs(out_dir, exist_ok=True)
file_path = os.path.join(out_dir, f"flux_{key}.png")
image.save(file_path)
base_url = _space_base_url()
public_url = None
if base_url:
# Gradio serves local files via /file=<absolute-path>
# Normalize backslashes to forward slashes in case of local dev on Windows
posix_path = file_path.replace("\\", "/")
public_url = f"{base_url}/file={posix_path}"
return file_path, public_url
def flux_krea_generate_mcp(
prompt: str,
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",
steps: int = 35,
cfg_scale: float = 7,
sampler: str = "DPM++ 2M Karras",
seed: int = -1,
strength: float = 0.7,
width: int = 1024,
height: int = 1024,
) -> Dict[str, object]:
"""Generate an image (MCP tool) and return a JSON payload with a public URL.
This endpoint is tailored for Model Context Protocol (MCP) clients per the
latest Hugging Face MCP Space guidance (see hf-docs-search: "Spaces as MCP servers").
Inputs:
- prompt (str, required): Description of the desired image.
- negative_prompt (str): Items to avoid in the generation.
- steps (int, 1-100): Denoising steps. Higher is slower and may add detail.
- cfg_scale (float, 1-20): Guidance strength. Higher adheres more to the prompt.
- sampler (str): Sampler label (informational; provider may auto-select).
- seed (int): -1 for random per call; otherwise a deterministic seed >= 0.
- strength (float, 0-1): No-op in pure text-to-image; kept for cross-app parity.
- width (int, 64-1216): Output width (prefer multiples of 32).
- height (int, 64-1216): Output height (prefer multiples of 32).
Returns (JSON):
- image_path (str): Absolute path to the saved image on the Space VM.
- image_url (str|None): Publicly accessible URL to the image on the Space
(present when running on Spaces; None when running locally).
- seed (int): The seed used for this run (randomized if input was -1).
- width (int), height (int): Echo of output dimensions.
- sampler (str): Echo of requested sampler.
Error Modes:
- Raises a Gradio-friendly error with a concise message for common HTTP
failure codes (401/403 auth; 404 model; 503 warmup).
"""
if not prompt:
raise gr.Error("'prompt' is required and cannot be empty.")
# Reuse core generator for image creation
image = flux_krea_generate(
prompt=prompt,
negative_prompt=negative_prompt,
steps=steps,
cfg_scale=cfg_scale,
sampler=sampler,
seed=seed,
strength=strength,
width=width,
height=height,
)
if image is None:
raise gr.Error("No image generated.")
# Save and build URLs
key = random.randint(0, 999)
file_path, public_url = _save_image_and_url(image, key)
# Expose URL explicitly for MCP clients (LLMs need a resolvable URL)
return {
"image_path": file_path,
"image_url": public_url,
"seed": seed,
"width": width,
"height": height,
"sampler": sampler,
}
# CSS to style the app
css = """
#app-container {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
"""
# Build the Gradio UI with Blocks
with gr.Blocks(theme='Nymbo/Nymbo_Theme', css=css) as app:
# Add a title to the app
gr.HTML("<center><h1>FLUX.1-Krea-dev</h1></center>")
gr.HTML("<center><p>High-quality image generation via Model Context Protocol</p></center>")
# Container for all the UI elements
with gr.Column(elem_id="app-container"):
# Add a text input for the main prompt
with gr.Row():
with gr.Column(elem_id="prompt-container"):
with gr.Row():
text_prompt = gr.Textbox(label="Prompt", placeholder="Enter a prompt here", lines=2, elem_id="prompt-text-input")
# Accordion for advanced settings
with gr.Row():
with gr.Accordion("Advanced Settings", open=False):
negative_prompt = gr.Textbox(label="Negative Prompt", placeholder="What should not be in the image", value="(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", lines=3, elem_id="negative-prompt-text-input")
with gr.Row():
width = gr.Slider(label="Width", value=1024, minimum=64, maximum=1216, step=32)
height = gr.Slider(label="Height", value=1024, minimum=64, maximum=1216, step=32)
steps = gr.Slider(label="Sampling steps", value=35, minimum=1, maximum=100, step=1)
cfg = gr.Slider(label="CFG Scale", value=7, minimum=1, maximum=20, step=1)
strength = gr.Slider(label="Strength", value=0.7, minimum=0, maximum=1, step=0.001)
seed = gr.Slider(label="Seed", value=-1, minimum=-1, maximum=1000000000, step=1) # Setting the seed to -1 will make it random
method = gr.Radio(label="Sampling method", value="DPM++ 2M Karras", choices=["DPM++ 2M Karras", "DPM++ SDE Karras", "Euler", "Euler a", "Heun", "DDIM"])
# Add a button to trigger the image generation
with gr.Row():
text_button = gr.Button("Run", variant='primary', elem_id="gen-button")
# Image output area to display the generated image
with gr.Row():
# Output component only; no input image is required by the tool
image_output = gr.Image(label="Image Output", elem_id="gallery")
# Bind the button to the flux_krea_generate function for the UI only
# Hide this event as an MCP tool to avoid schema confusion (UI wires image output)
text_button.click(
flux_krea_generate,
inputs=[text_prompt, negative_prompt, steps, cfg, method, seed, strength, width, height],
outputs=image_output,
show_api=False,
api_description=False,
)
# Expose a dedicated MCP/API endpoint with a clear schema (text-to-image only)
# This wrapper returns both a local file path and a fully-qualified public URL
# when running on Spaces so LLMs can access the finished image.
gr.api(
flux_krea_generate_mcp,
api_name="generate_image",
api_description=(
"Generate an image from a text prompt using FLUX.1-Krea-dev. "
"Returns JSON with image_path and image_url (public URL when on Spaces)."
),
)
# Launch the Gradio app with MCP server enabled
app.launch(show_api=True, share=False, mcp_server=True) |