Skip to content

Commit 040f703

Browse files
Add support for code and MCP repository type (#118)
- Introduced MCP repository type - Updated API client to handle MCP - Enhanced logging for API operations - Modified download/upload commands for MCP - Updated constants and utility functions for MCP Co-authored-by: Haihui.Wang <wanghh2000@163.com>
1 parent 9f7b00f commit 040f703

13 files changed

Lines changed: 169 additions & 55 deletions

File tree

launch.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import re
22
import sys
3+
import logging
34
from pycsghub.cli import app
5+
46
if __name__ == '__main__':
7+
log_level = "INFO"
8+
logging.basicConfig(
9+
level=getattr(logging, log_level),
10+
format='%(asctime)s - %(levelname)s - %(message)s',
11+
datefmt='%Y-%m-%d %H:%M:%S',
12+
handlers=[logging.StreamHandler()]
13+
)
514
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
6-
sys.exit(app())
15+
sys.exit(app())

pycsghub/api_client/__init__.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
from typing import Optional
2-
2+
import logging
3+
from pycsghub.constants import REPO_TYPE_CODE, REPO_TYPE_MCPSERVER
34
from pycsghub.utils import disable_xnet, get_endpoint, get_token_to_send
45
from .api_client import CsghubApi
56
from .api_client_interface import HubApi
67
from .api_client_xet import CsgXnetApi
8+
from pycsghub.cmd.repo_types import RepoType
9+
10+
logger = logging.getLogger(__name__)
711

8-
def get_csghub_api(token: Optional[str] = None,
12+
def get_csghub_api(repo_type: Optional[RepoType] = None,
13+
token: Optional[str] = None,
914
endpoint: Optional[str] = None,
1015
user_name: Optional[str] = None) -> HubApi:
1116
token = get_token_to_send(token)
1217
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)
18+
19+
if repo_type in [REPO_TYPE_CODE, REPO_TYPE_MCPSERVER]:
20+
logger.debug(f"Use CsghubApi for repo_type {repo_type}")
21+
return CsghubApi(token=token, endpoint=endpoint, user_name=user_name)
22+
23+
if disable_xnet():
24+
logger.debug("xnet disabled")
25+
return CsghubApi(token=token, endpoint=endpoint, user_name=user_name)
26+
else:
27+
logger.debug("xnet enabled")
28+
return CsgXnetApi(token=token, endpoint=endpoint, user_name=user_name)

pycsghub/api_client/api_client_xet.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import os
2+
import logging
23
from typing import Optional
34

45
from huggingface_hub import constants, HfApi
56

67
from pycsghub.utils import get_default_cache_dir, get_xnet_endpoint, get_cache_dir
78

9+
logger = logging.getLogger(__name__)
10+
811
class CsgXnetApi(HfApi):
912
def __init__(self, token: Optional[str] = None, endpoint: Optional[str] = None, user_name: Optional[str] = None):
1013
endpoint = get_xnet_endpoint(endpoint=endpoint)
@@ -102,5 +105,6 @@ def snapshot_download(self, *args, **kwargs):
102105
# Ensure token is passed
103106
if 'cache_dir' not in kwargs:
104107
kwargs['cache_dir'] = self._cache_dir
105-
108+
109+
logging.debug(f"invoke origin_snapshot_download")
106110
return origin_snapshot_download(*args, **kwargs)

pycsghub/cli.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from pycsghub.cmd.repo_types import RepoType
2222
from pycsghub.constants import DEFAULT_CSGHUB_DOMAIN, DEFAULT_REVISION, REPO_SOURCE_CSG
2323
from pycsghub.api_client import get_csghub_api
24-
from .utils import print_download_result
24+
from .utils import print_download_result, disable_xnet
2525
from .lfs import LfsEnableCommand, LfsUploadCommand
2626
from .upload_large_folder.main import upload_large_folder_internal
2727

@@ -89,7 +89,7 @@ def version_callback(value: bool):
8989
"path" : typer.Argument(help="Local path to repository you want to configure."),
9090
}
9191

92-
@app.command(name="download", help="Download model/dataset/space from OpenCSG Hub", no_args_is_help=True)
92+
@app.command(name="download", help="Download model/dataset/space/code/mcp from OpenCSG Hub", no_args_is_help=True)
9393
def download(
9494
repo_id: Annotated[str, OPTIONS["repoID"]],
9595
filenames: Annotated[
@@ -112,7 +112,7 @@ def download(
112112
force_download: Annotated[Optional[bool], OPTIONS["force_download"]] = False,
113113
max_workers: Annotated[Optional[int], OPTIONS["max_workers"]] = 8,
114114
):
115-
api = get_csghub_api(token=token, endpoint=endpoint)
115+
api = get_csghub_api(repo_type=repo_type, token=token, endpoint=endpoint)
116116

117117
# Handle single/multiple file args similar to HF
118118
filenames_list = filenames if filenames is not None else []
@@ -186,7 +186,8 @@ def download(
186186
except Exception as e:
187187
if quiet:
188188
raise e
189-
print(f"Download failed: {e}")
189+
190+
logger.error(f"Download with xnet-disabled {disable_xnet()} failed: {e} ")
190191
raise typer.Exit(code=1)
191192

192193
if quiet:
@@ -196,7 +197,7 @@ def download(
196197
else:
197198
print_download_result(result)
198199

199-
@app.command(name="upload", help="Upload repository files to OpenCSG Hub", no_args_is_help=True)
200+
@app.command(name="upload", help="Upload model/dataset/space/code/mcp files to OpenCSG Hub", no_args_is_help=True)
200201
def upload(
201202
repo_id: Annotated[str, OPTIONS["repoID"]],
202203
local_path: Annotated[str, OPTIONS["localPath"]],
@@ -216,8 +217,9 @@ def upload(
216217
every: Annotated[Optional[float], OPTIONS["every"]] = None,
217218
quiet: Annotated[bool, OPTIONS["quiet"]] = False,
218219
):
220+
219221
repo_type_str = repo_type.value
220-
api = get_csghub_api(token=token, endpoint=endpoint, user_name=user_name)
222+
api = get_csghub_api(repo_type=repo_type, token=token, endpoint=endpoint, user_name=user_name)
221223

222224
resolved_local_path = local_path
223225
resolved_path_in_repo = path_in_repo
@@ -236,7 +238,7 @@ def run_upload() -> dict:
236238
# Placeholder for Scheduler
237239
# Scheduler not implemented yet in csghub-sdk
238240
# Similar to HF CommitScheduler logic
239-
print(f"Scheduling commits every {every} minutes... (Not fully implemented)")
241+
logger.info(f"Scheduling commits every {every} minutes... (Not fully implemented)")
240242
try:
241243
while True:
242244
time.sleep(100)
@@ -246,6 +248,7 @@ def run_upload() -> dict:
246248
if not os.path.isfile(resolved_local_path) and not os.path.isdir(resolved_local_path):
247249
raise FileNotFoundError(f"No such file or directory: '{resolved_local_path}'.")
248250

251+
logger.debug(f"Uploading {repo_type_str} {repo_id} from {resolved_local_path} to {resolved_path_in_repo}")
249252
# Create repo if needed
250253
api.create_repo(
251254
repo_id=repo_id,
@@ -259,7 +262,7 @@ def run_upload() -> dict:
259262
try:
260263
api.repo_info(repo_id=repo_id, repo_type=repo_type_str, revision=revision)
261264
except Exception: # RevisionNotFoundError
262-
logger.info(f"Branch '{revision}' not found. Creating it...")
265+
logger.warning(f"Branch '{revision}' not found. Creating it...")
263266
api.create_branch(repo_id=repo_id, repo_type=repo_type_str, branch=revision, exist_ok=True)
264267

265268
if os.path.isfile(resolved_local_path):
@@ -292,10 +295,12 @@ def run_upload() -> dict:
292295
disable_progress_bars()
293296
with warnings.catch_warnings():
294297
warnings.simplefilter("ignore")
295-
print(run_upload())
298+
result = run_upload()
299+
logger.debug(f"Upload result {result}")
296300
enable_progress_bars()
297301
else:
298-
print(run_upload())
302+
result = run_upload()
303+
logger.debug(f"Upload result {result}")
299304

300305
@app.command(name="upload-large-folder", help="Upload large folder to OpenCSG Hub using multiple workers",
301306
no_args_is_help=True)

pycsghub/cmd/repo_types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from enum import Enum
2-
from pycsghub.constants import REPO_TYPE_MODEL, REPO_TYPE_DATASET, REPO_TYPE_SPACE, REPO_TYPE_CODE
2+
from pycsghub.constants import REPO_TYPE_MODEL, REPO_TYPE_DATASET, REPO_TYPE_SPACE, REPO_TYPE_CODE, REPO_TYPE_MCPSERVER
33

44
class RepoType(str, Enum):
55
MODEL = REPO_TYPE_MODEL
66
DATASET = REPO_TYPE_DATASET
77
SPACE = REPO_TYPE_SPACE
88
CODE = REPO_TYPE_CODE
9+
MCP = REPO_TYPE_MCPSERVER

pycsghub/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
REPO_TYPE_MODEL = "model"
88
REPO_TYPE_SPACE = "space"
99
REPO_TYPE_CODE = "code"
10-
REPO_TYPES = [None, REPO_TYPE_MODEL, REPO_TYPE_DATASET, REPO_TYPE_SPACE]
10+
REPO_TYPE_MCPSERVER = "mcp"
11+
REPO_TYPES = [None, REPO_TYPE_MODEL, REPO_TYPE_DATASET, REPO_TYPE_SPACE, REPO_TYPE_CODE, REPO_TYPE_MCPSERVER]
1112

1213
REPO_SOURCE_CSG = "csg"
1314
REPO_SOURCE_HF = "hf"

pycsghub/file_download.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def file_download(
122122
if force_download or not cache.exists(repo_file_info):
123123
# get download url
124124
url = get_file_download_url(
125-
model_id=repo_id,
125+
repo_id=repo_id,
126126
file_path=file_name,
127127
revision=revision,
128128
endpoint=download_endpoint,

pycsghub/file_upload.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import requests
33
from typing import Optional
44
from pycsghub.constants import (DEFAULT_REVISION)
5-
from pycsghub.utils import (build_csg_headers, get_endpoint)
5+
from pycsghub.utils import (build_csg_headers, get_endpoint, get_repo_url_prefix)
66
import logging
77

88
logger = logging.getLogger(__name__)
@@ -23,7 +23,7 @@ def http_upload_file(
2323
http_endpoint = endpoint if endpoint is not None else get_endpoint()
2424
if not http_endpoint.endswith("/"):
2525
http_endpoint += "/"
26-
http_url = http_endpoint + "api/v1/" + repo_type + "s/" + repo_id + "/upload_file"
26+
http_url = http_endpoint + "api/v1/" + get_repo_url_prefix(repo_type=repo_type) + "/" + repo_id + "/upload_file"
2727
post_headers = build_csg_headers(token=token)
2828
file_data = {'file': open(file_path, 'rb')}
2929
form_data = {'file_path': destination_path, 'branch': revision, 'message': (commit_message or ('upload ' + os.path.basename(file_path)))}
@@ -34,7 +34,7 @@ def http_upload_file(
3434
elif response.status_code != 200 and exist_msg in response.text:
3535
logger.info(f"file '{file_path}' already exists.")
3636
else:
37-
msg = f"fail to upload {file_path} with response code: {response.status_code}, error: {response.content.decode()}"
37+
msg = f"failed to upload {file_path} to branch {revision} on {http_url} with response code: {response.status_code}, error: {response.content.decode()}"
3838
logger.error(msg)
3939
raise RuntimeError(msg)
4040

pycsghub/repository.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
from pycsghub.constants import (GIT_HIDDEN_DIR, GIT_ATTRIBUTES_FILE)
1818
from pycsghub.utils import (build_csg_headers,
1919
model_id_to_group_owner_name,
20-
get_endpoint)
20+
get_endpoint,
21+
get_repo_url_prefix,
22+
get_repo_git_prefix)
2123
import logging
2224

2325
logger = logging.getLogger(__name__)
@@ -88,14 +90,7 @@ def __init__(
8890
self.delete_patterns = delete_patterns
8991

9092
def get_url_prefix(self):
91-
if self.repo_type == REPO_TYPE_DATASET:
92-
return "datasets"
93-
if self.repo_type == REPO_TYPE_SPACE:
94-
return "spaces"
95-
if self.repo_type == REPO_TYPE_CODE:
96-
return "codes"
97-
else:
98-
return "models"
93+
return get_repo_url_prefix(repo_type=self.repo_type)
9994

10095
def upload(self) -> None:
10196
if not os.path.exists(self.upload_path):
@@ -267,17 +262,18 @@ def create_new_repo(self):
267262
"Content-Type": "application/json"
268263
})
269264
response = requests.post(url, json=data, headers=headers)
270-
exist_msg = "SYS-ERR-4: Duplicate entry for key"
265+
exist_msg = "duplicate key value violates unique constraint"
271266
if response.status_code != 200 and exist_msg not in response.text :
272-
logger.info(f"create repo on {url} response: {response.text}")
267+
logger.error(f"create repo on {url} response: {response.text}")
273268
response.raise_for_status()
274269
return response
275270

276271
def generate_repo_clone_url(self) -> str:
277272
clone_endpoint = get_endpoint(endpoint=self.endpoint, operation=OPERATION_ACTION_GIT)
278-
clone_url = f"{clone_endpoint}/{self.repo_url_prefix}/{self.repo_id}.git"
273+
clone_url = f"{clone_endpoint}/{get_repo_git_prefix(repo_type=self.repo_type)}/{self.repo_id}.git"
279274
scheme = urlparse(clone_url).scheme
280275
clone_url = clone_url.replace(f"{scheme}://", f"{scheme}://{self.user_name}:{self.token}@")
276+
logger.debug(f"generate clone url: {clone_url} on branch {self.branch_name}")
281277
return clone_url
282278

283279
def git_clone(

pycsghub/snapshot_download.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def _download_one(repo_file: str):
124124
logger.info(f"File {file_name} already in '{cache.get_root_location()}', skip downloading!")
125125
return
126126
url = get_file_download_url(
127-
model_id=repo_id,
127+
repo_id=repo_id,
128128
file_path=repo_file,
129129
repo_type=repo_type,
130130
revision=revision,

0 commit comments

Comments
 (0)