techfreakworm's picture
Add custom user-LoRA (Quality mode) for the MPS route
47baa19 unverified
Raw
History Blame Contribute Delete
8.14 kB
"""Gradio UI builders for qwen-image-editor (Soft Dark Restraint — violet accent).
Each ``build_*_tab()`` returns a ``dict[str, gr.components.Component]`` so
``app.py:build_app`` can wire ``.click()`` / ``.change()`` handlers without
reaching into local scopes. No event wiring lives here.
All param help text flows through Gradio's native ``info=`` parameter (dim
subtitle under each label). ``gr.Image`` does not support ``info=``, so those
components use a descriptive label instead.
"""
from __future__ import annotations
import gradio as gr
from tooltips import TOOLTIPS
# Common slider bounds shared across both tabs.
_STEPS_MIN = 1
_STEPS_MAX = 50
_STEPS_DEFAULT = 4 # Fast mode default; app.py swaps to 40 on Quality.
_CFG_MIN = 0.5
_CFG_MAX = 12.0
_CFG_DEFAULT = 1.0 # Fast mode default; app.py swaps to 4.0 on Quality.
_LORA_PLACEHOLDER = "e.g. fal/Qwen-Image-Edit-2511-Multiple-Angles-LoRA"
def _lora_controls() -> dict[str, gr.components.Component]:
"""Custom-LoRA controls (repo id + upload + weight), shared by both tabs.
Custom LoRAs apply in **Quality** mode only — in Fast mode the Lightning 4-step
distillation fights a content LoRA, so it's intentionally ignored there.
"""
with gr.Accordion("Custom LoRA (Quality mode)", open=False):
gr.Markdown(
"Apply a custom LoRA on top of the base model. **Quality mode only** — ignored in Fast. "
"Leave blank for the default model.",
)
lora_repo = gr.Textbox(
label="LoRA — Hugging Face repo id",
placeholder=_LORA_PLACEHOLDER,
lines=1,
)
lora_file = gr.File(
label="…or upload a LoRA (.safetensors only)",
file_types=[".safetensors"],
file_count="single",
)
lora_weight = gr.Slider(
0.0, 1.5, value=0.9, step=0.05,
label="LoRA weight",
info="Strength of the custom LoRA (0 = off, 1.0 = full).",
)
return dict(lora_repo=lora_repo, lora_file=lora_file, lora_weight=lora_weight)
def build_edit_tab() -> dict[str, gr.components.Component]:
"""Build the Edit tab layout.
Returns a flat dict of all components. Keys:
``prompt, speed, steps, true_cfg, negative_prompt, seed, generate_btn,
output_image, output_meta, advanced_grp, quality_grp, image``.
"""
with gr.Row():
with gr.Column(scale=4):
image = gr.Image(
label="Input image",
type="pil",
height=300,
)
prompt = gr.Textbox(
label="Prompt",
info=TOOLTIPS["prompt"],
lines=3,
placeholder="Make the sky dramatic with orange-purple sunset hues…",
)
speed = gr.Radio(
["Fast", "Quality"],
value="Fast",
label="Speed",
info=TOOLTIPS["speed"],
)
with gr.Group(visible=False) as quality_grp:
negative_prompt = gr.Textbox(
label="Negative prompt",
info=TOOLTIPS["negative_prompt"],
lines=2,
placeholder="blurry, lowres, distorted",
)
true_cfg = gr.Slider(
_CFG_MIN,
_CFG_MAX,
value=_CFG_DEFAULT,
step=0.1,
label="CFG scale",
info=TOOLTIPS["true_cfg"],
)
with gr.Accordion("Advanced", open=False) as advanced_grp:
steps = gr.Slider(
_STEPS_MIN,
_STEPS_MAX,
value=_STEPS_DEFAULT,
step=1,
label="Steps",
info=TOOLTIPS["steps"],
)
seed = gr.Number(
value=-1,
precision=0,
label="Seed",
info=TOOLTIPS["seed"],
)
lora = _lora_controls()
generate_btn = gr.Button("Generate", variant="primary")
with gr.Column(scale=5):
output_image = gr.Image(
show_label=False,
type="pil",
height=512,
show_download_button=True,
)
output_meta = gr.JSON(label="Meta", value={})
return dict(
image=image,
prompt=prompt,
speed=speed,
quality_grp=quality_grp,
negative_prompt=negative_prompt,
true_cfg=true_cfg,
advanced_grp=advanced_grp,
steps=steps,
seed=seed,
generate_btn=generate_btn,
output_image=output_image,
output_meta=output_meta,
**lora,
)
def build_compose_tab() -> dict[str, gr.components.Component]:
"""Build the Compose tab layout.
Returns a flat dict of all components. Keys:
``prompt, speed, steps, true_cfg, negative_prompt, seed, generate_btn,
output_image, output_meta, advanced_grp, quality_grp,
target_image, ref_image_1, ref_image_2``.
"""
with gr.Row():
with gr.Column(scale=4):
target_image = gr.Image(
label="Target image",
type="pil",
height=240,
)
with gr.Row():
ref_image_1 = gr.Image(
label="Reference 1",
type="pil",
height=200,
)
ref_image_2 = gr.Image(
label="Reference 2 (optional)",
type="pil",
height=200,
)
prompt = gr.Textbox(
label="Prompt",
info=TOOLTIPS["prompt"],
lines=3,
placeholder="Wearing the jacket from the reference, same background…",
)
speed = gr.Radio(
["Fast", "Quality"],
value="Fast",
label="Speed",
info=TOOLTIPS["speed"],
)
with gr.Group(visible=False) as quality_grp:
negative_prompt = gr.Textbox(
label="Negative prompt",
info=TOOLTIPS["negative_prompt"],
lines=2,
placeholder="blurry, lowres, distorted",
)
true_cfg = gr.Slider(
_CFG_MIN,
_CFG_MAX,
value=_CFG_DEFAULT,
step=0.1,
label="CFG scale",
info=TOOLTIPS["true_cfg"],
)
with gr.Accordion("Advanced", open=False) as advanced_grp:
steps = gr.Slider(
_STEPS_MIN,
_STEPS_MAX,
value=_STEPS_DEFAULT,
step=1,
label="Steps",
info=TOOLTIPS["steps"],
)
seed = gr.Number(
value=-1,
precision=0,
label="Seed",
info=TOOLTIPS["seed"],
)
lora = _lora_controls()
generate_btn = gr.Button("Generate", variant="primary")
with gr.Column(scale=5):
output_image = gr.Image(
show_label=False,
type="pil",
height=512,
show_download_button=True,
)
output_meta = gr.JSON(label="Meta", value={})
return dict(
target_image=target_image,
ref_image_1=ref_image_1,
ref_image_2=ref_image_2,
prompt=prompt,
speed=speed,
quality_grp=quality_grp,
negative_prompt=negative_prompt,
true_cfg=true_cfg,
advanced_grp=advanced_grp,
steps=steps,
seed=seed,
generate_btn=generate_btn,
output_image=output_image,
output_meta=output_meta,
**lora,
)