Spaces:
Running
Running
| from __future__ import annotations | |
| import argparse | |
| import os | |
| import shutil | |
| import sys | |
| import tempfile | |
| from pathlib import Path | |
| PROJECT_ROOT = Path(__file__).resolve().parents[1] | |
| if str(PROJECT_ROOT) not in sys.path: | |
| sys.path.insert(0, str(PROJECT_ROOT)) | |
| from src.executive_assistant.deployment import ( | |
| DEFAULT_SPACE_TITLE, | |
| HFSpaceDeployConfig, | |
| parse_hf_usernames, | |
| stage_space_bundle, | |
| ) | |
| def build_parser() -> argparse.ArgumentParser: | |
| parser = argparse.ArgumentParser( | |
| description="Create or update a Hugging Face Space from this repository in one command." | |
| ) | |
| parser.add_argument( | |
| "--repo-id", | |
| default=os.environ.get("HF_SPACE_REPO", "").strip(), | |
| help="Target Space repo in owner/name form. Defaults to HF_SPACE_REPO.", | |
| ) | |
| parser.add_argument( | |
| "--token", | |
| default=os.environ.get("HF_TOKEN", "").strip(), | |
| help="Hugging Face token. Defaults to HF_TOKEN.", | |
| ) | |
| parser.add_argument( | |
| "--title", | |
| default=os.environ.get("HF_SPACE_TITLE", DEFAULT_SPACE_TITLE), | |
| help="Space title used in the generated HF README.", | |
| ) | |
| parser.add_argument( | |
| "--team-name", | |
| default=os.environ.get("HF_SPACE_TEAM_NAME", "Team Epsilon"), | |
| help="Team name shown in the generated HF README.", | |
| ) | |
| parser.add_argument( | |
| "--hf-usernames", | |
| default=os.environ.get( | |
| "HF_SPACE_TEAM_USERNAMES", | |
| "flickinshots,ShreyaKhatik,itsayushdey", | |
| ), | |
| help="Comma-separated HF usernames for the HF README placeholders.", | |
| ) | |
| parser.add_argument( | |
| "--checkpoint-name", | |
| default=os.environ.get("HF_SPACE_CHECKPOINT_NAME", "q_policy_notebook.json"), | |
| help="Checkpoint filename staged into artifacts/checkpoints/ for RL replay.", | |
| ) | |
| parser.add_argument( | |
| "--openrouter-api-key", | |
| default=os.environ.get("OPENROUTER_API_KEY", "").strip(), | |
| help="Optional secret to set on the Space during deployment.", | |
| ) | |
| parser.add_argument( | |
| "--api-base-url", | |
| default=os.environ.get("API_BASE_URL", "https://openrouter.ai/api/v1").strip(), | |
| help="OpenAI-compatible API base URL for inference.py. Defaults to OpenRouter.", | |
| ) | |
| parser.add_argument( | |
| "--model-name", | |
| default=os.environ.get("MODEL_NAME", "google/gemma-4-31b-it").strip(), | |
| help="OpenRouter model id for inference.py. Defaults to Gemma 4.", | |
| ) | |
| parser.add_argument( | |
| "--private", | |
| action="store_true", | |
| default=os.environ.get("HF_SPACE_PRIVATE", "").strip().lower() == "true", | |
| help="Create or keep the Space private.", | |
| ) | |
| parser.add_argument( | |
| "--skip-checkpoint", | |
| action="store_true", | |
| help="Skip bundling the RL checkpoint.", | |
| ) | |
| parser.add_argument( | |
| "--keep-stage-dir", | |
| default="", | |
| help="Optional local folder where the prepared Space bundle should be copied after upload.", | |
| ) | |
| return parser | |
| def require_huggingface_hub(): | |
| try: | |
| from huggingface_hub import HfApi # type: ignore | |
| except ImportError as exc: | |
| raise SystemExit( | |
| "huggingface_hub is required for deployment. Install the training environment " | |
| "or run `python -m pip install huggingface_hub` first." | |
| ) from exc | |
| return HfApi | |
| def maybe_set_space_secret(api, repo_id: str, key: str, value: str) -> str: | |
| if not value.strip(): | |
| return f"Skipped secret {key} because no value was provided." | |
| add_secret = getattr(api, "add_space_secret", None) | |
| if add_secret is None: | |
| return f"Upload succeeded, but this huggingface_hub version cannot set {key} automatically." | |
| add_secret(repo_id=repo_id, key=key, value=value) | |
| return f"Set Space secret {key}." | |
| def maybe_set_space_variable(api, repo_id: str, key: str, value: str) -> str: | |
| add_variable = getattr(api, "add_space_variable", None) | |
| if add_variable is None: | |
| return f"Upload succeeded, but this huggingface_hub version cannot set variable {key} automatically." | |
| add_variable(repo_id=repo_id, key=key, value=value) | |
| return f"Set Space variable {key}={value}." | |
| def main() -> int: | |
| parser = build_parser() | |
| args = parser.parse_args() | |
| if not args.repo_id: | |
| parser.error("A Space repo id is required. Pass --repo-id or set HF_SPACE_REPO.") | |
| if "/" not in args.repo_id: | |
| parser.error("Space repo id must be in owner/name form.") | |
| if not args.token: | |
| parser.error("A Hugging Face token is required. Pass --token or set HF_TOKEN.") | |
| config = HFSpaceDeployConfig( | |
| repo_id=args.repo_id, | |
| title=args.title, | |
| team_name=args.team_name, | |
| hf_usernames=parse_hf_usernames(args.hf_usernames), | |
| checkpoint_name=args.checkpoint_name, | |
| private=args.private, | |
| include_checkpoint=not args.skip_checkpoint, | |
| ) | |
| HfApi = require_huggingface_hub() | |
| api = HfApi(token=args.token) | |
| with tempfile.TemporaryDirectory(prefix="hf-space-stage-") as tmp_dir: | |
| stage_dir = Path(tmp_dir) | |
| checkpoint_path = stage_space_bundle(config, stage_dir) | |
| api.create_repo( | |
| repo_id=config.repo_id, | |
| repo_type="space", | |
| space_sdk="docker", | |
| private=config.private, | |
| exist_ok=True, | |
| ) | |
| api.upload_folder( | |
| folder_path=str(stage_dir), | |
| repo_id=config.repo_id, | |
| repo_type="space", | |
| commit_message="Deploy Project Epsilon Space bundle", | |
| delete_patterns=["*", "**/*"], | |
| ) | |
| messages = [ | |
| f"Uploaded Space bundle to {config.space_url}", | |
| f"App URL: {config.app_url}", | |
| ] | |
| if checkpoint_path is not None: | |
| messages.append(f"Bundled RL checkpoint: {checkpoint_path.relative_to(stage_dir)}") | |
| messages.append(maybe_set_space_secret(api, config.repo_id, "OPENROUTER_API_KEY", args.openrouter_api_key)) | |
| messages.append(maybe_set_space_secret(api, config.repo_id, "OPENAI_API_KEY", args.openrouter_api_key)) | |
| messages.append(maybe_set_space_variable(api, config.repo_id, "API_BASE_URL", args.api_base_url)) | |
| messages.append(maybe_set_space_variable(api, config.repo_id, "MODEL_NAME", args.model_name)) | |
| messages.append(maybe_set_space_variable(api, config.repo_id, "OPENROUTER_APP_NAME", config.title)) | |
| messages.append(maybe_set_space_variable(api, config.repo_id, "OPENROUTER_SITE_URL", config.app_url)) | |
| if args.keep_stage_dir: | |
| target_dir = Path(args.keep_stage_dir).resolve() | |
| if target_dir.exists(): | |
| shutil.rmtree(target_dir) | |
| shutil.copytree(stage_dir, target_dir) | |
| messages.append(f"Saved staged bundle to {target_dir}") | |
| for message in messages: | |
| print(message) | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |