John6666's picture
Upload 39 files
e8b6587 verified
Raw
History Blame Contribute Delete
23.6 kB
"""LoRA UI construction and event wiring for the main Gradio surface.
Keep this module deliberately concrete: it owns the standard LoRA controls and
wires them to app-level callbacks, but it does not abstract Hub and Civitai into
one transport or invent a generic UI framework. Search/discovery state remains
non-authoritative; exact session definitions remain the generation boundary.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Callable
import gradio as gr
from .gradio_policy import INTERNAL_API_VISIBILITY
@dataclass(frozen=True)
class LoraUI:
dropdown: Any
strength: Any
prepare_button: Any
remove_selected_session_button: Any
prepare_status: Any
hub_quick_search: Any
hub_search_status: Any
hub_full_query: Any
hub_full_author: Any
hub_full_tags: Any
hub_pipeline_filter: Any
hub_pipeline_tags: Any
hub_full_sort: Any
hub_full_limit: Any
hub_full_search_button: Any
hub_full_cancel_button: Any
hub_full_results: Any
hub_full_status: Any
hf_name: Any
hf_repo: Any
hf_revision: Any
hf_inspect_button: Any
hf_file: Any
hf_add_button: Any
hf_status: Any
civitai_api_key: Any
civitai_gallery_items: Any
civitai_cursor_state: Any
civitai_search_state: Any
civitai_query: Any
civitai_search_button: Any
civitai_cancel_button: Any
civitai_tag: Any
civitai_username: Any
civitai_base_model: Any
civitai_sort: Any
civitai_period: Any
civitai_gallery: Any
civitai_load_more: Any
civitai_search_status: Any
civitai_url: Any
civitai_url_resolve: Any
civitai_version_state: Any
civitai_version: Any
civitai_file: Any
civitai_detail: Any
civitai_name: Any
civitai_apply_triggers: Any
civitai_load_examples: Any
civitai_example_prompt: Any
civitai_apply_example: Any
civitai_add: Any
civitai_status: Any
@dataclass(frozen=True)
class LoraHandlers:
apply_hub_quick_selection: Callable
inspect_hub_repo: Callable
search_hf_models: Callable
apply_hf_full_selection: Callable
search_civitai_models: Callable
load_more_civitai_models: Callable
inspect_civitai_gallery_selection: Callable
resolve_civitai_url: Callable
inspect_civitai_version_selection: Callable
apply_civitai_trigger_words: Callable
load_civitai_example_prompts: Callable
apply_civitai_example_prompt: Callable
add_civitai_session_lora: Callable
add_hf_session_lora: Callable
remove_selected_session_loras: Callable
prepare_selected_loras: Callable
invalidate_prepared_loras: Callable
def build_lora_ui(*, lora_dropdown_factory: Callable[[], Any], default_strength: float, hub_backend, civitai_backend) -> LoraUI:
"""Render the LoRA accordion and return concrete component handles."""
with gr.Accordion("LoRA", open=False):
gr.Markdown(
"Optional built-in or session LoRAs. Discovery/search state is never generation authority; "
"a selected source is copied into an exact session definition first. Hub/Civitai acquisition runs on CPU before GPU allocation."
)
dropdown = lora_dropdown_factory()
strength = gr.Slider(
minimum=0.0,
maximum=2.0,
step=0.05,
value=default_strength,
interactive=True,
label="LoRA strength",
info="Applied equally to all selected adapters. LoRA weights are not fused into base weights.",
)
with gr.Row():
prepare_button = gr.Button("Prepare selected LoRAs", variant="secondary", interactive=True, scale=3)
remove_selected_session_button = gr.Button("Remove selected session LoRAs", variant="secondary", interactive=True, scale=2)
prepare_status = gr.Markdown(
"Selected HF/Civitai files are prepared and verified on CPU before generation; GPU quota is not used for source acquisition."
)
with gr.Accordion("Add session LoRA", open=False):
with gr.Tabs():
with gr.Tab("Hugging Face Hub"):
gr.Markdown(
"Quick search is the lightweight route. Full discovery uses bounded `HfApi.list_models()` metadata search. "
"You can also paste an HF model page or `.safetensors` file URL; Inspect canonicalizes it to repo/revision/file identity."
)
hub_quick_search = hub_backend.HuggingFaceHubSearch(
label="Quick Hub search · public models",
placeholder="Search model repositories...",
result_limit=10,
)
hub_search_status = gr.Markdown(
"Search failure never disables manual repo entry. Private/gated repos may be entered directly and inspected with the Space token."
)
with gr.Accordion("Full Hub discovery", open=False):
with gr.Row():
hub_full_query = gr.Textbox(label="Query", value="LTX", placeholder="LTX / motion / style …")
hub_full_author = gr.Textbox(label="Author · optional", placeholder="owner or organization")
hub_full_tags = gr.Textbox(
label="HF tags · optional",
placeholder="comma-separated tags, e.g. lora, safetensors",
)
with gr.Row():
hub_pipeline_filter = gr.Checkbox(
value=True,
label="Filter by pipeline tag",
info="Advisory only. Turn this off when Hub metadata is wrong or missing.",
)
hub_pipeline_tags = gr.CheckboxGroup(
choices=list(hub_backend.HF_VIDEO_PIPELINE_TAGS),
value=list(hub_backend.HF_VIDEO_PIPELINE_TAGS),
label="Accepted video pipeline hints",
)
with gr.Row():
hub_full_sort = gr.Dropdown(
choices=list(hub_backend.HF_DISCOVERY_SORT_CHOICES),
value="trending_score",
label="Sort",
)
hub_full_limit = gr.Slider(5, 60, value=30, step=5, label="Result limit")
with gr.Row():
hub_full_search_button = gr.Button("Search Hub", variant="secondary", scale=4)
hub_full_cancel_button = gr.Button("Cancel", variant="stop", scale=1)
hub_full_results = gr.Dropdown(
choices=[], value=None, label="Full discovery results", interactive=True
)
hub_full_status = gr.Markdown()
hf_name = gr.Textbox(label="Display name", placeholder="My LTX-2.5 LoRA")
hf_repo = gr.Textbox(label="HF repo ID or URL", placeholder="owner/repo or https://huggingface.co/owner/repo/blob/main/file.safetensors")
hf_revision = gr.Textbox(label="Revision · optional", placeholder="commit / tag / branch")
hf_inspect_button = gr.Button("Inspect HF source", variant="secondary")
hf_file = gr.Dropdown(
label="Safetensors file",
choices=[],
value=None,
allow_custom_value=True,
interactive=True,
info="Inspect the repo to list .safetensors files, or type a repository-relative path manually.",
)
hf_add_button = gr.Button("Add HF LoRA", variant="secondary")
hf_status = gr.Markdown()
with gr.Tab("Civitai"):
if civitai_backend.api_key_from_env():
gr.Markdown(
"Type is fixed to **LORA**. Base Model defaults to **Auto · LTX 2.x family** and can be narrowed when Civitai exposes individual LTX values. "
"Owner `CIVITAI_API_KEY` Secret is active automatically; the key is never shown or serialized."
)
civitai_api_key = gr.State("")
else:
gr.Markdown(
"Type is fixed to **LORA**. Base Model defaults to **Auto · LTX 2.x family** and can be narrowed when Civitai exposes individual LTX values. "
"No owner `CIVITAI_API_KEY` Secret was found. Public discovery works anonymously; enter a key below when an authenticated download requires one."
)
civitai_api_key = gr.Textbox(
label="Civitai API Key · optional · session only",
type="password",
placeholder="Enter only if needed for authenticated Civitai access",
info="Used only by backend Civitai metadata/download requests in this browser session. Not exported to settings, logs, diagnostics, or LoRA definitions.",
)
civitai_gallery_items = gr.State([])
civitai_cursor_state = gr.State("")
civitai_search_state = gr.State({})
with gr.Row():
civitai_query = gr.Textbox(label="Search", placeholder="Search Civitai LoRAs...", scale=4)
civitai_search_button = gr.Button("Search Civitai", variant="secondary", scale=2)
civitai_cancel_button = gr.Button("Cancel", variant="stop", scale=1)
with gr.Row():
civitai_tag = gr.Textbox(label="Tag · optional", placeholder="tag")
civitai_username = gr.Textbox(label="Username · optional", placeholder="creator")
civitai_base_model = gr.Dropdown(
choices=[("Auto · LTX 2.x family", "__auto_ltx__"), ("All base models", "__all__")],
value="__auto_ltx__",
label="Base Model",
interactive=True,
)
with gr.Row():
civitai_sort = gr.Dropdown(
choices=list(civitai_backend.SORT_CHOICES),
value=civitai_backend.DEFAULT_SORT,
label="Sort",
interactive=True,
)
civitai_period = gr.Dropdown(
choices=list(civitai_backend.PERIOD_CHOICES),
value=civitai_backend.DEFAULT_PERIOD,
label="Period",
interactive=True,
)
civitai_gallery = gr.Gallery(
value=[],
label="Civitai LoRA discovery",
columns=4,
height=620,
object_fit="cover",
format="webp",
allow_preview=False,
buttons=[],
interactive=False,
)
with gr.Row():
civitai_load_more = gr.Button("Load more", variant="secondary", scale=1)
civitai_search_status = gr.Markdown(
"Search results use standard Gradio Gallery. **Load more** appends the next Civitai cursor batch."
)
with gr.Row():
civitai_url = gr.Textbox(
label="Civitai URL · optional direct route",
placeholder="https://civitai.com/models/...?...modelVersionId=...",
scale=5,
)
civitai_url_resolve = gr.Button("Resolve URL", variant="secondary", scale=1)
civitai_version_state = gr.State({})
civitai_version = gr.Dropdown(choices=[], value=None, label="Model version", interactive=True)
civitai_file = gr.Dropdown(choices=[], value=None, label="Exact .safetensors file", interactive=True)
civitai_detail = gr.Markdown(
"Select a Gallery result or resolve a Civitai URL. Version/file identity is verified before registration."
)
civitai_name = gr.Textbox(label="Display name", placeholder="My Civitai LTX LoRA")
with gr.Row():
civitai_apply_triggers = gr.Button("Apply trigger words", variant="secondary")
civitai_load_examples = gr.Button("Load example prompts", variant="secondary")
civitai_example_prompt = gr.Dropdown(
choices=[], value=None, label="Example prompt", interactive=True
)
civitai_apply_example = gr.Button("Apply example prompt", variant="secondary")
civitai_add = gr.Button("Add Civitai LoRA", variant="secondary")
civitai_status = gr.Markdown(
"Trigger words and example prompts are applied only by explicit button presses; selecting a result never changes the prompt."
)
return LoraUI(
dropdown=dropdown,
strength=strength,
prepare_button=prepare_button,
remove_selected_session_button=remove_selected_session_button,
prepare_status=prepare_status,
hub_quick_search=hub_quick_search,
hub_search_status=hub_search_status,
hub_full_query=hub_full_query,
hub_full_author=hub_full_author,
hub_full_tags=hub_full_tags,
hub_pipeline_filter=hub_pipeline_filter,
hub_pipeline_tags=hub_pipeline_tags,
hub_full_sort=hub_full_sort,
hub_full_limit=hub_full_limit,
hub_full_search_button=hub_full_search_button,
hub_full_cancel_button=hub_full_cancel_button,
hub_full_results=hub_full_results,
hub_full_status=hub_full_status,
hf_name=hf_name,
hf_repo=hf_repo,
hf_revision=hf_revision,
hf_inspect_button=hf_inspect_button,
hf_file=hf_file,
hf_add_button=hf_add_button,
hf_status=hf_status,
civitai_api_key=civitai_api_key,
civitai_gallery_items=civitai_gallery_items,
civitai_cursor_state=civitai_cursor_state,
civitai_search_state=civitai_search_state,
civitai_query=civitai_query,
civitai_search_button=civitai_search_button,
civitai_cancel_button=civitai_cancel_button,
civitai_tag=civitai_tag,
civitai_username=civitai_username,
civitai_base_model=civitai_base_model,
civitai_sort=civitai_sort,
civitai_period=civitai_period,
civitai_gallery=civitai_gallery,
civitai_load_more=civitai_load_more,
civitai_search_status=civitai_search_status,
civitai_url=civitai_url,
civitai_url_resolve=civitai_url_resolve,
civitai_version_state=civitai_version_state,
civitai_version=civitai_version,
civitai_file=civitai_file,
civitai_detail=civitai_detail,
civitai_name=civitai_name,
civitai_apply_triggers=civitai_apply_triggers,
civitai_load_examples=civitai_load_examples,
civitai_example_prompt=civitai_example_prompt,
civitai_apply_example=civitai_apply_example,
civitai_add=civitai_add,
civitai_status=civitai_status,
)
def wire_lora_events(
ui: LoraUI,
*,
prompt,
custom_lora_state,
prepared_lora_state,
handlers: LoraHandlers,
) -> None:
"""Wire LoRA discovery/registration events without owning backend semantics."""
ui.hub_quick_search.change(
handlers.apply_hub_quick_selection,
inputs=[ui.hub_quick_search, ui.hf_repo, ui.hf_file],
outputs=[ui.hf_repo, ui.hf_file, ui.hub_search_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.hf_inspect_button.click(
handlers.inspect_hub_repo,
inputs=[ui.hf_repo, ui.hf_revision, ui.hf_file],
outputs=[ui.hf_repo, ui.hf_revision, ui.hf_file, ui.hub_search_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
hub_full_search_event = ui.hub_full_search_button.click(
handlers.search_hf_models,
inputs=[
ui.hub_full_query, ui.hub_full_author, ui.hub_full_tags, ui.hub_full_sort,
ui.hub_pipeline_filter, ui.hub_pipeline_tags, ui.hub_full_limit,
],
outputs=[ui.hub_full_results, ui.hub_full_status],
queue=True,
concurrency_limit=1,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.hub_full_cancel_button.click(
lambda: "Hub search cancelled. Existing discovery results and session LoRAs were not changed.",
inputs=None,
outputs=[ui.hub_full_status],
cancels=[hub_full_search_event],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.hub_full_results.input(
handlers.apply_hf_full_selection,
inputs=[ui.hub_full_results, ui.hf_repo, ui.hf_file],
outputs=[ui.hf_repo, ui.hf_file, ui.hub_search_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
civitai_search_inputs = [
ui.civitai_query, ui.civitai_tag, ui.civitai_username, ui.civitai_sort,
ui.civitai_period, ui.civitai_base_model, ui.civitai_api_key,
]
civitai_search_outputs = [
ui.civitai_gallery, ui.civitai_gallery_items, ui.civitai_cursor_state,
ui.civitai_search_state, ui.civitai_base_model, ui.civitai_search_status,
]
civitai_search_event = ui.civitai_search_button.click(
handlers.search_civitai_models,
inputs=civitai_search_inputs,
outputs=civitai_search_outputs,
queue=True,
concurrency_limit=1,
concurrency_id="civitai-discovery",
api_visibility=INTERNAL_API_VISIBILITY,
)
civitai_query_submit_event = ui.civitai_query.submit(
handlers.search_civitai_models,
inputs=civitai_search_inputs,
outputs=civitai_search_outputs,
queue=True,
concurrency_limit=1,
concurrency_id="civitai-discovery",
api_visibility=INTERNAL_API_VISIBILITY,
)
civitai_load_more_event = ui.civitai_load_more.click(
handlers.load_more_civitai_models,
inputs=[ui.civitai_gallery_items, ui.civitai_cursor_state, ui.civitai_search_state, ui.civitai_api_key],
outputs=[ui.civitai_gallery, ui.civitai_gallery_items, ui.civitai_cursor_state, ui.civitai_search_status],
queue=True,
concurrency_limit=1,
concurrency_id="civitai-discovery",
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.civitai_cancel_button.click(
lambda: "Civitai search cancelled. Existing Gallery results and session LoRAs were not changed.",
inputs=None,
outputs=[ui.civitai_search_status],
cancels=[civitai_search_event, civitai_query_submit_event, civitai_load_more_event],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.civitai_gallery.select(
handlers.inspect_civitai_gallery_selection,
inputs=[ui.civitai_gallery_items, ui.civitai_name, ui.civitai_api_key],
outputs=[ui.civitai_version, ui.civitai_file, ui.civitai_version_state, ui.civitai_name, ui.civitai_detail],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.civitai_url_resolve.click(
handlers.resolve_civitai_url,
inputs=[ui.civitai_url, ui.civitai_name, ui.civitai_api_key],
outputs=[ui.civitai_version, ui.civitai_file, ui.civitai_version_state, ui.civitai_name, ui.civitai_detail],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.civitai_version.input(
handlers.inspect_civitai_version_selection,
inputs=[ui.civitai_version, ui.civitai_name, ui.civitai_api_key],
outputs=[ui.civitai_version, ui.civitai_file, ui.civitai_version_state, ui.civitai_name, ui.civitai_detail],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.civitai_apply_triggers.click(
handlers.apply_civitai_trigger_words,
inputs=[prompt, ui.civitai_version_state],
outputs=[prompt, ui.civitai_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.civitai_load_examples.click(
handlers.load_civitai_example_prompts,
inputs=[ui.civitai_version_state, ui.civitai_api_key],
outputs=[ui.civitai_example_prompt, ui.civitai_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.civitai_apply_example.click(
handlers.apply_civitai_example_prompt,
inputs=[prompt, ui.civitai_example_prompt],
outputs=[prompt, ui.civitai_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
civitai_add_event = ui.civitai_add.click(
handlers.add_civitai_session_lora,
inputs=[custom_lora_state, ui.civitai_name, ui.civitai_version_state, ui.civitai_file, ui.dropdown],
outputs=[custom_lora_state, ui.dropdown, ui.civitai_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
civitai_add_event.success(
handlers.invalidate_prepared_loras,
inputs=[],
outputs=[prepared_lora_state, ui.prepare_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
hf_add_event = ui.hf_add_button.click(
handlers.add_hf_session_lora,
inputs=[custom_lora_state, ui.hf_name, ui.hf_repo, ui.hf_file, ui.hf_revision, ui.dropdown],
outputs=[custom_lora_state, ui.dropdown, ui.hf_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
hf_add_event.success(
handlers.invalidate_prepared_loras,
inputs=[],
outputs=[prepared_lora_state, ui.prepare_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.remove_selected_session_button.click(
handlers.remove_selected_session_loras,
inputs=[custom_lora_state, ui.dropdown],
outputs=[custom_lora_state, ui.dropdown, prepared_lora_state, ui.prepare_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.prepare_button.click(
handlers.prepare_selected_loras,
inputs=[ui.dropdown, custom_lora_state, ui.civitai_api_key],
outputs=[prepared_lora_state, ui.prepare_status],
api_visibility=INTERNAL_API_VISIBILITY,
)
ui.dropdown.change(
handlers.invalidate_prepared_loras,
inputs=[],
outputs=[prepared_lora_state, ui.prepare_status],
queue=False,
api_visibility=INTERNAL_API_VISIBILITY,
)