Spaces:
Running
Running
feat: add dynamic model search for custom GGUF loader
Browse files- Add model_search_results dropdown to show matching GGUF models from HF
- Implement search_models_dynamic() to search as user types (2+ chars)
- Add on_model_selected_from_search() to auto-fill repo and discover files
- Connect event handlers: custom_repo_id.change triggers search
- Connect event handlers: model_search_results.change triggers file discovery
- Show model metadata: params, downloads in search results
- Keep manual 'Discover Files' button as backup option
app.py
CHANGED
|
@@ -1673,20 +1673,30 @@ def create_interface():
|
|
| 1673 |
|
| 1674 |
custom_repo_id = gr.Textbox(
|
| 1675 |
label="HuggingFace Repo ID",
|
| 1676 |
-
placeholder="e.g., unsloth/DeepSeek-R1-Distill-Qwen-7B-GGUF",
|
| 1677 |
-
info="
|
| 1678 |
interactive=True,
|
| 1679 |
)
|
| 1680 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1681 |
# Hidden fields to store discovered file data
|
| 1682 |
custom_repo_files = gr.State([])
|
| 1683 |
|
| 1684 |
# File dropdown (populated after repo discovery)
|
| 1685 |
custom_file_dropdown = gr.Dropdown(
|
| 1686 |
-
label="Available GGUF Files",
|
| 1687 |
choices=[],
|
| 1688 |
value=None,
|
| 1689 |
-
info="
|
| 1690 |
interactive=True,
|
| 1691 |
visible=True,
|
| 1692 |
)
|
|
@@ -1999,7 +2009,101 @@ def create_interface():
|
|
| 1999 |
gr.update(visible=True, value="β
Files discovered! Select one and click 'Load Selected Model'")
|
| 2000 |
)
|
| 2001 |
|
| 2002 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2003 |
discover_btn.click(
|
| 2004 |
fn=discover_custom_files,
|
| 2005 |
inputs=[custom_repo_id],
|
|
|
|
| 1673 |
|
| 1674 |
custom_repo_id = gr.Textbox(
|
| 1675 |
label="HuggingFace Repo ID",
|
| 1676 |
+
placeholder="e.g., unsloth/DeepSeek-R1-Distill-Qwen-7B-GGUF or type to search...",
|
| 1677 |
+
info="Type model name to search GGUF models on HF, or paste full repo ID",
|
| 1678 |
interactive=True,
|
| 1679 |
)
|
| 1680 |
|
| 1681 |
+
# Search results dropdown (shows matching models from HF)
|
| 1682 |
+
model_search_results = gr.Dropdown(
|
| 1683 |
+
label="π Search Results - Select a Model",
|
| 1684 |
+
choices=[],
|
| 1685 |
+
value=None,
|
| 1686 |
+
info="Matching GGUF models from HuggingFace Hub will appear here as you type",
|
| 1687 |
+
interactive=True,
|
| 1688 |
+
visible=True,
|
| 1689 |
+
)
|
| 1690 |
+
|
| 1691 |
# Hidden fields to store discovered file data
|
| 1692 |
custom_repo_files = gr.State([])
|
| 1693 |
|
| 1694 |
# File dropdown (populated after repo discovery)
|
| 1695 |
custom_file_dropdown = gr.Dropdown(
|
| 1696 |
+
label="π¦ Available GGUF Files - Select Precision",
|
| 1697 |
choices=[],
|
| 1698 |
value=None,
|
| 1699 |
+
info="GGUF files from selected repo (alphabetically sorted)",
|
| 1700 |
interactive=True,
|
| 1701 |
visible=True,
|
| 1702 |
)
|
|
|
|
| 2009 |
gr.update(visible=True, value="β
Files discovered! Select one and click 'Load Selected Model'")
|
| 2010 |
)
|
| 2011 |
|
| 2012 |
+
# ==========================================
|
| 2013 |
+
# DYNAMIC MODEL SEARCH - New Feature
|
| 2014 |
+
# ==========================================
|
| 2015 |
+
|
| 2016 |
+
def search_models_dynamic(query):
|
| 2017 |
+
"""Search for GGUF models as user types."""
|
| 2018 |
+
if not query or len(query) < 2:
|
| 2019 |
+
# Clear search results if query too short
|
| 2020 |
+
return gr.update(choices=[], value=None)
|
| 2021 |
+
|
| 2022 |
+
# Search popular GGUF models from cache
|
| 2023 |
+
matches = search_gguf_models(query, limit=10)
|
| 2024 |
+
|
| 2025 |
+
if not matches:
|
| 2026 |
+
# No matches found
|
| 2027 |
+
return gr.update(choices=["No matching models found"], value=None)
|
| 2028 |
+
|
| 2029 |
+
# Format choices with metadata
|
| 2030 |
+
choices = []
|
| 2031 |
+
for m in matches:
|
| 2032 |
+
repo_id = m["repo_id"]
|
| 2033 |
+
params = m.get("params", "Unknown")
|
| 2034 |
+
downloads = m.get("downloads", 0)
|
| 2035 |
+
# Format downloads
|
| 2036 |
+
if downloads >= 1000000:
|
| 2037 |
+
dl_str = f"{downloads/1000000:.1f}M"
|
| 2038 |
+
elif downloads >= 1000:
|
| 2039 |
+
dl_str = f"{downloads/1000:.1f}K"
|
| 2040 |
+
else:
|
| 2041 |
+
dl_str = str(downloads)
|
| 2042 |
+
|
| 2043 |
+
display = f"{repo_id} | {params} params | β¬οΈ {dl_str}"
|
| 2044 |
+
choices.append((display, repo_id))
|
| 2045 |
+
|
| 2046 |
+
return gr.update(choices=choices, value=None)
|
| 2047 |
+
|
| 2048 |
+
# Auto-search as user types (with small delay via change event)
|
| 2049 |
+
custom_repo_id.change(
|
| 2050 |
+
fn=search_models_dynamic,
|
| 2051 |
+
inputs=[custom_repo_id],
|
| 2052 |
+
outputs=[model_search_results],
|
| 2053 |
+
)
|
| 2054 |
+
|
| 2055 |
+
def on_model_selected_from_search(selected_repo):
|
| 2056 |
+
"""Handle when user selects a model from search results."""
|
| 2057 |
+
if not selected_repo or selected_repo == "No matching models found":
|
| 2058 |
+
return (
|
| 2059 |
+
gr.update(value=""),
|
| 2060 |
+
gr.update(choices=[], value=None),
|
| 2061 |
+
gr.update(visible=True, value="Please select a model from search results"),
|
| 2062 |
+
[],
|
| 2063 |
+
)
|
| 2064 |
+
|
| 2065 |
+
# Auto-discover files for selected repo
|
| 2066 |
+
# First show searching status
|
| 2067 |
+
yield (
|
| 2068 |
+
gr.update(value=selected_repo),
|
| 2069 |
+
gr.update(choices=["Searching..."], value=None, interactive=False),
|
| 2070 |
+
gr.update(visible=True, value="π Discovering GGUF files..."),
|
| 2071 |
+
[],
|
| 2072 |
+
)
|
| 2073 |
+
|
| 2074 |
+
files, error = list_repo_gguf_files(selected_repo)
|
| 2075 |
+
|
| 2076 |
+
if error:
|
| 2077 |
+
yield (
|
| 2078 |
+
gr.update(value=selected_repo),
|
| 2079 |
+
gr.update(choices=[], value=None, interactive=True),
|
| 2080 |
+
gr.update(visible=True, value=f"β {error}"),
|
| 2081 |
+
[],
|
| 2082 |
+
)
|
| 2083 |
+
elif not files:
|
| 2084 |
+
yield (
|
| 2085 |
+
gr.update(value=selected_repo),
|
| 2086 |
+
gr.update(choices=[], value=None, interactive=True),
|
| 2087 |
+
gr.update(visible=True, value="β No GGUF files found in this repository"),
|
| 2088 |
+
[],
|
| 2089 |
+
)
|
| 2090 |
+
else:
|
| 2091 |
+
choices = [format_file_choice(f) for f in files]
|
| 2092 |
+
yield (
|
| 2093 |
+
gr.update(value=selected_repo),
|
| 2094 |
+
gr.update(choices=choices, value=choices[0] if choices else None, interactive=True),
|
| 2095 |
+
gr.update(visible=True, value=f"β
Found {len(files)} GGUF files! Select precision and click 'Load Selected Model'"),
|
| 2096 |
+
files,
|
| 2097 |
+
)
|
| 2098 |
+
|
| 2099 |
+
# When user selects from search results, auto-fill repo and discover files
|
| 2100 |
+
model_search_results.change(
|
| 2101 |
+
fn=on_model_selected_from_search,
|
| 2102 |
+
inputs=[model_search_results],
|
| 2103 |
+
outputs=[custom_repo_id, custom_file_dropdown, custom_status, custom_repo_files],
|
| 2104 |
+
)
|
| 2105 |
+
|
| 2106 |
+
# Manual discover button (kept as backup)
|
| 2107 |
discover_btn.click(
|
| 2108 |
fn=discover_custom_files,
|
| 2109 |
inputs=[custom_repo_id],
|