project_02_DS / deploy_hf.py
griddev's picture
Deploy Streamlit Space app
241ce59 verified
#!/usr/bin/env python3
"""
Deploy helper for:
1) Uploading fine-tuned weights to a model repo.
2) Uploading Space code to a Streamlit Space repo.
Usage:
HF_TOKEN=hf_xxx python3 deploy_hf.py
"""
from __future__ import annotations
import argparse
import os
import sys
from typing import Iterable
from huggingface_hub import HfApi
from huggingface_hub.errors import BadRequestError
DEFAULT_WEIGHTS_REPO = "griddev/vlm-caption-weights"
DEFAULT_SPACE_REPO = "griddev/project_02_DS"
DEFAULT_WEIGHTS_SOURCE = "../project_02"
DEFAULT_SPACE_DIR = "."
DEFAULT_SPACE_SDK = "streamlit"
def _abort(message: str) -> None:
print(f"ERROR: {message}", file=sys.stderr)
raise SystemExit(1)
def _ensure_exists(path: str) -> None:
if not os.path.exists(path):
_abort(f"Required path not found: {path}")
def _print_list(header: str, items: Iterable[str]) -> None:
print(header)
for item in items:
print(f" - {item}")
def upload_weights(api: HfApi, repo_id: str, weights_source: str) -> None:
source_root = os.path.abspath(weights_source)
outputs_dir = os.path.join(source_root, "outputs")
shakespeare_txt = os.path.join(source_root, "input.txt")
shakespeare_weights = os.path.join(source_root, "shakespeare_transformer.pt")
_ensure_exists(outputs_dir)
_ensure_exists(shakespeare_txt)
_ensure_exists(shakespeare_weights)
api.create_repo(repo_id=repo_id, repo_type="model", exist_ok=True)
print(f"Uploading weights to model repo: {repo_id}")
api.upload_folder(
repo_id=repo_id,
repo_type="model",
folder_path=outputs_dir,
path_in_repo="outputs",
commit_message="Upload fine-tuned checkpoints",
)
api.upload_file(
repo_id=repo_id,
repo_type="model",
path_or_fileobj=shakespeare_txt,
path_in_repo="input.txt",
commit_message="Upload input corpus for custom VLM",
)
api.upload_file(
repo_id=repo_id,
repo_type="model",
path_or_fileobj=shakespeare_weights,
path_in_repo="shakespeare_transformer.pt",
commit_message="Upload Shakespeare decoder weights",
)
def _create_space_repo(api: HfApi, repo_id: str, space_sdk: str) -> None:
try:
api.create_repo(
repo_id=repo_id,
repo_type="space",
space_sdk=space_sdk,
exist_ok=True,
)
return
except BadRequestError as e:
msg = str(e)
if "Invalid option" not in msg or "sdk" not in msg:
raise
print(
"Space creation rejected sdk value "
f"'{space_sdk}'. Retrying with 'gradio' "
"and relying on README.md front matter for streamlit."
)
api.create_repo(
repo_id=repo_id,
repo_type="space",
space_sdk="gradio",
exist_ok=True,
)
def upload_space_code(api: HfApi, repo_id: str, space_dir: str, space_sdk: str) -> None:
space_dir = os.path.abspath(space_dir)
_ensure_exists(space_dir)
_create_space_repo(api, repo_id=repo_id, space_sdk=space_sdk)
print(f"Uploading Space code to: {repo_id}")
ignore_patterns = [
".git/**",
"__pycache__/**",
"*.pyc",
".DS_Store",
"venv/**",
"weights_bundle/**",
"outputs/**",
"*.pt",
]
_print_list("Ignoring during Space upload:", ignore_patterns)
api.upload_folder(
repo_id=repo_id,
repo_type="space",
folder_path=space_dir,
ignore_patterns=ignore_patterns,
commit_message="Deploy Streamlit Space app",
)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Deploy weights + Space to Hugging Face")
parser.add_argument(
"--weights-repo",
default=DEFAULT_WEIGHTS_REPO,
help=f"Model repo for fine-tuned checkpoints (default: {DEFAULT_WEIGHTS_REPO})",
)
parser.add_argument(
"--space-repo",
default=DEFAULT_SPACE_REPO,
help=f"Space repo id (default: {DEFAULT_SPACE_REPO})",
)
parser.add_argument(
"--weights-source",
default=DEFAULT_WEIGHTS_SOURCE,
help=f"Local folder containing outputs/, input.txt, shakespeare_transformer.pt (default: {DEFAULT_WEIGHTS_SOURCE})",
)
parser.add_argument(
"--space-dir",
default=DEFAULT_SPACE_DIR,
help=f"Local Space code folder to upload (default: {DEFAULT_SPACE_DIR})",
)
parser.add_argument(
"--space-sdk",
default=DEFAULT_SPACE_SDK,
help=f"Space SDK to request on create (default: {DEFAULT_SPACE_SDK})",
)
parser.add_argument(
"--skip-weights",
action="store_true",
help="Skip uploading weights repo.",
)
parser.add_argument(
"--skip-space",
action="store_true",
help="Skip uploading Space repo.",
)
parser.add_argument(
"--token",
default=os.getenv("HF_TOKEN"),
help="Hugging Face token. Defaults to HF_TOKEN env var.",
)
return parser.parse_args()
def main() -> None:
args = parse_args()
if not args.token:
_abort("No token found. Set HF_TOKEN or pass --token.")
api = HfApi(token=args.token)
if not args.skip_weights:
upload_weights(api, repo_id=args.weights_repo, weights_source=args.weights_source)
if not args.skip_space:
upload_space_code(
api,
repo_id=args.space_repo,
space_dir=args.space_dir,
space_sdk=args.space_sdk,
)
print("Deployment finished.")
print(f"Weights repo: https://huggingface.co/{args.weights_repo}")
print(f"Space repo: https://huggingface.co/spaces/{args.space_repo}")
if __name__ == "__main__":
main()