Spaces:
Running on Zero
Running on Zero
Download desktop/setup.py from AngeloUNIMI/document_exam_trainer: direct link, hf CLI and curl.
- Browser
- Download file 7.92 kB
-
https://huggingface.co/spaces/AngeloUNIMI/document_exam_trainer/resolve/main/desktop/setup.py
- Command line
-
hf download hf://spaces/AngeloUNIMI/document_exam_trainer/desktop/setup.py
-
curl -L -o setup.py https://huggingface.co/spaces/AngeloUNIMI/document_exam_trainer/resolve/main/desktop/setup.py
7.92 kB
| """Interactive installer runs INSIDE Docker; no host Python/venv required.""" | |
| from __future__ import annotations | |
| import argparse | |
| import getpass | |
| from pathlib import Path | |
| from .configuration import DATA, SECRETS, LocalConfig, DEFAULT_SPACE, load_config, save_config, repo_id | |
| from .storage import LocalStore | |
| def ask(text: str, default: str = '') -> str: | |
| value=input(f'{text}' + (f' [{default}]' if default else '') + ': ').strip() | |
| return value or default | |
| def yes(text: str, default: bool = False) -> bool: | |
| while True: | |
| v=ask(text+' (y/n)','y' if default else 'n').lower() | |
| if v in ('y','yes'): return True | |
| if v in ('n','no'): return False | |
| print('Enter y or n.') | |
| def password() -> str: | |
| while True: | |
| p=getpass.getpass('Local password (12+ characters, hidden): ') | |
| q=getpass.getpass('Repeat password: ') | |
| if p==q and 12<=len(p)<=512: | |
| return p | |
| print('Passwords must match and contain 12-512 characters.') | |
| def new_user(store, *, admin=False): | |
| while True: | |
| username=ask('Local username').lower() | |
| try: | |
| uid=store.create_user(username,password(),admin=admin) | |
| print(f'Created local account: {username}') | |
| return uid | |
| except ValueError as exc: | |
| print(exc) | |
| def configure_token(store,uid): | |
| u=store.by_id(uid) | |
| print(f"HF settings for {u['username']} (not your local password).") | |
| print('No token is needed for CPU mode. You may skip now and add your personal token in the app.') | |
| token=getpass.getpass('Personal HF token (hidden; Enter to keep/skip): ').strip() | |
| consent=yes('Allow sending source excerpts, question, rubric and transcript to the configured HF Space?',bool(u['remote_consent'])) | |
| store.preferences(uid,u['backend'],consent,token) | |
| def manage(store,config): | |
| # This CLI requires Docker/host access, which is already administrative. | |
| # Still authenticate a local admin when account login is enabled. | |
| if config.accounts: | |
| name=ask('Local administrator username') | |
| if not store.authenticate(name,getpass.getpass('Administrator password: ')): | |
| raise SystemExit('Administrator sign-in failed.') | |
| admin=store.user(name) | |
| if not admin['admin']: | |
| raise SystemExit('Administrator account required.') | |
| else: | |
| admin=store.user('local') | |
| while True: | |
| print('\nAccounts: '+', '.join(u['username']+(' (admin)' if u['admin'] else '') for u in store.list_users())) | |
| op=ask('Action: add / password / token / delete / done','done') | |
| if op=='done': return | |
| if op=='add': | |
| uid=new_user(store,admin=yes('Administrator account?')) | |
| if yes('Configure this account HF token now?'): configure_token(store,uid) | |
| continue | |
| target=store.user(ask('Target username')) | |
| if not target: | |
| print('Account not found.');continue | |
| try: | |
| if op=='password': | |
| store.reset_password(target['id'],password()) | |
| print('Password reset. Restart the app to invalidate existing sign-ins.') | |
| elif op=='token': configure_token(store,target['id']) | |
| elif op=='delete': | |
| if yes('Permanently delete this account, saved courses and history?'): | |
| store.delete_user(admin['id'],target['id']) | |
| print('Account deleted. Restart the app to end its existing sessions.') | |
| else: print('Unknown action.') | |
| except ValueError as exc: print(exc) | |
| def main(): | |
| parser=argparse.ArgumentParser() | |
| parser.add_argument('--manage-users',action='store_true') | |
| args=parser.parse_args() | |
| store=LocalStore(DATA,SECRETS) | |
| config=load_config() if (DATA/'settings.json').exists() else LocalConfig() | |
| if args.manage_users: | |
| return manage(store,config) | |
| print('\nDOCUMENT EXAM TRAINER - LOCAL SETUP\n') | |
| print('Data and encrypted tokens will be saved in separate named Docker volumes.') | |
| print('The application port is restricted to localhost by default. No public access is configured.') | |
| if (DATA/'settings.json').exists() and not yes('An installation exists. Change its settings?',False): | |
| print('Keeping existing configuration and all saved data.');return | |
| config.accounts=yes('Require local username/password login? (recommended for shared computers)',config.accounts) | |
| if config.accounts: | |
| admins=[u for u in store.list_users() if u['admin'] and u['enabled'] and u['username']!='local'] | |
| if not admins: | |
| print('Create the first local administrator account.') | |
| uid=new_user(store,admin=True) | |
| else: | |
| uid=admins[0]['id'] | |
| print('Keeping existing accounts and passwords.') | |
| while yes('Add another local account?'): | |
| new_user(store) | |
| else: | |
| print('Single-user mode has no login. Anyone who can use this computer/localhost port can access its local profile.') | |
| u=store.user('local') | |
| uid=u['id'] if u else store.create_user('local','',admin=True,single=True) | |
| while True: | |
| choice=ask('Default inference: 1 = remote HF Space, 2 = local CPU', '1' if config.default_backend=='remote_hf' else '2') | |
| if choice in ('1','2'): break | |
| config.default_backend='remote_hf' if choice=='1' else 'local_cpu' | |
| while True: | |
| try: | |
| config.remote_space=repo_id(ask('Remote Space (used only in remote mode)',config.remote_space)) | |
| break | |
| except ValueError as exc: print(exc) | |
| config.allow_anonymous=yes('Allow remote requests without a personal HF token? Anonymous quotas are more restrictive.',config.allow_anonymous) | |
| print('\nCPU models: 1 = compact Qwen3-1.7B (both tasks), 2 = Qwen3-4B (both tasks), 3 = custom public HF model IDs.') | |
| print('CPU inference is slower and the compact model is less capable than the hosted 14B evaluator.') | |
| print('Planning estimates: allow roughly 12-16 GB RAM for compact CPU mode, 24+ GB for 4B; actual usage varies. No quantization is enabled for the CPU LLM.') | |
| choice=ask('CPU model preset','1') | |
| if choice=='1': config.cpu_question_model=config.cpu_evaluation_model='Qwen/Qwen3-1.7B' | |
| elif choice=='2': config.cpu_question_model=config.cpu_evaluation_model='Qwen/Qwen3-4B' | |
| elif choice=='3': | |
| config.cpu_question_model=repo_id(ask('CPU question model',config.cpu_question_model)) | |
| config.cpu_evaluation_model=repo_id(ask('CPU evaluation model',config.cpu_evaluation_model)) | |
| print('Custom checkpoints must work with Transformers/Qwen-style chat templates; not every model is compatible.') | |
| else: raise ValueError('Unknown CPU model preset.') | |
| config.cpu_threads=int(ask('CPU threads',str(config.cpu_threads))) | |
| config.offline_models=yes('Use cached models only? Choose no for the first installation.',config.offline_models) | |
| if yes('Configure the first account HF token now?',config.default_backend=='remote_hf'): | |
| configure_token(store,uid) | |
| if config.accounts: | |
| others = [u for u in store.list_users() if u['id'] != uid and u['username'] != 'local' and u['enabled']] | |
| for other in others: | |
| if yes(f"Configure HF token/consent for {other['username']} now?"): | |
| configure_token(store, other['id']) | |
| save_config(config) | |
| print('\nSetup saved. No passwords or HF tokens were written into the image or compose file.') | |
| print('Open http://localhost:7860 after the container starts. Each user can change backend and HF token in Account & inference.') | |
| print('Models download on first use. CPU-only mode sends no documents/answers to a remote inference service.') | |
| if __name__=='__main__': | |
| try: main() | |
| except (EOFError,KeyboardInterrupt): raise SystemExit('\nSetup cancelled. Rerun the installer to finish.') | |
| except ValueError as exc: raise SystemExit(str(exc)) | |