atomgit 1.0.0__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.
atomgit/atomgit_hub.py ADDED
@@ -0,0 +1,519 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ AtomGit Hub SDK - 类似huggingface_hub的SDK接口
6
+
7
+ 这个模块提供了类似huggingface_hub的编程接口,
8
+ 让用户可以通过Python代码直接使用AtomGit平台的功能。
9
+ """
10
+
11
+ import os
12
+ from typing import Optional, Union, List, Dict, Any
13
+ from pathlib import Path
14
+
15
+ # 设置Hugging Face Hub的API端点为AtomGit
16
+ os.environ["HF_ENDPOINT"] = "https://hub.atomgit.com"
17
+ # 设置缓存目录
18
+ cache_dir = os.path.expanduser("~/.cache/atomgit")
19
+ os.makedirs(cache_dir, exist_ok=True)
20
+ os.environ["HF_HOME"] = cache_dir
21
+
22
+ from huggingface_hub import snapshot_download as hf_snapshot_download
23
+ from huggingface_hub import hf_hub_download, upload_folder as hf_upload_folder, create_repo
24
+
25
+
26
+ try:
27
+ from datasets import load_dataset as ds_load_dataset # type: ignore
28
+ from datasets.config import HF_DATASETS_CACHE # type: ignore
29
+ DATASET_SUPPORT = True
30
+ except ImportError:
31
+ DATASET_SUPPORT = False
32
+ ds_load_dataset = None
33
+ HF_DATASETS_CACHE = None
34
+
35
+ try:
36
+ from .config import config
37
+ except ImportError:
38
+ from config import config
39
+
40
+
41
+ def _normalize_repo_id(repo_id: str) -> str:
42
+ """标准化仓库ID,处理三层格式转换"""
43
+ parts = repo_id.split('/')
44
+
45
+ # 如果是三层格式(如 wuyw/Qwen3-Reranker/0.6B-test)
46
+ # 转换为特殊格式(如 wuyw-Qwen3-Reranker/0.6B-test)
47
+ if len(parts) >= 3:
48
+ # 只编码第一个斜杠,保留后面的斜杠
49
+ first_part = parts[0]
50
+ second_part = parts[1]
51
+ remaining_parts = parts[2:]
52
+
53
+ # 构建新格式:第一部分-第二部分/其余部分
54
+ normalized = first_part + '-' + second_part
55
+ if remaining_parts:
56
+ normalized += '/' + '/'.join(remaining_parts)
57
+
58
+ return normalized
59
+
60
+ # 二层或单层格式直接返回
61
+ return repo_id
62
+
63
+
64
+ def _get_token() -> Optional[str]:
65
+ """获取保存的认证token"""
66
+ try:
67
+ credentials = config.get_credentials()
68
+ if credentials and 'token' in credentials:
69
+ return credentials['token']
70
+ except Exception:
71
+ pass
72
+ return None
73
+
74
+
75
+ def snapshot_download(
76
+ repo_id: str,
77
+ revision: Optional[str] = None,
78
+ cache_dir: Optional[Union[str, Path]] = None,
79
+ local_dir: Optional[Union[str, Path]] = None,
80
+ local_dir_use_symlinks: Union[bool, str] = "auto",
81
+ library_name: Optional[str] = None,
82
+ library_version: Optional[str] = None,
83
+ user_agent: Optional[Union[str, Dict[str, str]]] = None,
84
+ proxies: Optional[Dict[str, str]] = None,
85
+ etag_timeout: float = 10,
86
+ resume_download: bool = False,
87
+ force_download: bool = False,
88
+ token: Optional[Union[str, bool]] = None,
89
+ local_files_only: bool = False,
90
+ allow_patterns: Optional[Union[List[str], str]] = None,
91
+ ignore_patterns: Optional[Union[List[str], str]] = None,
92
+ max_workers: int = 8,
93
+ tqdm_class: Optional[Any] = None,
94
+ ) -> str:
95
+ """
96
+ 从AtomGit Hub下载整个仓库的快照到本地目录
97
+
98
+ 参数:
99
+ repo_id (str): 仓库ID,格式为 "username/repo-name" 或 "username/namespace/repo-name"
100
+ revision (str, 可选): 指定版本/分支/标签,默认为 "main"
101
+ cache_dir (str 或 Path, 可选): 缓存目录路径
102
+ local_dir (str 或 Path, 可选): 下载到的本地目录路径
103
+ local_dir_use_symlinks (bool 或 str, 可选): 是否使用符号链接,默认为 "auto"
104
+ library_name (str, 可选): 调用库的名称
105
+ library_version (str, 可选): 调用库的版本
106
+ user_agent (str 或 dict, 可选): 用户代理
107
+ proxies (dict, 可选): 代理配置
108
+ etag_timeout (float, 可选): ETag超时时间,默认10秒
109
+ resume_download (bool, 可选): 是否断点续传,默认False
110
+ force_download (bool, 可选): 是否强制重新下载,默认False
111
+ token (str 或 bool, 可选): 认证token,如果为None则尝试使用保存的token
112
+ local_files_only (bool, 可选): 仅使用本地文件,默认False
113
+ allow_patterns (list 或 str, 可选): 允许下载的文件模式
114
+ ignore_patterns (list 或 str, 可选): 忽略的文件模式
115
+ max_workers (int, 可选): 最大并发数,默认8
116
+ tqdm_class (可选): 进度条类
117
+
118
+ 返回:
119
+ str: 下载完成的本地目录路径
120
+
121
+ 示例:
122
+ >>> from atomgit_hub import snapshot_download
123
+ >>> local_path = snapshot_download("wuyw/Qwen3-Reranker-0.6B-test", local_dir="./models")
124
+ """
125
+ # 标准化仓库ID(处理三层格式)
126
+ normalized_repo_id = _normalize_repo_id(repo_id)
127
+
128
+ # 如果没有提供token,尝试使用保存的token
129
+ if token is None:
130
+ token = _get_token()
131
+
132
+ # 构建参数字典
133
+ kwargs = {
134
+ 'repo_id': normalized_repo_id,
135
+ }
136
+
137
+ # 添加可选参数
138
+ if revision is not None:
139
+ kwargs['revision'] = revision
140
+ if cache_dir is not None:
141
+ kwargs['cache_dir'] = str(cache_dir)
142
+ if local_dir is not None:
143
+ kwargs['local_dir'] = str(local_dir)
144
+ if token is not None:
145
+ kwargs['token'] = token
146
+
147
+ # 其他参数
148
+ kwargs.update({
149
+ 'local_dir_use_symlinks': local_dir_use_symlinks,
150
+ 'library_name': library_name or "atomgit_hub",
151
+ 'library_version': library_version,
152
+ 'user_agent': user_agent,
153
+ 'proxies': proxies,
154
+ 'etag_timeout': etag_timeout,
155
+ 'resume_download': resume_download,
156
+ 'force_download': force_download,
157
+ 'local_files_only': local_files_only,
158
+ 'allow_patterns': allow_patterns,
159
+ 'ignore_patterns': ignore_patterns,
160
+ 'max_workers': max_workers,
161
+ 'tqdm_class': tqdm_class,
162
+ })
163
+
164
+ try:
165
+ # 使用token下载
166
+ result = hf_snapshot_download(**{k: v for k, v in kwargs.items() if v is not None})
167
+ return result
168
+ except Exception as e:
169
+ error_msg = str(e)
170
+ if "401" in error_msg or "403" in error_msg:
171
+ raise Exception(f"认证失败:{error_msg}。请检查token是否正确,或使用 'atomgit login' 重新登录。")
172
+ elif "404" in error_msg:
173
+ raise Exception(f"仓库不存在:{repo_id}。请检查仓库名称是否正确。")
174
+ else:
175
+ raise Exception(f"下载失败:{error_msg}")
176
+
177
+
178
+ def hub_download_url(
179
+ repo_id: str,
180
+ filename: str,
181
+ revision: Optional[str] = None,
182
+ repo_type: Optional[str] = None,
183
+ ) -> str:
184
+ """
185
+ 获取AtomGit Hub上文件的下载URL
186
+
187
+ 参数:
188
+ repo_id (str): 仓库ID
189
+ filename (str): 文件名
190
+ revision (str, 可选): 版本/分支/标签
191
+ repo_type (str, 可选): 仓库类型
192
+
193
+ 返回:
194
+ str: 文件的下载URL
195
+ """
196
+ normalized_repo_id = _normalize_repo_id(repo_id)
197
+ base_url = "https://hub.atomgit.com"
198
+
199
+ if revision is None:
200
+ revision = "main"
201
+
202
+ if repo_type is None:
203
+ repo_type = "model"
204
+
205
+ # 构建URL
206
+ url = f"{base_url}/{normalized_repo_id}/resolve/{revision}/{filename}"
207
+ return url
208
+
209
+
210
+ def download_file(
211
+ repo_id: str,
212
+ filename: str,
213
+ local_dir: Optional[Union[str, Path]] = None,
214
+ revision: Optional[str] = None,
215
+ token: Optional[str] = None,
216
+ force_download: bool = False,
217
+ ) -> str:
218
+ """
219
+ 从AtomGit Hub下载单个文件
220
+
221
+ 参数:
222
+ repo_id (str): 仓库ID
223
+ filename (str): 文件名
224
+ local_dir (str 或 Path, 可选): 本地目录
225
+ revision (str, 可选): 版本/分支/标签
226
+ token (str, 可选): 认证token
227
+ force_download (bool, 可选): 是否强制重新下载
228
+
229
+ 返回:
230
+ str: 下载的文件路径
231
+ """
232
+ # 标准化仓库ID
233
+ normalized_repo_id = _normalize_repo_id(repo_id)
234
+
235
+ # 如果没有提供token,尝试使用保存的token
236
+ if token is None:
237
+ token = _get_token()
238
+
239
+ # 构建参数
240
+ kwargs = {
241
+ 'repo_id': normalized_repo_id,
242
+ 'filename': filename,
243
+ }
244
+
245
+ if local_dir is not None:
246
+ kwargs['local_dir'] = str(local_dir)
247
+ if revision is not None:
248
+ kwargs['revision'] = revision
249
+ if token is not None:
250
+ kwargs['token'] = token
251
+ if force_download:
252
+ kwargs['force_download'] = force_download
253
+
254
+ try:
255
+ result = hf_hub_download(**{k: v for k, v in kwargs.items() if v is not None})
256
+ return result
257
+ except Exception as e:
258
+ error_msg = str(e)
259
+ if "401" in error_msg or "403" in error_msg:
260
+ raise Exception(f"认证失败:{error_msg}")
261
+ elif "404" in error_msg:
262
+ raise Exception(f"文件不存在:{repo_id}/{filename}")
263
+ else:
264
+ raise Exception(f"下载失败:{error_msg}")
265
+
266
+
267
+ def upload_folder(
268
+ folder_path: Union[str, Path],
269
+ repo_id: str,
270
+ token: Optional[str] = None,
271
+ repo_type: Optional[str] = None,
272
+ revision: Optional[str] = None,
273
+ commit_message: Optional[str] = None,
274
+ commit_description: Optional[str] = None,
275
+ path_in_repo: str = "./",
276
+ ignore_patterns: Optional[List[str]] = None,
277
+ ) -> str:
278
+ """
279
+ 上传文件夹到AtomGit Hub
280
+
281
+ 参数:
282
+ folder_path (str 或 Path): 本地文件夹路径
283
+ repo_id (str): 仓库ID
284
+ token (str, 可选): 认证token
285
+ repo_type (str, 可选): 仓库类型
286
+ revision (str, 可选): 分支名
287
+ commit_message (str, 可选): 提交消息
288
+ commit_description (str, 可选): 提交描述
289
+ path_in_repo (str, 可选): 在仓库中的路径,默认为根目录
290
+ ignore_patterns (List[str], 可选): 要忽略的文件模式
291
+
292
+ 返回:
293
+ str: 提交的URL或ID
294
+
295
+ 示例:
296
+ >>> upload_folder("./my-model/", "username/repo-name")
297
+ >>> upload_folder("./data/", "username/repo", path_in_repo="datasets/")
298
+ """
299
+ # 标准化仓库ID
300
+ normalized_repo_id = _normalize_repo_id(repo_id)
301
+
302
+ # 转换为Path对象
303
+ folder_path = Path(folder_path)
304
+
305
+ if not folder_path.exists():
306
+ raise FileNotFoundError(f"文件夹不存在: {folder_path}")
307
+
308
+ if not folder_path.is_dir():
309
+ raise NotADirectoryError(f"路径不是目录: {folder_path}")
310
+
311
+ # 如果没有提供token,尝试使用保存的token
312
+ if token is None:
313
+ token = _get_token()
314
+ if token is None:
315
+ raise Exception("上传需要认证token,请先使用 'atomgit login' 登录,或提供token参数。")
316
+
317
+ # 直接使用原始目录,或者创建临时目录来重新组织结构
318
+ import tempfile
319
+ import shutil
320
+
321
+ if path_in_repo == "./" or path_in_repo == "." or path_in_repo == "":
322
+ # 如果要上传到根目录,直接使用源文件夹
323
+ upload_path = str(folder_path)
324
+ else:
325
+ # 如果要上传到特定路径,需要重新组织目录结构
326
+ with tempfile.TemporaryDirectory() as temp_dir:
327
+ temp_path = Path(temp_dir)
328
+
329
+ # 创建目标路径
330
+ target_path = temp_path / path_in_repo.strip('./')
331
+ target_path.parent.mkdir(parents=True, exist_ok=True)
332
+
333
+ # 复制整个目录树
334
+ shutil.copytree(folder_path, target_path, dirs_exist_ok=True)
335
+
336
+ upload_path = str(temp_path)
337
+
338
+ try:
339
+ # 使用huggingface_hub的upload_folder上传
340
+ commit_msg = commit_message or f"Upload folder {folder_path.name}"
341
+ result = hf_upload_folder(
342
+ repo_id=normalized_repo_id,
343
+ folder_path=upload_path,
344
+ token=token,
345
+ commit_message=commit_msg
346
+ )
347
+
348
+ return result
349
+
350
+ except Exception as e:
351
+ error_msg = str(e)
352
+ if "401" in error_msg or "403" in error_msg:
353
+ raise Exception(f"认证失败:{error_msg}")
354
+ elif "404" in error_msg:
355
+ raise Exception(f"仓库不存在:{repo_id}")
356
+ else:
357
+ raise Exception(f"上传失败:{error_msg}")
358
+
359
+
360
+ def create_repository(
361
+ repo_id: str,
362
+ token: Optional[str] = None,
363
+ private: bool = False,
364
+ repo_type: str = "model",
365
+ exist_ok: bool = False,
366
+ space_sdk: Optional[str] = None,
367
+ space_hardware: Optional[str] = None,
368
+ space_storage: Optional[str] = None,
369
+ space_sleep_time: Optional[int] = None,
370
+ space_secrets: Optional[List[Dict]] = None,
371
+ space_variables: Optional[List[Dict]] = None,
372
+ ) -> str:
373
+ """
374
+ 在AtomGit Hub上创建仓库
375
+
376
+ 参数:
377
+ repo_id (str): 仓库ID
378
+ token (str, 可选): 认证token
379
+ private (bool, 可选): 是否为私有仓库,默认False
380
+ repo_type (str, 可选): 仓库类型,默认"model"
381
+ exist_ok (bool, 可选): 如果仓库已存在是否报错,默认False
382
+ 其他参数: 主要用于Space类型仓库
383
+
384
+ 返回:
385
+ str: 仓库的URL
386
+ """
387
+ # 标准化仓库ID
388
+ normalized_repo_id = _normalize_repo_id(repo_id)
389
+
390
+ # 如果没有提供token,尝试使用保存的token
391
+ if token is None:
392
+ token = _get_token()
393
+ if token is None:
394
+ raise Exception("创建仓库需要认证token,请先使用 'atomgit login' 登录,或提供token参数。")
395
+
396
+ try:
397
+ result = create_repo(
398
+ repo_id=normalized_repo_id,
399
+ token=token,
400
+ private=private,
401
+ repo_type=repo_type,
402
+ exist_ok=exist_ok,
403
+ )
404
+ return result
405
+ except Exception as e:
406
+ error_msg = str(e)
407
+ if "401" in error_msg or "403" in error_msg:
408
+ raise Exception(f"认证失败:{error_msg}")
409
+ elif "409" in error_msg or "exists" in error_msg.lower():
410
+ if exist_ok:
411
+ return f"https://atomgit.com/{repo_id}"
412
+ else:
413
+ raise Exception(f"仓库已存在:{repo_id}")
414
+ else:
415
+ raise Exception(f"创建仓库失败:{error_msg}")
416
+
417
+
418
+ def load_dataset(
419
+ path: str,
420
+ name: Optional[str] = None,
421
+ data_dir: Optional[str] = None,
422
+ data_files: Optional[Union[str, List[str], Dict]] = None,
423
+ split: Optional[str] = None,
424
+ cache_dir: Optional[Union[str, Path]] = None,
425
+ token: Optional[str] = None,
426
+ streaming: bool = False,
427
+ revision: Optional[str] = None,
428
+ **kwargs
429
+ ) -> Any:
430
+ """
431
+ 从AtomGit Hub加载数据集
432
+
433
+ 参数:
434
+ path (str): 数据集路径,格式为 "username/dataset-name"
435
+ name (str, 可选): 数据集名称
436
+ data_dir (str, 可选): 数据目录路径
437
+ data_files (str, List[str], Dict, 可选): 数据文件
438
+ split (str, 可选): 数据集分割,如 "train", "test", "validation"
439
+ cache_dir (str 或 Path, 可选): 缓存目录路径,默认使用HF_DATASETS_CACHE
440
+ token (str, 可选): 认证token,如果为None则尝试使用保存的token
441
+ streaming (bool, 可选): 是否以流式方式加载数据集,默认False
442
+ revision (str, 可选): 指定版本/分支/标签,默认为 "main"
443
+ **kwargs: 其他传递给load_dataset的参数
444
+
445
+ 返回:
446
+ Dataset或DatasetDict: 加载的数据集对象
447
+
448
+ 示例:
449
+ >>> from atomgit_hub import load_dataset
450
+ >>> dataset = load_dataset("wuyw/my-dataset", split="train")
451
+ >>> dataset = load_dataset("wuyw/my-dataset", streaming=True)
452
+
453
+ 异常:
454
+ ImportError: 如果datasets库未安装
455
+ Exception: 各种下载和认证错误
456
+ """
457
+ # 检查数据集支持
458
+ if not DATASET_SUPPORT:
459
+ raise ImportError("数据集功能需要安装datasets库。请运行: pip install datasets")
460
+
461
+ # 标准化数据集路径(处理三层格式)
462
+ normalized_path = _normalize_repo_id(path)
463
+
464
+ # 如果没有提供token,尝试使用保存的token
465
+ if token is None:
466
+ token = _get_token()
467
+
468
+ # 设置缓存目录
469
+ if cache_dir is None:
470
+ cache_dir = HF_DATASETS_CACHE
471
+
472
+ # 构建参数字典
473
+ load_kwargs = {
474
+ 'path': normalized_path,
475
+ 'cache_dir': str(cache_dir) if cache_dir else None,
476
+ 'streaming': streaming,
477
+ }
478
+
479
+ # 添加可选参数
480
+ if name is not None:
481
+ load_kwargs['name'] = name
482
+ if data_dir is not None:
483
+ load_kwargs['data_dir'] = data_dir
484
+ if data_files is not None:
485
+ load_kwargs['data_files'] = data_files
486
+ if split is not None:
487
+ load_kwargs['split'] = split
488
+ if token is not None:
489
+ load_kwargs['token'] = token
490
+ if revision is not None:
491
+ load_kwargs['revision'] = revision
492
+
493
+ # 合并额外的关键字参数
494
+ load_kwargs.update(kwargs)
495
+
496
+ try:
497
+ dataset = ds_load_dataset(**{k: v for k, v in load_kwargs.items() if v is not None})
498
+ return dataset
499
+ except Exception as e:
500
+ error_msg = str(e)
501
+ if "401" in error_msg or "403" in error_msg:
502
+ raise Exception(f"认证失败:{error_msg}。请检查token是否正确,或使用 'atomgit login' 重新登录。")
503
+ elif "404" in error_msg:
504
+ raise Exception(f"数据集不存在:{path}。请检查数据集路径是否正确。")
505
+ elif "ImportError" in error_msg or "No module named" in error_msg:
506
+ raise ImportError("数据集功能需要安装datasets库。请运行: pip install datasets")
507
+ else:
508
+ raise Exception(f"加载数据集失败:{error_msg}")
509
+
510
+
511
+ # 为了兼容性,导出常用函数
512
+ __all__ = [
513
+ 'snapshot_download',
514
+ 'hub_download_url',
515
+ 'download_file',
516
+ 'upload_folder',
517
+ 'create_repository',
518
+ 'load_dataset',
519
+ ]