xAI / app.py
aexyb's picture
Update app.py
fd79a72 verified
Raw History Blame Contribute Delete
14.9 kB
"""
FLUX.2-klein-4B GGUF โ€” CPU-only Inference Space
็กฌไปถ๏ผšๆ—  GPU ยท CPU ยท 18 GB RAM
"""
import os
import sys
import json
import torch
import numpy as np
from huggingface_hub import login, hf_hub_download, list_repo_files
# โ”€โ”€ ็Žฏๅขƒ้…็ฝฎ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
os.environ["TOKENIZERS_PARALLELISM"] = "false"
torch.set_num_threads(os.cpu_count() or 4)
GGUF_REPO = "unsloth/FLUX-2-klein-4B-GGUF"
TOKEN = os.environ.get("HF_TOKEN", "")
# โ”€โ”€ ่ฎค่ฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
print("โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—")
print("โ•‘ FLUX.2-klein-4B GGUF โ€” CPU Inference โ•‘")
print("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")
if TOKEN:
login(token=TOKEN, add_to_git_credential=False)
print(" โœ“ HF_TOKEN authenticated")
else:
print(" โœ— HF_TOKEN not set!")
print(" โ†’ Go to Space Settings โ†’ Repository Secrets โ†’ Add HF_TOKEN")
print(" โ†’ Get token: https://huggingface.co/settings/tokens")
print(" โ†’ Also accept model license on the model page first!\n")
print(f" Model : {GGUF_REPO}")
print(f" Threads : {torch.get_num_threads()}")
print(f" PyTorch : {torch.__version__}")
# โ”€โ”€ ๅŠ ่ฝฝๆจกๅž‹๏ผˆๅŒ้‡็ญ–็•ฅ๏ผ‰ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
from diffusers import FluxPipeline, FluxTransformer2DModel
pipe = None
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# Strategy A๏ผš็›ดๆŽฅๅŠ ่ฝฝ๏ผˆdiffusers ๅŽŸ็”Ÿ GGUF ๆ”ฏๆŒ๏ผ‰
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
if pipe is None:
try:
print("\n[A] Trying direct diffusers loading...")
pipe = FluxPipeline.from_pretrained(
GGUF_REPO,
torch_dtype=torch.float32,
token=TOKEN or None,
)
pipe.to("cpu")
print("[A] โœ“ Direct loading succeeded!\n")
except Exception as e:
print(f"[A] โœ— Failed: {e}\n")
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# Strategy B๏ผšๆ‰‹ๅŠจ่งฃๆž GGUF โ†’ ๆณจๅ…ฅ transformer
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
if pipe is None:
try:
print("[B] Manual GGUF loading...")
from gguf import GGUFReader
# 1) ๅˆ—ๅ‡บไป“ๅบ“ๆ–‡ไปถ๏ผŒๆ‰พๅˆฐ .gguf
print(" Listing repo files...")
repo_files = list_repo_files(GGUF_REPO, token=TOKEN or None)
gguf_candidates = [f for f in repo_files if f.endswith(".gguf")]
if not gguf_candidates:
raise FileNotFoundError("No .gguf file found in repo")
# ไผ˜ๅ…ˆ้€‰ Q4_K_M๏ผˆ็ฒพๅบฆไธŽ้€Ÿๅบฆๅนณ่กก๏ผ‰๏ผŒๆฒกๆœ‰ๅˆ™้€‰็ฌฌไธ€ไธช
preferred = [f for f in gguf_candidates if "Q4_K_M" in f.upper()]
gguf_fname = preferred[0] if preferred else gguf_candidates[0]
print(f" Selected: {gguf_fname}")
# 2) ไธ‹่ฝฝ GGUF ๆ–‡ไปถ
print(" Downloading GGUF file...")
gguf_path = hf_hub_download(
GGUF_REPO, gguf_fname, token=TOKEN or None
)
print(f" Saved to: {gguf_path}")
# 3) ่ฏปๅ– GGUF ๅผ ้‡
print(" Reading GGUF tensors...")
reader = GGUFReader(gguf_path)
gguf_sd = {}
for tensor in reader.tensors:
name = tensor.name
if isinstance(name, bytes):
name = name.decode("utf-8")
data = np.array(tensor.data, dtype=np.float32)
data = data.reshape(tensor.shape).copy()
gguf_sd[name] = torch.from_numpy(data)
print(f" Loaded {len(gguf_sd)} tensors")
sample_keys = list(gguf_sd.keys())[:5]
print(f" Sample keys: {sample_keys}")
# 4) ไธ‹่ฝฝ transformer config
print(" Loading transformer config...")
config_path = hf_hub_download(
GGUF_REPO, "transformer/config.json", token=TOKEN or None
)
with open(config_path, "r") as f:
tf_config = json.load(f)
# 5) ๅˆ›ๅปบ็ฉบ transformer ๅนถๅŠ ่ฝฝ GGUF ๆƒ้‡
print(" Building transformer and loading weights...")
transformer = FluxTransformer2DModel.from_config(tf_config)
# ๅฐ่ฏ•ๅคš็ง key ๆ˜ ๅฐ„
loaded_ok = False
# B1: ็›ดๆŽฅๆ˜ ๅฐ„
try:
info = transformer.load_state_dict(gguf_sd, strict=False)
missing = [k for k in info.missing_keys if "bias" not in k.lower()]
if len(missing) < 5:
loaded_ok = True
print(f" B1 โœ“ Direct mapping (missing={len(missing)})")
except Exception:
pass
# B2: ๅŠ  "transformer." ๅ‰็ผ€
if not loaded_ok:
try:
mapped = {"transformer." + k: v for k, v in gguf_sd.items()}
info = transformer.load_state_dict(mapped, strict=False)
missing = [k for k in info.missing_keys if "bias" not in k.lower()]
if len(missing) < 5:
loaded_ok = True
print(f" B2 โœ“ Prefix mapping (missing={len(missing)})")
except Exception:
pass
# B3: ๅŽปๆމๅทฒๆœ‰ๅ‰็ผ€ๅ†ๆ˜ ๅฐ„
if not loaded_ok:
try:
mapped = {}
for k, v in gguf_sd.items():
new_key = k
for prefix in ["transformer.", "model."]:
if k.startswith(prefix):
new_key = k[len(prefix):]
break
mapped[new_key] = v
info = transformer.load_state_dict(mapped, strict=False)
missing = [k for k in info.missing_keys if "bias" not in k.lower()]
if len(missing) < 5:
loaded_ok = True
print(f" B3 โœ“ Strip-prefix mapping (missing={len(missing)})")
except Exception:
pass
if not loaded_ok:
# ๆ‰“ๅฐ่ฏŠๆ–ญไฟกๆฏๅธฎๅŠฉ่ฐƒ่ฏ•
model_keys = set(transformer.state_dict().keys())
gguf_keys = set(gguf_sd.keys())
print(f"\n โ”€โ”€ Key mismatch diagnostics โ”€โ”€")
print(f" Model expects {len(model_keys)} keys")
print(f" GGUF has {len(gguf_keys)} keys")
print(f" Model key sample : {list(model_keys)[:3]}")
print(f" GGUF key sample : {list(gguf_keys)[:3]}")
raise RuntimeError("Could not map GGUF keys to model keys")
# 6) ๆž„ๅปบๅฎŒๆ•ด pipeline๏ผˆไผ ๅ…ฅ transformer ่ทณ่ฟ‡็ฃ็›˜ๅŠ ่ฝฝ๏ผ‰
print(" Building full pipeline...")
pipe = FluxPipeline.from_pretrained(
GGUF_REPO,
transformer=transformer,
torch_dtype=torch.float32,
token=TOKEN or None,
)
pipe.to("cpu")
print("[B] โœ“ Manual GGUF loading succeeded!\n")
except Exception as e:
print(f"[B] โœ— Failed: {e}")
import traceback
traceback.print_exc()
print()
# โ”€โ”€ ๆœ€็ปˆๆฃ€ๆŸฅ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if pipe is None:
print("=" * 58)
print("FATAL: Could not load model.")
print("")
print("Please check:")
print(" 1. HF_TOKEN is set as a Space secret")
print(" 2. You have accepted the model license at:")
print(f" https://huggingface.co/{GGUF_REPO}")
print("=" * 58)
sys.exit(1)
print("โœ“ Model ready for inference!\n")
# โ”€โ”€ ๆŽจ็†ๅ‡ฝๆ•ฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@torch.no_grad()
def generate(
prompt: str,
negative_prompt: str,
width: int,
height: int,
steps: int,
guidance: float,
seed: int,
progress=gr.Progress(track_tqdm=True),
):
if not prompt.strip():
raise gr.Error("่ฏท่พ“ๅ…ฅๆ็คบ่ฏ")
seed = seed if seed >= 0 else int(time.time() * 1000) % (2**32)
generator = torch.Generator("cpu").manual_seed(seed)
t0 = time.time()
result = pipe(
prompt=prompt,
negative_prompt=negative_prompt if negative_prompt.strip() else None,
width=width,
height=height,
num_inference_steps=steps,
guidance_scale=guidance,
generator=generator,
)
dt = time.time() - t0
return result.images[0], seed, f"ๅฎŒๆˆ ยท ่€—ๆ—ถ {dt:.1f}s ยท seed {seed}"
# โ”€โ”€ ็•Œ้ขๆ ทๅผ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
CSS = """
:root{--bg:#0a0a0f;--card:#13131a;--border:#23233a;--fg:#e4e4ec;--muted:#6b6b80;--accent:#7c5cfc;--glow:rgba(124,92,252,.25)}
.gradio-container{background:var(--bg)!important;color:var(--fg)!important;max-width:1080px!important;padding-top:1rem!important}
.gr-block,.gr-panel{background:var(--card)!important;border:1px solid var(--border)!important;border-radius:14px!important}
.gr-input,textarea.gr-textbox,input[type=number].gr-number{background:#0e0e16!important;border:1px solid var(--border)!important;color:var(--fg)!important;border-radius:10px!important;font-size:.92rem!important}
textarea.gr-textbox{resize:vertical!important}
.gr-label{color:var(--muted)!important;font-size:.78rem!important;text-transform:uppercase!important;letter-spacing:.06em!important;font-weight:600!important}
.gr-button-primary{background:linear-gradient(135deg,#7c5cfc,#a78bfa)!important;border:none!important;font-weight:700!important;font-size:1rem!important;border-radius:10px!important;padding:.7rem 1.5rem!important;box-shadow:0 4px 24px var(--glow)!important;transition:all .2s ease!important}
.gr-button-primary:hover{transform:translateY(-1px)!important;box-shadow:0 6px 32px var(--glow)!important;filter:brightness(1.1)!important}
.gr-slider input[type=range]{accent-color:var(--accent)!important}
.gr-image{border-radius:12px!important;overflow:hidden!important}
footer{display:none!important}
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#333;border-radius:3px}
.warn-bar{background:linear-gradient(90deg,#1c1a2e,#1a1428);border:1px solid #2e2848;border-radius:10px;padding:.7rem 1rem;margin-bottom:1.2rem;font-size:.82rem;color:#a0a0b8;line-height:1.5}
.warn-bar strong{color:#c4b5fd}
.info-bar{background:#0e0e16;border:1px solid var(--border);border-radius:8px;padding:.5rem .8rem;font-size:.82rem;color:var(--muted);font-family:'SF Mono','Fira Code',monospace}
"""
import time
import gradio as gr
with gr.Blocks(title="FLUX.2-klein-4B GGUF", css=CSS, theme=gr.themes.Soft(primary_hue="violet")) as demo:
gr.HTML("""
<div style="text-align:center;padding:2rem 0 .6rem">
<h1 style="font-size:2.4rem;font-weight:800;letter-spacing:-.03em;
background:linear-gradient(135deg,#7c5cfc 0%,#c084fc 50%,#f0abfc 100%);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;
margin:0;line-height:1.2">FLUX.2-klein-4B</h1>
<p style="color:#555;font-size:.88rem;margin-top:.5rem;font-weight:500;
letter-spacing:.08em;text-transform:uppercase">
GGUF Quantized ยท CPU Inference ยท 4B Parameters</p>
</div>""")
gr.HTML("""
<div class="warn-bar">
&#9888;&nbsp; ็บฏ CPU ๆŽจ็†๏ผŒ้€Ÿๅบฆ่พƒๆ…ขใ€‚ๅปบ่ฎฎ <strong>512ร—512</strong> ไปฅไธ‹ +
<strong>8~12 ๆญฅ</strong>ใ€‚้ฆ–ๆฌก็”Ÿๆˆๅ›  JIT ็ผ–่ฏ‘ๆ›ดๆ…ข๏ผŒๅŽ็ปญๅŠ ้€Ÿใ€‚
่ถ…ๆ—ถ่ฏท้™ไฝŽๅˆ†่พจ็އๆˆ–ๆญฅๆ•ฐใ€‚
</div>""")
with gr.Row(equal_height=False):
with gr.Column(scale=3):
prompt = gr.Textbox(label="Prompt", placeholder="A cyberpunk city at night with neon signs reflecting in rain-soaked streets...", lines=3)
negative_prompt = gr.Textbox(label="Negative Prompt", placeholder="blurry, low quality, distorted...", lines=1)
with gr.Row():
w = gr.Slider(256, 768, value=512, step=64, label="Width")
h = gr.Slider(256, 768, value=512, step=64, label="Height")
with gr.Row():
steps = gr.Slider(4, 30, value=10, step=1, label="Steps")
guidance = gr.Slider(1.0, 10.0, value=3.5, step=0.5, label="Guidance Scale")
seed = gr.Number(label="Seed๏ผˆ-1 ้šๆœบ๏ผ‰", value=-1, precision=0)
btn = gr.Button("โœฆ Generate", variant="primary", size="lg")
gr.Examples(examples=[
["A serene Japanese garden with cherry blossoms over a stone bridge, koi pond, golden hour, 4K", ""],
["Futuristic space station interior, holographic star maps, bioluminescent plants, cinematic wide shot", ""],
["Oil painting of a cozy wooden cabin in snowy mountains, warm light from windows, impressionist style", ""],
["Macro photograph of a dewdrop on a spider web at sunrise, prismatic light, shallow depth of field", ""],
["Whimsical treehouse library at twilight, floating books with glowing pages, fairy tale atmosphere", "blurry"],
["Underwater coral reef city, bioluminescent architecture, tropical fish, volumetric god rays", ""],
], inputs=[prompt, negative_prompt], label="Quick Examples")
with gr.Column(scale=4):
output_img = gr.Image(label="Output", show_label=False, height=540)
info_text = gr.Textbox(label="Status", interactive=False, max_lines=1, elem_classes="info-bar", value="Ready")
btn.click(fn=generate, inputs=[prompt, negative_prompt, w, h, steps, guidance, seed], outputs=[output_img, seed, info_text])
prompt.submit(fn=generate, inputs=[prompt, negative_prompt, w, h, steps, guidance, seed], outputs=[output_img, seed, info_text])
demo.launch(server_name="0.0.0.0", server_port=7860)