Workflow-Canvas / app.py
honey90's picture
Update app.py
98d5ec0 verified
raw
history blame
12.7 kB
import os
import random
import numpy as np
import gradio as gr
# --- Hugging Face Spaces ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ(์—†์–ด๋„ ๋™์ž‘ํ•˜๋„๋ก ๋Œ€์ฒด) ---
try:
import spaces
GPU_DECORATOR = spaces.GPU
except Exception:
def GPU_DECORATOR(*args, **kwargs):
def _wrap(fn):
return fn
return _wrap
import torch
from diffusers import DiffusionPipeline
# -----------------------
# ์žฅ์น˜/์ •๋ฐ€๋„ ์ž๋™ ์„ ํƒ
# -----------------------
def select_device_dtype():
if torch.cuda.is_available():
device = "cuda"
dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
else:
device = "cpu"
dtype = torch.float32
return device, dtype
device, dtype = select_device_dtype()
if device == "cuda":
torch.backends.cuda.matmul.allow_tf32 = True
# ์ œํ•œ๊ฐ’
MAX_SEED = np.iinfo(np.uint32).max
MAX_SIDE = 2048
MAX_TOTAL_PIXELS = 2_359_296 # ํ•ด์ƒ๋„ ์ด ํ”ฝ์…€ ๊ฐ€๋“œ(์•ฝ 2.36MP)
# -----------------------
# ๋ชจ๋ธ ๋กœ๋“œ (FLUX.1-schnell)
# -----------------------
MODEL_ID = "black-forest-labs/FLUX.1-schnell"
pipe = DiffusionPipeline.from_pretrained(MODEL_ID, torch_dtype=dtype).to(device)
if hasattr(pipe, "enable_vae_slicing"):
pipe.enable_vae_slicing()
if hasattr(pipe, "enable_vae_tiling"):
pipe.enable_vae_tiling()
# -----------------------
# ์œ ํ‹ธ
# -----------------------
def _clamp_hw(width: int, height: int):
width = int(max(256, min(int(width), MAX_SIDE)))
height = int(max(256, min(int(height), MAX_SIDE)))
if width * height > MAX_TOTAL_PIXELS:
scale = (MAX_TOTAL_PIXELS / (width * height)) ** 0.5
width = int((width * scale) // 32 * 32)
height = int((height * scale) // 32 * 32)
width = max(256, min(width, MAX_SIDE))
height = max(256, min(height, MAX_SIDE))
return width, height
def _validate_params(prompt: str, steps: int, guidance: float):
if not prompt or not prompt.strip():
raise ValueError("ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.")
if not (1 <= steps <= 50):
raise ValueError("์ถ”๋ก  ์Šคํ…์€ 1~50 ๋ฒ”์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
if not (0.0 <= guidance <= 20.0):
raise ValueError("๊ฐ€์ด๋˜์Šค ์Šค์ผ€์ผ์€ 0.0~20.0 ๋ฒ”์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
# -----------------------
# ์ƒ์„ฑ ํ•จ์ˆ˜
# -----------------------
@GPU_DECORATOR()
def generate_image(prompt, seed, randomize_seed, width, height, steps, guidance_scale, progress=gr.Progress(track_tqdm=True)):
try:
prompt = prompt.strip()
_validate_params(prompt, steps, guidance_scale)
if randomize_seed:
seed = random.randint(0, MAX_SEED)
generator = torch.Generator(device=device).manual_seed(int(seed))
width, height = _clamp_hw(width, height)
progress(0.1, desc="์ดˆ๊ธฐํ™” ์ค‘โ€ฆ")
if device == "cuda":
autocast_ctx = torch.autocast(device_type="cuda", dtype=dtype)
elif device == "cpu":
autocast_ctx = torch.autocast(device_type="cpu", dtype=dtype) if dtype != torch.float32 else torch.no_grad()
else:
autocast_ctx = torch.no_grad()
with autocast_ctx:
progress(0.4, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘โ€ฆ")
out = pipe(
prompt=prompt,
width=int(width),
height=int(height),
num_inference_steps=int(steps),
generator=generator,
guidance_scale=float(guidance_scale)
)
image = out.images[0]
progress(1.0, desc="์™„๋ฃŒ")
return image, int(seed)
except Exception as e:
gr.Error(f"์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜: {type(e).__name__}: {e}")
return None, int(seed)
def set_prompt(example_text):
return example_text
# -----------------------
# ํ•œ๊ธ€ ์˜ˆ์‹œ ํ”„๋กฌํ”„ํŠธ
# -----------------------
example_prompts = {
"์ œํ’ˆ ๋””์ž์ธ": [
"""์Šฌ๋ฆญํ•œ ์ธ๋”์ŠคํŠธ๋ฆฌ์–ผ ์Šคํƒ€์ผ์˜ ์ปคํ”ผ๋จธ์‹  ์ปจ์…‰ ์Šค์ผ€์น˜:
- ๊ณก์„ ํ˜• ๋ฉ”ํƒˆ ๋ฐ”๋””, ๋ฒ ์ ค ์ตœ์†Œํ™”
- ์„ค์ •์šฉ ํ„ฐ์น˜์Šคํฌ๋ฆฐ ํŒจ๋„
- ๋ชจ๋˜ํ•œ ๋งคํŠธ ๋ธ”๋ž™ ๋งˆ๊ฐ
- ์†๊ทธ๋ฆผ(์Šค์ผ€์น˜) ์ปจ์…‰ ์•„ํŠธ ์Šคํƒ€์ผ"""
],
"๋งˆ์ธ๋“œ๋งต": [
"""์†๊ทธ๋ฆผ ์Šคํƒ€์ผ์˜ ๋‹ค์ฑ„๋กœ์šด ๋งˆ์ธ๋“œ๋งต, ๊ต์œก์šฉ, ์ƒ๋™๊ฐ ์žˆ๋Š” ์ƒ‰๊ฐ, ๋ช…ํ™•ํ•œ ๊ณ„์ธต ๊ตฌ์กฐ, ํ™ฉ๊ธˆ๋น„ ๋ ˆ์ด์•„์›ƒ.
KNOWLEDGE
โ”œโ”€โ”€ ACQUISITION [๋ฒˆ๊ฐœ๊ฐ€ ์นœ ๋‡Œ ~60px]
โ”‚ โ”œโ”€โ”€ READING [๋น›๋‚˜๋Š” ํŽผ์นœ ์ฑ…]
โ”‚ โ”œโ”€โ”€ PRACTICE [๊ณต๊ตฌ ์•„์ด์ฝ˜]
โ”‚ โ””โ”€โ”€ OBSERVATION [ํ™•๋Œ€๊ฒฝ ๋“  ๋ˆˆ]
โ”œโ”€โ”€ PROCESSING [๊ธฐ์–ด ๋„คํŠธ์›Œํฌ ~50px]
โ”‚ โ”œโ”€โ”€ ANALYSIS [์ƒ์Šน ๊ทธ๋ž˜ํ”„]
โ”‚ โ””โ”€โ”€ SYNTHESIS [ํผ์ฆ ์กฐ๊ฐ]
โ”œโ”€โ”€ RETENTION [๋ฉ”๋ชจ๋ฆฌ ์นฉ ~45px]
โ”‚ โ”œโ”€โ”€ SHORT-TERM [๋ฒˆ์ฉ์ž„]
โ”‚ โ””โ”€โ”€ LONG-TERM [๊ฒฌ๊ณ ํ•œ ์•„์นด์ด๋ธŒ]
โ””โ”€โ”€ APPLICATION
โ”œโ”€โ”€ CREATION [ํŒ”๋ ˆํŠธ]
โ””โ”€โ”€ INNOVATION [๋ณ„์ž๋ฆฌ ์ „๊ตฌ]"""
],
"๋ชฉ์—…": [
"""์†๊ทธ๋ฆผ ์™€์ด์–ดํ”„๋ ˆ์ž„ ์Šคํƒ€์ผ์˜ ๋ชจ๋ฐ”์ผ ๋ฑ…ํ‚น ์•ฑ ๋ชฉ์—…:
- ๋กœ๊ณ ๊ฐ€ ์žˆ๋Š” ํƒ€์ดํ‹€ ํ™”๋ฉด
- ๋กœ๊ทธ์ธ(์•„์ด๋””, ๋น„๋ฐ€๋ฒˆํ˜ธ, ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ)
- ๋Œ€์‹œ๋ณด๋“œ 3๊ฐœ ์„น์…˜(์ž”์•ก, ๊ฑฐ๋ž˜๋‚ด์—ญ, ๋น ๋ฅธ ์‹คํ–‰)
- ํ•˜๋‹จ ๋‚ด๋น„๊ฒŒ์ด์…˜(ํ™ˆ, ์ด์ฒด, ํ”„๋กœํ•„)"""
],
"์ธํฌ๊ทธ๋ž˜ํ”ฝ": [
"""๋Œ€๊ธฐ์—… ์—ฐ์ฐจ๋ณด๊ณ ์„œ์šฉ ํ”Œ๋žซ ์Šคํƒ€์ผ ์ธํฌ๊ทธ๋ž˜ํ”ฝ:
- ์ œ๋ชฉ: "Global Renewable Energy Trends 2025"
- ๋ถ€์ œ: "์‹œ์žฅ์ ์œ ์œจ๊ณผ ์„ฑ์žฅ๋ถ„์„"
- ์‹œ๊ฐ ์š”์†Œ:
- ์ง€์—ญ๋ณ„ ํƒœ์–‘๊ด‘ยทํ’๋ ฅยท์ˆ˜๋ ฅ ์ƒ์‚ฐ๋Ÿ‰ ๋ง‰๋Œ€๊ทธ๋ž˜ํ”„
- ์—๋„ˆ์ง€ ๋น„์ค‘ ํŒŒ์ด์ฐจํŠธ: ํƒœ์–‘๊ด‘(45%)ยทํ’๋ ฅ(30%)ยท์ˆ˜๋ ฅ(25%)
- ์—ฐ๋„๋ณ„ ์„ฑ์žฅ ์ถ”์„ธ์„ 
- ์•„์ด์ฝ˜: ๋ฏธ๋‹ˆ๋ฉ€ ํƒœ์–‘, ํ’๋ ฅ ํ„ฐ๋นˆ, ๋ฌผ๋ฐฉ์šธ
- ๋ ˆ์ด์•„์›ƒ: ๊ทธ๋ฆฌ๋“œ ๊ธฐ๋ฐ˜, ํŒŒ์Šคํ…” ํฌ์ธํŠธ, ํ™”์ดํŠธ ์ŠคํŽ˜์ด์Šค ์ถฉ๋ถ„
- ์ฃผ์„: KPI ํ•ต์‹ฌ ์ˆ˜์น˜ ๋ฐ ์ „๋ง ์ฝœ์•„์›ƒ"""
],
"๋‹ค์ด์–ด๊ทธ๋žจ": [
"""์—”๋“œํˆฌ์—”๋“œ ๋น„์ฆˆ๋‹ˆ์Šค ์›Œํฌํ”Œ๋กœ ๋‹ค์ด์–ด๊ทธ๋žจ(์†๊ทธ๋ฆผ, ๊ต์œก์ ยท์ „๋ฌธ์ ):
- ์ œ๋ชฉ: "ํ†ตํ•ฉ ๋น„์ฆˆ๋‹ˆ์Šค ํ”„๋กœ์„ธ์Šค"
- ๊ตฌ์„ฑ:
- ์‹œ์žฅ๋ถ„์„(์ฐจํŠธ, ๊ฒฝ์Ÿ์ž ๋งต)
- ์ „๋žต์ˆ˜๋ฆฝ(๋ธŒ๋ ˆ์ธ์Šคํ† ๋ฐ ํด๋ผ์šฐ๋“œ, ํ•ต์‹ฌ ํฌ์ปค์Šค)
- ์ œํ’ˆ์„ค๊ณ„(์Šค์ผ€์น˜, ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„)
- ๊ตฌํ˜„(ํƒ€์ž„๋ผ์ธ ๋งˆ์ปค, ๋ฆฌ์†Œ์Šค ์•„์ด์ฝ˜)
- ์ถœ์‹œ ํ›„ ๋ฆฌ๋ทฐ(์ง€ํ‘œ, ์ง€์† ๊ฐœ์„ )
- ๋ช…ํ™•ํ•œ ๋ฐฉํ–ฅ ํ™”์‚ดํ‘œ, ์ƒ‰์ƒ์œผ๋กœ ๋‹จ๊ณ„ ๊ตฌ๋ถ„"""
],
"ํ”Œ๋กœ์šฐ์ฐจํŠธ": [
"""์†๊ทธ๋ฆผ ์Šคํƒ€์ผ ํ”Œ๋กœ์šฐ์ฐจํŠธ, ์„ ๋ช…ํ•œ ์ƒ‰, ๋ฏธ๋‹ˆ๋ฉ€ ์•„์ด์ฝ˜.
BUSINESS WORKFLOW
โ”œโ”€โ”€ ์‹œ์ž‘ [์ดˆ๋ก ๋ฒ„ํŠผ ~40px]
โ”‚ โ”œโ”€โ”€ ์š”๊ตฌ์‚ฌํ•ญ ์ˆ˜์ง‘ [ํด๋” ์•„์ด์ฝ˜]
โ”‚ โ””โ”€โ”€ ๋ฐ์ดํ„ฐ ๋ถ„์„ [์ฐจํŠธ ์•„์ด์ฝ˜]
โ”œโ”€โ”€ ๊ตฌํ˜„ [์ฝ”๋”ฉ ์‹ฌ๋ณผ ~50px]
โ”‚ โ”œโ”€โ”€ ํ”„๋ก ํŠธ์—”๋“œ [๋ธŒ๋ผ์šฐ์ € ์•„์ด์ฝ˜]
โ”‚ โ””โ”€โ”€ ๋ฐฑ์—”๋“œ [์„œ๋ฒ„ ์•„์ด์ฝ˜]
โ”œโ”€โ”€ ํ…Œ์ŠคํŠธ & ํ†ตํ•ฉ [๊ธฐ์–ด ์•„์ด์ฝ˜ ~45px]
โ””โ”€โ”€ ๋ฐฐํฌ
โ””โ”€โ”€ ์ข…๋ฃŒ [์ฒด์ปค๊ธฐ ๊นƒ๋ฐœ ~40px]"""
]
}
# -----------------------
# Gradio UI (ํ•œ๊ตญ์–ด)
# -----------------------
css = """
* { box-sizing: border-box; }
body {
background: linear-gradient(135deg, #667eea, #764ba2);
font-family: 'Pretendard', 'Apple SD Gothic Neo', 'Noto Sans KR', 'Helvetica Neue', Arial, sans-serif;
color: #333; margin: 0; padding: 0;
}
.gradio-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
padding: 30px 40px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
margin: 40px auto;
width: 1200px;
overflow: visible !important;
}
.sidebar {
background: rgba(255, 255, 255, 0.98);
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
position: relative; z-index: 10;
overflow: visible !important;
}
button, .btn {
background: linear-gradient(90deg, #ff8a00, #e52e71);
border: none; color: #fff;
padding: 12px 24px; text-transform: uppercase;
font-weight: bold; letter-spacing: 1px;
border-radius: 5px; cursor: pointer;
transition: transform 0.2s ease-in-out;
}
button:hover, .btn:hover { transform: scale(1.05); }
.example-accordion { width: 100% !important; max-width: 100% !important; }
.example-accordion button { width: auto !important; white-space: normal !important; }
"""
with gr.Blocks(css=css, title="์›Œํฌํ”Œ๋กœ ์บ”๋ฒ„์Šค") as demo:
gr.Markdown(
"""
<div style="text-align:center;">
<h1>์›Œํฌํ”Œ๋กœ ์บ”๋ฒ„์Šค</h1>
<p>์—ฌ๋Ÿฌ ํƒญ์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค์— ํ•„์š”ํ•œ ๋””์ž์ธ ์ปจ์…‰๊ณผ ์›Œํฌํ”Œ๋กœ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์ƒ์„ฑํ•ด ๋ณด์„ธ์š”.</p>
<p><strong>์ปค๋ฎค๋‹ˆํ‹ฐ:</strong> <a href="https://discord.gg/openfreeai" target="_blank">https://discord.gg/openfreeai</a></p>
</div>
"""
)
gr.HTML(
"""<a href="https://visitorbadge.io/status?path=https%3A%2F%2Fginigen-Workflow-Canvas.hf.space">
<img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fginigen-Workflow-Canvas.hf.space&countColor=%23263759" alt="๋ฐฉ๋ฌธ์ž ๋ฐฐ์ง€"/>
</a>"""
)
with gr.Row():
# ์™ผ์ชฝ ์‚ฌ์ด๋“œ๋ฐ”: ๊ณตํ†ต ํŒŒ๋ผ๋ฏธํ„ฐ
with gr.Column(scale=2, elem_classes="sidebar"):
gr.Markdown("### ์ƒ์„ฑ ํŒŒ๋ผ๋ฏธํ„ฐ")
size_preset = gr.Dropdown(
label="ํ•ด์ƒ๋„ ํ”„๋ฆฌ์…‹",
choices=[
"1024x1024", "1536x1024", "1024x1536",
"1344x1344", "1536x1536", "1920x1080", "1080x1920"
],
value="1024x1024"
)
width_slider = gr.Slider(label="๊ฐ€๋กœ ํ•ด์ƒ๋„(px)", minimum=256, maximum=MAX_SIDE, step=32, value=1024)
height_slider = gr.Slider(label="์„ธ๋กœ ํ•ด์ƒ๋„(px)", minimum=256, maximum=MAX_SIDE, step=32, value=1024)
def apply_preset(preset):
w, h = map(int, preset.split("x"))
w, h = _clamp_hw(w, h)
return w, h
size_preset.change(fn=apply_preset, inputs=size_preset, outputs=[width_slider, height_slider])
seed_slider = gr.Slider(label="์‹œ๋“œ(Seed)", minimum=0, maximum=int(MAX_SEED), step=1, value=42)
randomize_seed = gr.Checkbox(label="์‹œ๋“œ ๋žœ๋ค", value=True)
def toggle_seed(disable):
return gr.update(interactive=not disable)
randomize_seed.change(fn=toggle_seed, inputs=randomize_seed, outputs=seed_slider)
steps_slider = gr.Slider(label="์ถ”๋ก  ์Šคํ…", minimum=1, maximum=50, step=1, value=20)
guidance_slider = gr.Slider(label="๊ฐ€์ด๋˜์Šค ์Šค์ผ€์ผ", minimum=0.0, maximum=20.0, step=0.5, value=7.5)
# ๋ฉ”์ธ: ํƒญ UI
with gr.Column(scale=8):
with gr.Tabs():
def build_tab(tab_title, ex_key, placeholder_text):
with gr.Tab(tab_title):
tb = gr.Textbox(
label=f"{tab_title} ํ”„๋กฌํ”„ํŠธ",
placeholder=placeholder_text,
lines=5,
value=example_prompts[ex_key][0]
)
btn = gr.Button(f"{tab_title} ์ƒ์„ฑ")
img = gr.Image(label=f"{tab_title} ๊ฒฐ๊ณผ", type="pil", value=None, height=512)
with gr.Accordion("์˜ˆ์‹œ ํ”„๋กฌํ”„ํŠธ", open=True, elem_classes="example-accordion"):
for ex in example_prompts[ex_key]:
gr.Button(ex, variant="secondary").click(
fn=lambda ex=ex: set_prompt(ex),
outputs=tb
)
btn.click(
fn=generate_image,
inputs=[tb, seed_slider, randomize_seed, width_slider, height_slider, steps_slider, guidance_slider],
outputs=[img, seed_slider]
)
build_tab("์ œํ’ˆ ๋””์ž์ธ", "์ œํ’ˆ ๋””์ž์ธ", "์ œํ’ˆ ๋””์ž์ธ ์ปจ์…‰์„ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
build_tab("๋งˆ์ธ๋“œ๋งต", "๋งˆ์ธ๋“œ๋งต", "๋งˆ์ธ๋“œ๋งต ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
build_tab("๋ชฉ์—…", "๋ชฉ์—…", "์•ฑ/์›น ๋ชฉ์—… ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
build_tab("์ธํฌ๊ทธ๋ž˜ํ”ฝ", "์ธํฌ๊ทธ๋ž˜ํ”ฝ", "์ธํฌ๊ทธ๋ž˜ํ”ฝ ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
build_tab("๋‹ค์ด์–ด๊ทธ๋žจ", "๋‹ค์ด์–ด๊ทธ๋žจ", "๋‹ค์ด์–ด๊ทธ๋žจ ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
build_tab("ํ”Œ๋กœ์šฐ์ฐจํŠธ", "ํ”Œ๋กœ์šฐ์ฐจํŠธ", "ํ”Œ๋กœ์šฐ์ฐจํŠธ ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”โ€ฆ")
if __name__ == "__main__":
demo.queue(concurrency_count=2, max_size=32)
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
debug=True
)