Spaces:
Sleeping
Sleeping
Deploy from GitHub Actions
Browse files- README.md +26 -5
- app.py +127 -0
- requirements.txt +15 -0
README.md
CHANGED
|
@@ -1,13 +1,34 @@
|
|
| 1 |
---
|
| 2 |
title: Cryptid Cannon
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
-
python_version: '3.13'
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: Cryptid Cannon
|
| 3 |
+
emoji: 🔮
|
| 4 |
+
colorFrom: purple
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 5.49.1
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
suggested_hardware: zero-a10g
|
| 12 |
+
short_description: Fire the cannon, summon a random absurd cryptid on klein 4B
|
| 13 |
+
tags:
|
| 14 |
+
- build-small-hackathon
|
| 15 |
+
- thousand-token-wood
|
| 16 |
+
- tiny-titan
|
| 17 |
+
- flux
|
| 18 |
+
- text-to-image
|
| 19 |
---
|
| 20 |
|
| 21 |
+
# 🔮 Cryptid Cannon
|
| 22 |
+
|
| 23 |
+
A deliberately silly [Build Small](https://huggingface.co/build-small-hackathon)
|
| 24 |
+
toy. Press the button, summon a random absurd cryptid — a marshmallow golem mid-
|
| 25 |
+
sneeze, a Victorian frog detective drinking tea — rendered by **FLUX.2 [klein]
|
| 26 |
+
4B** (4 billion parameters, Apache 2.0, ~1s per image on the Space GPU).
|
| 27 |
+
|
| 28 |
+
Leave the box empty for pure chaos, or whisper your own creature.
|
| 29 |
+
|
| 30 |
+
This is a quick smoke-test of the deploy pipeline — not a serious competition
|
| 31 |
+
entry. Built on the [klein starter](https://huggingface.co/spaces/stephenbtl/klein-build-small-starter).
|
| 32 |
+
|
| 33 |
+
**Model:** [`FLUX.2-klein-4B`](https://huggingface.co/black-forest-labs/FLUX.2-klein-4B)
|
| 34 |
+
· well under the hackathon's 32B cap (and under 4B-badge territory).
|
app.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
🔮 Cryptid Cannon — fire the cannon, summon a random absurd cryptid.
|
| 3 |
+
|
| 4 |
+
A deliberately silly FLUX.2 [klein] 4B toy for the Build Small hackathon.
|
| 5 |
+
One button, one chaotic creature. Built on the klein starter's verified
|
| 6 |
+
ZeroGPU + pipeline pattern.
|
| 7 |
+
"""
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import random
|
| 11 |
+
import time
|
| 12 |
+
|
| 13 |
+
# --- ZeroGPU shim: import `spaces` BEFORE torch -----------------------------
|
| 14 |
+
try:
|
| 15 |
+
import spaces # type: ignore
|
| 16 |
+
|
| 17 |
+
GPU = spaces.GPU
|
| 18 |
+
except Exception: # local / non-ZeroGPU fallback
|
| 19 |
+
|
| 20 |
+
def GPU(*dargs, **dkwargs): # noqa: N802
|
| 21 |
+
if len(dargs) == 1 and callable(dargs[0]) and not dkwargs:
|
| 22 |
+
return dargs[0]
|
| 23 |
+
|
| 24 |
+
def wrap(fn):
|
| 25 |
+
return fn
|
| 26 |
+
|
| 27 |
+
return wrap
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
import gradio as gr
|
| 31 |
+
import torch
|
| 32 |
+
from diffusers import Flux2KleinPipeline
|
| 33 |
+
|
| 34 |
+
MODEL_ID = "black-forest-labs/FLUX.2-klein-4B" # 4B, Apache 2.0, ungated
|
| 35 |
+
STEPS = 4
|
| 36 |
+
GUIDANCE = 1.0
|
| 37 |
+
MAX_SEED = 2**31 - 1
|
| 38 |
+
|
| 39 |
+
# Build on CPU at module scope (ZeroGPU pattern), but guard the load so the UI
|
| 40 |
+
# still boots on tiny hardware instead of showing a blank runtime error.
|
| 41 |
+
pipe = None
|
| 42 |
+
LOAD_ERR = ""
|
| 43 |
+
try:
|
| 44 |
+
print(f"Loading {MODEL_ID} on CPU…")
|
| 45 |
+
pipe = Flux2KleinPipeline.from_pretrained(MODEL_ID, torch_dtype=torch.bfloat16)
|
| 46 |
+
print(" loaded.")
|
| 47 |
+
except Exception as e: # noqa: BLE001
|
| 48 |
+
LOAD_ERR = str(e)
|
| 49 |
+
print("Model load failed:", e)
|
| 50 |
+
|
| 51 |
+
CREATURES = [
|
| 52 |
+
"mushroom knight", "cloud cat", "disgruntled potato wizard", "sock goblin",
|
| 53 |
+
"philosophical jellyfish", "tiny bureaucrat dragon", "moss yeti",
|
| 54 |
+
"origami werewolf", "caffeinated snail", "Victorian frog detective",
|
| 55 |
+
"marshmallow golem", "interdimensional library owl",
|
| 56 |
+
]
|
| 57 |
+
TRAITS = [
|
| 58 |
+
"wearing a tiny hat", "made of stained glass", "on a unicycle",
|
| 59 |
+
"knitting a scarf", "looking very smug", "mid-sneeze", "holding a lantern",
|
| 60 |
+
"deeply confused", "in formal attire", "glowing faintly", "drinking tea",
|
| 61 |
+
]
|
| 62 |
+
STYLES = [
|
| 63 |
+
"children's storybook illustration, soft watercolor",
|
| 64 |
+
"claymation still, studio lighting",
|
| 65 |
+
"bold flat vector sticker art",
|
| 66 |
+
"cozy painted scene, warm light",
|
| 67 |
+
"1970s fantasy paperback cover",
|
| 68 |
+
"cute isometric 3D render",
|
| 69 |
+
]
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def _roll(twist: str) -> str:
|
| 73 |
+
if twist and twist.strip():
|
| 74 |
+
subject = twist.strip()
|
| 75 |
+
else:
|
| 76 |
+
subject = f"{random.choice(CREATURES)} {random.choice(TRAITS)}"
|
| 77 |
+
return f"A {subject}, {random.choice(STYLES)}, centered, whimsical, no text"
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
@GPU(duration=60)
|
| 81 |
+
def summon(twist: str):
|
| 82 |
+
if pipe is None:
|
| 83 |
+
raise gr.Error(f"Model isn't loaded (this Space needs a GPU). {LOAD_ERR[:200]}")
|
| 84 |
+
prompt = _roll(twist)
|
| 85 |
+
pipe.to("cuda")
|
| 86 |
+
seed = random.randint(0, MAX_SEED)
|
| 87 |
+
t = time.time()
|
| 88 |
+
img = pipe(
|
| 89 |
+
prompt=prompt,
|
| 90 |
+
width=1024,
|
| 91 |
+
height=1024,
|
| 92 |
+
num_inference_steps=STEPS,
|
| 93 |
+
guidance_scale=GUIDANCE,
|
| 94 |
+
generator=torch.Generator(device="cuda").manual_seed(seed),
|
| 95 |
+
).images[0]
|
| 96 |
+
return img, f"🔮 {prompt}\n· seed {seed} · {time.time() - t:.1f}s"
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
with gr.Blocks(title="Cryptid Cannon", theme=gr.themes.Soft()) as demo:
|
| 100 |
+
gr.Markdown(
|
| 101 |
+
"# 🔮 Cryptid Cannon\n"
|
| 102 |
+
"Fire the cannon and summon a random absurd cryptid — powered by "
|
| 103 |
+
"**FLUX.2 [klein] 4B** (4B params, Apache 2.0, runs locally on the Space "
|
| 104 |
+
"GPU). Leave the box empty for pure chaos, or whisper your own creature."
|
| 105 |
+
)
|
| 106 |
+
with gr.Row():
|
| 107 |
+
with gr.Column():
|
| 108 |
+
twist = gr.Textbox(
|
| 109 |
+
label="Your creature (optional)",
|
| 110 |
+
placeholder="a grumpy mushroom knight",
|
| 111 |
+
lines=1,
|
| 112 |
+
)
|
| 113 |
+
btn = gr.Button("🔮 Fire the cannon", variant="primary")
|
| 114 |
+
gr.Examples(
|
| 115 |
+
["", "a sleepy cloud cat", "a tiny bureaucrat dragon", "a philosophical jellyfish"],
|
| 116 |
+
twist,
|
| 117 |
+
label="Try one",
|
| 118 |
+
)
|
| 119 |
+
with gr.Column():
|
| 120 |
+
out = gr.Image(label="Your cryptid", height=480)
|
| 121 |
+
info = gr.Textbox(label="What you summoned", lines=3)
|
| 122 |
+
btn.click(summon, twist, [out, info])
|
| 123 |
+
|
| 124 |
+
if __name__ == "__main__":
|
| 125 |
+
demo.queue(max_size=8).launch(
|
| 126 |
+
server_name="0.0.0.0", server_port=7860, show_error=True
|
| 127 |
+
)
|
requirements.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FLUX.2 [klein] needs a diffusers build that has Flux2KleinPipeline.
|
| 2 |
+
# As of writing that's diffusers main, not a tagged release.
|
| 3 |
+
git+https://github.com/huggingface/diffusers.git
|
| 4 |
+
torch
|
| 5 |
+
transformers
|
| 6 |
+
accelerate
|
| 7 |
+
# peft is the backend diffusers uses for load_lora_weights() — without it the
|
| 8 |
+
# LoRA tab raises "PEFT backend is required for this method."
|
| 9 |
+
peft
|
| 10 |
+
safetensors
|
| 11 |
+
sentencepiece
|
| 12 |
+
protobuf
|
| 13 |
+
pillow
|
| 14 |
+
# Provides @spaces.GPU on ZeroGPU hardware. Harmless elsewhere.
|
| 15 |
+
spaces
|