Github-Transfer / app.py
openfree's picture
Create app.py
26bad09 verified
raw
history blame
5.6 kB
#!/usr/bin/env python3
"""
repo_to_space_auto.py
---------------------
์ž๋™์œผ๋กœ Git ๋ ˆํฌ๋ฅผ Hugging Face Gradio Space๋กœ ๋ฐฐํฌ.
ํ•„์š” ํ™˜๊ฒฝ๋ณ€์ˆ˜:
BAPI_TOKEN : Brave Search API Key
FRIENDLI_TOKEN : Friendli Dedicated Endpoint Token
CLI ์ธ์ž:
--repo_url <URL> : ์›๋ณธ GitHub/GitLab ๋“ฑ ๊ณต๊ฐœ ๋ ˆํฌ URL
--hf_token <TOK> : ์“ฐ๊ธฐ ๊ถŒํ•œ Hugging Face Access Token
์˜ต์…˜:
--private : ๋น„๊ณต๊ฐœ Space ์ƒ์„ฑ
--hardware <tier> : ์˜ˆ) 't4-medium' GPU ์ธ์Šคํ„ด์Šค ์ง€์ •
"""
import os, sys, json, argparse, subprocess, tempfile, textwrap, requests, shutil
from pathlib import Path
import git # GitPython
from huggingface_hub import HfApi, login
# ---------- Brave Search ํ—ฌํผ ---------- #
def brave_search_repo(repo_url: str, count: int = 5) -> list[dict]:
api_key = os.getenv("BAPI_TOKEN")
if not api_key:
raise RuntimeError("ํ™˜๊ฒฝ๋ณ€์ˆ˜ BAPI_TOKEN์ด ์„ค์ •๋ผ ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
headers = {"X-Subscription-Token": api_key, "Accept": "application/json"}
params = {"q": f'site:github.com "{repo_url}"', "count": count, "search_lang": "en"}
resp = requests.get(
"https://api.search.brave.com/res/v1/web/search",
headers=headers, params=params, timeout=10
)
resp.raise_for_status()
return resp.json().get("web", {}).get("results", [])
# ---------- Friendli LLM ํ—ฌํผ ---------- #
def friendli_generate_scaffold(context: str) -> dict:
token = os.getenv("FRIENDLI_TOKEN")
if not token:
raise RuntimeError("ํ™˜๊ฒฝ๋ณ€์ˆ˜ FRIENDLI_TOKEN์ด ์„ค์ •๋ผ ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
payload = {
"model": "dep89a2fld32mcm",
"messages": [
{
"role": "system",
"content": (
"You are an expert Hugging Face Space architect. "
"Given repository context, output JSON with keys "
"`app_py`, `requirements_txt`, `need_docker` (bool), "
"`dockerfile` (if needed) and `summary`."
),
},
{"role": "user", "content": context},
],
"max_tokens": 16384,
"top_p": 0.8,
"stream": False,
}
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
r = requests.post(
"https://api.friendli.ai/dedicated/v1/chat/completions",
json=payload, headers=headers, timeout=120
)
r.raise_for_status()
return json.loads(r.json()["choices"][0]["message"]["content"])
# ---------- ๋ฉ”์ธ ๋ฐฐํฌ ๋กœ์ง ---------- #
def deploy(repo_url: str, hf_token: str, private=False, hardware=None) -> str:
"""๋ ˆํฌ๋ฅผ Gradio Space๋กœ ๋ฐฐํฌํ•˜๊ณ  Space URL ๋ฐ˜ํ™˜."""
login(hf_token)
api = HfApi(token=hf_token)
user = api.whoami()["name"]
space = f"{user}/{Path(repo_url).stem.lower().replace('.', '-')}"
api.create_repo(
repo_id=space,
repo_type="space",
space_sdk="gradio",
private=private,
exist_ok=True,
hardware=hardware,
)
with tempfile.TemporaryDirectory() as work:
# 1) Brave ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ
brave_meta = brave_search_repo(repo_url)
# 2) ์›๋ณธ ๋ ˆํฌ ํด๋ก 
src = Path(work) / "src"
git.Repo.clone_from(repo_url, src)
readme = ""
readme_path = src / "README.md"
if readme_path.exists():
readme = readme_path.read_text(encoding="utf-8", errors="ignore")[:4000]
tree_out = subprocess.run(
["bash", "-lc", f"tree -L 2 {src} | head -n 40"],
text=True, capture_output=True
).stdout
context = textwrap.dedent(f"""
## Brave meta
{json.dumps(brave_meta, ensure_ascii=False, indent=2)}
## Repository tree (depth 2)
{tree_out}
## README (first 4 kB)
{readme}
""")
# 3) Friendli ์Šค์บํด๋“œ ์ƒ์„ฑ
scaffold = friendli_generate_scaffold(context)
# 4) Space ๋ ˆํฌ ํด๋ก  & ํŒŒ์ผ ์“ฐ๊ธฐ
dst = Path(work) / "space"
api.clone_repo(space, local_dir=dst)
(dst / "app.py").write_text(scaffold["app_py"], encoding="utf-8")
(dst / "requirements.txt").write_text(scaffold["requirements_txt"], encoding="utf-8")
if scaffold.get("need_docker"):
(dst / "Dockerfile").write_text(scaffold["dockerfile"], encoding="utf-8")
(dst / "README.md").write_text(scaffold["summary"], encoding="utf-8")
# 5) ์ปค๋ฐ‹ & ํ‘ธ์‹œ
subprocess.run(["git", "-C", dst, "add", "."], check=True)
subprocess.run(["git", "-C", dst, "commit", "-m", "Initial auto-deploy"], check=True)
subprocess.run(["git", "-C", dst, "push"], check=True)
return f"https://huggingface.co/spaces/{space}"
# ---------- CLI ์—”ํŠธ๋ฆฌ ---------- #
def main():
p = argparse.ArgumentParser(description="Git ๋ ˆํฌ๋ฅผ Hugging Face Gradio Space๋กœ ์ž๋™ ๋ฐฐํฌ")
p.add_argument("--repo_url", required=True, help="์›๋ณธ Git ๋ ˆํฌ์ง€ํ† ๋ฆฌ URL")
p.add_argument("--hf_token", required=True, help="์“ฐ๊ธฐ ๊ถŒํ•œ Hugging Face ํ† ํฐ")
p.add_argument("--private", action="store_true", help="๋น„๊ณต๊ฐœ Space ์ƒ์„ฑ")
p.add_argument("--hardware", default=None, help="์˜ˆ: 't4-medium'")
args = p.parse_args()
try:
url = deploy(args.repo_url, args.hf_token, args.private, args.hardware)
print(f"โœ… ๋ฐฐํฌ ์„ฑ๊ณต: {url}")
except Exception as e:
print(f"โŒ ๋ฐฐํฌ ์‹คํŒจ: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()