"""Upload Pak Angels AI Tutor to a Hugging Face Space. Required environment variables: HF_TOKEN: A Hugging Face access token with write access to the Space. HF_SPACE_ID: The Space repo id, for example "your-username/pak-angels-ai-tutor". Optional environment variables: HF_COMMIT_MESSAGE: Custom commit message for the upload. HF_PRIVATE_SPACE: Set to "true" to create a private Space when using --create. """ from __future__ import annotations import argparse import os import sys from pathlib import Path from huggingface_hub import HfApi from huggingface_hub.utils import HfHubHTTPError PROJECT_ROOT = Path(__file__).resolve().parent DEFAULT_IGNORE_PATTERNS = [ ".env", ".env.local", ".env.*.local", ".git/", ".git/**", ".venv/", ".venv/**", "venv/", "venv/**", "__pycache__/", "__pycache__/**", "*.pyc", ".DS_Store", ".pytest_cache/", ".pytest_cache/**", ".ruff_cache/", ".ruff_cache/**", ".mypy_cache/", ".mypy_cache/**", "dist/", "dist/**", "build/", "build/**", "*.egg-info/", "*.egg-info/**", "*.log", "tmp/", "tmp/**", "temp/", "temp/**", ] def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Upload the current Pak Angels AI Tutor project to Hugging Face Spaces." ) parser.add_argument( "--space-id", default=os.getenv("HF_SPACE_ID", "").strip(), help='Hugging Face Space id, for example "username/pak-angels-ai-tutor".', ) parser.add_argument( "--token", default=os.getenv("HF_TOKEN", "").strip(), help="Hugging Face write token. Prefer setting HF_TOKEN instead of passing this flag.", ) parser.add_argument( "--commit-message", default=os.getenv("HF_COMMIT_MESSAGE", "Deploy Pak Angels AI Tutor"), help="Commit message shown in the Hugging Face Space repository.", ) parser.add_argument( "--repo-type", default="space", choices=["space"], help="Repository type. Spaces deployments use 'space'.", ) parser.add_argument( "--create", action="store_true", help="Create the Gradio Space if it does not already exist.", ) parser.add_argument( "--private", action="store_true", default=os.getenv("HF_PRIVATE_SPACE", "").lower() in {"1", "true", "yes"}, help="Create the Space as private when --create is used.", ) return parser.parse_args() def validate_inputs(space_id: str, token: str) -> None: missing = [] if not space_id: missing.append("HF_SPACE_ID") if not token: missing.append("HF_TOKEN") if missing: joined = ", ".join(missing) raise ValueError( f"Missing required setting(s): {joined}. Set them as environment variables " "or pass --space-id and --token." ) if "/" not in space_id: raise ValueError('HF_SPACE_ID should look like "username/space-name".') placeholder_values = { "your_hugging_face_write_token_here", "your-token", "your_token", } placeholder_space_ids = { "your-username/your-space-name", "username/space-name", "your-username/pak-angels-ai-tutor", } if token in placeholder_values: raise ValueError("HF_TOKEN is still a placeholder. Use a real Hugging Face write token.") if space_id in placeholder_space_ids: raise ValueError( "HF_SPACE_ID is still a placeholder. Use your real Space id, such as " '"mohammadanwarkhan/pak-angels-ai-tutor".' ) def ensure_space_exists(api: HfApi, args: argparse.Namespace) -> None: if not args.create: return api.create_repo( repo_id=args.space_id, repo_type=args.repo_type, private=args.private, space_sdk="gradio", exist_ok=True, ) def preflight_check(api: HfApi, args: argparse.Namespace) -> None: whoami = api.whoami() account_name = whoami.get("name") or whoami.get("fullname") or "authenticated account" print(f"Authenticated with Hugging Face as: {account_name}") try: api.repo_info(repo_id=args.space_id, repo_type=args.repo_type) print(f"Space is accessible: {args.space_id}") except HfHubHTTPError as error: if args.create: raise raise RuntimeError( f"The token cannot access the Space '{args.space_id}'. Check that the Space id " "is exact and that this Hugging Face token has write access to that Space. " "If the Space does not exist, rerun with --create." ) from error def upload_project(args: argparse.Namespace) -> str: api = HfApi(token=args.token) ensure_space_exists(api, args) preflight_check(api, args) api.upload_folder( folder_path=str(PROJECT_ROOT), repo_id=args.space_id, repo_type=args.repo_type, commit_message=args.commit_message, ignore_patterns=DEFAULT_IGNORE_PATTERNS, ) return f"https://huggingface.co/spaces/{args.space_id}" def main() -> int: args = parse_args() try: validate_inputs(args.space_id, args.token) url = upload_project(args) except ValueError as error: print(f"Configuration error: {error}", file=sys.stderr) return 2 except HfHubHTTPError as error: print(f"Hugging Face upload failed: {error}", file=sys.stderr) print( "Check that HF_SPACE_ID is your real Space id, HF_TOKEN is a real write token, " "and the Space exists. If it does not exist, rerun with --create.", file=sys.stderr, ) return 1 except Exception as error: print(f"Deployment failed: {error}", file=sys.stderr) return 1 print("Deployment upload complete.") print(f"Space URL: {url}") print("Remember to set OPENAI_API_KEY in the Space secrets before testing the tutor.") return 0 if __name__ == "__main__": raise SystemExit(main())