import glob import os import random import re import subprocess import sys import spaces import torch import gradio as gr from huggingface_hub import HfApi, ModelCard, hf_hub_download, login from diffusers import Krea2Pipeline # Optional HF token (needed for private repos or higher rate limits) TOKEN = os.environ.get("HF_TOKEN") if TOKEN: login(token=TOKEN) api = HfApi(token=TOKEN) DTYPE = torch.bfloat16 BASE_MODEL = "krea/Krea-2-Turbo" MAX_SEED = 2**31 - 1 TARGET_RANK = 64 BLOCK_NAME = "Krea2TransformerBlock" ARTIFACT_REPO = "multimodalart/krea2-aoti-kernels" ARTIFACT_FILE = f"Krea2TransformerBlock-lora-r{TARGET_RANK}/package.pt2" LORA_REPOS = [ ("krea/Krea-2-LoRA-retroanime", "Retro Anime"), ("krea/Krea-2-LoRA-rainywindow", "Rainy Window"), ("krea/Krea-2-LoRA-vintagetarot", "Vintage Tarot"), ("krea/Krea-2-LoRA-sunsetblur", "Sunset Blur"), ("krea/Krea-2-LoRA-dotmatrix", "Dot Matrix"), ("krea/Krea-2-LoRA-neondrip", "Neon Drip"), ("krea/Krea-2-LoRA-darkbrush", "Dark Brush"), ("krea/Krea-2-LoRA-kidsdrawing", "Kids Drawing"), ("krea/Krea-2-LoRA-softwatercolor", "Soft Watercolor Art Deco"), ] DEFAULT_SCALE = 1.0 def _read_trigger(repo: str) -> str: try: text = ModelCard.load(repo, token=TOKEN).text m = re.search(r"[Tt]rigger word[:\*\s]*`([^`]+)`", text) if m: return m.group(1).strip() except Exception: pass return "" def resolve_custom_lora(repo: str): files = api.list_repo_files(repo) safes = [f for f in files if f.endswith(".safetensors")] if not safes: raise gr.Error(f"No .safetensors weights found in {repo}.") weight_name = max(safes, key=lambda f: ("/" not in f, "lora" in f.lower())) return weight_name, _read_trigger(repo) pipe = Krea2Pipeline.from_pretrained(BASE_MODEL, torch_dtype=DTYPE) LORAS = [] for repo, title in LORA_REPOS: key = repo.split("LoRA-")[-1] try: weight_name, trigger = resolve_custom_lora(repo) except Exception as e: print(f"[lora] resolve failed for {repo}: {e}") continue LORAS.append( { "key": key, "title": title, "repo": repo, "weight_name": weight_name, "trigger": trigger, "scale": DEFAULT_SCALE, } ) print(f"[lora] {len(LORAS)}/{len(LORA_REPOS)} styles ready: {[l['key'] for l in LORAS]}") pipe.transformer.enable_lora_hotswap(target_rank=TARGET_RANK) FIRST = LORAS[0] pipe.transformer.load_lora_adapter( FIRST["repo"], weight_name=FIRST["weight_name"], adapter_name="style", token=TOKEN ) pipe.transformer.set_adapters("style", weights=1.0) pipe.to("cuda") CURRENT = { "repo": FIRST["repo"], "weight_name": FIRST["weight_name"], "scale": None, } AOTI_MODEL = None def _full_weights(block, scale: float): w = {} for n, p in block.named_parameters(remove_duplicate=False): if scale != 1.0 and ".lora_B." in n: w[n] = p * scale else: w[n] = p for n, b in block.named_buffers(remove_duplicate=False): w[n] = b return w def _patch_all(aoti_model, scale: float): n = 0 for block in pipe.transformer.modules(): if block.__class__.__name__ == BLOCK_NAME: block.forward = aoti_model.with_weights(_full_weights(block, scale)) n += 1 return n def _load_artifact(): global AOTI_MODEL from spaces.zero.torch.aoti import LazyAOTIModel pt2 = hf_hub_download( repo_id=ARTIFACT_REPO, filename=ARTIFACT_FILE, repo_type="dataset", token=TOKEN, ) AOTI_MODEL = LazyAOTIModel(pt2) print(f"AoTI artifact loaded: {ARTIFACT_FILE}") try: _load_artifact() except Exception as e: print(f"AoTI artifact unavailable ({e}); running eager.") def _ensure_adapter(repo: str, weight_name: str) -> bool: if CURRENT["repo"] == repo and CURRENT["weight_name"] == weight_name: return False pipe.transformer.load_lora_adapter( repo, weight_name=weight_name, adapter_name="style", hotswap=True, token=TOKEN, ) pipe.transformer.set_adapters("style", weights=1.0) CURRENT["repo"] = repo CURRENT["weight_name"] = weight_name CURRENT["scale"] = None return True gallery_items = [] for lora in LORAS: try: img = hf_hub_download(lora["repo"], "images/05_turbo.png") except Exception: img = None gallery_items.append((img, lora["title"])) def _mash_prompt(prompt: str, trigger: str) -> str: prompt = (prompt or "").strip() if trigger and trigger.lower() not in prompt.lower(): return f"{prompt}, {trigger}" if prompt else trigger return prompt CHIP = ( "background:#171717;border:1px solid #262626;border-radius:5px;padding:2px 8px;" "font-family:'JetBrains Mono',ui-monospace,monospace;font-size:12px;color:#d4d4d5;" ) def _info_html(title: str, trigger: str, scale=None, thumb=None) -> str: parts = [ '
' ] if thumb: parts.append( f'' ) parts.append(f'{title}') if trigger: parts.append(f'Trigger{trigger}') if scale is not None: parts.append(f'weight{scale}') parts.append("
") return "".join(parts) def _preview_path(repo: str): try: files = api.list_repo_files(repo) except Exception: return None imgs = sorted( f for f in files if f.lower().endswith((".jpg", ".jpeg", ".png", ".webp")) ) return imgs[0] if imgs else None _REPO_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*$") _NO_LORA_HTML = '
No LoRA loaded
' def preview_custom_lora(repo: str): repo = (repo or "").strip() if not repo: return ( None, _NO_LORA_HTML, gr.update(placeholder="Select a LoRA, then type a prompt"), ) if not _REPO_RE.match(repo): return gr.update(), gr.update(), gr.update() try: _weight, trigger = resolve_custom_lora(repo) except Exception: return None, _NO_LORA_HTML, gr.update() img = _preview_path(repo) thumb = f"https://huggingface.co/{repo}/resolve/main/{img}" if img else None info = _info_html(repo, trigger, thumb=thumb) placeholder = ( f'Type a prompt. "{trigger}" is added automatically.' if trigger else "Type a prompt" ) return None, info, gr.update(placeholder=placeholder) def update_selection(evt: gr.SelectData): lora = LORAS[evt.index] chip = CHIP info = ( '
' f'{lora["title"]}' f'Trigger{lora["trigger"]}' f'weight{lora["scale"]}' "
" ) placeholder = f'Type a prompt. "{lora["trigger"]}" is added automatically.' return evt.index, info, gr.update(placeholder=placeholder), gr.update( value=lora["scale"] ) @spaces.GPU(duration=120, size="large") def _generate( repo, weight_name, full_prompt, scale, steps, guidance, width, height, seed, ): scale = float(scale) swapped = _ensure_adapter(repo, weight_name) if AOTI_MODEL is not None: if swapped or CURRENT["scale"] != scale: _patch_all(AOTI_MODEL, scale) CURRENT["scale"] = scale else: pipe.transformer.set_adapters("style", weights=scale) generator = torch.Generator("cuda").manual_seed(int(seed)) return pipe( prompt=full_prompt, num_inference_steps=int(steps), guidance_scale=float(guidance), width=int(width), height=int(height), generator=generator, ).images[0] def run_lora( prompt, custom_lora, selected_index, lora_scale, steps, guidance, width, height, seed, randomize, progress=gr.Progress(track_tqdm=True), ): if custom_lora and custom_lora.strip(): repo = custom_lora.strip() weight_name, trigger = resolve_custom_lora(repo) elif selected_index is not None: lora = LORAS[selected_index] repo, weight_name, trigger = ( lora["repo"], lora["weight_name"], lora["trigger"], ) else: raise gr.Error( "Select a LoRA from the gallery or enter a custom LoRA path." ) if randomize: seed = random.randint(0, MAX_SEED) seed = int(seed) full_prompt = _mash_prompt(prompt, trigger) try: image = _generate( repo, weight_name, full_prompt, lora_scale, steps, guidance, width, height, seed, ) except Exception as e: msg = str(e) if ( "hotswap" in msg.lower() or "rank" in msg.lower() or "target" in msg.lower() ): raise gr.Error( f"This LoRA isn't hotswap-compatible (needs rank <= {TARGET_RANK} " f"and the same target layers as the Krea-2 built-ins). Details: {msg}" ) raise return image, seed KREA_ACCENT = "#000000" theme = gr.themes.Base( primary_hue=gr.themes.colors.gray, neutral_hue=gr.themes.colors.gray, font=[ gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif", ], font_mono=[ gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace", ], ).set( body_background_fill="#000000", body_background_fill_dark="#000000", body_text_color="#d7d7d7", background_fill_primary="#000000", background_fill_secondary="#000000", block_background_fill="#000000", block_border_color="#121212", block_border_width="1px", block_label_background_fill="#000000", block_label_text_color="#d7d7d7", block_title_text_color="#d7d7d7", border_color_primary="#121212", input_background_fill="#000000", input_border_color="#121212", input_border_color_focus="#121212", button_primary_background_fill="#000000", button_primary_background_fill_hover="#121212", button_primary_text_color="#d7d7d7", button_primary_border_color="#121212", button_secondary_background_fill="#000000", button_secondary_background_fill_hover="#121212", button_secondary_text_color="#d7d7d7", button_secondary_border_color="#121212", slider_color="#000000", ) CSS = """ .gradio-container { background: #000 !important; } #page { max-width: 1120px; margin: 0 auto; padding: 4px 8px 32px; } #krea-header { padding: 32px 6px 22px; border-bottom: 1px solid #1a1a1a; margin-bottom: 22px; } #krea-header .eyebrow { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase; color: #737373; } #krea-header h1 { font-size: 42px; font-weight: 600; letter-spacing: -0.025em; line-height: 1.05; margin: 10px 0 6px; color: #fff; } #krea-header .subtitle { font-size: 15px; line-height: 1.5; color: #a3a3a3; margin: 0; max-width: 60ch; } #krea-header .meta { margin-top: 18px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; } #krea-header .badges { display: flex; gap: 8px; } #krea-header .badge { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: #d4d4d5; border: 1px solid #262626; border-radius: 999px; padding: 4px 10px; } #krea-header .links { display: flex; gap: 16px; } #krea-header .links a { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: #737373; text-decoration: none; transition: color 0.15s ease; } #krea-header .links a:hover { color: #f5f5f5; } #generate-btn { font-weight: 600; letter-spacing: 0.01em; } #result-image { min-height: 420px; border-radius: 10px; overflow: hidden; } .gradio-container code, .gradio-container .prose code { background: #171717 !important; color: #d4d4d5 !important; border: 1px solid #262626 !important; border-radius: 5px !important; padding: 2px 7px !important; font-family: 'JetBrains Mono', ui-monospace, monospace !important; font-size: 0.85em !important; } footer { display: none !important; } .gradio-container .prose a { color: """ + KREA_ACCENT + """; } """ HEADER = """ """ with gr.Blocks(title="Krea 2 LoRA Explorer", css=CSS, theme=theme) as demo: with gr.Column(elem_id="page"): gr.HTML(HEADER) selected_index = gr.State(None) with gr.Column(): result = gr.Image(label="", format="png", elem_id="result-image", height=690) with gr.Column(): generate_button = gr.Button( "", variant="primary", elem_id="generate-btn" ) prompt = gr.Textbox( label="", lines=40, placeholder="", ) with gr.Accordion(open=True): lora_scale = gr.Slider(0.0, 2.0, value=0.4, step=0.1, label="") steps = gr.Slider(1, 30, value=8, step=1, label="") guidance = gr.Slider(0.0, 10.0, value=0.4, step=0.1, label="") width = gr.Slider(1024, 2048, value=1024, step=512, label="") height = gr.Slider(1024, 2048, value=1024, step=512, label="") seed = gr.Slider(0, MAX_SEED, value=0, step=1, label="") randomize = gr.Checkbox(value=True, label="") with gr.Column(): selected_info = gr.HTML("") gallery = gr.Gallery( value=gallery_items, label="", columns=3, height="auto", object_fit="cover", allow_preview=False, elem_id="lora-gallery", ) with gr.Column(): custom_lora = gr.Textbox( label="", info="", placeholder="", ) custom_info = gr.HTML(_NO_LORA_HTML) gr.Markdown("") gallery.select( update_selection, outputs=[selected_index, selected_info, prompt, lora_scale], ) custom_lora.change( preview_custom_lora, custom_lora, [selected_index, custom_info, prompt], ) custom_lora.submit( preview_custom_lora, custom_lora, [selected_index, custom_info, prompt], ) inputs = [ prompt, custom_lora, selected_index, lora_scale, steps, guidance, width, height, seed, randomize, ] gr.on( [generate_button.click, prompt.submit], run_lora, inputs, [result, seed], ) if __name__ == "__main__": demo.launch()