Lisandro's picture
Refactor run_lora_multi for multi-LoRA support, MOCK mode, and UI improvements
be27524
Raw
History Blame Contribute Delete
6.52 kB
import gradio as gr
from functools import partial
NUM_LORAS = 5
LORA_LIST = [
{"image": "https://picsum.photos/seed/1/400/300", "title": "Cinematic Style"},
{"image": "https://picsum.photos/seed/2/400/300", "title": "Anime Style"},
{"image": "https://picsum.photos/seed/3/400/300", "title": "Portrait LoRA"},
{"image": "https://picsum.photos/seed/4/400/300", "title": "Landscape LoRA"},
{"image": "https://picsum.photos/seed/5/400/300", "title": "Sci-Fi Style"}
]
def update_slider_state(val, state, idx):
# state is list of tuples (image_index, scale)
new_state = list(state)
current_image = new_state[idx][0]
new_state[idx] = (current_image, val)
return new_state, f"State Updated: {new_state}"
def remove_lora(state, idx):
new_state = list(state)
new_state[idx] = (None, 1.0) # Reset to default
# Return updates for: global_state, radio, slider, markdown, delete_btn, output_text
return (
new_state, # selected_loras
gr.update(value=None), # radio
gr.update(visible=False, value=1.0), # slider
gr.update(value=""), # markdown
gr.update(visible=False), # delete_btn
f"State Updated: {new_state}" # output
)
def get_selection(evt: gr.SelectData, *args):
# args: radios (N) + scales (N) + mds (N) + selected_loras (1) + gallery (1)
num_radios = NUM_LORAS
radio_vals = args[:num_radios]
scale_vals = args[num_radios:num_radios*2]
md_vals = args[num_radios*2:num_radios*3]
current_state = args[-2]
gallery_val = args[-1]
selected_val = next((val for val in radio_vals if val is not None), None)
# Identify index of selected radio
selected_index = -1
for i, val in enumerate(radio_vals):
if val is not None:
selected_index = i
break
# Prepare outputs: [output_text] + [md_0_update, ...] + [selected_loras_update] + [scale_0_update, ...] + [del_btn_0_update, ...] + [thumb_0_update, ...]
md_updates = [gr.update() for _ in range(num_radios)]
scale_updates = [gr.update() for _ in range(num_radios)]
del_btn_updates = [gr.update() for _ in range(num_radios)]
new_state = current_state
selected_image_info = ""
if selected_index != -1:
# Get LoRA details
lora = LORA_LIST[evt.index]
lora_name = lora["title"]
lora_image = lora["image"]
# Update specific markdown with image and name
selected_image_info = f"Selected LoRA: {lora_name}"
markdown_content = f"<img src='{lora_image}' style='height:100px; display:block; margin-bottom:10px;' />\n\n**{lora_name}**"
md_updates[selected_index] = gr.update(value=markdown_content)
scale_updates[selected_index] = gr.update(visible=True)
del_btn_updates[selected_index] = gr.update(visible=True)
# Update state for this column: (image_index, scale)
new_state = list(current_state)
new_state[selected_index] = (evt.index, scale_vals[selected_index])
return [f"Selected Radio: {selected_val}. {selected_image_info}. State: {new_state}"] + md_updates + [new_state] + scale_updates + del_btn_updates
def greet(*args):
# args will contain: radio_0_val, radio_1_val, ..., radio_N-1_val, name, intensity
radio_vals = args[:NUM_LORAS]
name = args[-2]
intensity = args[-1]
selected = next((val for val in radio_vals if val is not None), None)
return "Hola, qué tal " + name + "!" * int(intensity) + f". Selected: {selected}"
css = """
.disabled {
pointer-events: none;
opacity: 0.5;
}
"""
with gr.Blocks(css=css) as demo:
selected_loras = gr.State([(None, 1.0)] * NUM_LORAS)
radios = []
scales = []
mds = []
delete_btns = []
with gr.Row():
for i in range(NUM_LORAS):
with gr.Column():
# Each radio has a single choice which is its column number
r = gr.Radio([str(i + 1)], label=f"Option {i + 1}")
radios.append(r)
md = gr.Markdown("")
mds.append(md)
lora_scale = gr.Slider(label="LoRA Scale", minimum=0, maximum=3, step=0.1, value=1.0, interactive=True, visible=False)
scales.append(lora_scale)
del_btn = gr.Button("🗑️", visible=False)
delete_btns.append(del_btn)
name = gr.Textbox(label="Name")
intensity = gr.Slider(label="Intensity", minimum=1, maximum=10, step=1)
output = gr.Textbox(label="Output")
gallery = gr.Gallery(
label="Generated Images",
value=[(item["image"], item["title"]) for item in LORA_LIST],
columns=5,
height="auto",
interactive=False,
allow_preview=False,
elem_classes=["disabled"],
elem_id="gallery"
)
btn = gr.Button("Submit")
btn.click(fn=greet, inputs=radios + [name, intensity], outputs=output)
for i, r in enumerate(radios):
others = radios[:i] + radios[i+1:]
# JS: if val is selected (true), return nulls for all others.
# Otherwise return current values (no change).
js_code = f"(val, ...args) => val ? args.map(_ => null) : args"
r.change(fn=None, inputs=[r] + others, outputs=others, js=js_code)
# JS toggle for gallery class
js_gallery_toggle = "(...args) => { const gallery = document.getElementById('gallery'); const anySelected = args.some(v => v !== null && v !== ''); if (gallery) { if (anySelected) gallery.classList.remove('disabled'); else gallery.classList.add('disabled'); } }"
r.change(fn=None, inputs=radios, outputs=None, js=js_gallery_toggle)
# Bind slider changes separately to ensure all inputs/outputs are available
for i, scale in enumerate(scales):
scale.change(fn=partial(update_slider_state, idx=i), inputs=[scale, selected_loras], outputs=[selected_loras, output])
# Bind delete buttons
for i, del_btn in enumerate(delete_btns):
del_btn.click(
fn=partial(remove_lora, idx=i),
inputs=[selected_loras],
outputs=[selected_loras, radios[i], scales[i], mds[i], del_btn, output]
)
gallery.select(fn=get_selection, inputs=radios + scales + mds + [selected_loras, gallery], outputs=[output] + mds + [selected_loras] + scales + delete_btns)
demo.launch()