Skip to content

Commit 805e2a4

Browse files
CementZhangcemeng
andauthored
feat: Support XNet accelerated transfer and enhance CLI commands (#113)
* Add Xnet upload and download * Add Xnet upload and download * Download compatible with old version * enhance tips msg * fix ut * fix ut * Add system info * Adaptation setup * enhance code * override the default cache dir for hf * Add cli doc * remove version and __init__ version * remove version * fix conflic int transformer and hunggingface-hub * fix databaset load error with databaset version 4.4.1 * fix finetune issue * fix finetune issue * Add download cli for single file * Add the feature doc in README.md/README-CN.md * Add the feature doc in README.md/README-CN.md --------- Co-authored-by: cemeng <cemengzhang@yntengyun.com>
1 parent 6ce197e commit 805e2a4

30 files changed

Lines changed: 1504 additions & 181 deletions

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# csghub user token
2+
CSGHUB_TOKEN=your_csghub_token
3+
# csghub domain
4+
CSGHUB_DOMAIN==https://hub.csghub.com
5+
# test repo and types
6+
CSGHUB_TEST_REPOS='
7+
[
8+
{"repo_id": "ns/model-repo", "type": "model", "disable_xnet": "false", "size_kb": 200},
9+
{"repo_id": "ns/dataset-repo", "type": "dataset", "disable_xnet": "true"}
10+
]'

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ dist/
1111
.idea
1212
.pypirc
1313
.vscode/
14+
.trae/
1415
.DS_Store
16+
/.env

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ The main functions are:
2828
1. Repo downloading(model/dataset)
2929
2. Repo information query(Compatible with huggingface)
3030

31+
**XNet Accelerated Transfer (New!)**: Next-generation storage and version control technology for large-scale AI/ML data.
32+
- **Storage Optimization**: Significantly reduces storage costs (tested savings > 50%) via intelligent Content-Defined Chunking and deduplication.
33+
- **High-Speed Transfer**: Incremental updates ensure only changed data chunks are transferred, boosting upload/download speeds by multiples.
34+
- **Enabled by Default**: Automatically optimizes upload, download, and storage for LFS large files. To disable, set the environment variable `CSGHUB_DISABLE_XNET=true` to fallback to standard LFS mode.
35+
3136
## Get My Token
3237

3338
Visit [OpenCSG](https://opencsg.com/), click on Sign Up in the top right corner to complete the user registration process. Use the successfully registered username and password to log in to [OpenCSG](https://opencsg.com/). After logging in, find [Access Token](https://opencsg.com/settings/access-token) under Account Settings to obtain the token.
@@ -96,4 +101,4 @@ For detailed SDK usage examples, including model/dataset downloading, file uploa
96101
1. Interacting with CSGHub via command-line tools
97102
2. Management operations such as creation and modification of CSGHub repositories
98103
3. Model deployment locally or online
99-
4. Model fine-tuning locally or online
104+
4. Model fine-tuning locally or online

README_cn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ CSGHub SDK 是一个强大的 Python 客户端,专门设计用于与 CSGHub
2828
1. 仓库下载(模型/数据集)
2929
2. 仓库信息查询(与huggingface兼容)
3030

31+
**XNet 加速传输(新!)**:面向大规模 AI/ML 数据的下一代存储与版本控制技术。
32+
- **存储优化**:通过智能分块(Content-Defined Chunking)与数据去重,大幅降低存储成本(实测节省 50% 以上)。
33+
- **加速传输**:增量更新机制,仅传输变化的数据块,上传/下载速度提升数倍。
34+
- **默认开启**:针对 LFS 大文件的上传、下载与存储自动优化。如需禁用,设置环境变量 `CSGHUB_DISABLE_XNET=true` 即可降级为普通 LFS 模式。
35+
3136
## 获取Token
3237

3338
浏览器访问[OpenCSG](https://opencsg.com/),点击右上角`注册`完成用户注册过程,使用已经注册成功的用户和密码登录[OpenCSG](https://opencsg.com/),登录成功后在`账号设置`中找到[`Access Token`](https://opencsg.com/settings/access-token)来获取token。

doc/cli.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ $env:CSGHUB_TOKEN="your_access_token"
1717
# download model
1818
csghub-cli download OpenCSG/csg-wukong-1B
1919

20+
# download single file
21+
csghub-cli download OpenCSG/csg-wukong-1B config.json
22+
2023
# download model with allow patterns '*.json' and ignore '*_config.json' pattern of files
2124
csghub-cli download OpenCSG/csg-wukong-1B --allow-patterns "*.json" --ignore-patterns "tokenizer.json"
2225

@@ -67,6 +70,12 @@ csghub-cli upload wanghh2000/m01 /Users/hhwang/temp/jsonl test/files -k xxxxxx
6770

6871
# auto upload large file in multi-part mode by 'git push' under working directory
6972
csghub-cli lfs-enable-largefiles ./
73+
74+
# print csghub-cli version
75+
csghub-cli version
76+
77+
# print csghub-cli environment for debug or report issue with `csghub-cli env`
78+
csghub-cli env
7079
```
7180

7281
### Example for Window Powershell

doc/cli_cn.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ $env:CSGHUB_TOKEN="your_access_token"
1717
# 模型下载
1818
csghub-cli download OpenCSG/csg-wukong-1B
1919

20+
# 下载单个文件
21+
csghub-cli download OpenCSG/csg-wukong-1B config.json
22+
2023
# 模型下载时允许'*.json'模式的文件并忽略'*_config.json'模式的文件
2124
csghub-cli download OpenCSG/csg-wukong-1B --allow-patterns "*.json" --ignore-patterns "*_config.json"
2225

@@ -67,6 +70,12 @@ csghub-cli upload wanghh2000/m01 /Users/hhwang/temp/jsonl test/files -k xxxxxx
6770

6871
# 在当前工作目录启用大文件分片上传功能
6972
csghub-cli lfs-enable-largefiles ./
73+
74+
# 查看csghub-sdk版本号
75+
csghub-cli version
76+
77+
# 打印csghub-cli环境变量,如果需要提issue,请执行此命令,并且复制输出到issue中。
78+
csghub-cli env
7079
```
7180

7281
### 示例命令使用在Window Powershell环境

examples/run_finetune_bert.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def data_collator() -> Any:
3535
outputDir = "/Users/hhwang/temp/ff"
3636
args = TrainingArguments(
3737
outputDir,
38-
evaluation_strategy="steps",
38+
eval_strategy="steps",
3939
save_strategy="steps",
4040
logging_strategy="steps",
4141
logging_steps = 2,
@@ -57,7 +57,7 @@ def data_collator() -> Any:
5757
args,
5858
train_dataset=train_dataset,
5959
eval_dataset=eval_dataset,
60-
tokenizer=tokenizer,
60+
data_collator=data_collator(),
6161
)
6262

6363
trainResult = trainer.train()

pycsghub/api_client/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Optional
2+
3+
from pycsghub.utils import disable_xnet, get_endpoint, get_token_to_send
4+
from .api_client import CsghubApi
5+
from .api_client_interface import HubApi
6+
from .api_client_xet import CsgXnetApi
7+
8+
def get_csghub_api(token: Optional[str] = None,
9+
endpoint: Optional[str] = None,
10+
user_name: Optional[str] = None) -> HubApi:
11+
token = get_token_to_send(token)
12+
endpoint = get_endpoint(endpoint=endpoint)
13+
return CsghubApi(token=token, endpoint=endpoint, user_name=user_name) if disable_xnet() else CsgXnetApi(token=token,
14+
endpoint=endpoint,
15+
user_name=user_name)

pycsghub/api_client/api_client.py

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
from __future__ import annotations
2+
3+
from fnmatch import fnmatch
4+
from pathlib import Path
5+
from typing import Any, Dict, List, Optional, Union
6+
7+
from huggingface_hub.utils import filter_repo_objects
8+
9+
from pycsghub.commit_ops import build_payload, CommitOperationAdd, CommitOperationDelete
10+
from pycsghub.csghub_api import CsgHubApi
11+
from pycsghub.file_download import file_download as csghub_file_download
12+
from pycsghub.snapshot_download import snapshot_download as csghub_snapshot_download
13+
from pycsghub.utils import get_endpoint, get_repo_info
14+
from pycsghub.cmd import repo
15+
16+
class CsghubApi:
17+
def __init__(self, token: Optional[str] = None, endpoint: Optional[str] = None, user_name: Optional[str] = None):
18+
self._api = CsgHubApi(token=token, endpoint=endpoint)
19+
self._token = token
20+
self._endpoint = endpoint
21+
self._user_name = user_name
22+
23+
def hf_hub_download(
24+
self,
25+
repo_id: str,
26+
filename: str,
27+
subfolder: Optional[str] = None,
28+
repo_type: Optional[str] = None,
29+
revision: Optional[str] = None,
30+
library_name: Optional[str] = None,
31+
library_version: Optional[str] = None,
32+
cache_dir: Optional[Union[str, Path]] = None,
33+
local_dir: Optional[Union[str, Path]] = None,
34+
user_agent: Optional[Union[Dict, str]] = None,
35+
force_download: bool = False,
36+
force_filename: Optional[str] = None,
37+
proxies: Optional[Dict] = None,
38+
etag_timeout: float = 10,
39+
resume_download: bool = False,
40+
token: Optional[Union[bool, str]] = None,
41+
local_files_only: bool = False,
42+
headers: Optional[Dict[str, str]] = None,
43+
endpoint: Optional[str] = None,
44+
**kwargs
45+
) -> Union[str, Path]:
46+
# Adapt to csghub_file_download signature
47+
# Note: subfolder handling in csghub might be different, usually path in filename
48+
final_filename = f"{subfolder}/{filename}" if subfolder else filename
49+
50+
# Extract dry_run/quiet/source from kwargs if present
51+
dry_run = kwargs.get('dry_run', False)
52+
quiet = kwargs.get('quiet', False)
53+
source = kwargs.get('source', None)
54+
55+
return csghub_file_download(
56+
repo_id=repo_id,
57+
file_name=final_filename,
58+
revision=revision,
59+
repo_type=repo_type,
60+
cache_dir=cache_dir,
61+
local_dir=local_dir,
62+
local_files_only=local_files_only,
63+
headers=headers,
64+
endpoint=endpoint or self._endpoint,
65+
token=token or self._token,
66+
force_download=force_download,
67+
dry_run=dry_run,
68+
quiet=quiet,
69+
source=source
70+
)
71+
72+
def snapshot_download(
73+
self,
74+
repo_id: str,
75+
revision: Optional[str] = None,
76+
repo_type: Optional[str] = None,
77+
cache_dir: Optional[Union[str, Path]] = None,
78+
local_dir: Optional[Union[str, Path]] = None,
79+
library_name: Optional[str] = None,
80+
library_version: Optional[str] = None,
81+
user_agent: Optional[Union[Dict, str]] = None,
82+
proxies: Optional[Dict] = None,
83+
etag_timeout: float = 10,
84+
resume_download: bool = False,
85+
token: Optional[Union[bool, str]] = None,
86+
local_files_only: bool = False,
87+
allow_patterns: Optional[Union[List[str], str]] = None,
88+
ignore_patterns: Optional[Union[List[str], str]] = None,
89+
max_workers: int = 8,
90+
tqdm_class: Optional[Any] = None,
91+
# Extra args for CSGHub compatibility or enhancements
92+
endpoint: Optional[str] = None,
93+
source: Optional[str] = None,
94+
quiet: Optional[bool] = False,
95+
dry_run: Optional[bool] = False,
96+
force_download: Optional[bool] = False,
97+
):
98+
# Delegate to pycsghub.snapshot_download
99+
return csghub_snapshot_download(
100+
repo_id=repo_id,
101+
repo_type=repo_type,
102+
revision=revision,
103+
cache_dir=cache_dir,
104+
local_dir=local_dir,
105+
local_files_only=local_files_only,
106+
allow_patterns=allow_patterns,
107+
ignore_patterns=ignore_patterns,
108+
endpoint=endpoint or self._endpoint,
109+
token=token or self._token,
110+
source=source,
111+
dry_run=dry_run,
112+
force_download=force_download,
113+
max_workers=max_workers,
114+
quiet=quiet
115+
)
116+
117+
def create_repo(
118+
self,
119+
repo_id: str,
120+
repo_type: Optional[str] = None,
121+
exist_ok: bool = False,
122+
private: bool = False,
123+
**kwargs
124+
):
125+
# Reusing Repository logic for creation
126+
from pycsghub.repository import Repository
127+
repo = Repository(
128+
repo_id=repo_id,
129+
upload_path="..",
130+
repo_type=repo_type,
131+
token=self._token,
132+
endpoint=self._endpoint,
133+
auto_create=False
134+
)
135+
try:
136+
repo.create_new_repo()
137+
except Exception:
138+
if not exist_ok:
139+
raise
140+
return repo
141+
142+
def repo_info(
143+
self,
144+
repo_id: str,
145+
revision: Optional[str] = None,
146+
repo_type: Optional[str] = None,
147+
token: Optional[str] = None,
148+
):
149+
return get_repo_info(
150+
repo_id=repo_id,
151+
revision=revision,
152+
repo_type=repo_type,
153+
token=token or self._token,
154+
endpoint=self._endpoint
155+
)
156+
157+
def create_branch(
158+
self,
159+
repo_id: str,
160+
branch: str,
161+
repo_type: Optional[str] = None,
162+
exist_ok: bool = False
163+
):
164+
try:
165+
# Reusing Repository logic until CsgHubApi supports create_branch directly or via new endpoint
166+
from pycsghub.repository import Repository
167+
repo = Repository(
168+
repo_id=repo_id,
169+
upload_path="..",
170+
repo_type=repo_type,
171+
branch_name=branch,
172+
token=self._token,
173+
endpoint=self._endpoint
174+
)
175+
return repo.create_new_branch()
176+
except Exception:
177+
if not exist_ok:
178+
raise
179+
180+
def upload_file(
181+
self,
182+
*,
183+
path_or_fileobj: Union[str, Path, bytes],
184+
path_in_repo: str,
185+
repo_id: str,
186+
repo_type: Optional[str] = None,
187+
revision: Optional[str] = None,
188+
commit_message: Optional[str] = None,
189+
commit_description: Optional[str] = None,
190+
create_pr: Optional[bool] = None,
191+
parent_commit: Optional[str] = None,
192+
) -> dict:
193+
return repo.upload_files(
194+
repo_id=repo_id,
195+
repo_type=repo_type,
196+
repo_file=path_or_fileobj,
197+
path_in_repo=path_in_repo,
198+
revision=revision,
199+
endpoint=self._endpoint,
200+
token=self._token,
201+
)
202+
203+
def upload_folder(
204+
self,
205+
*,
206+
repo_id: str,
207+
folder_path: Union[str, Path],
208+
path_in_repo: Optional[str] = None,
209+
repo_type: Optional[str] = None,
210+
revision: Optional[str] = None,
211+
commit_message: Optional[str] = None,
212+
commit_description: Optional[str] = None,
213+
allow_patterns: Optional[Union[List[str], str]] = None,
214+
ignore_patterns: Optional[Union[List[str], str]] = None,
215+
delete_patterns: Optional[Union[List[str], str]] = None,
216+
create_pr: Optional[bool] = None,
217+
parent_commit: Optional[str] = None,
218+
) -> dict:
219+
return repo.upload_folder(
220+
repo_id=repo_id,
221+
repo_type=repo_type,
222+
local_path=folder_path,
223+
path_in_repo=path_in_repo,
224+
revision=revision,
225+
endpoint=self._endpoint,
226+
token=self._token,
227+
user_name=self._user_name,
228+
)

0 commit comments

Comments
 (0)