"""Deploy BankMind to a Hugging Face Spaces Docker SDK Space. Reads `HUGGINGFACE_TOKEN`, `ANTHROPIC_API_KEY`, `QDRANT_URL`, `QDRANT_API_KEY` from .env. Creates the Space (idempotent: ok if it already exists), sets the 3 runtime secrets, then uploads the repo contents (excluding raw/processed data, .venv, .git, etc.). Prints the resulting URL. Usage: python scripts/deploy_to_hf_spaces.py [--space-name USER/REPO] Default: Arjun10g/bankmind """ from __future__ import annotations import argparse import os import sys from pathlib import Path from dotenv import load_dotenv ROOT = Path(__file__).resolve().parents[1] load_dotenv(ROOT / ".env") DEFAULT_SPACE = "Arjun10g/bankmind" # Runtime secrets to set on the Space. These are required for queries that hit # Qdrant or invoke the LLM. They never leave the user's HF account once set. SECRETS_TO_SET = ("ANTHROPIC_API_KEY", "QDRANT_URL", "QDRANT_API_KEY") IGNORE_PATTERNS = [ ".git/*", ".git/**", ".venv/*", ".venv/**", ".claude/*", ".claude/**", "venv/*", "venv/**", "__pycache__/*", "__pycache__/**", "*/__pycache__/*", "*/__pycache__/**", "*.pyc", "*.pyo", ".env", ".env.local", "data/raw/*", "data/raw/**", "data/processed/*", "data/processed/**", "logs/*", "logs/**", ".pytest_cache/*", ".pytest_cache/**", ".ruff_cache/*", ".ruff_cache/**", ".mypy_cache/*", ".mypy_cache/**", ".DS_Store", "*.egg-info/*", "*.egg-info/**", "build/*", "build/**", "dist/*", "dist/**", ".uv-cache/*", ".uv-cache/**", ".cache/*", ".cache/**", ] def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--space-name", default=DEFAULT_SPACE, help=f"HF Space repo (USER/REPO). Default: {DEFAULT_SPACE}") ap.add_argument("--private", action="store_true", help="Create the Space as private (default: public)") args = ap.parse_args() token = os.environ.get("HUGGINGFACE_TOKEN", "").strip() if not token: print("ERROR: HUGGINGFACE_TOKEN not set in .env", file=sys.stderr) print(" Get one at https://huggingface.co/settings/tokens (Write scope)", file=sys.stderr) return 1 from huggingface_hub import HfApi from huggingface_hub.utils import RepositoryNotFoundError api = HfApi(token=token) print(f"\n=== Deploying to {args.space_name} ===") # 1. Create Space (idempotent) print(f"\n[1/3] Creating Space (Docker SDK, " f"{'private' if args.private else 'public'})...") try: api.repo_info(args.space_name, repo_type="space") print(f" Space {args.space_name} already exists — will overwrite contents.") except RepositoryNotFoundError: api.create_repo( repo_id=args.space_name, repo_type="space", space_sdk="docker", private=args.private, ) print(f" Created new Space.") # 2. Set secrets print(f"\n[2/3] Setting Space secrets...") n_set = 0 for key in SECRETS_TO_SET: value = os.environ.get(key, "").strip() if not value: print(f" SKIP {key} (not in .env)") continue api.add_space_secret(repo_id=args.space_name, key=key, value=value) n_set += 1 print(f" SET {key}") print(f" → {n_set}/{len(SECRETS_TO_SET)} secrets set.") # 3. Upload repo contents print(f"\n[3/3] Uploading repo contents...") api.upload_folder( repo_id=args.space_name, repo_type="space", folder_path=str(ROOT), ignore_patterns=IGNORE_PATTERNS, commit_message="Deploy BankMind", ) url = f"https://huggingface.co/spaces/{args.space_name}" print(f"\nāœ… Deployed → {url}") print(f"\nThe Space will rebuild from the Dockerfile. First build takes ~10-15 minutes.") print(f"Monitor build progress at: {url}/?logs=container") return 0 if __name__ == "__main__": sys.exit(main())