File size: 6,137 Bytes
cebd780
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
"""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())