xiaoshiai-hub 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xiaoshiai_hub/__init__.py CHANGED
@@ -6,7 +6,7 @@ A Python library for interacting with XiaoShi AI Hub repositories.
6
6
 
7
7
  from .client import HubClient, DEFAULT_BASE_URL
8
8
  from .download import (
9
- hf_hub_download,
9
+ moha_hub_download,
10
10
  snapshot_download,
11
11
  )
12
12
  from .exceptions import (
@@ -14,6 +14,7 @@ from .exceptions import (
14
14
  RepositoryNotFoundError,
15
15
  FileNotFoundError,
16
16
  AuthenticationError,
17
+ EncryptionError,
17
18
  )
18
19
  from .types import (
19
20
  Repository,
@@ -22,20 +23,61 @@ from .types import (
22
23
  Commit,
23
24
  )
24
25
 
25
- __version__ = "0.2.0"
26
+ # Upload functionality (requires GitPython)
27
+ try:
28
+ from .upload import (
29
+ upload_file,
30
+ upload_folder,
31
+ UploadError,
32
+ )
33
+ _upload_available = True
34
+ except ImportError:
35
+ _upload_available = False
36
+ upload_file = None
37
+ upload_folder = None
38
+ UploadError = None
39
+
40
+ # Encryption functionality
41
+ try:
42
+ from .encryption import (
43
+ EncryptionAlgorithm,
44
+ encrypt_file,
45
+ decrypt_file,
46
+ )
47
+ _encryption_available = True
48
+ except ImportError:
49
+ _encryption_available = False
50
+ EncryptionAlgorithm = None
51
+ encrypt_file = None
52
+ decrypt_file = None
53
+
54
+ __version__ = "0.1.0"
26
55
 
27
56
  __all__ = [
57
+ # Client
28
58
  "HubClient",
29
59
  "DEFAULT_BASE_URL",
30
- "hf_hub_download",
60
+ # Download
61
+ "moha_hub_download",
31
62
  "snapshot_download",
63
+ # Upload
64
+ "upload_file",
65
+ "upload_folder",
66
+ # Exceptions
32
67
  "HubException",
33
68
  "RepositoryNotFoundError",
34
69
  "FileNotFoundError",
35
70
  "AuthenticationError",
71
+ "EncryptionError",
72
+ "UploadError",
73
+ # Types
36
74
  "Repository",
37
75
  "Ref",
38
76
  "GitContent",
39
77
  "Commit",
78
+ # Encryption
79
+ "EncryptionAlgorithm",
80
+ "encrypt_file",
81
+ "decrypt_file",
40
82
  ]
41
83
 
xiaoshiai_hub/client.py CHANGED
@@ -3,10 +3,9 @@ XiaoShi AI Hub Client
3
3
  """
4
4
 
5
5
  import base64
6
- import json
7
6
  import os
8
7
  from typing import List, Optional
9
- from urllib.parse import urljoin
8
+
10
9
 
11
10
  import requests
12
11
 
@@ -20,13 +19,13 @@ from .exceptions import (
20
19
  HTTPError,
21
20
  RepositoryNotFoundError,
22
21
  )
23
- from .types import Repository, Ref, GitContent
22
+ from .types import EncryptionMetadata, Repository, Ref, GitContent
24
23
 
25
24
 
26
25
  # 默认基础 URL,可通过环境变量 MOHA_ENDPOINT 覆盖
27
26
  DEFAULT_BASE_URL = os.environ.get(
28
27
  "MOHA_ENDPOINT",
29
- "https://rune.develop.xiaoshiai.cn/api/moha"
28
+ "https://rune-api.develop.xiaoshiai.cn/moha"
30
29
  )
31
30
 
32
31
 
@@ -86,6 +85,60 @@ class HubClient:
86
85
  return response
87
86
  except requests.RequestException as e:
88
87
  raise HTTPError(f"Request failed: {str(e)}")
88
+
89
+ def get_moha_encryption(
90
+ self,
91
+ organization: str,
92
+ repo_type: str,
93
+ repo_name: str,
94
+ reference: str,
95
+ ) -> Optional[EncryptionMetadata]:
96
+ """
97
+ Get .moha_encryption file content from the repository.
98
+
99
+ Args:
100
+ organization: Organization name
101
+ repo_type: Repository type ("models" or "datasets")
102
+ repo_name: Repository name
103
+ reference: Branch/tag/commit reference
104
+
105
+ Returns:
106
+ Encryption metadata if the repository has encrypted files, None otherwise
107
+
108
+ Raises:
109
+ AuthenticationError: If authentication fails
110
+ RepositoryNotFoundError: If the repository is not found
111
+ HTTPError: If the request fails
112
+ """
113
+ url = f"{self.base_url}/v1/organizations/{organization}/{repo_type}/{repo_name}/encryption/{reference}"
114
+ response = self._make_request("GET", url)
115
+ if response.status_code == 204 or not response.content:
116
+ return None
117
+ try:
118
+ data = response.json()
119
+ except ValueError:
120
+ return None
121
+ if not data:
122
+ return None
123
+
124
+ from datetime import datetime
125
+ files = None
126
+ if 'files' in data and data['files']:
127
+ from .types import FileEncryptionMetadata
128
+ files = [
129
+ FileEncryptionMetadata(
130
+ path=f.get('path', ''),
131
+ algorithm=f.get('algorithm', ''),
132
+ encryptedHash=f.get('encryptedHash', ''),
133
+ encryptedSize=f.get('encryptedSize', 0),
134
+ )
135
+ for f in data['files']
136
+ ]
137
+ return EncryptionMetadata(
138
+ version=data.get('version', '1.0'),
139
+ createAt=datetime.fromisoformat(data['createAt'].replace('Z', '+00:00')) if 'createAt' in data else datetime.now(),
140
+ files=files,
141
+ )
89
142
 
90
143
  def get_repository_info(
91
144
  self,
@@ -95,25 +148,36 @@ class HubClient:
95
148
  ) -> Repository:
96
149
  """
97
150
  Get repository information.
98
-
151
+
99
152
  Args:
100
153
  organization: Organization name
101
154
  repo_type: Repository type ("models" or "datasets")
102
155
  repo_name: Repository name
103
-
156
+
104
157
  Returns:
105
158
  Repository information
106
159
  """
107
- url = f"{self.base_url}/organizations/{organization}/{repo_type}/{repo_name}"
160
+ url = f"{self.base_url}/v1/organizations/{organization}/{repo_type}/{repo_name}"
108
161
  response = self._make_request("GET", url)
109
162
  data = response.json()
110
-
163
+
164
+ # Parse annotations if present
165
+ annotations = {}
166
+ if 'annotations' in data and isinstance(data['annotations'], dict):
167
+ annotations = data['annotations']
168
+
169
+ # Parse metadata if present
170
+ metadata = {}
171
+ if 'metadata' in data and isinstance(data['metadata'], dict):
172
+ metadata = data['metadata']
173
+
111
174
  return Repository(
112
175
  name=data.get('name', repo_name),
113
176
  organization=organization,
114
177
  type=repo_type,
115
- default_branch=data.get('defaultBranch'),
116
178
  description=data.get('description'),
179
+ metadata=metadata,
180
+ annotations=annotations,
117
181
  )
118
182
 
119
183
  def get_repository_refs(
@@ -124,31 +188,53 @@ class HubClient:
124
188
  ) -> List[Ref]:
125
189
  """
126
190
  Get repository references (branches and tags).
127
-
191
+
128
192
  Args:
129
193
  organization: Organization name
130
194
  repo_type: Repository type ("models" or "datasets")
131
195
  repo_name: Repository name
132
-
196
+
133
197
  Returns:
134
198
  List of references
135
199
  """
136
- url = f"{self.base_url}/organizations/{organization}/{repo_type}/{repo_name}/refs"
200
+ url = f"{self.base_url}/v1/organizations/{organization}/{repo_type}/{repo_name}/refs"
137
201
  response = self._make_request("GET", url)
138
202
  data = response.json()
139
-
203
+
140
204
  refs = []
141
205
  for ref_data in data:
142
206
  refs.append(Ref(
143
207
  name=ref_data.get('name', ''),
144
208
  ref=ref_data.get('ref', ''),
145
- fully_name=ref_data.get('fullyName', ''),
146
209
  type=ref_data.get('type', ''),
147
210
  hash=ref_data.get('hash', ''),
148
211
  is_default=ref_data.get('isDefault', False),
149
212
  ))
150
-
213
+
151
214
  return refs
215
+
216
+ def get_default_branch(
217
+ self,
218
+ organization: str,
219
+ repo_type: str,
220
+ repo_name: str,
221
+ ) -> str:
222
+ """
223
+ Get the default branch name for a repository.
224
+
225
+ Args:
226
+ organization: Organization name
227
+ repo_type: Repository type ("models" or "datasets")
228
+ repo_name: Repository name
229
+
230
+ Returns:
231
+ Default branch name (defaults to "main" if not found)
232
+ """
233
+ refs = self.get_repository_refs(organization, repo_type, repo_name)
234
+ for ref in refs:
235
+ if ref.is_default and ref.type == "branch":
236
+ return ref.name
237
+ return "main"
152
238
 
153
239
  def get_repository_content(
154
240
  self,
@@ -172,9 +258,9 @@ class HubClient:
172
258
  Git content information
173
259
  """
174
260
  if path:
175
- url = f"{self.base_url}/organizations/{organization}/{repo_type}/{repo_name}/contents/{branch}/{path}"
261
+ url = f"{self.base_url}/v1/organizations/{organization}/{repo_type}/{repo_name}/contents/{branch}/{path}"
176
262
  else:
177
- url = f"{self.base_url}/organizations/{organization}/{repo_type}/{repo_name}/contents/{branch}"
263
+ url = f"{self.base_url}/v1/organizations/{organization}/{repo_type}/{repo_name}/contents/{branch}"
178
264
 
179
265
  response = self._make_request("GET", url)
180
266
  data = response.json()
@@ -199,6 +285,21 @@ class HubClient:
199
285
  entries=entries,
200
286
  )
201
287
 
288
+ def add_download_count(self, organization: str, repo_type: str, repo_name: str) -> None:
289
+ """
290
+ Add download count for a repository.
291
+
292
+ Args:
293
+ organization: Organization name
294
+ repo_type: Repository type ("models" or "datasets")
295
+ repo_name: Repository name
296
+ """
297
+ url = f"{self.base_url}/v1/organizations/{organization}/{repo_type}/{repo_name}/downloads"
298
+ response = self._make_request("POST", url)
299
+ # 检查http code
300
+ if response.status_code != 200:
301
+ raise HTTPError(f"Failed to add download count: {response.text}")
302
+
202
303
  def download_file(
203
304
  self,
204
305
  organization: str,
@@ -221,7 +322,7 @@ class HubClient:
221
322
  local_path: Local path to save the file
222
323
  show_progress: Whether to show download progress bar
223
324
  """
224
- url = f"{self.base_url}/organizations/{organization}/{repo_type}/{repo_name}/resolve/{branch}/{file_path}"
325
+ url = f"{self.base_url}/v1/organizations/{organization}/{repo_type}/{repo_name}/resolve/{branch}/{file_path}"
225
326
  response = self._make_request("GET", url, stream=True)
226
327
 
227
328
  # Get file size from headers