Omarelrayes commited on
Commit
b6a3870
·
verified ·
1 Parent(s): 7e17f68

Create hf_artifact_repo.py

Browse files
Files changed (1) hide show
  1. hf_artifact_repo.py +189 -0
hf_artifact_repo.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # hf_artifact_repo.py
2
+ import os
3
+ import tempfile
4
+ import shutil
5
+ from pathlib import Path
6
+ from typing import List
7
+ from huggingface_hub import HfApi, hf_hub_download, list_repo_files
8
+ from mlflow.store.artifact.artifact_repo import ArtifactRepository
9
+ from mlflow.entities import FileInfo
10
+
11
+
12
+ class HuggingFaceArtifactRepository(ArtifactRepository):
13
+ """
14
+ MLflow Artifact Repository that uses HuggingFace Hub as storage
15
+ """
16
+
17
+ def __init__(self, artifact_uri, hf_token=None, **kwargs):
18
+ super().__init__(artifact_uri)
19
+ self.hf_token = hf_token or os.getenv("HF_TOKEN")
20
+ self.api = HfApi()
21
+
22
+ if artifact_uri.startswith("hf://"):
23
+ parts = artifact_uri.replace("hf://", "").split("/")
24
+ raw_type = parts[0] if len(parts) > 0 else "models"
25
+ type_mapping = {
26
+ "models": "model",
27
+ "datasets": "dataset",
28
+ "spaces": "space",
29
+ "model": "model",
30
+ "dataset": "dataset",
31
+ "space": "space"
32
+ }
33
+ self.repo_type = type_mapping.get(raw_type, None)
34
+ self.repo_id = f"{parts[1]}/{parts[2]}" if len(parts) > 2 else ""
35
+ self.base_path = "/".join(parts[3:]) if len(parts) > 3 else ""
36
+ else:
37
+ raise ValueError(f"Invalid HF URI: {artifact_uri}")
38
+
39
+ print(f"🔧 Initialized HF Artifact Repository:")
40
+ print(f" - Repo: {self.repo_id}")
41
+ print(f" - Type: {self.repo_type}")
42
+ print(f" - Base Path: {self.base_path}")
43
+
44
+ def _get_full_path_in_repo(self, artifact_path):
45
+ if self.base_path and artifact_path:
46
+ return f"{self.base_path}/{artifact_path}".strip("/")
47
+ elif self.base_path:
48
+ return self.base_path.strip("/")
49
+ elif artifact_path:
50
+ return artifact_path.strip("/")
51
+ return ""
52
+
53
+ def log_artifact(self, local_file, artifact_path=None):
54
+ artifact_path = artifact_path or ""
55
+ full_path = self._get_full_path_in_repo(artifact_path)
56
+ file_name = os.path.basename(local_file)
57
+ path_in_repo = f"{full_path}/{file_name}".strip("/") if full_path else file_name
58
+
59
+ self.api.upload_file(
60
+ path_or_fileobj=local_file,
61
+ path_in_repo=path_in_repo,
62
+ repo_id=self.repo_id,
63
+ repo_type=self.repo_type,
64
+ token=self.hf_token
65
+ )
66
+
67
+ def log_artifacts(self, local_dir, artifact_path=None):
68
+ artifact_path = artifact_path or ""
69
+ full_path = self._get_full_path_in_repo(artifact_path)
70
+
71
+ for root, dirs, files in os.walk(local_dir):
72
+ for file in files:
73
+ local_file_path = os.path.join(root, file)
74
+ relative_path = os.path.relpath(local_file_path, local_dir)
75
+
76
+ if full_path:
77
+ path_in_repo = f"{full_path}/{relative_path}".replace("\\", "/")
78
+ else:
79
+ path_in_repo = relative_path.replace("\\", "/")
80
+
81
+ self.api.upload_file(
82
+ path_or_fileobj=local_file_path,
83
+ path_in_repo=path_in_repo,
84
+ repo_id=self.repo_id,
85
+ repo_type=self.repo_type,
86
+ token=self.hf_token
87
+ )
88
+
89
+ def list_artifacts(self, path=None):
90
+ try:
91
+ full_path = self._get_full_path_in_repo(path or "")
92
+ all_files = list_repo_files(
93
+ repo_id=self.repo_id,
94
+ repo_type=self.repo_type,
95
+ token=self.hf_token
96
+ )
97
+
98
+ artifacts = []
99
+ for file_path in all_files:
100
+ if full_path:
101
+ if file_path.startswith(full_path + "/") or file_path == full_path:
102
+ relative_path = file_path[len(full_path):].lstrip("/")
103
+ if relative_path:
104
+ artifacts.append(FileInfo(relative_path, False, 0))
105
+ else:
106
+ artifacts.append(FileInfo(file_path, False, 0))
107
+
108
+ return artifacts
109
+ except Exception as e:
110
+ print(f"⚠️ Error listing artifacts: {e}")
111
+ return []
112
+
113
+ def _download_file(self, remote_file_path, local_path):
114
+ full_path = self._get_full_path_in_repo(remote_file_path)
115
+
116
+ downloaded_path = hf_hub_download(
117
+ repo_id=self.repo_id,
118
+ filename=full_path,
119
+ repo_type=self.repo_type,
120
+ token=self.hf_token,
121
+ local_dir=os.path.dirname(local_path)
122
+ )
123
+
124
+ if downloaded_path != local_path:
125
+ shutil.copy2(downloaded_path, local_path)
126
+
127
+ def download_artifacts(self, artifact_path, dst_path=None):
128
+ if dst_path is None:
129
+ dst_path = tempfile.mkdtemp()
130
+
131
+ artifact_path = artifact_path or ""
132
+ full_path = self._get_full_path_in_repo(artifact_path)
133
+
134
+ try:
135
+ all_files = list_repo_files(
136
+ repo_id=self.repo_id,
137
+ repo_type=self.repo_type,
138
+ token=self.hf_token
139
+ )
140
+
141
+ for file_path in all_files:
142
+ if full_path:
143
+ if file_path.startswith(full_path + "/") or file_path == full_path:
144
+ relative_path = file_path[len(full_path):].lstrip("/")
145
+ if relative_path:
146
+ local_file_path = os.path.join(dst_path, relative_path)
147
+ os.makedirs(os.path.dirname(local_file_path), exist_ok=True)
148
+
149
+ hf_hub_download(
150
+ repo_id=self.repo_id,
151
+ filename=file_path,
152
+ repo_type=self.repo_type,
153
+ token=self.hf_token,
154
+ local_dir=dst_path
155
+ )
156
+ else:
157
+ local_file_path = os.path.join(dst_path, file_path)
158
+ os.makedirs(os.path.dirname(local_file_path), exist_ok=True)
159
+
160
+ hf_hub_download(
161
+ repo_id=self.repo_id,
162
+ filename=file_path,
163
+ repo_type=self.repo_type,
164
+ token=self.hf_token,
165
+ local_dir=dst_path
166
+ )
167
+
168
+ return dst_path
169
+ except Exception as e:
170
+ print(f"❌ Error downloading artifacts: {e}")
171
+ raise
172
+
173
+
174
+ # ======================
175
+ # Auto-register when imported
176
+ # ======================
177
+ def _register_plugin():
178
+ try:
179
+ from mlflow.store.artifact import artifact_repository_registry
180
+ artifact_repository_registry._artifact_repository_registry.register(
181
+ "hf", HuggingFaceArtifactRepository
182
+ )
183
+ print("✅ HuggingFace Artifact Repository registered!")
184
+ return True
185
+ except Exception as e:
186
+ print(f"⚠️ Could not auto-register: {e}")
187
+ return False
188
+
189
+ _register_plugin()