akhaliq HF Staff commited on
Commit
3eb00a5
·
1 Parent(s): d4d57c4

add import feature

Browse files
backend_api.py CHANGED
@@ -28,6 +28,9 @@ from backend_models import (
28
  is_mistral_model
29
  )
30
 
 
 
 
31
  # Import system prompts from standalone backend_prompts.py
32
  # No dependencies on Gradio or heavy libraries
33
  print("[Startup] Loading system prompts from backend_prompts...")
@@ -145,6 +148,20 @@ class CodeGenerationResponse(BaseModel):
145
  status: str
146
 
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  # Mock authentication for development
149
  # In production, integrate with HuggingFace OAuth
150
  class MockAuth:
@@ -638,6 +655,100 @@ async def deploy(
638
  )
639
 
640
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  @app.websocket("/ws/generate")
642
  async def websocket_generate(websocket: WebSocket):
643
  """WebSocket endpoint for real-time code generation"""
 
28
  is_mistral_model
29
  )
30
 
31
+ # Import project importer for importing from HF/GitHub
32
+ from project_importer import ProjectImporter
33
+
34
  # Import system prompts from standalone backend_prompts.py
35
  # No dependencies on Gradio or heavy libraries
36
  print("[Startup] Loading system prompts from backend_prompts...")
 
148
  status: str
149
 
150
 
151
+ class ImportRequest(BaseModel):
152
+ url: str
153
+ prefer_local: bool = False
154
+
155
+
156
+ class ImportResponse(BaseModel):
157
+ status: str
158
+ message: str
159
+ code: str
160
+ language: str
161
+ url: str
162
+ metadata: Dict
163
+
164
+
165
  # Mock authentication for development
166
  # In production, integrate with HuggingFace OAuth
167
  class MockAuth:
 
655
  )
656
 
657
 
658
+ @app.post("/api/import", response_model=ImportResponse)
659
+ async def import_project(request: ImportRequest):
660
+ """
661
+ Import a project from HuggingFace Space, HuggingFace Model, or GitHub repo
662
+
663
+ Supports URLs like:
664
+ - https://huggingface.co/spaces/username/space-name
665
+ - https://huggingface.co/username/model-name
666
+ - https://github.com/username/repo-name
667
+ """
668
+ try:
669
+ importer = ProjectImporter()
670
+ result = importer.import_from_url(request.url)
671
+
672
+ # Handle model-specific prefer_local flag
673
+ if request.prefer_local and result.get('metadata', {}).get('has_alternatives'):
674
+ # Switch to local code if available
675
+ local_code = result['metadata'].get('local_code')
676
+ if local_code:
677
+ result['code'] = local_code
678
+ result['metadata']['code_type'] = 'local'
679
+ result['message'] = result['message'].replace('inference', 'local')
680
+
681
+ return ImportResponse(**result)
682
+
683
+ except Exception as e:
684
+ return ImportResponse(
685
+ status="error",
686
+ message=f"Import failed: {str(e)}",
687
+ code="",
688
+ language="unknown",
689
+ url=request.url,
690
+ metadata={}
691
+ )
692
+
693
+
694
+ @app.get("/api/import/space/{username}/{space_name}")
695
+ async def import_space(username: str, space_name: str):
696
+ """Import a specific HuggingFace Space by username and space name"""
697
+ try:
698
+ importer = ProjectImporter()
699
+ result = importer.import_space(username, space_name)
700
+ return result
701
+ except Exception as e:
702
+ return {
703
+ "status": "error",
704
+ "message": f"Failed to import space: {str(e)}",
705
+ "code": "",
706
+ "language": "unknown",
707
+ "url": f"https://huggingface.co/spaces/{username}/{space_name}",
708
+ "metadata": {}
709
+ }
710
+
711
+
712
+ @app.get("/api/import/model/{path:path}")
713
+ async def import_model(path: str, prefer_local: bool = False):
714
+ """
715
+ Import a specific HuggingFace Model by model ID
716
+
717
+ Example: /api/import/model/meta-llama/Llama-3.2-1B-Instruct
718
+ """
719
+ try:
720
+ importer = ProjectImporter()
721
+ result = importer.import_model(path, prefer_local=prefer_local)
722
+ return result
723
+ except Exception as e:
724
+ return {
725
+ "status": "error",
726
+ "message": f"Failed to import model: {str(e)}",
727
+ "code": "",
728
+ "language": "python",
729
+ "url": f"https://huggingface.co/{path}",
730
+ "metadata": {}
731
+ }
732
+
733
+
734
+ @app.get("/api/import/github/{owner}/{repo}")
735
+ async def import_github(owner: str, repo: str):
736
+ """Import a GitHub repository by owner and repo name"""
737
+ try:
738
+ importer = ProjectImporter()
739
+ result = importer.import_github_repo(owner, repo)
740
+ return result
741
+ except Exception as e:
742
+ return {
743
+ "status": "error",
744
+ "message": f"Failed to import repository: {str(e)}",
745
+ "code": "",
746
+ "language": "python",
747
+ "url": f"https://github.com/{owner}/{repo}",
748
+ "metadata": {}
749
+ }
750
+
751
+
752
  @app.websocket("/ws/generate")
753
  async def websocket_generate(websocket: WebSocket):
754
  """WebSocket endpoint for real-time code generation"""
examples/import_project_example.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Example usage of the ProjectImporter module
3
+
4
+ This script demonstrates how to use the standalone project importer
5
+ to fetch code from various sources without Gradio.
6
+ """
7
+
8
+ import sys
9
+ import os
10
+
11
+ # Add parent directory to path
12
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13
+
14
+ from project_importer import ProjectImporter
15
+
16
+
17
+ def example_import_space():
18
+ """Example: Import a HuggingFace Space"""
19
+ print("=" * 80)
20
+ print("Example 1: Importing a HuggingFace Space")
21
+ print("=" * 80)
22
+
23
+ importer = ProjectImporter()
24
+ result = importer.import_space("akhaliq", "anycoder")
25
+
26
+ print(f"Status: {result['status']}")
27
+ print(f"Message: {result['message']}")
28
+ print(f"Language: {result['language']}")
29
+ print(f"Files: {len(result['metadata'].get('files', []))}")
30
+ print(f"\nFirst 500 characters of code:\n{result['code'][:500]}...")
31
+ print()
32
+
33
+
34
+ def example_import_model():
35
+ """Example: Import a HuggingFace Model"""
36
+ print("=" * 80)
37
+ print("Example 2: Importing a HuggingFace Model")
38
+ print("=" * 80)
39
+
40
+ importer = ProjectImporter()
41
+ result = importer.import_model("meta-llama/Llama-3.2-1B-Instruct")
42
+
43
+ print(f"Status: {result['status']}")
44
+ print(f"Message: {result['message']}")
45
+ print(f"Language: {result['language']}")
46
+ print(f"Pipeline Tag: {result['metadata'].get('pipeline_tag')}")
47
+ print(f"\nCode:\n{result['code']}")
48
+ print()
49
+
50
+
51
+ def example_import_github():
52
+ """Example: Import a GitHub Repository"""
53
+ print("=" * 80)
54
+ print("Example 3: Importing from GitHub")
55
+ print("=" * 80)
56
+
57
+ importer = ProjectImporter()
58
+ result = importer.import_github_repo("huggingface", "transformers")
59
+
60
+ print(f"Status: {result['status']}")
61
+ print(f"Message: {result['message']}")
62
+ print(f"Language: {result['language']}")
63
+ print(f"\nFirst 500 characters of code:\n{result['code'][:500]}...")
64
+ print()
65
+
66
+
67
+ def example_import_from_url():
68
+ """Example: Import from any URL"""
69
+ print("=" * 80)
70
+ print("Example 4: Import from URL (automatic detection)")
71
+ print("=" * 80)
72
+
73
+ importer = ProjectImporter()
74
+
75
+ # Test different URL types
76
+ urls = [
77
+ "https://huggingface.co/spaces/akhaliq/anycoder",
78
+ "https://huggingface.co/meta-llama/Llama-3.2-1B-Instruct",
79
+ "https://github.com/huggingface/diffusers"
80
+ ]
81
+
82
+ for url in urls:
83
+ print(f"\nImporting: {url}")
84
+ result = importer.import_from_url(url)
85
+ print(f" Status: {result['status']}")
86
+ print(f" Language: {result['language']}")
87
+ print(f" Message: {result['message']}")
88
+
89
+
90
+ def example_save_to_file():
91
+ """Example: Save imported code to a file"""
92
+ print("=" * 80)
93
+ print("Example 5: Save imported code to file")
94
+ print("=" * 80)
95
+
96
+ importer = ProjectImporter()
97
+ result = importer.import_model("stabilityai/stable-diffusion-3.5-large")
98
+
99
+ if result['status'] == 'success':
100
+ output_file = "imported_sd3.5_code.py"
101
+ with open(output_file, 'w', encoding='utf-8') as f:
102
+ f.write(result['code'])
103
+ print(f"Code saved to: {output_file}")
104
+ else:
105
+ print(f"Failed to import: {result['message']}")
106
+ print()
107
+
108
+
109
+ def example_with_metadata():
110
+ """Example: Working with metadata"""
111
+ print("=" * 80)
112
+ print("Example 6: Working with metadata")
113
+ print("=" * 80)
114
+
115
+ importer = ProjectImporter()
116
+ result = importer.import_model("Qwen/Qwen2.5-Coder-32B-Instruct")
117
+
118
+ print(f"Status: {result['status']}")
119
+ print(f"Message: {result['message']}")
120
+ print(f"\nMetadata:")
121
+ for key, value in result['metadata'].items():
122
+ print(f" {key}: {value}")
123
+
124
+ # Check if there are alternatives
125
+ if result['metadata'].get('has_alternatives'):
126
+ print("\n✨ This model has multiple code options available!")
127
+ print(" - Inference code (serverless)")
128
+ print(" - Local code (transformers/diffusers)")
129
+ print()
130
+
131
+
132
+ def main():
133
+ """Run all examples"""
134
+ print("\n🚀 ProjectImporter Examples\n")
135
+
136
+ try:
137
+ example_import_space()
138
+ except Exception as e:
139
+ print(f"❌ Space import failed: {e}\n")
140
+
141
+ try:
142
+ example_import_model()
143
+ except Exception as e:
144
+ print(f"❌ Model import failed: {e}\n")
145
+
146
+ try:
147
+ example_import_github()
148
+ except Exception as e:
149
+ print(f"❌ GitHub import failed: {e}\n")
150
+
151
+ try:
152
+ example_import_from_url()
153
+ except Exception as e:
154
+ print(f"❌ URL import failed: {e}\n")
155
+
156
+ try:
157
+ example_with_metadata()
158
+ except Exception as e:
159
+ print(f"❌ Metadata example failed: {e}\n")
160
+
161
+ print("\n✅ Examples completed!")
162
+
163
+
164
+ if __name__ == "__main__":
165
+ main()
166
+
frontend/src/app/page.tsx CHANGED
@@ -176,6 +176,22 @@ export default function Home() {
176
  }
177
  };
178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  return (
180
  <div className="h-screen flex flex-col bg-[#1d1d1f]">
181
  <Header />
@@ -263,6 +279,7 @@ export default function Home() {
263
  onModelChange={setSelectedModel}
264
  onDeploy={handleDeploy}
265
  onClear={handleClear}
 
266
  isGenerating={isGenerating}
267
  />
268
  </div>
 
176
  }
177
  };
178
 
179
+ const handleImport = (code: string, language: Language) => {
180
+ setGeneratedCode(code);
181
+ setSelectedLanguage(language);
182
+
183
+ // Add a system message
184
+ const importMessage: Message = {
185
+ role: 'assistant',
186
+ content: `✅ Project imported successfully! Language: ${language}`,
187
+ timestamp: new Date().toISOString(),
188
+ };
189
+ setMessages((prev) => [...prev, importMessage]);
190
+
191
+ // Switch to editor view on mobile
192
+ setMobileView('editor');
193
+ };
194
+
195
  return (
196
  <div className="h-screen flex flex-col bg-[#1d1d1f]">
197
  <Header />
 
279
  onModelChange={setSelectedModel}
280
  onDeploy={handleDeploy}
281
  onClear={handleClear}
282
+ onImport={handleImport}
283
  isGenerating={isGenerating}
284
  />
285
  </div>
frontend/src/components/ControlPanel.tsx CHANGED
@@ -11,6 +11,7 @@ interface ControlPanelProps {
11
  onModelChange: (modelId: string) => void;
12
  onDeploy: () => void;
13
  onClear: () => void;
 
14
  isGenerating: boolean;
15
  }
16
 
@@ -21,11 +22,16 @@ export default function ControlPanel({
21
  onModelChange,
22
  onDeploy,
23
  onClear,
 
24
  isGenerating,
25
  }: ControlPanelProps) {
26
  const [models, setModels] = useState<Model[]>([]);
27
  const [languages, setLanguages] = useState<Language[]>([]);
28
  const [isLoading, setIsLoading] = useState(true);
 
 
 
 
29
 
30
  useEffect(() => {
31
  loadData();
@@ -59,6 +65,42 @@ export default function ControlPanel({
59
  }
60
  };
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  return (
63
  <div className="bg-[#28282a] p-5 space-y-6 h-full">
64
  <h3 className="text-base font-semibold text-[#e5e5e7] tracking-tight mb-2">Configuration</h3>
@@ -120,6 +162,14 @@ export default function ControlPanel({
120
 
121
  {/* Action Buttons */}
122
  <div className="flex flex-col space-y-3 pt-4">
 
 
 
 
 
 
 
 
123
  <button
124
  onClick={onDeploy}
125
  disabled={isGenerating}
@@ -142,12 +192,68 @@ export default function ControlPanel({
142
  <div className="mt-6 p-4 bg-[#2c2c2e] border border-[#48484a] rounded-xl shadow-sm">
143
  <h4 className="text-sm font-semibold text-[#e5e5e7] mb-3 tracking-tight">💡 Tips</h4>
144
  <ul className="text-xs text-[#86868b] space-y-2 leading-relaxed">
 
145
  <li>• Be specific in your requirements</li>
146
  <li>• Try different AI models</li>
147
- <li>• Edit code in the editor</li>
148
  <li>• Deploy to HF Spaces</li>
149
  </ul>
150
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  </div>
152
  );
153
  }
 
11
  onModelChange: (modelId: string) => void;
12
  onDeploy: () => void;
13
  onClear: () => void;
14
+ onImport?: (code: string, language: Language) => void;
15
  isGenerating: boolean;
16
  }
17
 
 
22
  onModelChange,
23
  onDeploy,
24
  onClear,
25
+ onImport,
26
  isGenerating,
27
  }: ControlPanelProps) {
28
  const [models, setModels] = useState<Model[]>([]);
29
  const [languages, setLanguages] = useState<Language[]>([]);
30
  const [isLoading, setIsLoading] = useState(true);
31
+ const [showImportModal, setShowImportModal] = useState(false);
32
+ const [importUrl, setImportUrl] = useState('');
33
+ const [isImporting, setIsImporting] = useState(false);
34
+ const [importError, setImportError] = useState<string | null>(null);
35
 
36
  useEffect(() => {
37
  loadData();
 
65
  }
66
  };
67
 
68
+ const handleImport = async () => {
69
+ if (!importUrl.trim()) {
70
+ setImportError('Please enter a valid URL');
71
+ return;
72
+ }
73
+
74
+ setIsImporting(true);
75
+ setImportError(null);
76
+
77
+ try {
78
+ console.log('Importing from:', importUrl);
79
+ const result = await apiClient.importProject(importUrl);
80
+
81
+ if (result.status === 'success') {
82
+ console.log('Import successful:', result);
83
+
84
+ // Call the onImport callback if provided
85
+ if (onImport && result.code) {
86
+ onImport(result.code, result.language || 'html');
87
+ }
88
+
89
+ // Close modal and reset
90
+ setShowImportModal(false);
91
+ setImportUrl('');
92
+ setImportError(null);
93
+ } else {
94
+ setImportError(result.message || 'Import failed');
95
+ }
96
+ } catch (error: any) {
97
+ console.error('Import error:', error);
98
+ setImportError(error.response?.data?.message || error.message || 'Failed to import project');
99
+ } finally {
100
+ setIsImporting(false);
101
+ }
102
+ };
103
+
104
  return (
105
  <div className="bg-[#28282a] p-5 space-y-6 h-full">
106
  <h3 className="text-base font-semibold text-[#e5e5e7] tracking-tight mb-2">Configuration</h3>
 
162
 
163
  {/* Action Buttons */}
164
  <div className="flex flex-col space-y-3 pt-4">
165
+ <button
166
+ onClick={() => setShowImportModal(true)}
167
+ disabled={isGenerating}
168
+ className="w-full px-4 py-3.5 bg-[#34c759] text-white text-sm rounded-xl hover:bg-[#30b350] disabled:opacity-50 disabled:cursor-not-allowed transition-all font-semibold flex items-center justify-center space-x-2 shadow-md active:scale-95"
169
+ >
170
+ <span>📥</span>
171
+ <span>Import Project</span>
172
+ </button>
173
  <button
174
  onClick={onDeploy}
175
  disabled={isGenerating}
 
192
  <div className="mt-6 p-4 bg-[#2c2c2e] border border-[#48484a] rounded-xl shadow-sm">
193
  <h4 className="text-sm font-semibold text-[#e5e5e7] mb-3 tracking-tight">💡 Tips</h4>
194
  <ul className="text-xs text-[#86868b] space-y-2 leading-relaxed">
195
+ <li>• Import projects from HF/GitHub</li>
196
  <li>• Be specific in your requirements</li>
197
  <li>• Try different AI models</li>
 
198
  <li>• Deploy to HF Spaces</li>
199
  </ul>
200
  </div>
201
+
202
+ {/* Import Modal */}
203
+ {showImportModal && (
204
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
205
+ <div className="bg-[#2c2c2e] border border-[#48484a] rounded-2xl p-6 max-w-md w-full shadow-2xl">
206
+ <h3 className="text-lg font-semibold text-[#e5e5e7] mb-4 tracking-tight">📥 Import Project</h3>
207
+
208
+ <div className="space-y-4">
209
+ <div>
210
+ <label className="block text-sm font-medium text-[#e5e5e7] mb-2">
211
+ Project URL
212
+ </label>
213
+ <input
214
+ type="text"
215
+ value={importUrl}
216
+ onChange={(e) => setImportUrl(e.target.value)}
217
+ placeholder="https://huggingface.co/spaces/..."
218
+ disabled={isImporting}
219
+ className="w-full px-4 py-3 bg-[#3a3a3c] text-[#e5e5e7] text-sm border border-[#48484a] rounded-xl focus:outline-none focus:ring-2 focus:ring-[#34c759] focus:border-transparent disabled:opacity-50 placeholder-[#86868b] font-medium"
220
+ onKeyDown={(e) => e.key === 'Enter' && handleImport()}
221
+ />
222
+ <p className="text-xs text-[#86868b] mt-2">
223
+ Supported: HF Spaces, HF Models, GitHub repos
224
+ </p>
225
+ </div>
226
+
227
+ {importError && (
228
+ <div className="p-3 bg-[#ff3b30] bg-opacity-10 border border-[#ff3b30] rounded-xl">
229
+ <p className="text-sm text-[#ff3b30]">{importError}</p>
230
+ </div>
231
+ )}
232
+
233
+ <div className="flex space-x-3">
234
+ <button
235
+ onClick={handleImport}
236
+ disabled={isImporting || !importUrl.trim()}
237
+ className="flex-1 px-4 py-3 bg-[#34c759] text-white text-sm rounded-xl hover:bg-[#30b350] disabled:opacity-50 disabled:cursor-not-allowed transition-all font-semibold active:scale-95"
238
+ >
239
+ {isImporting ? '⏳ Importing...' : '✓ Import'}
240
+ </button>
241
+ <button
242
+ onClick={() => {
243
+ setShowImportModal(false);
244
+ setImportUrl('');
245
+ setImportError(null);
246
+ }}
247
+ disabled={isImporting}
248
+ className="flex-1 px-4 py-3 bg-[#3a3a3c] text-[#e5e5e7] text-sm rounded-xl hover:bg-[#48484a] disabled:opacity-50 disabled:cursor-not-allowed transition-all font-semibold border border-[#48484a] active:scale-95"
249
+ >
250
+ Cancel
251
+ </button>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ </div>
256
+ )}
257
  </div>
258
  );
259
  }
frontend/src/lib/api.ts CHANGED
@@ -196,6 +196,28 @@ class ApiClient {
196
  return response.data;
197
  }
198
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  logout() {
200
  this.token = null;
201
  }
 
196
  return response.data;
197
  }
198
 
199
+ async importProject(url: string, preferLocal: boolean = false): Promise<any> {
200
+ const response = await this.client.post('/api/import', { url, prefer_local: preferLocal });
201
+ return response.data;
202
+ }
203
+
204
+ async importSpace(username: string, spaceName: string): Promise<any> {
205
+ const response = await this.client.get(`/api/import/space/${username}/${spaceName}`);
206
+ return response.data;
207
+ }
208
+
209
+ async importModel(modelId: string, preferLocal: boolean = false): Promise<any> {
210
+ const response = await this.client.get(`/api/import/model/${modelId}`, {
211
+ params: { prefer_local: preferLocal }
212
+ });
213
+ return response.data;
214
+ }
215
+
216
+ async importGithub(owner: string, repo: string): Promise<any> {
217
+ const response = await this.client.get(`/api/import/github/${owner}/${repo}`);
218
+ return response.data;
219
+ }
220
+
221
  logout() {
222
  this.token = null;
223
  }
project_importer.py ADDED
@@ -0,0 +1,682 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Project Importer - Standalone module for importing projects from various sources
3
+
4
+ This module provides functionality to import projects from:
5
+ - HuggingFace Spaces
6
+ - HuggingFace Models
7
+ - GitHub Repositories
8
+
9
+ No Gradio dependency required - pure Python implementation.
10
+ """
11
+
12
+ import os
13
+ import re
14
+ import requests
15
+ from typing import Dict, List, Optional, Tuple
16
+ from urllib.parse import urlparse
17
+ from huggingface_hub import HfApi, list_repo_files
18
+
19
+
20
+ class ProjectImporter:
21
+ """Main class for importing projects from various sources"""
22
+
23
+ def __init__(self, hf_token: Optional[str] = None):
24
+ """
25
+ Initialize the ProjectImporter.
26
+
27
+ Args:
28
+ hf_token: Optional HuggingFace token for authenticated requests
29
+ """
30
+ self.hf_token = hf_token or os.environ.get("HF_TOKEN")
31
+ self.api = HfApi(token=self.hf_token)
32
+
33
+ def import_from_url(self, url: str) -> Dict[str, any]:
34
+ """
35
+ Import a project from any supported URL.
36
+
37
+ Args:
38
+ url: URL to import from (HF Space, HF Model, or GitHub)
39
+
40
+ Returns:
41
+ Dictionary containing:
42
+ - status: Success/error message
43
+ - code: Extracted code content
44
+ - language: Detected language/framework
45
+ - url: Original URL
46
+ - metadata: Additional metadata
47
+ """
48
+ if not url or not url.strip():
49
+ return {
50
+ "status": "error",
51
+ "message": "Please provide a valid URL",
52
+ "code": "",
53
+ "language": "unknown",
54
+ "url": url,
55
+ "metadata": {}
56
+ }
57
+
58
+ # Parse URL to determine source type
59
+ kind, meta = self._parse_url(url)
60
+
61
+ if kind == "hf_space":
62
+ return self.import_space(meta["username"], meta["project"])
63
+ elif kind == "hf_model":
64
+ return self.import_model(meta["repo_id"])
65
+ elif kind == "github":
66
+ return self.import_github_repo(meta["owner"], meta["repo"])
67
+ else:
68
+ return {
69
+ "status": "error",
70
+ "message": "Unsupported URL format. Supported: HF Spaces, HF Models, GitHub repos",
71
+ "code": "",
72
+ "language": "unknown",
73
+ "url": url,
74
+ "metadata": {}
75
+ }
76
+
77
+ def import_space(self, username: str, project_name: str) -> Dict[str, any]:
78
+ """
79
+ Import a HuggingFace Space.
80
+
81
+ Args:
82
+ username: HuggingFace username
83
+ project_name: Space name
84
+
85
+ Returns:
86
+ Dictionary with imported project data
87
+ """
88
+ try:
89
+ space_id = f"{username}/{project_name}"
90
+ space_info = self.api.space_info(space_id)
91
+
92
+ # Detect if this is a transformers.js space
93
+ if space_info.sdk == "static" and self._is_transformers_js_space(username, project_name):
94
+ code, files = self._fetch_transformers_js_files(username, project_name)
95
+ return {
96
+ "status": "success",
97
+ "message": f"Successfully imported transformers.js space: {space_id}",
98
+ "code": code,
99
+ "language": "transformers.js",
100
+ "url": f"https://huggingface.co/spaces/{space_id}",
101
+ "metadata": {
102
+ "sdk": "static",
103
+ "type": "transformers.js",
104
+ "files": files
105
+ }
106
+ }
107
+
108
+ # Handle multi-file spaces
109
+ files = self._fetch_all_space_files(username, project_name, space_info.sdk)
110
+
111
+ if files:
112
+ code = self._format_multi_file_content(files, username, project_name, space_info.sdk)
113
+ language = self._detect_language_from_sdk(space_info.sdk)
114
+
115
+ return {
116
+ "status": "success",
117
+ "message": f"Successfully imported space: {space_id}",
118
+ "code": code,
119
+ "language": language,
120
+ "url": f"https://huggingface.co/spaces/{space_id}",
121
+ "metadata": {
122
+ "sdk": space_info.sdk,
123
+ "files": list(files.keys())
124
+ }
125
+ }
126
+ else:
127
+ # Fallback to single file
128
+ main_file, content = self._fetch_main_file(username, project_name, space_info.sdk)
129
+
130
+ if content:
131
+ return {
132
+ "status": "success",
133
+ "message": f"Successfully imported space: {space_id}",
134
+ "code": content,
135
+ "language": self._detect_language_from_sdk(space_info.sdk),
136
+ "url": f"https://huggingface.co/spaces/{space_id}",
137
+ "metadata": {
138
+ "sdk": space_info.sdk,
139
+ "main_file": main_file
140
+ }
141
+ }
142
+ else:
143
+ return {
144
+ "status": "error",
145
+ "message": f"Could not find main file in space {space_id}",
146
+ "code": "",
147
+ "language": "unknown",
148
+ "url": f"https://huggingface.co/spaces/{space_id}",
149
+ "metadata": {"sdk": space_info.sdk}
150
+ }
151
+
152
+ except Exception as e:
153
+ return {
154
+ "status": "error",
155
+ "message": f"Failed to import space: {str(e)}",
156
+ "code": "",
157
+ "language": "unknown",
158
+ "url": f"https://huggingface.co/spaces/{username}/{project_name}",
159
+ "metadata": {}
160
+ }
161
+
162
+ def import_model(self, model_id: str, prefer_local: bool = False) -> Dict[str, any]:
163
+ """
164
+ Import a HuggingFace Model.
165
+
166
+ Args:
167
+ model_id: HuggingFace model ID (e.g., "meta-llama/Llama-2-7b")
168
+ prefer_local: If True, prefer local inference code over serverless
169
+
170
+ Returns:
171
+ Dictionary with imported model data
172
+ """
173
+ try:
174
+ # Get model info
175
+ model_info = self.api.model_info(model_id)
176
+ pipeline_tag = getattr(model_info, "pipeline_tag", None)
177
+
178
+ # Try to get inference provider code
179
+ inference_code = self._generate_inference_code(model_id, pipeline_tag)
180
+
181
+ # Try to get transformers/diffusers code from README
182
+ readme_code = None
183
+ try:
184
+ readme = self._fetch_hf_model_readme(model_id)
185
+ if readme:
186
+ _, readme_code = self._extract_code_from_markdown(readme)
187
+ except:
188
+ pass
189
+
190
+ # Determine which code to return
191
+ if inference_code and readme_code:
192
+ code = readme_code if prefer_local else inference_code
193
+ code_type = "local" if prefer_local else "inference"
194
+
195
+ return {
196
+ "status": "success",
197
+ "message": f"Successfully imported model: {model_id} ({code_type} code)",
198
+ "code": code,
199
+ "language": "python",
200
+ "url": f"https://huggingface.co/{model_id}",
201
+ "metadata": {
202
+ "pipeline_tag": pipeline_tag,
203
+ "code_type": code_type,
204
+ "has_alternatives": True,
205
+ "inference_code": inference_code,
206
+ "local_code": readme_code
207
+ }
208
+ }
209
+ elif inference_code:
210
+ return {
211
+ "status": "success",
212
+ "message": f"Successfully imported model: {model_id} (inference code)",
213
+ "code": inference_code,
214
+ "language": "python",
215
+ "url": f"https://huggingface.co/{model_id}",
216
+ "metadata": {
217
+ "pipeline_tag": pipeline_tag,
218
+ "code_type": "inference"
219
+ }
220
+ }
221
+ elif readme_code:
222
+ return {
223
+ "status": "success",
224
+ "message": f"Successfully imported model: {model_id} (local code)",
225
+ "code": readme_code,
226
+ "language": "python",
227
+ "url": f"https://huggingface.co/{model_id}",
228
+ "metadata": {
229
+ "pipeline_tag": pipeline_tag,
230
+ "code_type": "local"
231
+ }
232
+ }
233
+ else:
234
+ return {
235
+ "status": "error",
236
+ "message": f"No code found for model: {model_id}",
237
+ "code": "",
238
+ "language": "python",
239
+ "url": f"https://huggingface.co/{model_id}",
240
+ "metadata": {"pipeline_tag": pipeline_tag}
241
+ }
242
+
243
+ except Exception as e:
244
+ return {
245
+ "status": "error",
246
+ "message": f"Failed to import model: {str(e)}",
247
+ "code": "",
248
+ "language": "python",
249
+ "url": f"https://huggingface.co/{model_id}",
250
+ "metadata": {}
251
+ }
252
+
253
+ def import_github_repo(self, owner: str, repo: str) -> Dict[str, any]:
254
+ """
255
+ Import a GitHub repository.
256
+
257
+ Args:
258
+ owner: GitHub username/organization
259
+ repo: Repository name
260
+
261
+ Returns:
262
+ Dictionary with imported repository data
263
+ """
264
+ try:
265
+ readme = self._fetch_github_readme(owner, repo)
266
+
267
+ if not readme:
268
+ return {
269
+ "status": "error",
270
+ "message": f"Could not fetch README from {owner}/{repo}",
271
+ "code": "",
272
+ "language": "python",
273
+ "url": f"https://github.com/{owner}/{repo}",
274
+ "metadata": {}
275
+ }
276
+
277
+ lang, code = self._extract_code_from_markdown(readme)
278
+
279
+ if code:
280
+ return {
281
+ "status": "success",
282
+ "message": f"Successfully imported code from {owner}/{repo}",
283
+ "code": code,
284
+ "language": lang or "python",
285
+ "url": f"https://github.com/{owner}/{repo}",
286
+ "metadata": {
287
+ "source": "github",
288
+ "detected_language": lang
289
+ }
290
+ }
291
+ else:
292
+ return {
293
+ "status": "error",
294
+ "message": f"No relevant code found in README of {owner}/{repo}",
295
+ "code": "",
296
+ "language": "python",
297
+ "url": f"https://github.com/{owner}/{repo}",
298
+ "metadata": {}
299
+ }
300
+
301
+ except Exception as e:
302
+ return {
303
+ "status": "error",
304
+ "message": f"Failed to import repository: {str(e)}",
305
+ "code": "",
306
+ "language": "python",
307
+ "url": f"https://github.com/{owner}/{repo}",
308
+ "metadata": {}
309
+ }
310
+
311
+ # ==================== Private Helper Methods ====================
312
+
313
+ def _parse_url(self, url: str) -> Tuple[str, Optional[Dict]]:
314
+ """Parse URL and detect source type"""
315
+ try:
316
+ parsed = urlparse(url.strip())
317
+ netloc = (parsed.netloc or "").lower()
318
+ path = (parsed.path or "").strip("/")
319
+
320
+ # HuggingFace Spaces
321
+ if ("huggingface.co" in netloc or "hf.co" in netloc) and path.startswith("spaces/"):
322
+ parts = path.split("/")
323
+ if len(parts) >= 3:
324
+ return "hf_space", {"username": parts[1], "project": parts[2]}
325
+
326
+ # HuggingFace Model
327
+ if ("huggingface.co" in netloc or "hf.co" in netloc) and not path.startswith(("spaces/", "datasets/")):
328
+ parts = path.split("/")
329
+ if len(parts) >= 2:
330
+ return "hf_model", {"repo_id": f"{parts[0]}/{parts[1]}"}
331
+
332
+ # GitHub Repository
333
+ if "github.com" in netloc:
334
+ parts = path.split("/")
335
+ if len(parts) >= 2:
336
+ return "github", {"owner": parts[0], "repo": parts[1]}
337
+
338
+ except Exception:
339
+ pass
340
+
341
+ return "unknown", None
342
+
343
+ def _is_transformers_js_space(self, username: str, project_name: str) -> bool:
344
+ """Check if space is a transformers.js app"""
345
+ try:
346
+ files = list_repo_files(
347
+ repo_id=f"{username}/{project_name}",
348
+ repo_type="space",
349
+ token=self.hf_token
350
+ )
351
+
352
+ has_html = any('index.html' in f for f in files)
353
+ has_js = any('index.js' in f for f in files)
354
+ has_css = any('style.css' in f for f in files)
355
+
356
+ return has_html and has_js and has_css
357
+ except:
358
+ return False
359
+
360
+ def _fetch_transformers_js_files(self, username: str, project_name: str) -> Tuple[str, Dict]:
361
+ """Fetch transformers.js files and combine them"""
362
+ files = {}
363
+ file_names = ['index.html', 'index.js', 'style.css']
364
+
365
+ for file_name in file_names:
366
+ try:
367
+ content_path = self.api.hf_hub_download(
368
+ repo_id=f"{username}/{project_name}",
369
+ filename=file_name,
370
+ repo_type="space"
371
+ )
372
+ with open(content_path, 'r', encoding='utf-8') as f:
373
+ files[file_name] = f.read()
374
+ except:
375
+ files[file_name] = ""
376
+
377
+ # Combine files
378
+ combined = f"""=== index.html ===
379
+ {files.get('index.html', '')}
380
+
381
+ === index.js ===
382
+ {files.get('index.js', '')}
383
+
384
+ === style.css ===
385
+ {files.get('style.css', '')}"""
386
+
387
+ return combined, files
388
+
389
+ def _fetch_all_space_files(self, username: str, project_name: str, sdk: str) -> Optional[Dict[str, str]]:
390
+ """Fetch all relevant files from a space"""
391
+ try:
392
+ space_id = f"{username}/{project_name}"
393
+ files = list_repo_files(repo_id=space_id, repo_type="space", token=self.hf_token)
394
+
395
+ # Define file extensions to include
396
+ include_extensions = {
397
+ '.py', '.js', '.html', '.css', '.json', '.txt', '.yml', '.yaml',
398
+ '.toml', '.cfg', '.ini', '.sh', '.md'
399
+ }
400
+
401
+ # Filter files
402
+ relevant_files = [
403
+ f for f in files
404
+ if any(f.endswith(ext) for ext in include_extensions)
405
+ and not f.startswith('.')
406
+ and not f.startswith('__pycache__')
407
+ ]
408
+
409
+ # Limit number of files
410
+ if len(relevant_files) > 50:
411
+ relevant_files = relevant_files[:50]
412
+
413
+ # Fetch file contents
414
+ file_contents = {}
415
+ for file in relevant_files:
416
+ try:
417
+ file_path = self.api.hf_hub_download(
418
+ repo_id=space_id,
419
+ filename=file,
420
+ repo_type="space"
421
+ )
422
+ with open(file_path, 'r', encoding='utf-8') as f:
423
+ file_contents[file] = f.read()
424
+ except:
425
+ continue
426
+
427
+ return file_contents if file_contents else None
428
+
429
+ except:
430
+ return None
431
+
432
+ def _format_multi_file_content(self, files: Dict[str, str], username: str, project_name: str, sdk: str) -> str:
433
+ """Format multi-file content"""
434
+ header = f"""IMPORTED PROJECT FROM HUGGING FACE SPACE
435
+ ==============================================
436
+
437
+ Space: {username}/{project_name}
438
+ SDK: {sdk}
439
+ Files: {len(files)}
440
+
441
+ """
442
+
443
+ file_sections = []
444
+ for filename, content in files.items():
445
+ file_sections.append(f"=== {filename} ===\n{content}")
446
+
447
+ return header + "\n\n".join(file_sections)
448
+
449
+ def _fetch_main_file(self, username: str, project_name: str, sdk: str) -> Tuple[Optional[str], Optional[str]]:
450
+ """Fetch main file from space"""
451
+ file_patterns = self._get_file_patterns_for_sdk(sdk)
452
+
453
+ for file_pattern in file_patterns:
454
+ try:
455
+ content_path = self.api.hf_hub_download(
456
+ repo_id=f"{username}/{project_name}",
457
+ filename=file_pattern,
458
+ repo_type="space"
459
+ )
460
+ with open(content_path, 'r', encoding='utf-8') as f:
461
+ return file_pattern, f.read()
462
+ except:
463
+ continue
464
+
465
+ return None, None
466
+
467
+ def _get_file_patterns_for_sdk(self, sdk: str) -> List[str]:
468
+ """Get file patterns to try based on SDK"""
469
+ patterns = {
470
+ "static": ["index.html"],
471
+ "gradio": ["app.py", "main.py", "gradio_app.py"],
472
+ "streamlit": [
473
+ "streamlit_app.py", "src/streamlit_app.py",
474
+ "app.py", "src/app.py",
475
+ "main.py", "src/main.py",
476
+ "Home.py", "src/Home.py"
477
+ ]
478
+ }
479
+
480
+ return patterns.get(sdk, ["app.py", "main.py", "index.html"])
481
+
482
+ def _detect_language_from_sdk(self, sdk: str) -> str:
483
+ """Detect language/framework from SDK"""
484
+ sdk_map = {
485
+ "gradio": "gradio",
486
+ "streamlit": "streamlit",
487
+ "static": "html",
488
+ "docker": "docker"
489
+ }
490
+ return sdk_map.get(sdk, "python")
491
+
492
+ def _generate_inference_code(self, model_id: str, pipeline_tag: Optional[str]) -> Optional[str]:
493
+ """Generate inference provider code based on pipeline tag"""
494
+ if not pipeline_tag:
495
+ return None
496
+
497
+ templates = {
498
+ "text-generation": f'''import os
499
+ from huggingface_hub import InferenceClient
500
+
501
+ client = InferenceClient(api_key=os.environ["HF_TOKEN"])
502
+
503
+ completion = client.chat.completions.create(
504
+ model="{model_id}",
505
+ messages=[
506
+ {{"role": "user", "content": "What is the capital of France?"}}
507
+ ],
508
+ )
509
+
510
+ print(completion.choices[0].message)''',
511
+
512
+ "text-to-image": f'''import os
513
+ from huggingface_hub import InferenceClient
514
+
515
+ client = InferenceClient(api_key=os.environ["HF_TOKEN"])
516
+
517
+ # output is a PIL.Image object
518
+ image = client.text_to_image(
519
+ "Astronaut riding a horse",
520
+ model="{model_id}",
521
+ )
522
+
523
+ # Save the image
524
+ image.save("output.png")''',
525
+
526
+ "automatic-speech-recognition": f'''import os
527
+ from huggingface_hub import InferenceClient
528
+
529
+ client = InferenceClient(api_key=os.environ["HF_TOKEN"])
530
+
531
+ with open("audio.mp3", "rb") as f:
532
+ audio_data = f.read()
533
+
534
+ result = client.automatic_speech_recognition(
535
+ audio_data,
536
+ model="{model_id}",
537
+ )
538
+
539
+ print(result)''',
540
+
541
+ "text-to-speech": f'''import os
542
+ from huggingface_hub import InferenceClient
543
+
544
+ client = InferenceClient(api_key=os.environ["HF_TOKEN"])
545
+
546
+ audio = client.text_to_speech(
547
+ "Hello world",
548
+ model="{model_id}",
549
+ )
550
+
551
+ # Save the audio
552
+ with open("output.mp3", "wb") as f:
553
+ f.write(audio)''',
554
+ }
555
+
556
+ return templates.get(pipeline_tag)
557
+
558
+ def _fetch_hf_model_readme(self, repo_id: str) -> Optional[str]:
559
+ """Fetch README from HuggingFace model"""
560
+ try:
561
+ local_path = self.api.hf_hub_download(
562
+ repo_id=repo_id,
563
+ filename="README.md",
564
+ repo_type="model"
565
+ )
566
+ with open(local_path, "r", encoding="utf-8") as f:
567
+ return f.read()
568
+ except:
569
+ return None
570
+
571
+ def _fetch_github_readme(self, owner: str, repo: str) -> Optional[str]:
572
+ """Fetch README from GitHub repository"""
573
+ urls = [
574
+ f"https://raw.githubusercontent.com/{owner}/{repo}/HEAD/README.md",
575
+ f"https://raw.githubusercontent.com/{owner}/{repo}/main/README.md",
576
+ f"https://raw.githubusercontent.com/{owner}/{repo}/master/README.md",
577
+ ]
578
+
579
+ for url in urls:
580
+ try:
581
+ resp = requests.get(url, timeout=10)
582
+ if resp.status_code == 200 and resp.text:
583
+ return resp.text
584
+ except:
585
+ continue
586
+
587
+ return None
588
+
589
+ def _extract_code_from_markdown(self, markdown: str) -> Tuple[Optional[str], Optional[str]]:
590
+ """Extract relevant code from markdown"""
591
+ if not markdown:
592
+ return None, None
593
+
594
+ # Find all code blocks
595
+ code_blocks = []
596
+ for match in re.finditer(r"```([\w+-]+)?\s*\n([\s\S]*?)```", markdown, re.IGNORECASE):
597
+ lang = (match.group(1) or "").lower()
598
+ code = match.group(2) or ""
599
+ code_blocks.append((lang, code.strip()))
600
+
601
+ # Score blocks based on relevance
602
+ def score_block(code: str) -> int:
603
+ score = 0
604
+ keywords = [
605
+ "from transformers", "import transformers", "pipeline(",
606
+ "AutoModel", "AutoTokenizer", "text-generation",
607
+ "from diffusers", "import diffusers", "DiffusionPipeline",
608
+ "StableDiffusion", "from gradio", "import gradio"
609
+ ]
610
+ for kw in keywords:
611
+ if kw in code:
612
+ score += 1
613
+ score += min(len(code) // 200, 5)
614
+ return score
615
+
616
+ # Filter and sort
617
+ relevant = [
618
+ cb for cb in code_blocks
619
+ if any(kw in cb[1] for kw in ["transformers", "diffusers", "pipeline(", "gradio", "import"])
620
+ ]
621
+
622
+ if relevant:
623
+ sorted_blocks = sorted(relevant, key=lambda x: score_block(x[1]), reverse=True)
624
+ return sorted_blocks[0][0] or "python", sorted_blocks[0][1]
625
+
626
+ return None, None
627
+
628
+
629
+ # ==================== CLI Interface ====================
630
+
631
+ def main():
632
+ """CLI interface for project importer"""
633
+ import argparse
634
+
635
+ parser = argparse.ArgumentParser(
636
+ description="Import projects from HuggingFace Spaces, Models, or GitHub repos"
637
+ )
638
+ parser.add_argument("url", help="URL to import from")
639
+ parser.add_argument("-o", "--output", help="Output file to save code", default=None)
640
+ parser.add_argument("--prefer-local", action="store_true",
641
+ help="Prefer local inference code over serverless (for models)")
642
+ parser.add_argument("--token", help="HuggingFace token", default=None)
643
+
644
+ args = parser.parse_args()
645
+
646
+ # Initialize importer
647
+ importer = ProjectImporter(hf_token=args.token)
648
+
649
+ # Import project
650
+ print(f"Importing from: {args.url}")
651
+ print("-" * 60)
652
+
653
+ result = importer.import_from_url(args.url)
654
+
655
+ # Print results
656
+ print(f"Status: {result['status']}")
657
+ print(f"Message: {result['message']}")
658
+ print(f"Language: {result['language']}")
659
+ print(f"URL: {result['url']}")
660
+
661
+ if result.get('metadata'):
662
+ print(f"Metadata: {result['metadata']}")
663
+
664
+ print("-" * 60)
665
+
666
+ if result['code']:
667
+ if args.output:
668
+ with open(args.output, 'w', encoding='utf-8') as f:
669
+ f.write(result['code'])
670
+ print(f"Code saved to: {args.output}")
671
+ else:
672
+ print("Code:")
673
+ print("=" * 60)
674
+ print(result['code'])
675
+ print("=" * 60)
676
+ else:
677
+ print("No code to display")
678
+
679
+
680
+ if __name__ == "__main__":
681
+ main()
682
+