Spaces:
Running on Zero
Running on Zero
ahora se permiten versiones de loras
Browse files- app.py +183 -34
- loras.json +21 -7
app.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
|
|
| 1 |
|
| 2 |
-
MOCK =
|
| 3 |
|
| 4 |
if (MOCK):
|
| 5 |
import sys
|
|
@@ -20,7 +21,6 @@ if (MOCK):
|
|
| 20 |
sys.modules["spaces"] = mock_spaces
|
| 21 |
|
| 22 |
|
| 23 |
-
import os
|
| 24 |
import gradio as gr
|
| 25 |
import json
|
| 26 |
import logging
|
|
@@ -112,6 +112,9 @@ def load_loras_from_file():
|
|
| 112 |
#loras = load_loras_from_file()
|
| 113 |
loras = load_loras_hot()
|
| 114 |
|
|
|
|
|
|
|
|
|
|
| 115 |
selected_loras = []
|
| 116 |
|
| 117 |
if not MOCK:
|
|
@@ -209,6 +212,13 @@ class calculateDuration:
|
|
| 209 |
|
| 210 |
def update_selection(evt: gr.SelectData, width, height):
|
| 211 |
selected_lora = loras[evt.index]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
new_placeholder = f"Type a prompt for {selected_lora['title']}"
|
| 213 |
lora_repo = selected_lora["repo"]
|
| 214 |
updated_text = f"### Selected: [{lora_repo}](https://huggingface.co/{lora_repo}) ✨"
|
|
@@ -231,10 +241,54 @@ def update_selection(evt: gr.SelectData, width, height):
|
|
| 231 |
updated_text,
|
| 232 |
evt.index,
|
| 233 |
width,
|
|
|
|
| 234 |
height
|
| 235 |
)
|
| 236 |
|
| 237 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
def handle_speed_mode(speed_mode):
|
| 239 |
"""Update UI based on speed/quality toggle."""
|
| 240 |
if speed_mode == "light 4":
|
|
@@ -278,9 +332,21 @@ def run_lora_multi(
|
|
| 278 |
prompt_enhance=False,
|
| 279 |
progress=gr.Progress(track_tqdm=True)
|
| 280 |
):
|
| 281 |
-
# selected_loras_state is a list of tuples: [(image_index, scale), ...]
|
|
|
|
|
|
|
|
|
|
| 282 |
# Filter to get only columns with loaded LoRAs
|
| 283 |
-
loaded_loras = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
print(f"Loaded LoRAs: {loaded_loras}")
|
| 285 |
print(f"Quantity: {quantity}")
|
| 286 |
print(f"Selected LoRAs: {selected_loras_state}")
|
|
@@ -349,14 +415,24 @@ def run_lora_multi(
|
|
| 349 |
adapter_weights.append(1.0)
|
| 350 |
|
| 351 |
# Load all selected LoRAs from columns
|
| 352 |
-
for col_idx, image_idx, scale in loaded_loras:
|
| 353 |
selected_lora = loras[image_idx]
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
adapter_name = f"lora_{col_idx}"
|
| 356 |
|
| 357 |
pipe.load_lora_weights(
|
| 358 |
lora_path,
|
| 359 |
-
weight_name=
|
| 360 |
adapter_name=adapter_name
|
| 361 |
)
|
| 362 |
print(f"Loaded LoRA: {lora_path} as {adapter_name} with scale {scale}")
|
|
@@ -369,7 +445,8 @@ def run_lora_multi(
|
|
| 369 |
|
| 370 |
# Colectar trigger words de todos los LoRAs cargados
|
| 371 |
all_trigger_words = []
|
| 372 |
-
for _, image_idx, _ in loaded_loras:
|
|
|
|
| 373 |
trigger = loras[image_idx].get("trigger_word", "")
|
| 374 |
if trigger:
|
| 375 |
all_trigger_words.append(trigger)
|
|
@@ -595,41 +672,48 @@ def init(speed_mode, aspect_ratio):
|
|
| 595 |
|
| 596 |
|
| 597 |
def update_slider_state(val, state, idx):
|
| 598 |
-
# state is list of tuples (image_index, scale)
|
| 599 |
-
# Force a deep copy or comprehensive new list creation to avoid reference issues
|
| 600 |
new_state = [tuple(item) for item in state]
|
| 601 |
-
|
| 602 |
-
new_state[idx] =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 603 |
print(f"Slider {idx} updated. New state: {new_state}")
|
| 604 |
return new_state
|
| 605 |
|
| 606 |
def remove_lora(state, idx):
|
| 607 |
new_state = list(state)
|
| 608 |
-
new_state[idx] = (None, 1.0) # Reset to default
|
| 609 |
|
| 610 |
-
# Return updates for: global_state, radio, slider, markdown, delete_btn, output_text
|
| 611 |
return (
|
| 612 |
new_state, # selected_loras
|
| 613 |
gr.update(value=None), # radio
|
| 614 |
gr.update(visible=False, value=1.0), # slider
|
| 615 |
gr.update(value=""), # markdown
|
| 616 |
gr.update(visible=False), # delete_btn
|
|
|
|
| 617 |
)
|
| 618 |
|
| 619 |
def validate_generate_button(state):
|
| 620 |
"""Enable generate button if at least one LoRA is loaded."""
|
| 621 |
-
# state is list of tuples (image_index, scale)
|
| 622 |
# Check if any column has a LoRA loaded (image_index is not None)
|
| 623 |
has_lora = any(item[0] is not None for item in state)
|
| 624 |
return gr.update(interactive=has_lora)
|
| 625 |
|
| 626 |
def get_selection(evt: gr.SelectData, *args):
|
| 627 |
-
# args: radios (N) + scales (N) + mds (N) + selected_loras (1) + gallery (1)
|
| 628 |
|
| 629 |
num_radios = NUM_LORAS
|
| 630 |
radio_vals = args[:num_radios]
|
| 631 |
scale_vals = args[num_radios:num_radios*2]
|
| 632 |
-
|
|
|
|
| 633 |
current_state = args[-2]
|
| 634 |
gallery_val = args[-1]
|
| 635 |
|
|
@@ -643,11 +727,12 @@ def get_selection(evt: gr.SelectData, *args):
|
|
| 643 |
selected_index = i
|
| 644 |
break
|
| 645 |
|
| 646 |
-
# Prepare outputs: [output_text] + [md_0_update, ...] + [selected_loras_update] + [scale_0_update, ...] + [del_btn_0_update, ...] + [
|
| 647 |
|
| 648 |
md_updates = [gr.update() for _ in range(num_radios)]
|
| 649 |
scale_updates = [gr.update() for _ in range(num_radios)]
|
| 650 |
del_btn_updates = [gr.update() for _ in range(num_radios)]
|
|
|
|
| 651 |
|
| 652 |
new_state = current_state
|
| 653 |
|
|
@@ -658,19 +743,52 @@ def get_selection(evt: gr.SelectData, *args):
|
|
| 658 |
lora = loras[evt.index]
|
| 659 |
lora_name = lora["title"]
|
| 660 |
lora_image = lora["image"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 661 |
|
| 662 |
# Update specific markdown with image and name
|
| 663 |
selected_image_info = f"Selected LoRA: {lora_name}"
|
| 664 |
-
markdown_content = f"<img src='{lora_image}' style='height:100px; display:block; margin-bottom:10px;' />\n\n**{lora_name}**"
|
| 665 |
md_updates[selected_index] = gr.update(value=markdown_content)
|
| 666 |
scale_updates[selected_index] = gr.update(visible=True)
|
| 667 |
del_btn_updates[selected_index] = gr.update(visible=True)
|
| 668 |
|
| 669 |
-
# Update state for this column: (image_index, scale)
|
| 670 |
new_state = list(current_state)
|
| 671 |
-
|
|
|
|
| 672 |
|
| 673 |
-
return md_updates + [new_state] + scale_updates + del_btn_updates
|
| 674 |
|
| 675 |
|
| 676 |
|
|
@@ -714,11 +832,13 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css, delete_cache=(60, 60)) as app:
|
|
| 714 |
|
| 715 |
|
| 716 |
|
| 717 |
-
|
|
|
|
| 718 |
radios = []
|
| 719 |
scales = []
|
| 720 |
mds = []
|
| 721 |
delete_btns = []
|
|
|
|
| 722 |
|
| 723 |
|
| 724 |
with gr.Row():
|
|
@@ -738,6 +858,10 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css, delete_cache=(60, 60)) as app:
|
|
| 738 |
md = gr.Markdown("")
|
| 739 |
mds.append(md)
|
| 740 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 741 |
lora_scale = gr.Slider(label="LoRA Scale", minimum=0, maximum=3, step=0.1, value=1.0, interactive=True, visible=False)
|
| 742 |
scales.append(lora_scale)
|
| 743 |
|
|
@@ -945,21 +1069,46 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css, delete_cache=(60, 60)) as app:
|
|
| 945 |
r.change(fn=None, inputs=radios, outputs=None, js=js_gallery_toggle)
|
| 946 |
|
| 947 |
# Bind slider changes separately to ensure all inputs/outputs are available
|
| 948 |
-
for i
|
| 949 |
-
|
| 950 |
-
|
| 951 |
-
|
| 952 |
-
|
| 953 |
-
|
| 954 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 955 |
fn=partial(remove_lora, idx=i),
|
| 956 |
inputs=[selected_loras],
|
| 957 |
-
outputs=[selected_loras, radios[i], scales[i], mds[i],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 958 |
)
|
| 959 |
-
del_event.then(fn=validate_generate_button, inputs=[selected_loras], outputs=[generate_button])
|
| 960 |
|
| 961 |
-
|
| 962 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 963 |
|
| 964 |
|
| 965 |
|
|
|
|
| 1 |
+
import os
|
| 2 |
|
| 3 |
+
MOCK = not os.environ.get("HF_SPACE_ID")
|
| 4 |
|
| 5 |
if (MOCK):
|
| 6 |
import sys
|
|
|
|
| 21 |
sys.modules["spaces"] = mock_spaces
|
| 22 |
|
| 23 |
|
|
|
|
| 24 |
import gradio as gr
|
| 25 |
import json
|
| 26 |
import logging
|
|
|
|
| 112 |
#loras = load_loras_from_file()
|
| 113 |
loras = load_loras_hot()
|
| 114 |
|
| 115 |
+
# loras = load_loras_hot()
|
| 116 |
+
loras = load_loras_hot()
|
| 117 |
+
|
| 118 |
selected_loras = []
|
| 119 |
|
| 120 |
if not MOCK:
|
|
|
|
| 212 |
|
| 213 |
def update_selection(evt: gr.SelectData, width, height):
|
| 214 |
selected_lora = loras[evt.index]
|
| 215 |
+
versions = selected_lora.get("versions", [])
|
| 216 |
+
if versions:
|
| 217 |
+
# Use first version or logic to pick one? Logic in get_selection handles the "active" one.
|
| 218 |
+
# This function updates just the placeholder and info text, might not be fully used anymore or overlapping logic.
|
| 219 |
+
# We will keep it but maybe it's not the primary update mechanism for state.
|
| 220 |
+
pass
|
| 221 |
+
|
| 222 |
new_placeholder = f"Type a prompt for {selected_lora['title']}"
|
| 223 |
lora_repo = selected_lora["repo"]
|
| 224 |
updated_text = f"### Selected: [{lora_repo}](https://huggingface.co/{lora_repo}) ✨"
|
|
|
|
| 241 |
updated_text,
|
| 242 |
evt.index,
|
| 243 |
width,
|
| 244 |
+
width,
|
| 245 |
height
|
| 246 |
)
|
| 247 |
|
| 248 |
|
| 249 |
+
def update_version_selection(version_title, current_state, idx):
|
| 250 |
+
"""
|
| 251 |
+
Handle version dropdown change.
|
| 252 |
+
Update state and the specific Markdown to show the version's image.
|
| 253 |
+
"""
|
| 254 |
+
# current_state is list of tuples: (image_index, version_index, scale)
|
| 255 |
+
# idx is the column index (0-based)
|
| 256 |
+
|
| 257 |
+
new_state = [tuple(item) for item in current_state]
|
| 258 |
+
current_image_index = new_state[idx][0]
|
| 259 |
+
|
| 260 |
+
if current_image_index is None:
|
| 261 |
+
return gr.update(), new_state # Should not happen if logic is correct
|
| 262 |
+
|
| 263 |
+
# Handle legacy state (2 elements) or new state (3 elements)
|
| 264 |
+
if len(new_state[idx]) == 3:
|
| 265 |
+
current_image_index, current_version_index, current_scale = new_state[idx]
|
| 266 |
+
else:
|
| 267 |
+
current_image_index, current_scale = new_state[idx]
|
| 268 |
+
current_version_index = None
|
| 269 |
+
|
| 270 |
+
lora = loras[current_image_index]
|
| 271 |
+
versions = lora.get("versions", [])
|
| 272 |
+
|
| 273 |
+
# Find selected version object
|
| 274 |
+
selected_version = next((v for i, v in enumerate(versions) if v["title"] == version_title), None)
|
| 275 |
+
selected_version_index = next((i for i, v in enumerate(versions) if v["title"] == version_title), None)
|
| 276 |
+
|
| 277 |
+
if selected_version:
|
| 278 |
+
# Update Markdown image
|
| 279 |
+
lora_name = lora["title"]
|
| 280 |
+
version_image = selected_version.get("image", lora.get("image"))
|
| 281 |
+
markdown_content = f"<img src='{version_image}' style='height:100px; display:block; margin-bottom:10px;' />\n\n**{lora_name}** ({version_title})"
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
# Update state: (image_index, version_index, scale)
|
| 285 |
+
new_state[idx] = (current_image_index, selected_version_index, current_scale)
|
| 286 |
+
|
| 287 |
+
return gr.update(value=markdown_content), new_state
|
| 288 |
+
|
| 289 |
+
return gr.update(), new_state
|
| 290 |
+
|
| 291 |
+
|
| 292 |
def handle_speed_mode(speed_mode):
|
| 293 |
"""Update UI based on speed/quality toggle."""
|
| 294 |
if speed_mode == "light 4":
|
|
|
|
| 332 |
prompt_enhance=False,
|
| 333 |
progress=gr.Progress(track_tqdm=True)
|
| 334 |
):
|
| 335 |
+
# selected_loras_state is a list of tuples: [(image_index, version_index, scale), ...]
|
| 336 |
+
if selected_loras_state is None:
|
| 337 |
+
selected_loras_state = []
|
| 338 |
+
|
| 339 |
# Filter to get only columns with loaded LoRAs
|
| 340 |
+
loaded_loras = []
|
| 341 |
+
for idx, item in enumerate(selected_loras_state):
|
| 342 |
+
if item[0] is not None:
|
| 343 |
+
# item structure: (image_index, version_index, scale)
|
| 344 |
+
if len(item) == 3:
|
| 345 |
+
loaded_loras.append((idx, item[0], item[1], item[2]))
|
| 346 |
+
else:
|
| 347 |
+
# Legacy state support
|
| 348 |
+
loaded_loras.append((idx, item[0], None, item[1]))
|
| 349 |
+
|
| 350 |
print(f"Loaded LoRAs: {loaded_loras}")
|
| 351 |
print(f"Quantity: {quantity}")
|
| 352 |
print(f"Selected LoRAs: {selected_loras_state}")
|
|
|
|
| 415 |
adapter_weights.append(1.0)
|
| 416 |
|
| 417 |
# Load all selected LoRAs from columns
|
| 418 |
+
for col_idx, image_idx, version_idx, scale in loaded_loras:
|
| 419 |
selected_lora = loras[image_idx]
|
| 420 |
+
|
| 421 |
+
# Determine weights and repo based on version
|
| 422 |
+
if version_idx is not None and "versions" in selected_lora:
|
| 423 |
+
version_data = selected_lora["versions"][version_idx]
|
| 424 |
+
lora_path = version_data.get("repo", selected_lora["repo"])
|
| 425 |
+
weight_name = version_data.get("weights", selected_lora.get("weights"))
|
| 426 |
+
print(f"Using version '{version_data['title']}' for LoRA {selected_lora['title']}")
|
| 427 |
+
else:
|
| 428 |
+
lora_path = selected_lora["repo"]
|
| 429 |
+
weight_name = selected_lora.get("weights")
|
| 430 |
+
|
| 431 |
adapter_name = f"lora_{col_idx}"
|
| 432 |
|
| 433 |
pipe.load_lora_weights(
|
| 434 |
lora_path,
|
| 435 |
+
weight_name=weight_name,
|
| 436 |
adapter_name=adapter_name
|
| 437 |
)
|
| 438 |
print(f"Loaded LoRA: {lora_path} as {adapter_name} with scale {scale}")
|
|
|
|
| 445 |
|
| 446 |
# Colectar trigger words de todos los LoRAs cargados
|
| 447 |
all_trigger_words = []
|
| 448 |
+
for _, image_idx, version_idx, _ in loaded_loras:
|
| 449 |
+
# PENDING: Could also fetch trigger words from version specific data if available
|
| 450 |
trigger = loras[image_idx].get("trigger_word", "")
|
| 451 |
if trigger:
|
| 452 |
all_trigger_words.append(trigger)
|
|
|
|
| 672 |
|
| 673 |
|
| 674 |
def update_slider_state(val, state, idx):
|
| 675 |
+
# state is list of tuples (image_index, version_index, scale)
|
|
|
|
| 676 |
new_state = [tuple(item) for item in state]
|
| 677 |
+
|
| 678 |
+
if len(new_state[idx]) == 3:
|
| 679 |
+
current_image, current_version, current_scale = new_state[idx]
|
| 680 |
+
else:
|
| 681 |
+
current_image, current_scale = new_state[idx]
|
| 682 |
+
current_version = None
|
| 683 |
+
|
| 684 |
+
new_state[idx] = (current_image, current_version, float(val))
|
| 685 |
print(f"Slider {idx} updated. New state: {new_state}")
|
| 686 |
return new_state
|
| 687 |
|
| 688 |
def remove_lora(state, idx):
|
| 689 |
new_state = list(state)
|
| 690 |
+
new_state[idx] = (None, None, 1.0) # Reset to default
|
| 691 |
|
| 692 |
+
# Return updates for: global_state, radio, slider, markdown, delete_btn, output_text, version_dropdown
|
| 693 |
return (
|
| 694 |
new_state, # selected_loras
|
| 695 |
gr.update(value=None), # radio
|
| 696 |
gr.update(visible=False, value=1.0), # slider
|
| 697 |
gr.update(value=""), # markdown
|
| 698 |
gr.update(visible=False), # delete_btn
|
| 699 |
+
gr.update(visible=False, value=None, choices=[]) # version_dropdown
|
| 700 |
)
|
| 701 |
|
| 702 |
def validate_generate_button(state):
|
| 703 |
"""Enable generate button if at least one LoRA is loaded."""
|
| 704 |
+
# state is list of tuples (image_index, version_index, scale)
|
| 705 |
# Check if any column has a LoRA loaded (image_index is not None)
|
| 706 |
has_lora = any(item[0] is not None for item in state)
|
| 707 |
return gr.update(interactive=has_lora)
|
| 708 |
|
| 709 |
def get_selection(evt: gr.SelectData, *args):
|
| 710 |
+
# args: radios (N) + scales (N) + mds (N) + dropdowns (N) + selected_loras (1) + gallery (1)
|
| 711 |
|
| 712 |
num_radios = NUM_LORAS
|
| 713 |
radio_vals = args[:num_radios]
|
| 714 |
scale_vals = args[num_radios:num_radios*2]
|
| 715 |
+
# mds
|
| 716 |
+
# dropdowns (version)
|
| 717 |
current_state = args[-2]
|
| 718 |
gallery_val = args[-1]
|
| 719 |
|
|
|
|
| 727 |
selected_index = i
|
| 728 |
break
|
| 729 |
|
| 730 |
+
# Prepare outputs: [output_text] + [md_0_update, ...] + [selected_loras_update] + [scale_0_update, ...] + [del_btn_0_update, ...] + [dropdown_0_update, ...]
|
| 731 |
|
| 732 |
md_updates = [gr.update() for _ in range(num_radios)]
|
| 733 |
scale_updates = [gr.update() for _ in range(num_radios)]
|
| 734 |
del_btn_updates = [gr.update() for _ in range(num_radios)]
|
| 735 |
+
dropdown_updates = [gr.update() for _ in range(num_radios)]
|
| 736 |
|
| 737 |
new_state = current_state
|
| 738 |
|
|
|
|
| 743 |
lora = loras[evt.index]
|
| 744 |
lora_name = lora["title"]
|
| 745 |
lora_image = lora["image"]
|
| 746 |
+
|
| 747 |
+
versions = lora.get("versions", [])
|
| 748 |
+
|
| 749 |
+
version_index = None
|
| 750 |
+
version_title = ""
|
| 751 |
+
|
| 752 |
+
if versions:
|
| 753 |
+
# Has versions. Find default (match main weights or last one)
|
| 754 |
+
main_weights = lora.get("weights")
|
| 755 |
+
|
| 756 |
+
# Try to match by weights first
|
| 757 |
+
found = False
|
| 758 |
+
for i, v in enumerate(versions):
|
| 759 |
+
if v.get("weights") == main_weights:
|
| 760 |
+
version_index = i
|
| 761 |
+
found = True
|
| 762 |
+
break
|
| 763 |
+
|
| 764 |
+
if not found:
|
| 765 |
+
# Default to the last one if not found (convention mostly)
|
| 766 |
+
version_index = len(versions) - 1
|
| 767 |
+
|
| 768 |
+
version_data = versions[version_index]
|
| 769 |
+
version_title = version_data["title"]
|
| 770 |
+
version_image = version_data.get("image", lora_image)
|
| 771 |
+
|
| 772 |
+
version_choices = [v["title"] for v in versions]
|
| 773 |
+
|
| 774 |
+
dropdown_updates[selected_index] = gr.update(visible=True, choices=version_choices, value=version_title)
|
| 775 |
+
else:
|
| 776 |
+
dropdown_updates[selected_index] = gr.update(visible=False, value=None, choices=[])
|
| 777 |
+
|
| 778 |
|
| 779 |
# Update specific markdown with image and name
|
| 780 |
selected_image_info = f"Selected LoRA: {lora_name}"
|
| 781 |
+
markdown_content = f"<img src='{version_image if versions else lora_image}' style='height:100px; display:block; margin-bottom:10px;' />\n\n**{lora_name}**{' ('+version_title+')' if version_title else ''}"
|
| 782 |
md_updates[selected_index] = gr.update(value=markdown_content)
|
| 783 |
scale_updates[selected_index] = gr.update(visible=True)
|
| 784 |
del_btn_updates[selected_index] = gr.update(visible=True)
|
| 785 |
|
| 786 |
+
# Update state for this column: (image_index, version_index, scale)
|
| 787 |
new_state = list(current_state)
|
| 788 |
+
# Preserve scale if it was already set? No, taking from current slider val usually 1.0 or user set
|
| 789 |
+
new_state[selected_index] = (evt.index, version_index, scale_vals[selected_index])
|
| 790 |
|
| 791 |
+
return md_updates + [new_state] + scale_updates + del_btn_updates + dropdown_updates
|
| 792 |
|
| 793 |
|
| 794 |
|
|
|
|
| 832 |
|
| 833 |
|
| 834 |
|
| 835 |
+
|
| 836 |
+
selected_loras = gr.State([(None, None, 1.0)] * NUM_LORAS)
|
| 837 |
radios = []
|
| 838 |
scales = []
|
| 839 |
mds = []
|
| 840 |
delete_btns = []
|
| 841 |
+
version_dropdowns = []
|
| 842 |
|
| 843 |
|
| 844 |
with gr.Row():
|
|
|
|
| 858 |
md = gr.Markdown("")
|
| 859 |
mds.append(md)
|
| 860 |
|
| 861 |
+
# New version dropdown
|
| 862 |
+
v_dd = gr.Dropdown(label="Version", choices=[], visible=False, interactive=True)
|
| 863 |
+
version_dropdowns.append(v_dd)
|
| 864 |
+
|
| 865 |
lora_scale = gr.Slider(label="LoRA Scale", minimum=0, maximum=3, step=0.1, value=1.0, interactive=True, visible=False)
|
| 866 |
scales.append(lora_scale)
|
| 867 |
|
|
|
|
| 1069 |
r.change(fn=None, inputs=radios, outputs=None, js=js_gallery_toggle)
|
| 1070 |
|
| 1071 |
# Bind slider changes separately to ensure all inputs/outputs are available
|
| 1072 |
+
for i in range(NUM_LORAS):
|
| 1073 |
+
# Slider change
|
| 1074 |
+
scales[i].change(
|
| 1075 |
+
fn=partial(update_slider_state, idx=i),
|
| 1076 |
+
inputs=[scales[i], selected_loras],
|
| 1077 |
+
outputs=[selected_loras]
|
| 1078 |
+
)
|
| 1079 |
+
scales[i].change(fn=validate_generate_button, inputs=[selected_loras], outputs=[generate_button])
|
| 1080 |
+
|
| 1081 |
+
# Delete LoRA
|
| 1082 |
+
delete_btns[i].click(
|
| 1083 |
fn=partial(remove_lora, idx=i),
|
| 1084 |
inputs=[selected_loras],
|
| 1085 |
+
outputs=[selected_loras, radios[i], scales[i], mds[i], delete_btns[i], version_dropdowns[i]]
|
| 1086 |
+
)
|
| 1087 |
+
delete_btns[i].click(fn=validate_generate_button, inputs=[selected_loras], outputs=[generate_button])
|
| 1088 |
+
|
| 1089 |
+
# Version change
|
| 1090 |
+
version_dropdowns[i].change(
|
| 1091 |
+
fn=partial(update_version_selection, idx=i),
|
| 1092 |
+
inputs=[version_dropdowns[i], selected_loras],
|
| 1093 |
+
outputs=[mds[i], selected_loras]
|
| 1094 |
)
|
|
|
|
| 1095 |
|
| 1096 |
+
|
| 1097 |
+
# When gallery is selected
|
| 1098 |
+
gallery.select(
|
| 1099 |
+
fn=get_selection,
|
| 1100 |
+
inputs=radios + scales + mds + version_dropdowns + [selected_loras, gallery], # Pass dropdowns too
|
| 1101 |
+
outputs=mds + [selected_loras] + scales + delete_btns + version_dropdowns
|
| 1102 |
+
)
|
| 1103 |
+
|
| 1104 |
+
# Also trigger validation on selection
|
| 1105 |
+
gallery.select(fn=validate_generate_button, inputs=[selected_loras], outputs=[generate_button])
|
| 1106 |
+
|
| 1107 |
+
|
| 1108 |
+
# -----------------------------------------------------
|
| 1109 |
+
# START GENERATION
|
| 1110 |
+
# -----------------------------------------------------
|
| 1111 |
+
# Handler is already defined in generate_event (gr.on triggers)
|
| 1112 |
|
| 1113 |
|
| 1114 |
|
loras.json
CHANGED
|
@@ -102,7 +102,8 @@
|
|
| 102 |
"trigger_position": "",
|
| 103 |
"aspect": "square",
|
| 104 |
"title": "Masha 2d anime"
|
| 105 |
-
},
|
|
|
|
| 106 |
"repo": "lichorosario/lobato-qwen-image-lora",
|
| 107 |
"image": "https://huggingface.co/lichorosario/lobato-qwen-image-lora/resolve/main/images/milei.png",
|
| 108 |
"trigger_word": "",
|
|
@@ -111,11 +112,26 @@
|
|
| 111 |
"title": "Pablo Lobato"
|
| 112 |
},
|
| 113 |
{
|
| 114 |
-
"
|
| 115 |
-
"image": "https://huggingface.co/lichorosario/tosti-qwen-image-lora/resolve/main/images/example_85rpareuq.png",
|
| 116 |
"trigger_word": "",
|
| 117 |
-
"
|
| 118 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
},
|
| 120 |
{
|
| 121 |
"repo": "lichorosario/blbfsh-qwen-image-lora",
|
|
@@ -181,7 +197,6 @@
|
|
| 181 |
"aspect": "square",
|
| 182 |
"title": "China Zorrilla"
|
| 183 |
},
|
| 184 |
-
|
| 185 |
{
|
| 186 |
"repo": "lichorosario/martha-qwen-image-lora",
|
| 187 |
"image": "https://huggingface.co/lichorosario/martha-qwen-image-lora/resolve/main/images/17.png",
|
|
@@ -206,7 +221,6 @@
|
|
| 206 |
"aspect": "square",
|
| 207 |
"title": "Estoy Presa"
|
| 208 |
},
|
| 209 |
-
|
| 210 |
{
|
| 211 |
"repo": "lichorosario/panorama-qwen-image-lora",
|
| 212 |
"image": "https://huggingface.co/lichorosario/samuraijack-qwen-image-lora/resolve/main/images/7.png",
|
|
|
|
| 102 |
"trigger_position": "",
|
| 103 |
"aspect": "square",
|
| 104 |
"title": "Masha 2d anime"
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
"repo": "lichorosario/lobato-qwen-image-lora",
|
| 108 |
"image": "https://huggingface.co/lichorosario/lobato-qwen-image-lora/resolve/main/images/milei.png",
|
| 109 |
"trigger_word": "",
|
|
|
|
| 112 |
"title": "Pablo Lobato"
|
| 113 |
},
|
| 114 |
{
|
| 115 |
+
"image": "https://huggingface.co/lichorosario/tostiadvbg-v1-qwen-image2512/resolve/main/images/tostiadv-v1-qwen-image-2512_20.webp",
|
|
|
|
| 116 |
"trigger_word": "",
|
| 117 |
+
"trigger_position": "",
|
| 118 |
+
"aspect": "landscape",
|
| 119 |
+
"title": "TostiAdvBg",
|
| 120 |
+
"description": "Model trained on synth pictures mimicking Day of the Tentacle style",
|
| 121 |
+
"versions": [
|
| 122 |
+
{
|
| 123 |
+
"repo": "lichorosario/tostiadvbg-v1-qwen-image2512",
|
| 124 |
+
"weights": "tostiadv-v1-qwen-image-2512_5.safetensors",
|
| 125 |
+
"image": "https://huggingface.co/lichorosario/tostiadvbg-v1-qwen-image2512/resolve/main/images/tostiadv-v1-qwen-image-2512_5.webp",
|
| 126 |
+
"title": "2512.ckpt5"
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"repo": "lichorosario/tostiadvbg-v1-qwen-image2512",
|
| 130 |
+
"weights": "tostiadv-v1-qwen-image-2512_20.safetensors",
|
| 131 |
+
"image": "https://huggingface.co/lichorosario/tostiadvbg-v1-qwen-image2512/resolve/main/images/tostiadv-v1-qwen-image-2512_20.webp",
|
| 132 |
+
"title": "2512.ckpt20"
|
| 133 |
+
}
|
| 134 |
+
]
|
| 135 |
},
|
| 136 |
{
|
| 137 |
"repo": "lichorosario/blbfsh-qwen-image-lora",
|
|
|
|
| 197 |
"aspect": "square",
|
| 198 |
"title": "China Zorrilla"
|
| 199 |
},
|
|
|
|
| 200 |
{
|
| 201 |
"repo": "lichorosario/martha-qwen-image-lora",
|
| 202 |
"image": "https://huggingface.co/lichorosario/martha-qwen-image-lora/resolve/main/images/17.png",
|
|
|
|
| 221 |
"aspect": "square",
|
| 222 |
"title": "Estoy Presa"
|
| 223 |
},
|
|
|
|
| 224 |
{
|
| 225 |
"repo": "lichorosario/panorama-qwen-image-lora",
|
| 226 |
"image": "https://huggingface.co/lichorosario/samuraijack-qwen-image-lora/resolve/main/images/7.png",
|