Github-Transfer / app.py
openfree's picture
Update app.py
210a986 verified
raw
history blame
20 kB
#!/usr/bin/env python3
"""
HF Space Auto-Deployer
----------------------
๊ณต๊ฐœ Git ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ Hugging Face Gradio Space๋กœ ์ž๋™ ๋ณ€ํ™˜ ๋ฐ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.
ํ•„์š” ํ™˜๊ฒฝ๋ณ€์ˆ˜:
- BAPI_TOKEN: Brave Search API Key
- FRIENDLI_TOKEN: Friendli AI API Token
- HF_TOKEN (์„ ํƒ): ๊ธฐ๋ณธ HuggingFace ํ† ํฐ
"""
import os
import sys
import json
import argparse
import subprocess
import tempfile
import textwrap
import requests
import shutil
from pathlib import Path
from typing import Optional, Dict, List
import gradio as gr
import git
from huggingface_hub import HfApi, login
# ========== Brave Search ํ—ฌํผ ========== #
def brave_search_repo(repo_url: str, count: int = 5) -> List[Dict]:
"""Brave Search API๋กœ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ˆ˜์ง‘"""
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"}
try:
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", [])
except Exception as e:
print(f"โš ๏ธ Brave Search ๊ฒฝ๊ณ : {e}")
return []
# ========== Friendli LLM ํ—ฌํผ ========== #
def friendli_generate_scaffold(context: str) -> Dict:
"""Friendli AI๋กœ app.py ๋ฐ requirements.txt ์ž๋™ ์ƒ์„ฑ"""
token = os.getenv("FRIENDLI_TOKEN")
if not token:
raise RuntimeError("ํ™˜๊ฒฝ๋ณ€์ˆ˜ FRIENDLI_TOKEN์ด ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
payload = {
"model": "meta-llama-3.1-70b-instruct",
"messages": [
{
"role": "system",
"content": textwrap.dedent("""
You are an expert Hugging Face Space architect. Given repository context,
generate a Gradio app that showcases the repository's functionality.
Output a JSON with these keys:
- app_py: Complete Gradio app code
- requirements_txt: Required Python packages
- need_docker: Boolean for Docker requirement
- dockerfile: Dockerfile content (if need_docker is true)
- summary: Brief description of the generated app
Important guidelines:
1. Always use Gradio for the interface
2. Make the app functional and user-friendly
3. Include proper error handling
4. Add clear instructions in the interface
5. Use appropriate Gradio components for the task
""")
},
{"role": "user", "content": context}
],
"max_tokens": 16384,
"temperature": 0.7,
"top_p": 0.9,
"stream": False
}
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
try:
r = requests.post(
"https://api.friendli.ai/serverless/v1/chat/completions",
json=payload,
headers=headers,
timeout=120
)
r.raise_for_status()
# JSON ์‘๋‹ต ํŒŒ์‹ฑ
response_text = r.json()["choices"][0]["message"]["content"]
# JSON ๋ธ”๋ก ์ถ”์ถœ (```json ... ``` ํ˜•์‹ ์ฒ˜๋ฆฌ)
if "```json" in response_text:
start = response_text.find("```json") + 7
end = response_text.find("```", start)
response_text = response_text[start:end].strip()
return json.loads(response_text)
except json.JSONDecodeError as e:
print(f"โš ๏ธ JSON ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {e}")
# ๊ธฐ๋ณธ Gradio ์•ฑ ๋ฐ˜ํ™˜
return {
"app_py": textwrap.dedent("""
import gradio as gr
def main():
return "This is an auto-generated Gradio app. Please customize it based on your repository."
demo = gr.Interface(
fn=main,
inputs=None,
outputs="text",
title="Auto-Generated Space",
description="This Space was automatically created. Please update app.py to add your functionality."
)
if __name__ == "__main__":
demo.launch()
"""),
"requirements_txt": "gradio>=4.0.0",
"need_docker": False,
"summary": "Basic Gradio template - please customize based on your repository"
}
# ========== ๋ฉ”์ธ ๋ฐฐํฌ ๋กœ์ง ========== #
def deploy(repo_url: str, hf_token: str, private: bool = False, hardware: Optional[str] = None) -> str:
"""๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ Gradio Space๋กœ ๋ฐฐํฌํ•˜๊ณ  Space URL ๋ฐ˜ํ™˜"""
# HF ๋กœ๊ทธ์ธ
login(hf_token)
api = HfApi(token=hf_token)
# ์‚ฌ์šฉ์ž ์ •๋ณด ๋ฐ Space ์ด๋ฆ„ ์ƒ์„ฑ
user = api.whoami()["name"]
repo_name = Path(repo_url.rstrip("/")).name.lower()
repo_name = repo_name.replace(".", "-").replace("_", "-")
space_id = f"{user}/{repo_name}-space"
print(f"๐Ÿ“ฆ Space ID: {space_id}")
# Space ์ƒ์„ฑ
api.create_repo(
repo_id=space_id,
repo_type="space",
space_sdk="gradio",
private=private,
exist_ok=True
)
# Hardware ์„ค์ • (ํ•„์š”ํ•œ ๊ฒฝ์šฐ)
if hardware:
try:
api.request_space_hardware(repo_id=space_id, hardware=hardware)
print(f"๐Ÿ–ฅ๏ธ Hardware ์„ค์ •: {hardware}")
except Exception as e:
print(f"โš ๏ธ Hardware ์„ค์ • ์‹คํŒจ: {e}")
with tempfile.TemporaryDirectory() as work_dir:
# 1) Brave Search๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
print("๐Ÿ” ๋ ˆํฌ์ง€ํ† ๋ฆฌ ์ •๋ณด ์ˆ˜์ง‘ ์ค‘...")
brave_meta = brave_search_repo(repo_url)
# 2) ์›๋ณธ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํด๋ก 
print("๐Ÿ“ฅ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํด๋ก  ์ค‘...")
src_path = Path(work_dir) / "source"
git.Repo.clone_from(repo_url, src_path, depth=1)
# README ์ฝ๊ธฐ
readme_content = ""
readme_path = src_path / "README.md"
if readme_path.exists():
readme_content = readme_path.read_text(encoding="utf-8", errors="ignore")[:4000]
# ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ํŒŒ์•…
tree_output = subprocess.run(
["bash", "-c", f"find {src_path} -type f -name '*.py' -o -name '*.md' -o -name '*.txt' -o -name '*.json' -o -name '*.yaml' -o -name '*.yml' | head -n 50"],
capture_output=True,
text=True
).stdout
# ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ
context = textwrap.dedent(f"""
## Repository URL
{repo_url}
## Brave Search Metadata
{json.dumps(brave_meta, ensure_ascii=False, indent=2)}
## Repository Structure
{tree_output}
## README.md Content (first 4KB)
{readme_content}
Please create a Gradio app that best showcases this repository's functionality.
If it's a model, create an inference interface. If it's a dataset, create a viewer.
If it's a library, create a demo. Make it user-friendly and functional.
""")
# 3) Friendli AI๋กœ ์Šค์บํด๋“œ ์ƒ์„ฑ
print("๐Ÿค– AI๋กœ Gradio ์•ฑ ์ƒ์„ฑ ์ค‘...")
scaffold = friendli_generate_scaffold(context)
# 4) Space ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํด๋ก  ๋ฐ ํŒŒ์ผ ์ž‘์„ฑ
print("๐Ÿ“ค Space์— ํŒŒ์ผ ์—…๋กœ๋“œ ์ค‘...")
dst_path = Path(work_dir) / "space"
# HTTPS URL with token for authentication
space_url = f"https://{hf_token}@huggingface.co/spaces/{space_id}"
space_repo = git.Repo.clone_from(space_url, dst_path, depth=1)
# ํŒŒ์ผ ์ž‘์„ฑ
(dst_path / "app.py").write_text(scaffold["app_py"], encoding="utf-8")
(dst_path / "requirements.txt").write_text(scaffold["requirements_txt"], encoding="utf-8")
if scaffold.get("need_docker"):
(dst_path / "Dockerfile").write_text(scaffold["dockerfile"], encoding="utf-8")
# README.md ์ƒ์„ฑ
readme_content = f"""---
title: {repo_name.replace("-", " ").title()}
emoji: ๐Ÿš€
colorFrom: blue
colorTo: purple
sdk: gradio
sdk_version: 4.44.1
app_file: app.py
pinned: false
---
# {repo_name.replace("-", " ").title()}
Automatically deployed from: {repo_url}
## Summary
{scaffold.get("summary", "Auto-generated Gradio Space")}
---
*Created by HF Space Auto-Deployer*
"""
(dst_path / "README.md").write_text(readme_content, encoding="utf-8")
# Git ์ปค๋ฐ‹ ๋ฐ ํ‘ธ์‹œ
space_repo.index.add(["app.py", "requirements.txt", "README.md"])
if scaffold.get("need_docker"):
space_repo.index.add(["Dockerfile"])
# Git ์„ค์ •
space_repo.config_writer().set_value("user", "name", user).release()
space_repo.config_writer().set_value("user", "email", f"{user}@users.noreply.huggingface.co").release()
space_repo.index.commit("Initial auto-deployment")
# ํ‘ธ์‹œ
origin = space_repo.remote("origin")
origin.push()
return f"https://huggingface.co/spaces/{space_id}"
# ========== Gradio UI ========== #
def launch_deploy(repo_url: str, private: bool, request: gr.Request) -> str:
"""Gradio UI์—์„œ ํ˜ธ์ถœ๋˜๋Š” ๋ฐฐํฌ ํ•จ์ˆ˜"""
# ํ† ํฐ ๊ฐ€์ ธ์˜ค๊ธฐ (์šฐ์„ ์ˆœ์œ„: Gradio ์ธ์ฆ โ†’ ํ™˜๊ฒฝ๋ณ€์ˆ˜)
hf_token = None
# Gradio ์ธ์ฆ ํ—ค๋” ํ™•์ธ
auth_header = request.headers.get("authorization")
if auth_header and auth_header.startswith("Bearer "):
hf_token = auth_header.split(" ")[1].strip()
# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํด๋ฐฑ
if not hf_token:
hf_token = os.environ.get("HF_TOKEN")
if not hf_token:
return """### โŒ ์ธ์ฆ ํ•„์š”
๋‹ค์Œ ์ค‘ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์œผ๋กœ ์ธ์ฆํ•ด์ฃผ์„ธ์š”:
1. **์šฐ์ธก ์ƒ๋‹จ ํ”„๋กœํ•„ โ†’ Sign in with Hugging Face**
2. **Space Settings โ†’ Variables and secrets โ†’ HF_TOKEN ์„ค์ •**"""
# URL ๊ฒ€์ฆ
repo_url = repo_url.strip()
if not repo_url:
return "### โŒ Repository URL์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
if not any(repo_url.startswith(prefix) for prefix in ["https://github.com/", "http://github.com/"]):
return """### โŒ ์ž˜๋ชป๋œ URL ํ˜•์‹
GitHub URL์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.
์˜ˆ: https://github.com/username/repository"""
# ๋ฐฐํฌ ์‹คํ–‰
try:
space_url = deploy(repo_url, hf_token, private)
return f"""### โœ… Space ๋ฐฐํฌ ์™„๋ฃŒ!
๐ŸŽ‰ ์„ฑ๊ณต์ ์œผ๋กœ ๋ฐฐํฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
**Space URL**: [{space_url}]({space_url})
**๋‹ค์Œ ๋‹จ๊ณ„:**
1. Space ํŽ˜์ด์ง€ ๋ฐฉ๋ฌธ
2. ๋นŒ๋“œ ๋กœ๊ทธ ํ™•์ธ (์šฐ์ธก ์ƒ๋‹จ "Logs" ํƒญ)
3. ๋นŒ๋“œ ์™„๋ฃŒ ๋Œ€๊ธฐ (๋ณดํ†ต 2-5๋ถ„)
4. ํ•„์š”์‹œ app.py ์ˆ˜์ •
---
๐Ÿ’ก **ํŒ**: Space๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉด Files ํƒญ์—์„œ app.py๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."""
except Exception as e:
error_msg = str(e)
# ์ƒ์„ธํ•œ ์˜ค๋ฅ˜ ์•ˆ๋‚ด
if "BAPI_TOKEN" in error_msg:
return """### โŒ Brave Search API ํ† ํฐ ํ•„์š”
**์„ค์ • ๋ฐฉ๋ฒ•:**
1. [Brave Search API](https://brave.com/search/api/) ๊ฐ€์ž…
2. ๋ฌด๋ฃŒ API Key ๋ฐœ๊ธ‰
3. **์ด Space์˜** Settings โ†’ Variables and secrets
4. `BAPI_TOKEN` = `[๋ฐœ๊ธ‰๋ฐ›์€ ํ‚ค]` ์ถ”๊ฐ€"""
elif "FRIENDLI_TOKEN" in error_msg:
return """### โŒ Friendli AI ํ† ํฐ ํ•„์š”
**์„ค์ • ๋ฐฉ๋ฒ•:**
1. [Friendli AI](https://friendli.ai/) ๊ฐ€์ž…
2. API Key ๋ฐœ๊ธ‰
3. **์ด Space์˜** Settings โ†’ Variables and secrets
4. `FRIENDLI_TOKEN` = `[๋ฐœ๊ธ‰๋ฐ›์€ ํ‚ค]` ์ถ”๊ฐ€"""
elif "401" in error_msg or "403" in error_msg or "Unauthorized" in error_msg:
return """### โŒ ์ธ์ฆ ์‹คํŒจ
**ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:**
1. [ํ† ํฐ ํŽ˜์ด์ง€](https://huggingface.co/settings/tokens) ๋ฐฉ๋ฌธ
2. "New token" ํด๋ฆญ
3. **Write** ๊ถŒํ•œ ์ฒดํฌ โœ…
4. Space Settings์—์„œ HF_TOKEN ์—…๋ฐ์ดํŠธ"""
else:
return f"""### โŒ ๋ฐฐํฌ ์˜ค๋ฅ˜
**์˜ค๋ฅ˜ ๋‚ด์šฉ:**
```
{error_msg}
```
**์ผ๋ฐ˜์ ์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:**
1. Repository๊ฐ€ public์ธ์ง€ ํ™•์ธ
2. URL ํ˜•์‹ ํ™•์ธ (https://github.com/user/repo)
3. ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ํ™•์ธ
4. ์ž ์‹œ ํ›„ ์žฌ์‹œ๋„"""
# ========== Gradio App ========== #
def create_ui():
"""Gradio UI ์ƒ์„ฑ"""
with gr.Blocks(
title="HF Space Auto-Deployer",
theme=gr.themes.Soft(),
css="""
.main-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
}
.header h1 {
margin: 0;
font-size: 2.5em;
}
.header p {
margin: 10px 0 0 0;
opacity: 0.9;
}
.gr-button-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
font-size: 1.1em !important;
padding: 12px 30px !important;
}
.gr-button-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.status-box {
padding: 20px;
border-radius: 10px;
margin-top: 20px;
border: 1px solid #e0e0e0;
background-color: #f9f9f9;
}
.info-box {
background-color: #e3f2fd;
padding: 15px;
border-radius: 8px;
border-left: 4px solid #2196F3;
margin: 10px 0;
}
.examples-box {
background-color: #f3e5f5;
padding: 15px;
border-radius: 8px;
margin: 10px 0;
}
"""
) as demo:
with gr.Column(elem_classes="main-container"):
# ํ—ค๋”
gr.HTML("""
<div class="header">
<h1>๐Ÿš€ GitHub โ†’ HuggingFace Space</h1>
<p>AI๊ฐ€ ๋‹น์‹ ์˜ GitHub ํ”„๋กœ์ ํŠธ๋ฅผ Gradio ์•ฑ์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค</p>
</div>
""")
# ์ธ์ฆ ์•ˆ๋‚ด
gr.Markdown("""
<div class="info-box">
<b>๐Ÿ” ์ธ์ฆ ๋ฐฉ๋ฒ•:</b><br>
โ€ข <b>๋ฐฉ๋ฒ• 1</b>: ์šฐ์ธก ์ƒ๋‹จ ํ”„๋กœํ•„ โ†’ Sign in with Hugging Face<br>
โ€ข <b>๋ฐฉ๋ฒ• 2</b>: Space Settings์—์„œ HF_TOKEN ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •
</div>
""")
# ์ž…๋ ฅ ํ•„๋“œ
with gr.Group():
repo_input = gr.Textbox(
label="๐Ÿ“ฆ GitHub Repository URL",
placeholder="https://github.com/username/repository",
info="Public repository URL์„ ์ž…๋ ฅํ•˜์„ธ์š”",
lines=1
)
private_checkbox = gr.Checkbox(
label="๐Ÿ”’ Private Space๋กœ ์ƒ์„ฑ",
value=False,
info="์ฒดํฌํ•˜๋ฉด ๋ณธ์ธ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ Space๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค"
)
# ๋ฐฐํฌ ๋ฒ„ํŠผ
deploy_btn = gr.Button(
"๐Ÿš€ Space ์ƒ์„ฑํ•˜๊ธฐ",
variant="primary",
size="lg"
)
# ์ƒํƒœ ์ถœ๋ ฅ
output_status = gr.Markdown(
elem_classes="status-box",
visible=False
)
# ์˜ˆ์‹œ
gr.HTML("""
<div class="examples-box">
<b>๐Ÿ’ก ์˜ˆ์‹œ ํ”„๋กœ์ ํŠธ:</b><br>
โ€ข <code>https://github.com/gradio-app/gradio/tree/main/demo/hello_world</code><br>
โ€ข <code>https://github.com/huggingface/transformers</code><br>
โ€ข <code>https://github.com/CompVis/stable-diffusion</code>
</div>
""")
# ์‚ฌ์šฉ ๊ฐ€์ด๋“œ
with gr.Accordion("๐Ÿ“š ์‚ฌ์šฉ ๊ฐ€์ด๋“œ", open=False):
gr.Markdown("""
### ์ž‘๋™ ์›๋ฆฌ
1. **๐Ÿ” ๋ถ„์„**: Brave Search๋กœ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ์ •๋ณด ์ˆ˜์ง‘
2. **๐Ÿค– ์ƒ์„ฑ**: Friendli AI๊ฐ€ ๋งž์ถคํ˜• Gradio ์•ฑ ์ฝ”๋“œ ์ƒ์„ฑ
3. **๐Ÿ“ค ๋ฐฐํฌ**: HuggingFace Space์— ์ž๋™ ์—…๋กœ๋“œ ๋ฐ ๋นŒ๋“œ
### ์ง€์›ํ•˜๋Š” ํ”„๋กœ์ ํŠธ ์œ ํ˜•
- โœ… **ML ๋ชจ๋ธ**: ์ถ”๋ก  ์ธํ„ฐํŽ˜์ด์Šค ์ž๋™ ์ƒ์„ฑ
- โœ… **๋ฐ์ดํ„ฐ์…‹**: ๋ฐ์ดํ„ฐ ํƒ์ƒ‰๊ธฐ ์ƒ์„ฑ
- โœ… **๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ**: ๋ฐ๋ชจ ์•ฑ ์ƒ์„ฑ
- โœ… **์ผ๋ฐ˜ ํ”„๋กœ์ ํŠธ**: ๊ธฐ๋Šฅ ์‡ผ์ผ€์ด์Šค ์ƒ์„ฑ
### ํ•„์š”ํ•œ API ํ‚ค
1. **BAPI_TOKEN**: [Brave Search API](https://brave.com/search/api/) (๋ฌด๋ฃŒ)
2. **FRIENDLI_TOKEN**: [Friendli AI](https://friendli.ai/) (๋ฌด๋ฃŒ ํฌ๋ ˆ๋”ง ์ œ๊ณต)
### ๋ฌธ์ œ ํ•ด๊ฒฐ
- **๋นŒ๋“œ ์‹คํŒจ**: Space์˜ Logs ํƒญ ํ™•์ธ
- **์•ฑ ์ˆ˜์ •**: Files ํƒญ์—์„œ app.py ์ง์ ‘ ํŽธ์ง‘
- **GPU ํ•„์š”**: Space Settings์—์„œ Hardware ๋ณ€๊ฒฝ
""")
# ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
deploy_btn.click(
fn=launch_deploy,
inputs=[repo_input, private_checkbox],
outputs=output_status
).then(
fn=lambda: gr.update(visible=True),
outputs=output_status
)
return demo
# ========== CLI ์ง€์› ========== #
def main():
"""CLI ์‹คํ–‰์„ ์œ„ํ•œ ๋ฉ”์ธ ํ•จ์ˆ˜"""
parser = argparse.ArgumentParser(description="Git ๋ ˆํฌ๋ฅผ Hugging Face Space๋กœ ์ž๋™ ๋ฐฐํฌ")
parser.add_argument("--repo_url", help="GitHub repository URL")
parser.add_argument("--hf_token", help="HuggingFace write token")
parser.add_argument("--private", action="store_true", help="Create private Space")
parser.add_argument("--hardware", help="GPU tier (e.g., 't4-medium')")
parser.add_argument("--no-ui", action="store_true", help="Run without Gradio UI")
args = parser.parse_args()
# CLI ๋ชจ๋“œ
if args.no_ui and args.repo_url and args.hf_token:
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)
# Gradio UI ๋ชจ๋“œ
else:
# ํ™˜๊ฒฝ ์ƒํƒœ ์ถœ๋ ฅ
print("\n" + "="*60)
print("๐Ÿš€ HF Space Auto-Deployer")
print("="*60)
# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฒดํฌ
env_checks = []
env_checks.append("โœ… HF_TOKEN" if os.getenv("HF_TOKEN") else "โš ๏ธ HF_TOKEN (์„ ํƒ์‚ฌํ•ญ)")
env_checks.append("โœ… BAPI_TOKEN" if os.getenv("BAPI_TOKEN") else "โŒ BAPI_TOKEN (ํ•„์ˆ˜)")
env_checks.append("โœ… FRIENDLI_TOKEN" if os.getenv("FRIENDLI_TOKEN") else "โŒ FRIENDLI_TOKEN (ํ•„์ˆ˜)")
print("ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ƒํƒœ:")
for check in env_checks:
print(f" {check}")
print("="*60 + "\n")
# Gradio ์•ฑ ์‹คํ–‰
demo = create_ui()
demo.launch(
share=False,
server_name="0.0.0.0",
server_port=7860
)
if __name__ == "__main__":
main()