#!/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("""

🚀 GitHub → HuggingFace Space

AI가 당신의 GitHub 프로젝트를 Gradio 앱으로 변환합니다

""") # 인증 안내 gr.Markdown("""
🔐 인증 방법:
방법 1: 우측 상단 프로필 → Sign in with Hugging Face
방법 2: Space Settings에서 HF_TOKEN 환경변수 설정
""") # 입력 필드 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("""
💡 예시 프로젝트:
https://github.com/gradio-app/gradio/tree/main/demo/hello_world
https://github.com/huggingface/transformers
https://github.com/CompVis/stable-diffusion
""") # 사용 가이드 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()