token-visualiser / hub_ui.py
jejunepixels's picture
Update hub_ui.py
9d07534 verified
Raw History Blame Contribute Delete
6.91 kB
"""Hugging Face loading screen, keeping SDK download work off the UI thread."""
from concurrent.futures import ThreadPoolExecutor
from queue import Empty, Queue
import streamlit as st
from hub_models import build_plan, discover_models, download_model, format_size, friendly_hub_error, check_hosted_download
from hub_models import CACHE_ROOT
from app_environment import hosted_mode, hosted_download_limit, new_session_directory, PRIVATE_CACHE_ROOT
MODEL_PRESETS = {
'SmolLM2 135M · small & quick': ('HuggingFaceTB/SmolLM2-135M', 'About 271 MB · English · safetensors'),
'GPT-2 · classic English model': ('openai-community/gpt2', 'About 549 MB · English · safetensors'),
'Gemma 3 1B · Q4 GGUF': (
'https://huggingface.co/lmstudio-community/gemma-3-1B-it-qat-GGUF/blob/main/gemma-3-1B-it-QAT-Q4_0.gguf',
'About 720 MB · quantized GGUF'),
'Qwen3 0.6B · multilingual': ('Qwen/Qwen3-0.6B', 'About 1.52 GB · multilingual · safetensors'),
}
def render_hub_loader(activate_model):
st.caption('Choose a suggested model or paste a Hugging Face link. Weights download directly to this Space.'
if hosted_mode() else 'Choose a suggested model or paste a Hugging Face link. Weights download to this computer.')
source = st.radio('Model source', ['Choose from list', 'Paste URL or repository ID'], horizontal=True, key='hf_source')
if source == 'Choose from list':
preset = st.selectbox('Suggested model', list(MODEL_PRESETS), key='hf_preset')
repo, description = MODEL_PRESETS[preset]
st.caption(description + ' · downloaded on first use; cached copies are reused')
else:
repo = st.text_input('Hugging Face repository or URL', placeholder='owner/model or https://huggingface.co/owner/model', key='hf_repo')
if hosted_mode():
st.caption(f'Up to {format_size(hosted_download_limit())} per model, subject to free storage. Larger vocabularies take longer to search.')
with st.expander('Revision & access token (optional)'):
revision = st.text_input('Revision', placeholder='main, branch, tag or commit', key='hf_revision')
token = st.text_input('Hugging Face read token', type='password', key='hf_token',
help='For private or gated models. Sent to this app server and Hugging Face for this session; it is not saved as a login.')
identity = (repo.strip(), revision.strip())
if st.button('Find model files', key='hf_find_files', type='primary', disabled=not repo.strip(), use_container_width=True):
st.session_state.pop('hf_catalog', None)
try:
with st.spinner('Reading Hugging Face repository…'):
st.session_state.hf_catalog = discover_models(repo, revision, token)
st.session_state.hf_catalog_identity = identity
except Exception as error:
st.error(friendly_hub_error(error, token))
catalog = st.session_state.get('hf_catalog')
if catalog is None or st.session_state.get('hf_catalog_identity') != identity:
return
st.link_button('View repository on Hugging Face', f'https://huggingface.co/{catalog.repo_id}')
if not catalog.choices:
st.info('No supported weights found. Choose a single-file GGUF, standalone safetensors, or a safetensors index. Split GGUF and PyTorch .bin files are not supported.')
return
preferred = catalog.choices.index(catalog.preferred) if catalog.preferred else 0
selection = st.selectbox('Model weights', catalog.choices, index=preferred,
key=f'hf_weights:{catalog.repo_id}:{catalog.commit}:{catalog.preferred}')
private = hosted_mode() and bool(token)
if private and 'private_hub_cache' not in st.session_state:
st.session_state.private_hub_cache = new_session_directory(PRIVATE_CACHE_ROOT)
cache_root = st.session_state.private_hub_cache if private else CACHE_ROOT
plan_key = (catalog.repo_id, catalog.commit, selection, str(cache_root))
plans = st.session_state.setdefault('hf_plans', {})
try:
if plan_key not in plans:
with st.spinner('Checking required model files…'):
plans[plan_key] = build_plan(catalog, selection, token, cache_root=cache_root)
plan = plans[plan_key]
except Exception as error:
st.error(friendly_hub_error(error, token))
return
st.caption(f'{len(plan.weight_files)} weights file(s) · {format_size(plan.size)} total · revision {catalog.commit[:12]}')
if plan.embeddings_only:
st.caption('Only shards containing recognised token embeddings will be downloaded.')
elif selection.endswith('.index.json'):
st.caption('Embedding names were not recognised in this index; all listed weight shards will be downloaded.')
with st.expander('Files to download'):
for filename in plan.files:
st.text(f'{filename} · {format_size(catalog.files.get(filename))}')
try:
check_hosted_download(plan)
except (ValueError, OSError) as error:
st.info(str(error))
return
load_label = 'Load into Space' if hosted_mode() else 'Download & load'
if st.button(load_label, key='hf_download', type='primary', use_container_width=True):
events = Queue()
progress = st.progress(0, text='Preparing download…')
try:
with ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(download_model, plan, token, lambda done, text: events.put((done, text)))
while not future.done():
try:
done, text = events.get(timeout=.15)
# Drop outdated progress events without touching Streamlit on a worker.
while not events.empty():
done, text = events.get_nowait()
progress.progress(done, text=text)
except Empty:
pass
model_path, tokenizer_path = future.result()
progress.progress(1., text='Download complete. Reading embeddings…')
activate_model(model_path, tokenizer_path, f'{catalog.repo_id} · {selection}')
st.session_state.clear_hub_token = True
st.rerun()
except Exception as error:
st.error(friendly_hub_error(error, token))
finally:
progress.empty()
if private:
st.caption('Token-authenticated downloads use a private session folder, removed when you clear the model. The Space owner operates this server.')
else:
st.caption('Downloads are cached and reused. Clearing a model keeps public downloaded files. Space restarts may clear the cache.' if hosted_mode() else 'Downloads are cached in this project’s .model-cache folder and reused. Clearing a model keeps its downloaded files.')