"""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.')