vectorplasticity commited on
Commit
22878e2
·
verified ·
1 Parent(s): 810b6bc

Add HuggingFace Hub service

Browse files
Files changed (1) hide show
  1. app/services/hub_service.py +267 -0
app/services/hub_service.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace Hub Service - Handle model pushing and repository management
3
+ """
4
+
5
+ import logging
6
+ from typing import Dict, Any, Optional, List
7
+ from huggingface_hub import (
8
+ HfApi,
9
+ create_repo,
10
+ upload_folder,
11
+ upload_file,
12
+ delete_repo,
13
+ repo_info,
14
+ list_repo_files,
15
+ )
16
+ import os
17
+ import tempfile
18
+ import json
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class HuggingFaceHubService:
24
+ """Service for interacting with HuggingFace Hub."""
25
+
26
+ def __init__(self, token: Optional[str] = None):
27
+ self.token = token
28
+ self.api = HfApi(token=token)
29
+
30
+ def create_model_repo(
31
+ self,
32
+ repo_name: str,
33
+ private: bool = False,
34
+ exist_ok: bool = True,
35
+ ) -> Dict[str, Any]:
36
+ """Create a new model repository."""
37
+ try:
38
+ url = create_repo(
39
+ repo_id=repo_name,
40
+ token=self.token,
41
+ private=private,
42
+ exist_ok=exist_ok,
43
+ )
44
+ return {
45
+ "success": True,
46
+ "repo_id": repo_name,
47
+ "url": url,
48
+ "private": private,
49
+ }
50
+ except Exception as e:
51
+ logger.error(f"Error creating repo {repo_name}: {e}")
52
+ return {
53
+ "success": False,
54
+ "error": str(e),
55
+ }
56
+
57
+ def push_model(
58
+ self,
59
+ model_path: str,
60
+ repo_id: str,
61
+ commit_message: str = "Push model via Universal Trainer",
62
+ commit_description: Optional[str] = None,
63
+ private: bool = False,
64
+ ) -> Dict[str, Any]:
65
+ """Push a model to the HuggingFace Hub."""
66
+ try:
67
+ # Create repo if it doesn't exist
68
+ self.create_model_repo(repo_id, private=private)
69
+
70
+ # Upload the model folder
71
+ result = upload_folder(
72
+ folder_path=model_path,
73
+ repo_id=repo_id,
74
+ token=self.token,
75
+ commit_message=commit_message,
76
+ commit_description=commit_description,
77
+ )
78
+
79
+ return {
80
+ "success": True,
81
+ "repo_id": repo_id,
82
+ "url": f"https://huggingface.co/{repo_id}",
83
+ "commit_url": result.commit_url if hasattr(result, 'commit_url') else None,
84
+ }
85
+
86
+ except Exception as e:
87
+ logger.error(f"Error pushing model to {repo_id}: {e}")
88
+ return {
89
+ "success": False,
90
+ "error": str(e),
91
+ }
92
+
93
+ def push_training_artifacts(
94
+ self,
95
+ output_dir: str,
96
+ repo_id: str,
97
+ model_name: str,
98
+ task_type: str,
99
+ dataset_name: str,
100
+ training_config: Dict[str, Any],
101
+ metrics: Dict[str, float],
102
+ ) -> Dict[str, Any]:
103
+ """Push complete training artifacts to Hub."""
104
+ try:
105
+ # Create repo
106
+ create_result = self.create_model_repo(repo_id)
107
+
108
+ # Create/update model card
109
+ model_card = self._create_model_card(
110
+ model_name=model_name,
111
+ task_type=task_type,
112
+ dataset_name=dataset_name,
113
+ training_config=training_config,
114
+ metrics=metrics,
115
+ )
116
+
117
+ # Write model card
118
+ readme_path = os.path.join(output_dir, "README.md")
119
+ with open(readme_path, "w") as f:
120
+ f.write(model_card)
121
+
122
+ # Upload everything
123
+ result = upload_folder(
124
+ folder_path=output_dir,
125
+ repo_id=repo_id,
126
+ token=self.token,
127
+ commit_message=f"Upload fine-tuned {model_name}",
128
+ )
129
+
130
+ return {
131
+ "success": True,
132
+ "repo_id": repo_id,
133
+ "url": f"https://huggingface.co/{repo_id}",
134
+ }
135
+
136
+ except Exception as e:
137
+ logger.error(f"Error pushing artifacts: {e}")
138
+ return {
139
+ "success": False,
140
+ "error": str(e),
141
+ }
142
+
143
+ def delete_model(self, repo_id: str) -> Dict[str, Any]:
144
+ """Delete a model repository."""
145
+ try:
146
+ delete_repo(repo_id=repo_id, token=self.token)
147
+ return {
148
+ "success": True,
149
+ "message": f"Deleted {repo_id}",
150
+ }
151
+ except Exception as e:
152
+ logger.error(f"Error deleting repo {repo_id}: {e}")
153
+ return {
154
+ "success": False,
155
+ "error": str(e),
156
+ }
157
+
158
+ def get_repo_info(self, repo_id: str) -> Dict[str, Any]:
159
+ """Get repository information."""
160
+ try:
161
+ info = repo_info(repo_id=repo_id, token=self.token)
162
+ return {
163
+ "success": True,
164
+ "repo_id": info.id,
165
+ "author": info.author,
166
+ "private": info.private,
167
+ "downloads": getattr(info, "downloads", 0),
168
+ "likes": getattr(info, "likes", 0),
169
+ "tags": info.tags or [],
170
+ "sha": info.sha,
171
+ }
172
+ except Exception as e:
173
+ return {
174
+ "success": False,
175
+ "error": str(e),
176
+ }
177
+
178
+ def list_model_files(self, repo_id: str) -> Dict[str, Any]:
179
+ """List files in a model repository."""
180
+ try:
181
+ files = list_repo_files(repo_id=repo_id, token=self.token)
182
+ return {
183
+ "success": True,
184
+ "files": files,
185
+ }
186
+ except Exception as e:
187
+ return {
188
+ "success": False,
189
+ "error": str(e),
190
+ }
191
+
192
+ def _create_model_card(
193
+ self,
194
+ model_name: str,
195
+ task_type: str,
196
+ dataset_name: str,
197
+ training_config: Dict[str, Any],
198
+ metrics: Dict[str, float],
199
+ ) -> str:
200
+ """Create a model card README."""
201
+
202
+ metrics_str = "\n".join([f"- **{k}:** {v:.4f}" if isinstance(v, float) else f"- **{k}:** {v}" for k, v in metrics.items()])
203
+
204
+ return f"""---
205
+ license: apache-2.0
206
+ base_model: {model_name}
207
+ tags:
208
+ - {task_type}
209
+ - fine-tuned
210
+ - universal-trainer
211
+ ---
212
+
213
+ # {model_name} Fine-tuned
214
+
215
+ This model is a fine-tuned version of [{model_name}](https://huggingface.co/{model_name}) on the [{dataset_name}](https://huggingface.co/datasets/{dataset_name}) dataset.
216
+
217
+ ## Model Details
218
+
219
+ - **Base Model:** {model_name}
220
+ - **Task Type:** {task_type}
221
+ - **Dataset:** {dataset_name}
222
+
223
+ ## Training Configuration
224
+
225
+ | Parameter | Value |
226
+ |-----------|-------|
227
+ | Learning Rate | {training_config.get('learning_rate', 'N/A')} |
228
+ | Batch Size | {training_config.get('batch_size', 'N/A')} |
229
+ | Epochs | {training_config.get('epochs', 'N/A')} |
230
+ | Max Length | {training_config.get('max_length', 'N/A')} |
231
+ | PEFT | {training_config.get('use_peft', False)} |
232
+
233
+ ## Training Metrics
234
+
235
+ {metrics_str if metrics else 'No metrics available'}
236
+
237
+ ## Usage
238
+
239
+ ```python
240
+ from transformers import AutoModelForCausalLM, AutoTokenizer
241
+
242
+ model = AutoModelForCausalLM.from_pretrained("{model_name}")
243
+ tokenizer = AutoTokenizer.from_pretrained("{model_name}")
244
+ ```
245
+
246
+ ## Training Procedure
247
+
248
+ This model was trained using the [Universal Model Trainer](https://huggingface.co/spaces/vectorplasticity/universal-model-trainer).
249
+
250
+ ### Framework Versions
251
+
252
+ - Transformers: {__import__('transformers').__version__}
253
+ - PyTorch: {__import__('torch').__version__}
254
+
255
+ ## Limitations
256
+
257
+ This model inherits the limitations of its base model. Please refer to the original model card for details.
258
+
259
+ ## License
260
+
261
+ Please refer to the original model's license.
262
+ """
263
+
264
+
265
+ def get_hub_client(token: Optional[str] = None) -> HuggingFaceHubService:
266
+ """Get a HuggingFace Hub client."""
267
+ return HuggingFaceHubService(token=token)