seawolf2357 commited on
Commit
102861b
Β·
verified Β·
1 Parent(s): 5c4bee2

Deploy from GitHub repository

Browse files
Files changed (3) hide show
  1. README.md +11 -5
  2. app.py +1299 -0
  3. requirements.txt +10 -0
README.md CHANGED
@@ -1,12 +1,18 @@
1
  ---
2
- title: Github Tran
3
- emoji: πŸš€
4
- colorFrom: pink
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 5.44.1
8
  app_file: app.py
9
  pinned: false
 
 
 
 
 
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Github HF
3
+ emoji: 😻
4
+ colorFrom: indigo
5
+ colorTo: red
6
  sdk: gradio
7
+ sdk_version: 5.35.0
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
11
+ short_description: Github to HuggingFace
12
+ hf_oauth: true
13
+ hf_oauth_scopes:
14
+ - read-repos
15
+ - write-repos
16
  ---
17
 
18
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,1299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from huggingface_hub import HfApi
3
+ import uuid
4
+ from slugify import slugify
5
+ import os
6
+ import json
7
+ import subprocess
8
+ import tempfile
9
+ import requests
10
+ import shutil
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Optional, Dict, List
14
+
15
+ def is_lfs_pointer_file(filepath):
16
+ try:
17
+ with open(filepath, 'rb') as f:
18
+ header = f.read(100)
19
+ return header.startswith(b'version https://git-lfs.github.com/spec/v1')
20
+ except:
21
+ return False
22
+
23
+ def remove_lfs_files(folder):
24
+ removed_files = []
25
+ for root, dirs, files in os.walk(folder):
26
+ if '.git' in root:
27
+ continue
28
+ for file in files:
29
+ filepath = os.path.join(root, file)
30
+ if is_lfs_pointer_file(filepath):
31
+ os.remove(filepath)
32
+ removed_files.append(filepath.replace(folder + os.sep, ''))
33
+ return removed_files
34
+
35
+ def analyze_repository(src_path: Path) -> Dict:
36
+ analysis = {
37
+ "has_requirements": False,
38
+ "has_readme": False,
39
+ "main_language": "python",
40
+ "key_files": [],
41
+ "dependencies": [],
42
+ "description": "",
43
+ "entry_points": [],
44
+ "model_files": [],
45
+ "config_files": []
46
+ }
47
+
48
+ req_file = src_path / "requirements.txt"
49
+ if req_file.exists():
50
+ analysis["has_requirements"] = True
51
+ try:
52
+ reqs = req_file.read_text(encoding="utf-8").strip().split("\n")
53
+ cleaned_deps = []
54
+ for r in reqs:
55
+ r = r.strip()
56
+ if r and not r.startswith("#"):
57
+ if "opencv-python==4.10.0" in r:
58
+ r = "opencv-python>=4.10.0.82"
59
+ elif "opencv-python==4.10" in r:
60
+ r = "opencv-python>=4.10.0.82"
61
+
62
+ if "==" in r and not r.startswith("git+"):
63
+ pkg_name = r.split("==")[0]
64
+ if pkg_name.lower() in ["torch", "tensorflow", "transformers", "numpy"]:
65
+ cleaned_deps.append(r)
66
+ else:
67
+ version = r.split("==")[1]
68
+ if version.count('.') == 1:
69
+ version = version + ".0"
70
+ cleaned_deps.append(f"{pkg_name}>={version}")
71
+ else:
72
+ cleaned_deps.append(r)
73
+ analysis["dependencies"] = cleaned_deps
74
+ except:
75
+ analysis["dependencies"] = []
76
+
77
+ for readme_name in ["README.md", "readme.md", "README.rst", "README.txt"]:
78
+ readme_file = src_path / readme_name
79
+ if readme_file.exists():
80
+ analysis["has_readme"] = True
81
+ try:
82
+ readme_content = readme_file.read_text(encoding="utf-8")
83
+ analysis["readme_content"] = readme_content[:5000]
84
+ lines = readme_content.split("\n")
85
+ for i, line in enumerate(lines[:10]):
86
+ if line.strip() and not line.startswith("#") and not line.startswith("!"):
87
+ analysis["description"] = line.strip()
88
+ break
89
+ except:
90
+ pass
91
+
92
+ py_files = list(src_path.glob("**/*.py"))
93
+ for py_file in py_files[:20]:
94
+ if "__pycache__" not in str(py_file) and ".git" not in str(py_file):
95
+ relative_path = py_file.relative_to(src_path)
96
+
97
+ if any(name in py_file.name for name in ["main.py", "app.py", "demo.py", "run.py", "server.py", "streamlit_app.py"]):
98
+ analysis["entry_points"].append(str(relative_path))
99
+
100
+ try:
101
+ content = py_file.read_text(encoding="utf-8")[:1000]
102
+ if "if __name__" in content and "main" in content:
103
+ analysis["entry_points"].append(str(relative_path))
104
+
105
+ if any(lib in content for lib in ["torch", "tensorflow", "transformers", "numpy", "pandas", "cv2", "PIL"]):
106
+ analysis["key_files"].append({
107
+ "path": str(relative_path),
108
+ "preview": content[:500]
109
+ })
110
+ except:
111
+ pass
112
+
113
+ model_extensions = [".pth", ".pt", ".ckpt", ".h5", ".pb", ".onnx", ".safetensors"]
114
+ for ext in model_extensions:
115
+ model_files = list(src_path.glob(f"**/*{ext}"))
116
+ for mf in model_files[:5]:
117
+ if ".git" not in str(mf):
118
+ analysis["model_files"].append(str(mf.relative_to(src_path)))
119
+
120
+ config_patterns = ["config.json", "config.yaml", "config.yml", "*.json", "*.yaml"]
121
+ for pattern in config_patterns:
122
+ config_files = list(src_path.glob(pattern))
123
+ for cf in config_files[:5]:
124
+ if ".git" not in str(cf):
125
+ analysis["config_files"].append(str(cf.relative_to(src_path)))
126
+
127
+ return analysis
128
+
129
+ def generate_gradio_app(repo_url: str, analysis: Dict) -> Dict:
130
+ context = f"""Repository URL: {repo_url}
131
+
132
+ Repository Analysis:
133
+ - Description: {analysis.get('description', 'N/A')}
134
+ - Main Dependencies: {', '.join(analysis['dependencies'][:10])}
135
+ - Entry Points: {', '.join(analysis['entry_points'][:5])}
136
+ - Model Files: {', '.join(analysis['model_files'][:3])}
137
+ - Config Files: {', '.join(analysis['config_files'][:3])}
138
+
139
+ Key Files Found:
140
+ """
141
+
142
+ for kf in analysis.get('key_files', [])[:3]:
143
+ context += f"\n--- {kf['path']} ---\n{kf['preview']}\n"
144
+
145
+ if analysis.get('readme_content'):
146
+ context += f"\n--- README.md (excerpt) ---\n{analysis['readme_content'][:2000]}\n"
147
+
148
+ system_prompt = """You are an expert at creating Gradio apps from GitHub repositories.
149
+ Your task is to generate a complete, working Gradio interface that demonstrates the main functionality of the repository.
150
+
151
+ CRITICAL REQUIREMENTS:
152
+ 1. The app.py must be FULLY FUNCTIONAL and runnable
153
+ 2. DO NOT use 'from agent import' or any repository-specific imports that won't exist
154
+ 3. Handle errors gracefully with clear user feedback
155
+ 4. Include API key inputs when external services are required
156
+ 5. Create intuitive UI components for the main features
157
+ 6. Always use gradio>=5.35.0
158
+
159
+ Return ONLY valid JSON with these exact keys:
160
+ - app_py: Complete Gradio app code
161
+ - requirements_txt: All necessary dependencies including gradio>=5.35.0
162
+ - summary: Brief description of what the app does"""
163
+
164
+ fireworks_key = os.getenv("FIREWORKS_API_KEY")
165
+ if fireworks_key:
166
+ try:
167
+ url = "https://api.fireworks.ai/inference/v1/chat/completions"
168
+ payload = {
169
+ "model": "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct",
170
+ "max_tokens": 4096,
171
+ "top_p": 1,
172
+ "top_k": 40,
173
+ "presence_penalty": 0,
174
+ "frequency_penalty": 0,
175
+ "temperature": 0.6,
176
+ "messages": [
177
+ {"role": "system", "content": system_prompt},
178
+ {"role": "user", "content": f"Create a fully functional Gradio app for this repository:\n\n{context[:8000]}"}
179
+ ]
180
+ }
181
+ headers = {
182
+ "Accept": "application/json",
183
+ "Content-Type": "application/json",
184
+ "Authorization": f"Bearer {fireworks_key.strip()}"
185
+ }
186
+
187
+ r = requests.post(url, headers=headers, data=json.dumps(payload), timeout=30)
188
+
189
+ if r.status_code == 200:
190
+ response_text = r.json()["choices"][0]["message"]["content"]
191
+ print("βœ… Fireworks AI둜 μ•± 생성 성곡")
192
+
193
+ try:
194
+ if "```json" in response_text:
195
+ start = response_text.find("```json") + 7
196
+ end = response_text.find("```", start)
197
+ response_text = response_text[start:end].strip()
198
+ elif "```" in response_text:
199
+ start = response_text.find("```") + 3
200
+ end = response_text.find("```", start)
201
+ response_text = response_text[start:end].strip()
202
+
203
+ result = json.loads(response_text)
204
+
205
+ if not all(key in result for key in ["app_py", "requirements_txt", "summary"]):
206
+ raise ValueError("Missing required keys in response")
207
+
208
+ if "gradio" not in result.get("requirements_txt", "").lower():
209
+ result["requirements_txt"] = "gradio>=5.35.0\n" + result.get("requirements_txt", "")
210
+
211
+ return result
212
+
213
+ except (json.JSONDecodeError, ValueError) as e:
214
+ print(f"⚠️ JSON νŒŒμ‹± 였λ₯˜: {e}")
215
+ return None
216
+ except Exception as e:
217
+ print(f"⚠️ Fireworks AI API 였λ₯˜: {e}")
218
+
219
+ print("ℹ️ AI APIκ°€ μ—†μ–΄ κΈ°λ³Έ ν…œν”Œλ¦Ώμ„ μƒμ„±ν•©λ‹ˆλ‹€.")
220
+ return create_smart_template(repo_url, analysis)
221
+
222
+ def create_smart_template(repo_url: str, analysis: Dict) -> Dict:
223
+ repo_name = Path(repo_url.rstrip("/")).name
224
+ description = analysis.get("description", "A project deployed from GitHub") if analysis else "A project deployed from GitHub"
225
+
226
+ deps = " ".join(analysis.get("dependencies", [])) if analysis else ""
227
+ has_cv = any(lib in deps for lib in ["cv2", "PIL", "pillow", "opencv"])
228
+ has_nlp = any(lib in deps for lib in ["transformers", "nltk", "spacy"])
229
+ has_3d = any(lib in deps for lib in ["gaussian", "rasterizer", "plyfile", "trimesh"])
230
+
231
+ requirements = ["gradio>=5.35.0"]
232
+ if analysis and analysis.get("dependencies"):
233
+ filtered_deps = []
234
+ for dep in analysis["dependencies"][:15]:
235
+ if not dep.startswith("git+") and not dep.startswith("-e") and not dep.startswith("file:"):
236
+ if "==" in dep and dep.split("==")[0].lower() not in ["torch", "tensorflow", "numpy"]:
237
+ pkg_name = dep.split("==")[0]
238
+ version = dep.split("==")[1]
239
+ filtered_deps.append(f"{pkg_name}>={version}")
240
+ else:
241
+ filtered_deps.append(dep)
242
+ requirements.extend(filtered_deps)
243
+
244
+ if has_3d or "gaussian" in repo_name.lower():
245
+ app_code = f'''import gradio as gr
246
+ import os
247
+
248
+ def process_3d(input_file):
249
+ if input_file is None:
250
+ return "Please upload a 3D file or image"
251
+
252
+ info = """
253
+ ## ⚠️ Build Requirements Notice
254
+
255
+ This project requires:
256
+ 1. CUDA-enabled GPU
257
+ 2. Custom C++/CUDA extensions compilation
258
+
259
+ Original repository: {repo_url}
260
+ """
261
+ return info
262
+
263
+ with gr.Blocks(title="{repo_name}") as demo:
264
+ gr.Markdown(f"""
265
+ # {repo_name.replace("-", " ").title()}
266
+
267
+ {description}
268
+
269
+ This space was created from: [{repo_url}]({repo_url})
270
+ """)
271
+
272
+ with gr.Row():
273
+ with gr.Column():
274
+ input_file = gr.File(label="Upload 3D File or Image")
275
+ process_btn = gr.Button("Process", variant="primary")
276
+
277
+ with gr.Column():
278
+ output_info = gr.Markdown()
279
+
280
+ process_btn.click(
281
+ fn=process_3d,
282
+ inputs=input_file,
283
+ outputs=output_info
284
+ )
285
+
286
+ if __name__ == "__main__":
287
+ demo.launch()
288
+ '''
289
+ elif has_cv:
290
+ app_code = f'''import gradio as gr
291
+ from PIL import Image
292
+ import numpy as np
293
+
294
+ def process_image(image):
295
+ if image is None:
296
+ return None, "Please upload an image"
297
+
298
+ img_array = np.array(image)
299
+ processed = Image.fromarray(img_array)
300
+
301
+ info = f"Image shape: {{img_array.shape}}"
302
+ return processed, info
303
+
304
+ with gr.Blocks(title="{repo_name}") as demo:
305
+ gr.Markdown(f"""
306
+ # {repo_name.replace("-", " ").title()}
307
+
308
+ {description}
309
+
310
+ This space was created from: [{repo_url}]({repo_url})
311
+ """)
312
+
313
+ with gr.Row():
314
+ with gr.Column():
315
+ input_image = gr.Image(label="Input Image", type="pil")
316
+ process_btn = gr.Button("Process Image", variant="primary")
317
+
318
+ with gr.Column():
319
+ output_image = gr.Image(label="Output Image")
320
+ output_info = gr.Textbox(label="Information")
321
+
322
+ process_btn.click(
323
+ fn=process_image,
324
+ inputs=input_image,
325
+ outputs=[output_image, output_info]
326
+ )
327
+
328
+ if __name__ == "__main__":
329
+ demo.launch()
330
+ '''
331
+
332
+ elif has_nlp:
333
+ app_code = f'''import gradio as gr
334
+
335
+ def process_text(text, max_length=100):
336
+ if not text:
337
+ return "Please enter some text"
338
+
339
+ word_count = len(text.split())
340
+ char_count = len(text)
341
+
342
+ result = f"""
343
+ **Analysis Results:**
344
+ - Word count: {{word_count}}
345
+ - Character count: {{char_count}}
346
+ - Average word length: {{char_count/max(word_count, 1):.1f}}
347
+ """
348
+
349
+ return result
350
+
351
+ with gr.Blocks(title="{repo_name}") as demo:
352
+ gr.Markdown(f"""
353
+ # {repo_name.replace("-", " ").title()}
354
+
355
+ {description}
356
+
357
+ This space was created from: [{repo_url}]({repo_url})
358
+ """)
359
+
360
+ with gr.Row():
361
+ with gr.Column():
362
+ input_text = gr.Textbox(
363
+ label="Input Text",
364
+ placeholder="Enter your text here...",
365
+ lines=5
366
+ )
367
+ max_length = gr.Slider(
368
+ minimum=10,
369
+ maximum=500,
370
+ value=100,
371
+ label="Max Length"
372
+ )
373
+ process_btn = gr.Button("Process Text", variant="primary")
374
+
375
+ with gr.Column():
376
+ output_text = gr.Markdown(label="Results")
377
+
378
+ process_btn.click(
379
+ fn=process_text,
380
+ inputs=[input_text, max_length],
381
+ outputs=output_text
382
+ )
383
+
384
+ if __name__ == "__main__":
385
+ demo.launch()
386
+ '''
387
+
388
+ else:
389
+ app_code = f'''import gradio as gr
390
+
391
+ def main_function(input_data):
392
+ if not input_data:
393
+ return "Please provide input"
394
+
395
+ result = f"Processed successfully! Input received: {{input_data}}"
396
+ return result
397
+
398
+ with gr.Blocks(title="{repo_name}") as demo:
399
+ gr.Markdown(f"""
400
+ # {repo_name.replace("-", " ").title()}
401
+
402
+ {description}
403
+
404
+ This space was created from: [{repo_url}]({repo_url})
405
+ """)
406
+
407
+ with gr.Row():
408
+ with gr.Column():
409
+ input_data = gr.Textbox(
410
+ label="Input",
411
+ placeholder="Enter your input here...",
412
+ lines=3
413
+ )
414
+ process_btn = gr.Button("Process", variant="primary")
415
+
416
+ with gr.Column():
417
+ output_data = gr.Textbox(label="Output")
418
+
419
+ process_btn.click(
420
+ fn=main_function,
421
+ inputs=input_data,
422
+ outputs=output_data
423
+ )
424
+
425
+ if __name__ == "__main__":
426
+ demo.launch()
427
+ '''
428
+
429
+ return {
430
+ "app_py": app_code,
431
+ "requirements_txt": "\n".join(requirements),
432
+ "summary": f"Smart template created for {repo_name}"
433
+ }
434
+
435
+ def clone(repo_git, repo_hf, sdk_type, skip_lfs, enable_smart_generation):
436
+ folder = str(uuid.uuid4())
437
+
438
+ hf_token = os.getenv("HF_TOKEN")
439
+ if not hf_token:
440
+ yield "❌ Error: HF_TOKEN not found in environment variables."
441
+ return
442
+
443
+ try:
444
+ yield "πŸ”„ Starting clone process..."
445
+
446
+ api = HfApi(token=hf_token)
447
+ try:
448
+ user_info = api.whoami()
449
+ username = user_info["name"]
450
+ yield f"βœ… Authenticated as: {username}"
451
+ except Exception as e:
452
+ yield f"❌ Authentication failed: {str(e)}"
453
+ return
454
+
455
+ yield f"πŸ“₯ Cloning repository from {repo_git}..."
456
+
457
+ env = os.environ.copy()
458
+ env['GIT_LFS_SKIP_SMUDGE'] = '1'
459
+ clone_cmd = ['git', 'clone', '--recurse-submodules', repo_git, folder]
460
+ subprocess.run(clone_cmd, check=True, env=env)
461
+
462
+ if not skip_lfs:
463
+ yield "πŸ“¦ Attempting to download LFS files..."
464
+ try:
465
+ subprocess.run(['git', 'lfs', 'install'], cwd=folder, check=True)
466
+ lfs_result = subprocess.run(['git', 'lfs', 'pull'], cwd=folder, capture_output=True, text=True)
467
+
468
+ if lfs_result.returncode != 0:
469
+ yield f"⚠️ Warning: LFS download failed"
470
+ skip_lfs = True
471
+ else:
472
+ yield "βœ… LFS files downloaded successfully"
473
+ except Exception as e:
474
+ yield f"⚠️ LFS error: {str(e)}"
475
+ skip_lfs = True
476
+
477
+ if skip_lfs:
478
+ yield "🧹 Removing LFS pointer files..."
479
+ removed_files = remove_lfs_files(folder)
480
+ if removed_files:
481
+ yield f"πŸ“ Removed {len(removed_files)} LFS pointer files"
482
+
483
+ if enable_smart_generation:
484
+ yield "πŸ” Analyzing repository structure..."
485
+ folder_path = Path(folder)
486
+ analysis = analyze_repository(folder_path)
487
+
488
+ yield "πŸ€– Generating smart Gradio app..."
489
+ generated = generate_gradio_app(repo_git, analysis)
490
+
491
+ if generated and isinstance(generated, dict) and "app_py" in generated:
492
+ app_path = folder_path / "app.py"
493
+ app_path.write_text(generated["app_py"], encoding="utf-8")
494
+ yield "βœ… Smart app.py generated"
495
+
496
+ req_path = folder_path / "requirements.txt"
497
+ existing_reqs = []
498
+ if req_path.exists():
499
+ try:
500
+ existing_reqs = req_path.read_text(encoding="utf-8").strip().split("\n")
501
+ except:
502
+ existing_reqs = []
503
+
504
+ new_reqs = generated["requirements_txt"].strip().split("\n") if generated["requirements_txt"] else []
505
+
506
+ all_reqs = set()
507
+ git_reqs = []
508
+ torch_reqs = []
509
+ regular_reqs = []
510
+
511
+ for req in existing_reqs + new_reqs:
512
+ req = req.strip()
513
+ if not req or req.startswith("#"):
514
+ continue
515
+
516
+ if req.startswith("git+"):
517
+ git_reqs.append(req)
518
+ elif "torch" in req.lower() or "cuda" in req.lower():
519
+ torch_reqs.append(req)
520
+ else:
521
+ regular_reqs.append(req)
522
+
523
+ has_gradio = any("gradio" in req for req in regular_reqs)
524
+ if not has_gradio:
525
+ regular_reqs.append("gradio>=5.35.0")
526
+
527
+ final_reqs = []
528
+
529
+ if torch_reqs:
530
+ final_reqs.extend(sorted(set(torch_reqs)))
531
+ final_reqs.append("")
532
+
533
+ final_reqs.extend(sorted(set(regular_reqs)))
534
+
535
+ if git_reqs:
536
+ final_reqs.append("")
537
+ final_reqs.extend(sorted(set(git_reqs)))
538
+
539
+ req_content = "\n".join(final_reqs)
540
+ req_path.write_text(req_content, encoding="utf-8")
541
+ yield "βœ… Requirements.txt updated"
542
+
543
+ readme_path = folder_path / "README.md"
544
+ readme_content = f"""---
545
+ title: {repo_hf.replace("-", " ").title()}
546
+ emoji: πŸš€
547
+ colorFrom: blue
548
+ colorTo: green
549
+ sdk: {sdk_type}
550
+ sdk_version: "5.35.0"
551
+ app_file: app.py
552
+ pinned: false
553
+ ---
554
+
555
+ # {repo_hf.replace("-", " ").title()}
556
+
557
+ {analysis.get('description', 'Deployed from GitHub repository')}
558
+
559
+ Deployed from: {repo_git}
560
+ """
561
+ readme_path.write_text(readme_content, encoding="utf-8")
562
+ yield "βœ… README.md created/updated"
563
+
564
+ git_dir = os.path.join(folder, '.git')
565
+ if os.path.exists(git_dir):
566
+ shutil.rmtree(git_dir)
567
+ yield "🧹 Removed .git directory"
568
+
569
+ gitattributes_path = os.path.join(folder, '.gitattributes')
570
+ if os.path.exists(gitattributes_path):
571
+ with open(gitattributes_path, 'r') as f:
572
+ lines = f.readlines()
573
+
574
+ new_lines = []
575
+ for line in lines:
576
+ if 'filter=lfs' not in line:
577
+ new_lines.append(line)
578
+
579
+ if new_lines:
580
+ with open(gitattributes_path, 'w') as f:
581
+ f.writelines(new_lines)
582
+ else:
583
+ os.remove(gitattributes_path)
584
+
585
+ yield "πŸ—οΈ Creating Hugging Face Space..."
586
+
587
+ repo_id = f"{username}/{slugify(repo_hf)}"
588
+ space_created = False
589
+
590
+ for attempt in range(3):
591
+ try:
592
+ yield f" Creating Space: {repo_id} (attempt {attempt + 1}/3)"
593
+
594
+ try:
595
+ existing_space = api.space_info(repo_id=repo_id, token=hf_token)
596
+ yield f" ℹ️ Space already exists: {existing_space.id}"
597
+ space_created = True
598
+ break
599
+ except:
600
+ pass
601
+
602
+ create_result = api.create_repo(
603
+ repo_id=repo_id,
604
+ repo_type="space",
605
+ space_sdk=sdk_type,
606
+ exist_ok=True,
607
+ private=False,
608
+ token=hf_token
609
+ )
610
+
611
+ time.sleep(3)
612
+
613
+ space_info = api.space_info(repo_id=repo_id, token=hf_token)
614
+ yield f" βœ… Space created successfully: {space_info.id}"
615
+ space_created = True
616
+ break
617
+
618
+ except Exception as e:
619
+ error_msg = str(e)
620
+
621
+ if "429" in error_msg or "Too Many Requests" in error_msg:
622
+ yield f"❌ Rate Limit Error - Try again in 17-24 hours"
623
+ raise Exception(f"Rate limit reached.")
624
+
625
+ yield f" ⚠️ Attempt {attempt + 1} failed: {error_msg[:100]}..."
626
+ if attempt < 2:
627
+ yield " Retrying in 5 seconds..."
628
+ time.sleep(5)
629
+ else:
630
+ yield f" ❌ Failed to create space after 3 attempts"
631
+ raise Exception(f"Could not create space: {error_msg}")
632
+
633
+ if not space_created:
634
+ raise Exception("Failed to create space")
635
+
636
+ folder_size = sum(os.path.getsize(os.path.join(dirpath, filename))
637
+ for dirpath, dirnames, filenames in os.walk(folder)
638
+ for filename in filenames) / (1024 * 1024)
639
+
640
+ yield f"πŸ“Š Folder size: {folder_size:.2f} MB"
641
+
642
+ file_count = sum(len(files) for _, _, files in os.walk(folder))
643
+ yield f"πŸ“ Total files to upload: {file_count}"
644
+
645
+ upload_success = False
646
+ max_retries = 3
647
+
648
+ for attempt in range(max_retries):
649
+ try:
650
+ if attempt > 0:
651
+ yield f"πŸ“€ Upload attempt {attempt + 1}/{max_retries}..."
652
+ time.sleep(5)
653
+
654
+ if folder_size > 500:
655
+ yield "πŸ“€ Uploading large folder to Hugging Face..."
656
+ api.upload_large_folder(
657
+ folder_path=folder,
658
+ repo_id=repo_id,
659
+ repo_type="space",
660
+ token=hf_token,
661
+ commit_message="Deploy from GitHub repository",
662
+ ignore_patterns=["*.pyc", "__pycache__", ".git*", ".DS_Store", "*.egg-info"]
663
+ )
664
+ else:
665
+ yield "πŸ“€ Uploading to Hugging Face..."
666
+ api.upload_folder(
667
+ folder_path=folder,
668
+ repo_id=repo_id,
669
+ repo_type="space",
670
+ token=hf_token,
671
+ commit_message="Deploy from GitHub repository",
672
+ ignore_patterns=["*.pyc", "__pycache__", ".git*", ".DS_Store", "*.egg-info"]
673
+ )
674
+
675
+ upload_success = True
676
+ yield "βœ… Upload completed successfully"
677
+ break
678
+
679
+ except Exception as upload_error:
680
+ error_msg = str(upload_error)
681
+
682
+ if "404" in error_msg and attempt < max_retries - 1:
683
+ yield f" ⚠️ Upload failed (404). Retrying..."
684
+ time.sleep(10)
685
+
686
+ try:
687
+ space_info = api.space_info(repo_id=repo_id, token=hf_token)
688
+ yield f" βœ… Space confirmed to exist"
689
+ except:
690
+ yield " πŸ”„ Attempting to recreate space..."
691
+ try:
692
+ api.create_repo(
693
+ repo_id=repo_id,
694
+ repo_type="space",
695
+ space_sdk=sdk_type,
696
+ exist_ok=True,
697
+ private=False,
698
+ token=hf_token
699
+ )
700
+ yield " βœ… Space recreated"
701
+ except Exception as recreate_error:
702
+ yield f" ❌ Could not recreate space: {str(recreate_error)}"
703
+
704
+ elif "LFS pointer" in error_msg:
705
+ yield "❌ Upload failed due to remaining LFS pointer files"
706
+ raise upload_error
707
+
708
+ elif attempt == max_retries - 1:
709
+ yield f"❌ Upload failed after {max_retries} attempts"
710
+ raise upload_error
711
+ else:
712
+ yield f" ⚠️ Upload failed: {error_msg[:100]}..."
713
+
714
+ if not upload_success:
715
+ raise Exception("Upload failed after all retries")
716
+
717
+ shutil.rmtree(folder)
718
+
719
+ space_url = f"https://huggingface.co/spaces/{repo_id}"
720
+
721
+ yield f"""
722
+ βœ… **Successfully created Space!**
723
+
724
+ πŸ”— **Your Space URL**: [{space_url}]({space_url})
725
+
726
+ πŸ“‹ **Summary:**
727
+ - Space ID: `{repo_id}`
728
+ - Source: {repo_git}
729
+ - SDK: {sdk_type}
730
+ - Smart Generation: {'Enabled' if enable_smart_generation else 'Disabled'}
731
+ - LFS Files: {'Skipped' if skip_lfs else 'Included'}
732
+ """
733
+
734
+ if skip_lfs:
735
+ yield "\n⚠️ LFS files were removed."
736
+
737
+ if enable_smart_generation:
738
+ yield "\nπŸ€– AI-generated Gradio interface was created"
739
+
740
+ except subprocess.CalledProcessError as e:
741
+ if os.path.exists(folder):
742
+ shutil.rmtree(folder)
743
+ yield f"❌ Git error: {str(e)}"
744
+ except Exception as e:
745
+ if os.path.exists(folder):
746
+ shutil.rmtree(folder)
747
+ yield f"❌ Error: {str(e)}"
748
+
749
+ def space_to_github(hf_username, hf_space_name, github_username, github_repo_name, github_token):
750
+ """Clone HuggingFace Space and push to GitHub"""
751
+ tmp_dir = None
752
+ try:
753
+ # HF Space URL 생성
754
+ hf_repo_url = f"https://huggingface.co/spaces/{hf_username}/{hf_space_name}.git"
755
+
756
+ # μž„μ‹œ 디렉토리 생성 및 클둠
757
+ tmp_dir = tempfile.mkdtemp()
758
+ yield f"πŸ“₯ Cloning HF Space: {hf_username}/{hf_space_name}..."
759
+
760
+ # Git 클둠
761
+ env = os.environ.copy()
762
+ env['GIT_LFS_SKIP_SMUDGE'] = '1' # LFS 파일 μŠ€ν‚΅
763
+
764
+ clone_cmd = ['git', 'clone', hf_repo_url, tmp_dir]
765
+ result = subprocess.run(clone_cmd, env=env, capture_output=True, text=True)
766
+
767
+ if result.returncode != 0:
768
+ raise Exception(f"Clone failed: {result.stderr}")
769
+
770
+ yield "βœ… HF Space cloned successfully"
771
+
772
+ # GitHub 인증 및 레포 생성
773
+ yield "πŸ” Authenticating with GitHub..."
774
+
775
+ # GitHub API 호좜둜 레포 생성
776
+ headers = {
777
+ "Authorization": f"token {github_token}",
778
+ "Accept": "application/vnd.github.v3+json"
779
+ }
780
+
781
+ # μ‚¬μš©μž 정보 κ°€μ Έμ˜€κΈ°
782
+ user_response = requests.get("https://api.github.com/user", headers=headers)
783
+ if user_response.status_code != 200:
784
+ raise Exception("GitHub authentication failed. Please check your token.")
785
+
786
+ actual_github_username = user_response.json()["login"]
787
+
788
+ # μž…λ ₯된 usernameκ³Ό μ‹€μ œ username이 λ‹€λ₯Έ 경우 처리
789
+ if github_username.lower() != actual_github_username.lower():
790
+ yield f"⚠️ Note: Using authenticated user '{actual_github_username}' instead of '{github_username}'"
791
+ github_username = actual_github_username
792
+
793
+ yield f"βœ… Authenticated as GitHub user: {github_username}"
794
+
795
+ # λ ˆν¬μ§€ν† λ¦¬ 생성
796
+ yield f"πŸ“¦ Creating GitHub repository: {github_repo_name}..."
797
+ create_data = {
798
+ "name": github_repo_name,
799
+ "private": False,
800
+ "auto_init": False,
801
+ "description": f"Exported from HuggingFace Space: {hf_username}/{hf_space_name}"
802
+ }
803
+
804
+ create_response = requests.post(
805
+ "https://api.github.com/user/repos",
806
+ headers=headers,
807
+ json=create_data
808
+ )
809
+
810
+ if create_response.status_code == 201:
811
+ repo_info = create_response.json()
812
+ github_url = repo_info['html_url']
813
+ yield f"βœ… GitHub repository created: {github_url}"
814
+ elif create_response.status_code == 422:
815
+ # λ ˆν¬κ°€ 이미 μ‘΄μž¬ν•¨
816
+ github_url = f"https://github.com/{github_username}/{github_repo_name}"
817
+ yield f"ℹ️ Repository already exists: {github_url}"
818
+ else:
819
+ error_msg = create_response.json().get('message', 'Unknown error')
820
+ raise Exception(f"Failed to create repository: {error_msg}")
821
+
822
+ # Git remote λ³€κ²½ 및 ν‘Έμ‹œ
823
+ yield "πŸ“€ Pushing to GitHub..."
824
+
825
+ # git λͺ…λ Ήμ–΄λ‘œ remote λ³€κ²½ 및 ν‘Έμ‹œ
826
+ os.chdir(tmp_dir)
827
+
828
+ # 기쑴 origin 제거
829
+ subprocess.run(['git', 'remote', 'remove', 'origin'], capture_output=True)
830
+
831
+ # GitHub remote μΆ”κ°€
832
+ github_remote_url = f"https://{github_username}:{github_token}@github.com/{github_username}/{github_repo_name}.git"
833
+ subprocess.run(['git', 'remote', 'add', 'origin', github_remote_url], check=True)
834
+
835
+ # main 브랜치둜 ν‘Έμ‹œ
836
+ push_result = subprocess.run(
837
+ ['git', 'push', '-u', 'origin', 'HEAD:main', '--force'],
838
+ capture_output=True,
839
+ text=True
840
+ )
841
+
842
+ if push_result.returncode != 0:
843
+ # master 브랜치둜 μž¬μ‹œλ„
844
+ push_result = subprocess.run(
845
+ ['git', 'push', '-u', 'origin', 'HEAD:master', '--force'],
846
+ capture_output=True,
847
+ text=True
848
+ )
849
+
850
+ if push_result.returncode != 0:
851
+ raise Exception(f"Push failed: {push_result.stderr}")
852
+
853
+ yield "βœ… Successfully pushed to GitHub!"
854
+
855
+ # μ΅œμ’… κ²°κ³Ό 좜λ ₯ - 클릭 κ°€λŠ₯ν•œ 링크
856
+ final_url = f"https://github.com/{github_username}/{github_repo_name}"
857
+ yield f"""
858
+ πŸŽ‰ **Export Complete!**
859
+
860
+ πŸ“¦ **GitHub Repository**: [{final_url}]({final_url})
861
+
862
+ πŸ“‹ **Details:**
863
+ - Source: HuggingFace Space `{hf_username}/{hf_space_name}`
864
+ - Destination: GitHub `{github_username}/{github_repo_name}`
865
+
866
+ πŸ”§ **Next Steps:**
867
+ ```bash
868
+ git clone {final_url}.git
869
+ cd {github_repo_name}
870
+ ```
871
+ """
872
+
873
+ except Exception as e:
874
+ yield f"❌ Error: {str(e)}"
875
+ finally:
876
+ # μž„μ‹œ 디렉토리 정리
877
+ if tmp_dir and os.path.exists(tmp_dir):
878
+ os.chdir("/") # 디렉토리 λ°–μœΌλ‘œ 이동
879
+ shutil.rmtree(tmp_dir)
880
+
881
+ css = """
882
+ /* Modern Professional UI */
883
+ .container {
884
+ max-width: 1200px !important;
885
+ margin: auto;
886
+ padding: 20px;
887
+ }
888
+
889
+ /* Gradient backgrounds */
890
+ .main-header {
891
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
892
+ padding: 2rem;
893
+ border-radius: 15px;
894
+ margin-bottom: 2rem;
895
+ color: white;
896
+ text-align: center;
897
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
898
+ }
899
+
900
+ /* Tab styling */
901
+ .tabs {
902
+ box-shadow: 0 4px 6px rgba(0,0,0,0.07);
903
+ border-radius: 12px;
904
+ overflow: hidden;
905
+ }
906
+
907
+ /* Card-like sections */
908
+ .input-section {
909
+ background: white;
910
+ padding: 25px;
911
+ border-radius: 12px;
912
+ box-shadow: 0 2px 12px rgba(0,0,0,0.08);
913
+ margin-bottom: 20px;
914
+ border: 1px solid rgba(0,0,0,0.05);
915
+ }
916
+
917
+ /* Output box styling */
918
+ .output-box {
919
+ min-height: 400px !important;
920
+ max-height: 600px !important;
921
+ overflow-y: auto !important;
922
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
923
+ font-size: 13px !important;
924
+ line-height: 1.6 !important;
925
+ background: linear-gradient(to bottom, #1e1e1e, #2d2d30) !important;
926
+ color: #d4d4d4 !important;
927
+ padding: 20px !important;
928
+ border-radius: 10px !important;
929
+ border: 1px solid rgba(255,255,255,0.1) !important;
930
+ }
931
+
932
+ /* Custom button styling */
933
+ .primary-btn {
934
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
935
+ color: white !important;
936
+ border: none !important;
937
+ padding: 12px 30px !important;
938
+ font-size: 16px !important;
939
+ font-weight: 600 !important;
940
+ border-radius: 8px !important;
941
+ cursor: pointer !important;
942
+ transition: all 0.3s ease !important;
943
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
944
+ }
945
+
946
+ .primary-btn:hover {
947
+ transform: translateY(-2px) !important;
948
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6) !important;
949
+ }
950
+
951
+ /* Input field styling */
952
+ input[type="text"], input[type="password"], textarea {
953
+ border: 2px solid #e2e8f0 !important;
954
+ border-radius: 8px !important;
955
+ padding: 10px 15px !important;
956
+ font-size: 14px !important;
957
+ transition: all 0.3s ease !important;
958
+ background: #f8fafc !important;
959
+ }
960
+
961
+ input[type="text"]:focus, input[type="password"]:focus, textarea:focus {
962
+ border-color: #667eea !important;
963
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
964
+ background: white !important;
965
+ }
966
+
967
+ /* Radio button group styling */
968
+ .radio-group {
969
+ background: #f8fafc;
970
+ padding: 15px;
971
+ border-radius: 8px;
972
+ border: 1px solid #e2e8f0;
973
+ }
974
+
975
+ /* Checkbox styling */
976
+ input[type="checkbox"] {
977
+ width: 20px !important;
978
+ height: 20px !important;
979
+ cursor: pointer !important;
980
+ }
981
+
982
+ /* Label styling */
983
+ label {
984
+ font-weight: 600 !important;
985
+ color: #334155 !important;
986
+ margin-bottom: 8px !important;
987
+ display: block !important;
988
+ font-size: 14px !important;
989
+ }
990
+
991
+ /* Info text styling */
992
+ .info-text {
993
+ color: #64748b !important;
994
+ font-size: 13px !important;
995
+ margin-top: 5px !important;
996
+ }
997
+
998
+ /* Status badges */
999
+ .status-badge {
1000
+ display: inline-block;
1001
+ padding: 6px 12px;
1002
+ border-radius: 20px;
1003
+ font-size: 13px;
1004
+ font-weight: 600;
1005
+ margin: 5px 0;
1006
+ }
1007
+
1008
+ .status-success {
1009
+ background: #10b981;
1010
+ color: white;
1011
+ }
1012
+
1013
+ .status-error {
1014
+ background: #ef4444;
1015
+ color: white;
1016
+ }
1017
+
1018
+ .status-warning {
1019
+ background: #f59e0b;
1020
+ color: white;
1021
+ }
1022
+
1023
+ /* Tab button styling */
1024
+ button.tab-nav {
1025
+ padding: 12px 24px !important;
1026
+ font-weight: 600 !important;
1027
+ font-size: 15px !important;
1028
+ border-bottom: 3px solid transparent !important;
1029
+ transition: all 0.3s ease !important;
1030
+ }
1031
+
1032
+ button.tab-nav:hover {
1033
+ background: rgba(102, 126, 234, 0.05) !important;
1034
+ }
1035
+
1036
+ button.tab-nav.selected {
1037
+ border-bottom-color: #667eea !important;
1038
+ color: #667eea !important;
1039
+ }
1040
+
1041
+ /* Markdown styling */
1042
+ .markdown-text h1 {
1043
+ color: #1e293b !important;
1044
+ font-size: 28px !important;
1045
+ font-weight: 700 !important;
1046
+ margin-bottom: 10px !important;
1047
+ }
1048
+
1049
+ .markdown-text h2 {
1050
+ color: #334155 !important;
1051
+ font-size: 20px !important;
1052
+ font-weight: 600 !important;
1053
+ margin-top: 20px !important;
1054
+ }
1055
+
1056
+ .markdown-text a {
1057
+ color: #667eea !important;
1058
+ text-decoration: none !important;
1059
+ font-weight: 500 !important;
1060
+ }
1061
+
1062
+ .markdown-text a:hover {
1063
+ text-decoration: underline !important;
1064
+ }
1065
+
1066
+ .markdown-text code {
1067
+ background: #f1f5f9 !important;
1068
+ padding: 2px 6px !important;
1069
+ border-radius: 4px !important;
1070
+ font-size: 13px !important;
1071
+ color: #e11d48 !important;
1072
+ }
1073
+
1074
+ /* Scrollbar styling */
1075
+ .output-box::-webkit-scrollbar {
1076
+ width: 10px;
1077
+ }
1078
+
1079
+ .output-box::-webkit-scrollbar-track {
1080
+ background: #2d2d30;
1081
+ border-radius: 5px;
1082
+ }
1083
+
1084
+ .output-box::-webkit-scrollbar-thumb {
1085
+ background: #555;
1086
+ border-radius: 5px;
1087
+ }
1088
+
1089
+ .output-box::-webkit-scrollbar-thumb:hover {
1090
+ background: #666;
1091
+ }
1092
+
1093
+ /* Animation */
1094
+ @keyframes fadeIn {
1095
+ from { opacity: 0; transform: translateY(10px); }
1096
+ to { opacity: 1; transform: translateY(0); }
1097
+ }
1098
+
1099
+ .input-section {
1100
+ animation: fadeIn 0.5s ease;
1101
+ }
1102
+
1103
+ /* Responsive design */
1104
+ @media (max-width: 768px) {
1105
+ .container {
1106
+ padding: 10px;
1107
+ }
1108
+
1109
+ .main-header {
1110
+ padding: 1.5rem;
1111
+ }
1112
+
1113
+ .input-section {
1114
+ padding: 15px;
1115
+ }
1116
+ }
1117
+ """
1118
+
1119
+ with gr.Blocks(css=css, theme=gr.themes.Soft(
1120
+ primary_hue="purple",
1121
+ secondary_hue="purple",
1122
+ neutral_hue="slate"
1123
+ )) as demo:
1124
+ gr.HTML("""
1125
+ <div class="main-header">
1126
+ <h1 style="font-size: 2.5rem; margin: 0; font-weight: 700;">
1127
+ πŸ”„ Repository Converter Pro
1128
+ </h1>
1129
+ <p style="font-size: 1.1rem; margin-top: 10px; opacity: 0.95;">
1130
+ Seamlessly transfer repositories between GitHub and HuggingFace
1131
+ </p>
1132
+ </div>
1133
+ """)
1134
+
1135
+ with gr.Tabs(elem_classes="tabs"):
1136
+ with gr.Tab("πŸš€ GitHub β†’ HF Space", elem_id="tab1"):
1137
+ with gr.Row():
1138
+ with gr.Column(scale=5):
1139
+ with gr.Group(elem_classes="input-section"):
1140
+ gr.Markdown("### πŸ“¦ Source Repository")
1141
+ repo_git = gr.Textbox(
1142
+ label="GitHub Repository URL",
1143
+ placeholder="https://github.com/username/repository",
1144
+ info="Enter the full GitHub repository URL"
1145
+ )
1146
+
1147
+ gr.Markdown("### 🎯 Destination Settings")
1148
+ repo_hf = gr.Textbox(
1149
+ label="HuggingFace Space Name",
1150
+ placeholder="my-awesome-space",
1151
+ info="Choose a unique name for your Space"
1152
+ )
1153
+
1154
+ sdk_choices = gr.Radio(
1155
+ ["gradio", "streamlit", "docker", "static"],
1156
+ label="Space SDK Type",
1157
+ value="gradio",
1158
+ elem_classes="radio-group"
1159
+ )
1160
+
1161
+ gr.Markdown("### βš™οΈ Advanced Options")
1162
+ with gr.Row():
1163
+ skip_lfs = gr.Checkbox(
1164
+ label="Skip Large Files (LFS)",
1165
+ value=True,
1166
+ info="Recommended for faster deployment"
1167
+ )
1168
+ enable_smart_generation = gr.Checkbox(
1169
+ label="πŸ€– AI-Powered App Generation",
1170
+ value=False,
1171
+ info="Generate app.py automatically"
1172
+ )
1173
+
1174
+ btn_to_hf = gr.Button("πŸš€ Deploy to HuggingFace",
1175
+ variant="primary",
1176
+ elem_classes="primary-btn",
1177
+ size="lg")
1178
+
1179
+ with gr.Column(scale=7):
1180
+ with gr.Group(elem_classes="input-section"):
1181
+ gr.Markdown("### πŸ“Š Deployment Progress")
1182
+ output_to_hf = gr.Textbox(
1183
+ label="",
1184
+ lines=20,
1185
+ elem_classes="output-box",
1186
+ interactive=False,
1187
+ show_copy_button=True
1188
+ )
1189
+
1190
+ # Status indicator
1191
+ if not os.getenv("HF_TOKEN"):
1192
+ gr.HTML("""
1193
+ <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 12px; margin-top: 10px;">
1194
+ <span style="color: #dc2626; font-weight: 600;">⚠️ Configuration Required:</span>
1195
+ <span style="color: #7f1d1d;"> Please set HF_TOKEN in Space settings</span>
1196
+ </div>
1197
+ """)
1198
+ else:
1199
+ gr.HTML("""
1200
+ <div style="background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 12px; margin-top: 10px;">
1201
+ <span style="color: #16a34a; font-weight: 600;">βœ… Ready:</span>
1202
+ <span style="color: #14532d;"> HF_TOKEN configured successfully</span>
1203
+ </div>
1204
+ """)
1205
+
1206
+ btn_to_hf.click(
1207
+ fn=clone,
1208
+ inputs=[repo_git, repo_hf, sdk_choices, skip_lfs, enable_smart_generation],
1209
+ outputs=output_to_hf
1210
+ )
1211
+
1212
+ with gr.Tab("πŸ“€ HF Space β†’ GitHub", elem_id="tab2"):
1213
+ with gr.Row():
1214
+ with gr.Column(scale=5):
1215
+ with gr.Group(elem_classes="input-section"):
1216
+ gr.Markdown("### 🎯 Source Space")
1217
+ hf_username = gr.Textbox(
1218
+ label="HuggingFace Username",
1219
+ placeholder="your-hf-username",
1220
+ info="Your HuggingFace account name"
1221
+ )
1222
+ hf_space_name = gr.Textbox(
1223
+ label="Space Name",
1224
+ placeholder="your-space-name",
1225
+ info="Name of the Space to export"
1226
+ )
1227
+
1228
+ gr.Markdown("### πŸ“¦ GitHub Destination")
1229
+ github_username = gr.Textbox(
1230
+ label="GitHub Username",
1231
+ placeholder="your-github-username",
1232
+ info="Your GitHub account name"
1233
+ )
1234
+ github_repo_name = gr.Textbox(
1235
+ label="Repository Name",
1236
+ placeholder="new-repo-name",
1237
+ info="Name for the new GitHub repository"
1238
+ )
1239
+
1240
+ gr.Markdown("### πŸ” Authentication")
1241
+ github_token = gr.Textbox(
1242
+ label="GitHub Personal Access Token",
1243
+ type="password",
1244
+ placeholder="ghp_xxxxxxxxxxxxxxxxxxxx",
1245
+ info="Generate at: github.com/settings/tokens (needs 'repo' scope)"
1246
+ )
1247
+
1248
+ btn_to_github = gr.Button("πŸ“€ Export to GitHub",
1249
+ variant="primary",
1250
+ elem_classes="primary-btn",
1251
+ size="lg")
1252
+
1253
+ with gr.Column(scale=7):
1254
+ with gr.Group(elem_classes="input-section"):
1255
+ gr.Markdown("### πŸ“Š Export Progress")
1256
+ output_to_github = gr.Textbox(
1257
+ label="",
1258
+ lines=20,
1259
+ elem_classes="output-box",
1260
+ interactive=False,
1261
+ show_copy_button=True
1262
+ )
1263
+
1264
+ btn_to_github.click(
1265
+ fn=space_to_github,
1266
+ inputs=[hf_username, hf_space_name, github_username, github_repo_name, github_token],
1267
+ outputs=output_to_github
1268
+ )
1269
+
1270
+ gr.Markdown("""
1271
+ ---
1272
+ <div style="text-align: center; margin-top: 30px; color: #64748b;">
1273
+ <h3 style="color: #334155;">πŸ› οΈ Quick Start Guide</h3>
1274
+
1275
+ <div style="display: flex; justify-content: space-around; margin-top: 20px; flex-wrap: wrap;">
1276
+ <div style="flex: 1; min-width: 250px; margin: 10px; padding: 20px; background: #f8fafc; border-radius: 10px;">
1277
+ <h4 style="color: #667eea;">GitHub β†’ HuggingFace</h4>
1278
+ <p>Deploy any GitHub repository as a HuggingFace Space with automatic LFS handling and optional AI-powered interface generation.</p>
1279
+ </div>
1280
+
1281
+ <div style="flex: 1; min-width: 250px; margin: 10px; padding: 20px; background: #f8fafc; border-radius: 10px;">
1282
+ <h4 style="color: #667eea;">HuggingFace β†’ GitHub</h4>
1283
+ <p>Export your HuggingFace Spaces to GitHub for version control, collaboration, and backup.</p>
1284
+ </div>
1285
+ </div>
1286
+
1287
+ <div style="margin-top: 30px;">
1288
+ <h4 style="color: #334155;">Required Tokens</h4>
1289
+ <p>
1290
+ <code style="background: #e2e8f0; padding: 4px 8px; border-radius: 4px;">HF_TOKEN</code> for GitHub β†’ HF |
1291
+ <code style="background: #e2e8f0; padding: 4px 8px; border-radius: 4px;">GitHub PAT</code> for HF β†’ GitHub |
1292
+ <code style="background: #e2e8f0; padding: 4px 8px; border-radius: 4px;">FIREWORKS_API_KEY</code> for AI features
1293
+ </p>
1294
+ </div>
1295
+ </div>
1296
+ """)
1297
+
1298
+ if __name__ == "__main__":
1299
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ requests
3
+ gitpython
4
+ huggingface_hub
5
+
6
+ GitPython
7
+ python-slugify
8
+ uuid
9
+ gitpython
10
+ pygithub