xiaoshiai-hub 1.1.2__py3-none-any.whl → 1.1.3__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/cli.py +457 -5
- xiaoshiai_hub/client.py +191 -3
- xiaoshiai_hub/types.py +4 -0
- xiaoshiai_hub/upload.py +58 -22
- {xiaoshiai_hub-1.1.2.dist-info → xiaoshiai_hub-1.1.3.dist-info}/METADATA +157 -7
- xiaoshiai_hub-1.1.3.dist-info/RECORD +15 -0
- xiaoshiai_hub-1.1.2.dist-info/RECORD +0 -15
- {xiaoshiai_hub-1.1.2.dist-info → xiaoshiai_hub-1.1.3.dist-info}/WHEEL +0 -0
- {xiaoshiai_hub-1.1.2.dist-info → xiaoshiai_hub-1.1.3.dist-info}/entry_points.txt +0 -0
- {xiaoshiai_hub-1.1.2.dist-info → xiaoshiai_hub-1.1.3.dist-info}/licenses/LICENSE +0 -0
- {xiaoshiai_hub-1.1.2.dist-info → xiaoshiai_hub-1.1.3.dist-info}/top_level.txt +0 -0
xiaoshiai_hub/cli.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
XiaoShi AI Hub SDK 命令行工具
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
@@ -263,6 +263,248 @@ def cmd_whoami(args):
|
|
|
263
263
|
return 1
|
|
264
264
|
|
|
265
265
|
|
|
266
|
+
def _parse_repo_id(repo_id: str):
|
|
267
|
+
"""解析仓库 ID,返回 (organization, repo_name)"""
|
|
268
|
+
parts = repo_id.split("/")
|
|
269
|
+
if len(parts) != 2:
|
|
270
|
+
raise ValueError(f"无效的仓库 ID: {repo_id},格式应为: 组织/仓库名")
|
|
271
|
+
return parts[0], parts[1]
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _get_client(args):
|
|
275
|
+
"""创建 HubClient 实例"""
|
|
276
|
+
from xiaoshiai_hub.client import HubClient
|
|
277
|
+
token, username, password = get_auth(args)
|
|
278
|
+
return HubClient(
|
|
279
|
+
base_url=args.base_url,
|
|
280
|
+
username=username,
|
|
281
|
+
password=password,
|
|
282
|
+
token=token,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def cmd_repo_create(args):
|
|
287
|
+
"""处理 repo-create 命令"""
|
|
288
|
+
try:
|
|
289
|
+
org, repo_name = _parse_repo_id(args.repo_id)
|
|
290
|
+
client = _get_client(args)
|
|
291
|
+
|
|
292
|
+
# 解析 metadata
|
|
293
|
+
metadata = {}
|
|
294
|
+
if args.license:
|
|
295
|
+
metadata["license"] = args.license
|
|
296
|
+
if args.tasks:
|
|
297
|
+
metadata["tasks"] = args.tasks
|
|
298
|
+
if args.languages:
|
|
299
|
+
metadata["languages"] = args.languages
|
|
300
|
+
if args.tags:
|
|
301
|
+
metadata["tags"] = args.tags
|
|
302
|
+
if args.frameworks:
|
|
303
|
+
metadata["frameworks"] = args.frameworks
|
|
304
|
+
|
|
305
|
+
client.create_repository(
|
|
306
|
+
organization=org,
|
|
307
|
+
repo_type=args.repo_type,
|
|
308
|
+
repo_name=repo_name,
|
|
309
|
+
description=args.description,
|
|
310
|
+
visibility=args.visibility,
|
|
311
|
+
metadata=metadata if metadata else None,
|
|
312
|
+
base_model=args.base_model,
|
|
313
|
+
relationship=args.relationship,
|
|
314
|
+
)
|
|
315
|
+
print(f"仓库创建成功: {org}/{repo_name}")
|
|
316
|
+
return 0
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print(f"错误: {e}", file=sys.stderr)
|
|
319
|
+
return 1
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def cmd_repo_update(args):
|
|
323
|
+
"""处理 repo-update 命令"""
|
|
324
|
+
try:
|
|
325
|
+
org, repo_name = _parse_repo_id(args.repo_id)
|
|
326
|
+
client = _get_client(args)
|
|
327
|
+
|
|
328
|
+
# 先获取仓库当前信息
|
|
329
|
+
repo = client.get_repository_info(
|
|
330
|
+
organization=org,
|
|
331
|
+
repo_type=args.repo_type,
|
|
332
|
+
repo_name=repo_name,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# 使用现有值作为默认值,只更新用户指定的字段
|
|
336
|
+
description = args.description if args.description is not None else repo.description
|
|
337
|
+
visibility = args.visibility if args.visibility is not None else repo.visibility
|
|
338
|
+
|
|
339
|
+
# 合并 metadata:用户指定的覆盖现有的
|
|
340
|
+
metadata = dict(repo.metadata) if repo.metadata else {}
|
|
341
|
+
if args.license:
|
|
342
|
+
metadata["license"] = args.license
|
|
343
|
+
if args.tasks:
|
|
344
|
+
metadata["tasks"] = args.tasks
|
|
345
|
+
if args.languages:
|
|
346
|
+
metadata["languages"] = args.languages
|
|
347
|
+
if args.tags:
|
|
348
|
+
metadata["tags"] = args.tags
|
|
349
|
+
if args.frameworks:
|
|
350
|
+
metadata["frameworks"] = args.frameworks
|
|
351
|
+
|
|
352
|
+
# 处理 genealogy:用户指定的覆盖现有的
|
|
353
|
+
base_model = args.base_model
|
|
354
|
+
relationship = args.relationship
|
|
355
|
+
if repo.genealogy:
|
|
356
|
+
if base_model is None and 'baseModel' in repo.genealogy:
|
|
357
|
+
base_model = repo.genealogy['baseModel']
|
|
358
|
+
if relationship is None and 'relationship' in repo.genealogy:
|
|
359
|
+
relationship = repo.genealogy['relationship']
|
|
360
|
+
|
|
361
|
+
client.update_repository(
|
|
362
|
+
organization=org,
|
|
363
|
+
repo_type=args.repo_type,
|
|
364
|
+
repo_name=repo_name,
|
|
365
|
+
description=description,
|
|
366
|
+
visibility=visibility,
|
|
367
|
+
metadata=metadata if metadata else None,
|
|
368
|
+
annotations=repo.annotations,
|
|
369
|
+
base_model=base_model,
|
|
370
|
+
relationship=relationship,
|
|
371
|
+
)
|
|
372
|
+
print(f"仓库更新成功: {org}/{repo_name}")
|
|
373
|
+
return 0
|
|
374
|
+
except Exception as e:
|
|
375
|
+
print(f"错误: {e}", file=sys.stderr)
|
|
376
|
+
return 1
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def cmd_repo_delete(args):
|
|
380
|
+
"""处理 repo-delete 命令"""
|
|
381
|
+
try:
|
|
382
|
+
org, repo_name = _parse_repo_id(args.repo_id)
|
|
383
|
+
client = _get_client(args)
|
|
384
|
+
|
|
385
|
+
# 确认删除
|
|
386
|
+
if not args.yes:
|
|
387
|
+
confirm = input(f"确定要删除仓库 {org}/{repo_name} 吗?此操作不可恢复![y/N]: ")
|
|
388
|
+
if confirm.lower() != 'y':
|
|
389
|
+
print("已取消删除")
|
|
390
|
+
return 0
|
|
391
|
+
|
|
392
|
+
client.delete_repository(
|
|
393
|
+
organization=org,
|
|
394
|
+
repo_type=args.repo_type,
|
|
395
|
+
repo_name=repo_name,
|
|
396
|
+
)
|
|
397
|
+
print(f"仓库删除成功: {org}/{repo_name}")
|
|
398
|
+
return 0
|
|
399
|
+
except Exception as e:
|
|
400
|
+
print(f"错误: {e}", file=sys.stderr)
|
|
401
|
+
return 1
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def cmd_repo_info(args):
|
|
405
|
+
"""处理 repo-info 命令"""
|
|
406
|
+
try:
|
|
407
|
+
org, repo_name = _parse_repo_id(args.repo_id)
|
|
408
|
+
client = _get_client(args)
|
|
409
|
+
|
|
410
|
+
repo = client.get_repository_info(
|
|
411
|
+
organization=org,
|
|
412
|
+
repo_type=args.repo_type,
|
|
413
|
+
repo_name=repo_name,
|
|
414
|
+
)
|
|
415
|
+
print(f"仓库名称: {repo.name}")
|
|
416
|
+
print(f"组织: {repo.organization}")
|
|
417
|
+
print(f"所有者: {repo.owner}")
|
|
418
|
+
print(f"创建者: {repo.creator}")
|
|
419
|
+
print(f"类型: {repo.type}")
|
|
420
|
+
print(f"可见性: {repo.visibility}")
|
|
421
|
+
if repo.description:
|
|
422
|
+
print(f"描述: {repo.description}")
|
|
423
|
+
if repo.genealogy:
|
|
424
|
+
print(f"模型血缘: {repo.genealogy}")
|
|
425
|
+
if repo.metadata:
|
|
426
|
+
print(f"元数据: {repo.metadata}")
|
|
427
|
+
if repo.annotations:
|
|
428
|
+
print(f"注解: {repo.annotations}")
|
|
429
|
+
return 0
|
|
430
|
+
except Exception as e:
|
|
431
|
+
print(f"错误: {e}", file=sys.stderr)
|
|
432
|
+
return 1
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def cmd_branch_create(args):
|
|
436
|
+
"""处理 branch-create 命令"""
|
|
437
|
+
try:
|
|
438
|
+
org, repo_name = _parse_repo_id(args.repo_id)
|
|
439
|
+
client = _get_client(args)
|
|
440
|
+
|
|
441
|
+
client.create_branch(
|
|
442
|
+
organization=org,
|
|
443
|
+
repo_type=args.repo_type,
|
|
444
|
+
repo_name=repo_name,
|
|
445
|
+
branch_name=args.branch,
|
|
446
|
+
from_branch=args.from_branch,
|
|
447
|
+
)
|
|
448
|
+
print(f"分支创建成功: {args.branch}")
|
|
449
|
+
return 0
|
|
450
|
+
except Exception as e:
|
|
451
|
+
print(f"错误: {e}", file=sys.stderr)
|
|
452
|
+
return 1
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def cmd_branch_delete(args):
|
|
456
|
+
"""处理 branch-delete 命令"""
|
|
457
|
+
try:
|
|
458
|
+
org, repo_name = _parse_repo_id(args.repo_id)
|
|
459
|
+
client = _get_client(args)
|
|
460
|
+
|
|
461
|
+
# 确认删除
|
|
462
|
+
if not args.yes:
|
|
463
|
+
confirm = input(f"确定要删除分支 {args.branch} 吗?[y/N]: ")
|
|
464
|
+
if confirm.lower() != 'y':
|
|
465
|
+
print("已取消删除")
|
|
466
|
+
return 0
|
|
467
|
+
|
|
468
|
+
client.delete_branch(
|
|
469
|
+
organization=org,
|
|
470
|
+
repo_type=args.repo_type,
|
|
471
|
+
repo_name=repo_name,
|
|
472
|
+
branch_name=args.branch,
|
|
473
|
+
)
|
|
474
|
+
print(f"分支删除成功: {args.branch}")
|
|
475
|
+
return 0
|
|
476
|
+
except Exception as e:
|
|
477
|
+
print(f"错误: {e}", file=sys.stderr)
|
|
478
|
+
return 1
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def cmd_branch_list(args):
|
|
482
|
+
"""处理 branch-list 命令"""
|
|
483
|
+
try:
|
|
484
|
+
org, repo_name = _parse_repo_id(args.repo_id)
|
|
485
|
+
client = _get_client(args)
|
|
486
|
+
|
|
487
|
+
refs = client.get_repository_refs(
|
|
488
|
+
organization=org,
|
|
489
|
+
repo_type=args.repo_type,
|
|
490
|
+
repo_name=repo_name,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
if not refs:
|
|
494
|
+
print("没有找到分支")
|
|
495
|
+
return 0
|
|
496
|
+
|
|
497
|
+
print(f"仓库 {org}/{repo_name} 的分支列表:")
|
|
498
|
+
for ref in refs:
|
|
499
|
+
commit_hash = ref.hash[:8] if ref.hash else 'N/A'
|
|
500
|
+
default_mark = " (默认)" if ref.is_default else ""
|
|
501
|
+
print(f" - {ref.name} [{ref.type}] (commit: {commit_hash}){default_mark}")
|
|
502
|
+
return 0
|
|
503
|
+
except Exception as e:
|
|
504
|
+
print(f"错误: {e}", file=sys.stderr)
|
|
505
|
+
return 1
|
|
506
|
+
|
|
507
|
+
|
|
266
508
|
def _add_common_args(parser):
|
|
267
509
|
"""添加通用参数"""
|
|
268
510
|
parser.add_argument(
|
|
@@ -306,15 +548,14 @@ def create_parser():
|
|
|
306
548
|
"""创建命令行参数解析器"""
|
|
307
549
|
parser = argparse.ArgumentParser(
|
|
308
550
|
prog="moha",
|
|
309
|
-
description="
|
|
551
|
+
description="晓石 AI Hub 命令行工具 - 模型及数据集管理",
|
|
310
552
|
)
|
|
311
553
|
|
|
312
554
|
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
|
313
555
|
|
|
314
556
|
# ========== 上传文件夹命令 ==========
|
|
315
557
|
upload_folder_parser = subparsers.add_parser(
|
|
316
|
-
"upload
|
|
317
|
-
aliases=["upload"],
|
|
558
|
+
"upload",
|
|
318
559
|
help="上传文件夹到仓库",
|
|
319
560
|
)
|
|
320
561
|
upload_folder_parser.add_argument("folder", help="要上传的文件夹路径")
|
|
@@ -380,7 +621,6 @@ def create_parser():
|
|
|
380
621
|
# ========== 下载仓库命令 ==========
|
|
381
622
|
download_repo_parser = subparsers.add_parser(
|
|
382
623
|
"download",
|
|
383
|
-
aliases=["download-repo"],
|
|
384
624
|
help="下载整个仓库",
|
|
385
625
|
)
|
|
386
626
|
download_repo_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
@@ -490,6 +730,218 @@ def create_parser():
|
|
|
490
730
|
)
|
|
491
731
|
whoami_parser.set_defaults(func=cmd_whoami)
|
|
492
732
|
|
|
733
|
+
# ========== 创建仓库命令 ==========
|
|
734
|
+
repo_create_parser = subparsers.add_parser(
|
|
735
|
+
"repo-create",
|
|
736
|
+
help="创建仓库",
|
|
737
|
+
)
|
|
738
|
+
repo_create_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
739
|
+
repo_create_parser.add_argument(
|
|
740
|
+
"--repo-type", "-t",
|
|
741
|
+
default="models",
|
|
742
|
+
choices=["models", "datasets"],
|
|
743
|
+
help="仓库类型(默认: models)",
|
|
744
|
+
)
|
|
745
|
+
repo_create_parser.add_argument(
|
|
746
|
+
"--description", "-d",
|
|
747
|
+
help="仓库描述",
|
|
748
|
+
)
|
|
749
|
+
repo_create_parser.add_argument(
|
|
750
|
+
"--visibility", "-v",
|
|
751
|
+
default="internal",
|
|
752
|
+
choices=["public", "internal", "private"],
|
|
753
|
+
help="可见性(默认: internal)",
|
|
754
|
+
)
|
|
755
|
+
repo_create_parser.add_argument(
|
|
756
|
+
"--license",
|
|
757
|
+
action="append",
|
|
758
|
+
help="许可证(可多次指定)",
|
|
759
|
+
)
|
|
760
|
+
repo_create_parser.add_argument(
|
|
761
|
+
"--tasks",
|
|
762
|
+
action="append",
|
|
763
|
+
help="任务类型(可多次指定)",
|
|
764
|
+
)
|
|
765
|
+
repo_create_parser.add_argument(
|
|
766
|
+
"--languages",
|
|
767
|
+
action="append",
|
|
768
|
+
help="语言(可多次指定)",
|
|
769
|
+
)
|
|
770
|
+
repo_create_parser.add_argument(
|
|
771
|
+
"--tags",
|
|
772
|
+
action="append",
|
|
773
|
+
help="标签(可多次指定)",
|
|
774
|
+
)
|
|
775
|
+
repo_create_parser.add_argument(
|
|
776
|
+
"--frameworks",
|
|
777
|
+
action="append",
|
|
778
|
+
help="框架(可多次指定)",
|
|
779
|
+
)
|
|
780
|
+
repo_create_parser.add_argument(
|
|
781
|
+
"--base-model",
|
|
782
|
+
action="append",
|
|
783
|
+
help="基础模型(可多次指定,格式: 组织/模型名)",
|
|
784
|
+
)
|
|
785
|
+
repo_create_parser.add_argument(
|
|
786
|
+
"--relationship",
|
|
787
|
+
choices=["adapter", "finetune", "quantized", "merge", "repackage"],
|
|
788
|
+
help="与基础模型的关系",
|
|
789
|
+
)
|
|
790
|
+
_add_common_args(repo_create_parser)
|
|
791
|
+
repo_create_parser.set_defaults(func=cmd_repo_create)
|
|
792
|
+
|
|
793
|
+
# ========== 更新仓库命令 ==========
|
|
794
|
+
repo_update_parser = subparsers.add_parser(
|
|
795
|
+
"repo-update",
|
|
796
|
+
help="更新仓库",
|
|
797
|
+
)
|
|
798
|
+
repo_update_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
799
|
+
repo_update_parser.add_argument(
|
|
800
|
+
"--repo-type", "-t",
|
|
801
|
+
default="models",
|
|
802
|
+
choices=["models", "datasets"],
|
|
803
|
+
help="仓库类型(默认: models)",
|
|
804
|
+
)
|
|
805
|
+
repo_update_parser.add_argument(
|
|
806
|
+
"--description", "-d",
|
|
807
|
+
help="仓库描述",
|
|
808
|
+
)
|
|
809
|
+
repo_update_parser.add_argument(
|
|
810
|
+
"--visibility", "-v",
|
|
811
|
+
choices=["public", "internal", "private"],
|
|
812
|
+
help="可见性",
|
|
813
|
+
)
|
|
814
|
+
repo_update_parser.add_argument(
|
|
815
|
+
"--license",
|
|
816
|
+
action="append",
|
|
817
|
+
help="许可证(可多次指定)",
|
|
818
|
+
)
|
|
819
|
+
repo_update_parser.add_argument(
|
|
820
|
+
"--tasks",
|
|
821
|
+
action="append",
|
|
822
|
+
help="任务类型(可多次指定)",
|
|
823
|
+
)
|
|
824
|
+
repo_update_parser.add_argument(
|
|
825
|
+
"--languages",
|
|
826
|
+
action="append",
|
|
827
|
+
help="语言(可多次指定)",
|
|
828
|
+
)
|
|
829
|
+
repo_update_parser.add_argument(
|
|
830
|
+
"--tags",
|
|
831
|
+
action="append",
|
|
832
|
+
help="标签(可多次指定)",
|
|
833
|
+
)
|
|
834
|
+
repo_update_parser.add_argument(
|
|
835
|
+
"--frameworks",
|
|
836
|
+
action="append",
|
|
837
|
+
help="框架(可多次指定)",
|
|
838
|
+
)
|
|
839
|
+
repo_update_parser.add_argument(
|
|
840
|
+
"--base-model",
|
|
841
|
+
action="append",
|
|
842
|
+
help="基础模型(可多次指定,格式: 组织/模型名)",
|
|
843
|
+
)
|
|
844
|
+
repo_update_parser.add_argument(
|
|
845
|
+
"--relationship",
|
|
846
|
+
choices=["adapter", "finetune", "quantized", "merge", "repackage"],
|
|
847
|
+
help="与基础模型的关系",
|
|
848
|
+
)
|
|
849
|
+
_add_common_args(repo_update_parser)
|
|
850
|
+
repo_update_parser.set_defaults(func=cmd_repo_update)
|
|
851
|
+
|
|
852
|
+
# ========== 删除仓库命令 ==========
|
|
853
|
+
repo_delete_parser = subparsers.add_parser(
|
|
854
|
+
"repo-delete",
|
|
855
|
+
help="删除仓库",
|
|
856
|
+
)
|
|
857
|
+
repo_delete_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
858
|
+
repo_delete_parser.add_argument(
|
|
859
|
+
"--repo-type", "-t",
|
|
860
|
+
default="models",
|
|
861
|
+
choices=["models", "datasets"],
|
|
862
|
+
help="仓库类型(默认: models)",
|
|
863
|
+
)
|
|
864
|
+
repo_delete_parser.add_argument(
|
|
865
|
+
"--yes", "-y",
|
|
866
|
+
action="store_true",
|
|
867
|
+
help="跳过确认提示",
|
|
868
|
+
)
|
|
869
|
+
_add_common_args(repo_delete_parser)
|
|
870
|
+
repo_delete_parser.set_defaults(func=cmd_repo_delete)
|
|
871
|
+
|
|
872
|
+
# ========== 查看仓库信息命令 ==========
|
|
873
|
+
repo_info_parser = subparsers.add_parser(
|
|
874
|
+
"repo-info",
|
|
875
|
+
help="查看仓库信息",
|
|
876
|
+
)
|
|
877
|
+
repo_info_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
878
|
+
repo_info_parser.add_argument(
|
|
879
|
+
"--repo-type", "-t",
|
|
880
|
+
default="models",
|
|
881
|
+
choices=["models", "datasets"],
|
|
882
|
+
help="仓库类型(默认: models)",
|
|
883
|
+
)
|
|
884
|
+
_add_common_args(repo_info_parser)
|
|
885
|
+
repo_info_parser.set_defaults(func=cmd_repo_info)
|
|
886
|
+
|
|
887
|
+
# ========== 创建分支命令 ==========
|
|
888
|
+
branch_create_parser = subparsers.add_parser(
|
|
889
|
+
"branch-create",
|
|
890
|
+
help="创建分支",
|
|
891
|
+
)
|
|
892
|
+
branch_create_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
893
|
+
branch_create_parser.add_argument("branch", help="要创建的分支名称")
|
|
894
|
+
branch_create_parser.add_argument(
|
|
895
|
+
"--from", "-f",
|
|
896
|
+
dest="from_branch",
|
|
897
|
+
default="main",
|
|
898
|
+
help="基于哪个分支创建(默认: main)",
|
|
899
|
+
)
|
|
900
|
+
branch_create_parser.add_argument(
|
|
901
|
+
"--repo-type", "-t",
|
|
902
|
+
default="models",
|
|
903
|
+
choices=["models", "datasets"],
|
|
904
|
+
help="仓库类型(默认: models)",
|
|
905
|
+
)
|
|
906
|
+
_add_common_args(branch_create_parser)
|
|
907
|
+
branch_create_parser.set_defaults(func=cmd_branch_create)
|
|
908
|
+
|
|
909
|
+
# ========== 删除分支命令 ==========
|
|
910
|
+
branch_delete_parser = subparsers.add_parser(
|
|
911
|
+
"branch-delete",
|
|
912
|
+
help="删除分支",
|
|
913
|
+
)
|
|
914
|
+
branch_delete_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
915
|
+
branch_delete_parser.add_argument("branch", help="要删除的分支名称")
|
|
916
|
+
branch_delete_parser.add_argument(
|
|
917
|
+
"--repo-type", "-t",
|
|
918
|
+
default="models",
|
|
919
|
+
choices=["models", "datasets"],
|
|
920
|
+
help="仓库类型(默认: models)",
|
|
921
|
+
)
|
|
922
|
+
branch_delete_parser.add_argument(
|
|
923
|
+
"--yes", "-y",
|
|
924
|
+
action="store_true",
|
|
925
|
+
help="跳过确认提示",
|
|
926
|
+
)
|
|
927
|
+
_add_common_args(branch_delete_parser)
|
|
928
|
+
branch_delete_parser.set_defaults(func=cmd_branch_delete)
|
|
929
|
+
|
|
930
|
+
# ========== 列出分支命令 ==========
|
|
931
|
+
branch_list_parser = subparsers.add_parser(
|
|
932
|
+
"branch-list",
|
|
933
|
+
help="列出仓库的所有分支",
|
|
934
|
+
)
|
|
935
|
+
branch_list_parser.add_argument("repo_id", help="仓库 ID(格式: 组织/仓库名)")
|
|
936
|
+
branch_list_parser.add_argument(
|
|
937
|
+
"--repo-type", "-t",
|
|
938
|
+
default="models",
|
|
939
|
+
choices=["models", "datasets"],
|
|
940
|
+
help="仓库类型(默认: models)",
|
|
941
|
+
)
|
|
942
|
+
_add_common_args(branch_list_parser)
|
|
943
|
+
branch_list_parser.set_defaults(func=cmd_branch_list)
|
|
944
|
+
|
|
493
945
|
return parser
|
|
494
946
|
|
|
495
947
|
|
xiaoshiai_hub/client.py
CHANGED
|
@@ -110,8 +110,186 @@ class HubClient:
|
|
|
110
110
|
url = f"{self.base_url}/moha/v1/organizations/{organization}/{repo_type}/{repo_name}/encryption/set"
|
|
111
111
|
response = self._make_request("PUT", url)
|
|
112
112
|
if response.status_code != 200:
|
|
113
|
-
raise HTTPError(f"Failed to set repository encrypted flag: {response.text}")
|
|
113
|
+
raise HTTPError(f"Failed to set repository encrypted flag: {response.text}")
|
|
114
|
+
|
|
115
|
+
# 创建分支
|
|
116
|
+
def create_branch(
|
|
117
|
+
self,
|
|
118
|
+
organization: str,
|
|
119
|
+
repo_type: str,
|
|
120
|
+
repo_name: str,
|
|
121
|
+
branch_name: str,
|
|
122
|
+
from_branch: str,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""
|
|
125
|
+
创建新分支。如果分支已存在,则直接返回。
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
organization: 组织名称
|
|
129
|
+
repo_type: 仓库类型 ("models" 或 "datasets")
|
|
130
|
+
repo_name: 仓库名称
|
|
131
|
+
branch_name: 要创建的分支名称
|
|
132
|
+
from_branch: 基于哪个分支创建
|
|
133
|
+
"""
|
|
134
|
+
# 先检查分支是否已存在
|
|
135
|
+
refs = self.get_repository_refs(organization, repo_type, repo_name)
|
|
136
|
+
for ref in refs:
|
|
137
|
+
if ref.name == branch_name:
|
|
138
|
+
# 分支已存在,直接返回
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
# 分支不存在,创建新分支
|
|
142
|
+
url = f"{self.base_url}/moha/v1/organizations/{organization}/{repo_type}/{repo_name}/refs/{branch_name}"
|
|
143
|
+
body = {
|
|
144
|
+
"base": from_branch,
|
|
145
|
+
}
|
|
146
|
+
response = self._make_request("POST", url, json=body)
|
|
147
|
+
if response.status_code != 200:
|
|
148
|
+
raise HTTPError(f"Failed to create branch: {response.text}")
|
|
149
|
+
|
|
150
|
+
# 删除分支
|
|
151
|
+
def delete_branch(
|
|
152
|
+
self,
|
|
153
|
+
organization: str,
|
|
154
|
+
repo_type: str,
|
|
155
|
+
repo_name: str,
|
|
156
|
+
branch_name: str,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""
|
|
159
|
+
删除分支。如果分支不存在,则直接返回。
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
organization: 组织名称
|
|
163
|
+
repo_type: 仓库类型 ("models" 或 "datasets")
|
|
164
|
+
repo_name: 仓库名称
|
|
165
|
+
branch_name: 要删除的分支名称
|
|
166
|
+
"""
|
|
167
|
+
# 先检查分支是否存在
|
|
168
|
+
refs = self.get_repository_refs(organization, repo_type, repo_name)
|
|
169
|
+
branch_exists = False
|
|
170
|
+
for ref in refs:
|
|
171
|
+
if ref.name == branch_name:
|
|
172
|
+
branch_exists = True
|
|
173
|
+
break
|
|
174
|
+
|
|
175
|
+
if not branch_exists:
|
|
176
|
+
# 分支不存在,直接返回
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
# 分支存在,删除它
|
|
180
|
+
url = f"{self.base_url}/moha/v1/organizations/{organization}/{repo_type}/{repo_name}/refs/{branch_name}"
|
|
181
|
+
response = self._make_request("DELETE", url)
|
|
182
|
+
if response.status_code != 200:
|
|
183
|
+
raise HTTPError(f"Failed to delete branch: {response.text}")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# 更新仓库,先获取仓库信息,然后更新
|
|
187
|
+
def update_repository(
|
|
188
|
+
self,
|
|
189
|
+
organization: str,
|
|
190
|
+
repo_type: str,
|
|
191
|
+
repo_name: str,
|
|
192
|
+
description: Optional[str] = None,
|
|
193
|
+
visibility: str = "internal",
|
|
194
|
+
annotations: Optional[Dict[str, str]] = None,
|
|
195
|
+
metadata: Optional[Dict[str, List[str]]] = None,
|
|
196
|
+
base_model: Optional[List[str]] = None,
|
|
197
|
+
relationship: Optional[str] = None,
|
|
198
|
+
) -> None:
|
|
199
|
+
"""Update repository information."""
|
|
200
|
+
url = f"{self.base_url}/moha/v1/organizations/{organization}/{repo_type}/{repo_name}"
|
|
201
|
+
body: Dict = {
|
|
202
|
+
"name": repo_name,
|
|
203
|
+
"organization": organization,
|
|
204
|
+
"type": repo_type,
|
|
205
|
+
}
|
|
206
|
+
if annotations:
|
|
207
|
+
body["annotations"] = annotations
|
|
208
|
+
if description:
|
|
209
|
+
body["description"] = description
|
|
210
|
+
if visibility:
|
|
211
|
+
body["visibility"] = visibility
|
|
212
|
+
if metadata:
|
|
213
|
+
body["metadata"] = metadata
|
|
214
|
+
if base_model or relationship:
|
|
215
|
+
body["requestgenealogy"] = {}
|
|
216
|
+
if base_model:
|
|
217
|
+
body["requestgenealogy"]["baseModel"] = base_model
|
|
218
|
+
if relationship:
|
|
219
|
+
body["requestgenealogy"]["relationship"] = relationship
|
|
220
|
+
response = self._make_request("PUT", url, json=body)
|
|
221
|
+
if response.status_code != 200:
|
|
222
|
+
raise HTTPError(f"Failed to update repository: {response.text}")
|
|
114
223
|
|
|
224
|
+
# 删除仓库
|
|
225
|
+
def delete_repository(
|
|
226
|
+
self,
|
|
227
|
+
organization: str,
|
|
228
|
+
repo_type: str,
|
|
229
|
+
repo_name: str,
|
|
230
|
+
) -> None:
|
|
231
|
+
"""Delete repository."""
|
|
232
|
+
url = f"{self.base_url}/moha/v1/organizations/{organization}/{repo_type}/{repo_name}"
|
|
233
|
+
response = self._make_request("DELETE", url)
|
|
234
|
+
if response.status_code != 200:
|
|
235
|
+
raise HTTPError(f"Failed to delete repository: {response.text}")
|
|
236
|
+
|
|
237
|
+
# 创建仓库
|
|
238
|
+
def create_repository(
|
|
239
|
+
self,
|
|
240
|
+
organization: str,
|
|
241
|
+
repo_type: str,
|
|
242
|
+
repo_name: str,
|
|
243
|
+
description: Optional[str] = None,
|
|
244
|
+
visibility: str = "internal",
|
|
245
|
+
metadata: Optional[Dict[str, List[str]]] = None,
|
|
246
|
+
base_model: Optional[List[str]] = None,
|
|
247
|
+
relationship: Optional[str] = None,
|
|
248
|
+
) -> None:
|
|
249
|
+
"""
|
|
250
|
+
创建新仓库。
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
organization: 组织名称
|
|
254
|
+
repo_type: 仓库类型 ("models" 或 "datasets")
|
|
255
|
+
repo_name: 仓库名称
|
|
256
|
+
description: 仓库描述
|
|
257
|
+
visibility: 可见性 ("public", "internal", "private")
|
|
258
|
+
metadata: 元数据,包含 license, tasks, languages, tags, frameworks 等
|
|
259
|
+
annotations: 注解
|
|
260
|
+
base_model: 基础模型列表 (如 ["demo/yyyy"])
|
|
261
|
+
relationship: 与基础模型的关系 ("adapter", "finetune", "quantized", "merge", "repackage" 等)
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
创建的仓库信息
|
|
265
|
+
"""
|
|
266
|
+
url = f"{self.base_url}/moha/v1/organizations/{organization}/{repo_type}"
|
|
267
|
+
|
|
268
|
+
# 构建请求体
|
|
269
|
+
body: Dict = {
|
|
270
|
+
"name": repo_name,
|
|
271
|
+
"organization": organization,
|
|
272
|
+
"visibility": visibility,
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if description:
|
|
276
|
+
body["description"] = description
|
|
277
|
+
if metadata:
|
|
278
|
+
body["metadata"] = metadata
|
|
279
|
+
|
|
280
|
+
# 构建 genealogy(模型谱系)
|
|
281
|
+
if base_model or relationship:
|
|
282
|
+
body["requestgenealogy"] = {}
|
|
283
|
+
if base_model:
|
|
284
|
+
body["requestgenealogy"]["baseModel"] = base_model
|
|
285
|
+
if relationship:
|
|
286
|
+
body["requestgenealogy"]["relationship"] = relationship
|
|
287
|
+
|
|
288
|
+
response = self._make_request("POST", url, json=body)
|
|
289
|
+
if response.status_code != 200:
|
|
290
|
+
raise HTTPError(f"Failed to create repository: {response.text}")
|
|
291
|
+
|
|
292
|
+
# 获取仓库信息
|
|
115
293
|
def get_repository_info(
|
|
116
294
|
self,
|
|
117
295
|
organization: str,
|
|
@@ -143,15 +321,25 @@ class HubClient:
|
|
|
143
321
|
if 'metadata' in data and isinstance(data['metadata'], dict):
|
|
144
322
|
metadata = data['metadata']
|
|
145
323
|
|
|
324
|
+
# Parse genealogy if present
|
|
325
|
+
genealogy: Optional[Dict] = None
|
|
326
|
+
if 'genealogy' in data and isinstance(data['genealogy'], dict):
|
|
327
|
+
genealogy = data['genealogy']
|
|
328
|
+
|
|
146
329
|
return Repository(
|
|
147
330
|
name=data.get('name', repo_name),
|
|
148
|
-
organization=organization,
|
|
149
|
-
|
|
331
|
+
organization=data.get('organization', organization),
|
|
332
|
+
owner=data.get('owner', ''),
|
|
333
|
+
creator=data.get('creator', ''),
|
|
334
|
+
type=data.get('type', repo_type),
|
|
335
|
+
visibility=data.get('visibility', 'internal'),
|
|
336
|
+
genealogy=genealogy,
|
|
150
337
|
description=data.get('description'),
|
|
151
338
|
metadata=metadata,
|
|
152
339
|
annotations=annotations,
|
|
153
340
|
)
|
|
154
341
|
|
|
342
|
+
# 获取仓库的所有分支
|
|
155
343
|
def get_repository_refs(
|
|
156
344
|
self,
|
|
157
345
|
organization: str,
|
xiaoshiai_hub/types.py
CHANGED
|
@@ -12,7 +12,11 @@ class Repository:
|
|
|
12
12
|
"""Repository information."""
|
|
13
13
|
name: str
|
|
14
14
|
organization: str
|
|
15
|
+
owner: str
|
|
16
|
+
creator: str
|
|
15
17
|
type: str # "models" or "datasets"
|
|
18
|
+
visibility: str
|
|
19
|
+
genealogy: Optional[Dict] = None
|
|
16
20
|
description: Optional[str] = None
|
|
17
21
|
metadata: Dict[str, List[str]] = field(default_factory=dict) # Repository metadata
|
|
18
22
|
annotations: Dict[str, str] = field(default_factory=dict) # Repository annotations/metadata
|
xiaoshiai_hub/upload.py
CHANGED
|
@@ -70,55 +70,91 @@ def _calculate_file_sha256(file_path: Path) -> str:
|
|
|
70
70
|
return sha256_hash.hexdigest()
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
class
|
|
74
|
-
"""
|
|
73
|
+
class _ProgressFileReader:
|
|
74
|
+
"""
|
|
75
|
+
文件读取器,跟踪实际网络上传进度。
|
|
76
|
+
|
|
77
|
+
通过分块读取文件并在每次 read() 调用后更新进度条,
|
|
78
|
+
配合 requests 的流式上传,确保进度条反映实际网络传输进度。
|
|
79
|
+
"""
|
|
75
80
|
|
|
76
|
-
def __init__(self,
|
|
77
|
-
self.
|
|
78
|
-
self.
|
|
81
|
+
def __init__(self, file_path: Path, chunk_size: int = 8192, desc: Optional[str] = None):
|
|
82
|
+
self.file_path = file_path
|
|
83
|
+
self.file_size = file_path.stat().st_size
|
|
84
|
+
self.chunk_size = chunk_size
|
|
85
|
+
self.file_obj = open(file_path, 'rb')
|
|
86
|
+
self.bytes_read = 0
|
|
79
87
|
self.pbar = None
|
|
80
88
|
if tqdm:
|
|
81
89
|
self.pbar = tqdm(
|
|
82
|
-
total=
|
|
90
|
+
total=self.file_size,
|
|
83
91
|
unit='B',
|
|
84
92
|
unit_scale=True,
|
|
85
93
|
unit_divisor=1024,
|
|
86
94
|
desc=desc,
|
|
87
95
|
)
|
|
88
96
|
|
|
89
|
-
def read(self, size
|
|
90
|
-
"""
|
|
97
|
+
def read(self, size: int = -1) -> bytes:
|
|
98
|
+
"""读取数据并更新进度条。"""
|
|
99
|
+
if size == -1:
|
|
100
|
+
size = self.chunk_size
|
|
91
101
|
data = self.file_obj.read(size)
|
|
92
|
-
if
|
|
93
|
-
self.
|
|
102
|
+
if data:
|
|
103
|
+
self.bytes_read += len(data)
|
|
104
|
+
if self.pbar is not None:
|
|
105
|
+
self.pbar.update(len(data))
|
|
94
106
|
return data
|
|
95
107
|
|
|
96
|
-
def
|
|
108
|
+
def __len__(self) -> int:
|
|
109
|
+
"""返回文件大小,供 requests 设置 Content-Length。"""
|
|
110
|
+
return self.file_size
|
|
111
|
+
|
|
112
|
+
def __iter__(self):
|
|
113
|
+
"""迭代器,用于流式上传。"""
|
|
97
114
|
return self
|
|
98
115
|
|
|
99
|
-
def
|
|
100
|
-
|
|
116
|
+
def __next__(self) -> bytes:
|
|
117
|
+
data = self.read(self.chunk_size)
|
|
118
|
+
if data:
|
|
119
|
+
return data
|
|
120
|
+
raise StopIteration
|
|
121
|
+
|
|
122
|
+
def close(self):
|
|
101
123
|
if self.pbar is not None:
|
|
102
124
|
self.pbar.close()
|
|
103
125
|
self.file_obj.close()
|
|
104
126
|
|
|
127
|
+
def __enter__(self):
|
|
128
|
+
return self
|
|
129
|
+
|
|
130
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
131
|
+
del exc_type, exc_val, exc_tb
|
|
132
|
+
self.close()
|
|
133
|
+
|
|
105
134
|
|
|
106
135
|
def _upload_file_with_progress(
|
|
107
136
|
upload_url: str,
|
|
108
137
|
file_path: Path,
|
|
109
138
|
desc: Optional[str] = None,
|
|
139
|
+
chunk_size: int = 1024 * 1024, # 1MB chunks
|
|
110
140
|
) -> None:
|
|
111
|
-
"""
|
|
141
|
+
"""
|
|
142
|
+
上传文件到 URL 并显示真实的网络传输进度。
|
|
143
|
+
|
|
144
|
+
使用分块流式上传,进度条反映实际发送到服务器的字节数。
|
|
145
|
+
"""
|
|
112
146
|
file_size = file_path.stat().st_size
|
|
113
147
|
|
|
114
|
-
with
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
148
|
+
with _ProgressFileReader(file_path, chunk_size=chunk_size, desc=desc) as reader:
|
|
149
|
+
upload_response = requests.put(
|
|
150
|
+
upload_url,
|
|
151
|
+
data=reader,
|
|
152
|
+
headers={
|
|
153
|
+
'Content-Type': 'application/octet-stream',
|
|
154
|
+
'Content-Length': str(file_size),
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
upload_response.raise_for_status()
|
|
122
158
|
|
|
123
159
|
|
|
124
160
|
def _encrypt_file_if_needed(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xiaoshiai-hub
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
4
4
|
Summary: Python SDK for XiaoShi AI Hub - Upload, download, and manage AI models and datasets with xpai-enc encryption support
|
|
5
5
|
Home-page: https://github.com/poxiaoyun/moha-sdk
|
|
6
6
|
Author: XiaoShi AI
|
|
@@ -244,20 +244,53 @@ client = HubClient(
|
|
|
244
244
|
password="your-password",
|
|
245
245
|
)
|
|
246
246
|
|
|
247
|
+
# 创建仓库
|
|
248
|
+
repo = client.create_repository(
|
|
249
|
+
organization="demo",
|
|
250
|
+
repo_type="models",
|
|
251
|
+
repo_name="my-model",
|
|
252
|
+
description="我的模型",
|
|
253
|
+
visibility="internal",
|
|
254
|
+
metadata={
|
|
255
|
+
"license": ["apache-2.0"],
|
|
256
|
+
"frameworks": ["transformers"],
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
print(f"仓库已创建: {repo.name}")
|
|
260
|
+
|
|
247
261
|
# 获取仓库信息
|
|
248
262
|
repo_info = client.get_repository_info("demo", "models", "my-model")
|
|
249
263
|
print(f"仓库名称: {repo_info.name}")
|
|
250
264
|
print(f"组织: {repo_info.organization}")
|
|
265
|
+
print(f"所有者: {repo_info.owner}")
|
|
266
|
+
print(f"可见性: {repo_info.visibility}")
|
|
267
|
+
|
|
268
|
+
# 更新仓库
|
|
269
|
+
client.update_repository(
|
|
270
|
+
organization="demo",
|
|
271
|
+
repo_type="models",
|
|
272
|
+
repo_name="my-model",
|
|
273
|
+
description="更新后的描述",
|
|
274
|
+
)
|
|
251
275
|
|
|
252
276
|
# 列出分支
|
|
253
|
-
|
|
254
|
-
for
|
|
255
|
-
print(f"分支: {
|
|
277
|
+
refs = client.get_repository_refs("demo", "models", "my-model")
|
|
278
|
+
for ref in refs:
|
|
279
|
+
print(f"分支: {ref.name} (commit: {ref.hash[:8]})")
|
|
280
|
+
|
|
281
|
+
# 创建分支(幂等操作,已存在则直接返回)
|
|
282
|
+
client.create_branch("demo", "models", "my-model", "dev", "main")
|
|
283
|
+
|
|
284
|
+
# 删除分支(幂等操作,不存在则直接返回)
|
|
285
|
+
client.delete_branch("demo", "models", "my-model", "dev")
|
|
256
286
|
|
|
257
287
|
# 浏览仓库内容
|
|
258
288
|
content = client.get_repository_content("demo", "models", "my-model", "main")
|
|
259
289
|
for entry in content.entries:
|
|
260
290
|
print(f"{entry.type}: {entry.name}")
|
|
291
|
+
|
|
292
|
+
# 删除仓库
|
|
293
|
+
client.delete_repository("demo", "models", "my-model")
|
|
261
294
|
```
|
|
262
295
|
|
|
263
296
|
## 🔐 加密功能
|
|
@@ -343,7 +376,7 @@ export MOHA_ENCRYPTION_PASSWORD="your-encryption-password"
|
|
|
343
376
|
|
|
344
377
|
## 🖥️ 命令行工具 (CLI)
|
|
345
378
|
|
|
346
|
-
SDK 提供了 `moha`
|
|
379
|
+
SDK 提供了 `moha` 命令行工具,支持登录认证、仓库管理、分支管理、上传下载等操作。
|
|
347
380
|
|
|
348
381
|
### 基本用法
|
|
349
382
|
|
|
@@ -351,6 +384,75 @@ SDK 提供了 `moha` 命令行工具,支持常见的上传下载操作。
|
|
|
351
384
|
moha --help
|
|
352
385
|
```
|
|
353
386
|
|
|
387
|
+
### 登录认证
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
# 登录(交互式输入用户名和密码)
|
|
391
|
+
moha login
|
|
392
|
+
|
|
393
|
+
# 直接指定用户名和密码
|
|
394
|
+
moha login --username your-username --password your-password
|
|
395
|
+
|
|
396
|
+
# 查看当前登录状态
|
|
397
|
+
moha whoami
|
|
398
|
+
|
|
399
|
+
# 退出登录
|
|
400
|
+
moha logout
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
登录后,Token 会保存到 `~/.moha/token.json`,后续命令无需重复输入认证信息。
|
|
404
|
+
|
|
405
|
+
### 仓库管理
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
# 创建仓库
|
|
409
|
+
moha repo-create org/my-model \
|
|
410
|
+
--description "我的模型" \
|
|
411
|
+
--visibility internal \
|
|
412
|
+
--license apache-2.0 \
|
|
413
|
+
--tasks text-generation \
|
|
414
|
+
--frameworks transformers
|
|
415
|
+
|
|
416
|
+
# 创建数据集仓库
|
|
417
|
+
moha repo-create org/my-dataset \
|
|
418
|
+
--repo-type datasets \
|
|
419
|
+
--description "我的数据集" \
|
|
420
|
+
--visibility private
|
|
421
|
+
|
|
422
|
+
# 查看仓库信息
|
|
423
|
+
moha repo-info org/my-model
|
|
424
|
+
|
|
425
|
+
# 更新仓库信息
|
|
426
|
+
moha repo-update org/my-model \
|
|
427
|
+
--description "更新后的描述" \
|
|
428
|
+
--tags production
|
|
429
|
+
|
|
430
|
+
# 删除仓库(需要确认)
|
|
431
|
+
moha repo-delete org/my-model
|
|
432
|
+
|
|
433
|
+
# 跳过确认直接删除
|
|
434
|
+
moha repo-delete org/my-model -y
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 分支管理
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# 列出仓库的所有分支
|
|
441
|
+
moha branch-list org/my-model
|
|
442
|
+
|
|
443
|
+
# 创建分支(基于 main 分支)
|
|
444
|
+
moha branch-create org/my-model dev
|
|
445
|
+
|
|
446
|
+
# 创建分支(基于指定分支)
|
|
447
|
+
moha branch-create org/my-model feature --from dev
|
|
448
|
+
|
|
449
|
+
# 删除分支
|
|
450
|
+
moha branch-delete org/my-model dev
|
|
451
|
+
|
|
452
|
+
# 跳过确认直接删除
|
|
453
|
+
moha branch-delete org/my-model dev -y
|
|
454
|
+
```
|
|
455
|
+
|
|
354
456
|
### 上传文件夹
|
|
355
457
|
|
|
356
458
|
```bash
|
|
@@ -444,16 +546,42 @@ moha download-file org/my-model model.safetensors \
|
|
|
444
546
|
--password your-password
|
|
445
547
|
```
|
|
446
548
|
|
|
549
|
+
### CLI 命令列表
|
|
550
|
+
|
|
551
|
+
| 命令 | 说明 |
|
|
552
|
+
|------|------|
|
|
553
|
+
| `moha login` | 登录并保存 Token |
|
|
554
|
+
| `moha logout` | 退出登录并删除 Token |
|
|
555
|
+
| `moha whoami` | 查看当前登录状态 |
|
|
556
|
+
| `moha repo-create` | 创建仓库 |
|
|
557
|
+
| `moha repo-update` | 更新仓库 |
|
|
558
|
+
| `moha repo-delete` | 删除仓库 |
|
|
559
|
+
| `moha repo-info` | 查看仓库信息 |
|
|
560
|
+
| `moha branch-create` | 创建分支 |
|
|
561
|
+
| `moha branch-delete` | 删除分支 |
|
|
562
|
+
| `moha branch-list` | 列出仓库的所有分支 |
|
|
563
|
+
| `moha upload` | 上传文件夹到仓库 |
|
|
564
|
+
| `moha upload-file` | 上传单个文件到仓库 |
|
|
565
|
+
| `moha download` | 下载整个仓库 |
|
|
566
|
+
| `moha download-file` | 从仓库下载单个文件 |
|
|
567
|
+
|
|
447
568
|
### CLI 参数说明
|
|
448
569
|
|
|
570
|
+
#### 通用参数
|
|
571
|
+
|
|
449
572
|
| 参数 | 说明 | 适用命令 |
|
|
450
573
|
|------|------|----------|
|
|
451
|
-
| `--repo-type, -t` | 仓库类型:`models` 或 `datasets`(默认:models) |
|
|
452
|
-
| `--revision, -r` | 分支/标签/提交(默认:main) | 所有 |
|
|
574
|
+
| `--repo-type, -t` | 仓库类型:`models` 或 `datasets`(默认:models) | 大部分命令 |
|
|
453
575
|
| `--base-url` | API 基础 URL(默认:环境变量 MOHA_ENDPOINT) | 所有 |
|
|
454
576
|
| `--token` | 认证令牌 | 所有 |
|
|
455
577
|
| `--username` | 用户名 | 所有 |
|
|
456
578
|
| `--password` | 密码 | 所有 |
|
|
579
|
+
|
|
580
|
+
#### 上传/下载参数
|
|
581
|
+
|
|
582
|
+
| 参数 | 说明 | 适用命令 |
|
|
583
|
+
|------|------|----------|
|
|
584
|
+
| `--revision, -r` | 分支/标签/提交(默认:main) | upload, download |
|
|
457
585
|
| `--message, -m` | 提交消息 | upload, upload-file |
|
|
458
586
|
| `--ignore, -i` | 忽略模式(可多次使用) | upload, download |
|
|
459
587
|
| `--include` | 包含模式(可多次使用) | download |
|
|
@@ -465,6 +593,28 @@ moha download-file org/my-model model.safetensors \
|
|
|
465
593
|
| `--local-dir, -o` | 本地保存目录 | download, download-file |
|
|
466
594
|
| `--quiet, -q` | 禁用进度条 | download, download-file |
|
|
467
595
|
|
|
596
|
+
#### 仓库管理参数
|
|
597
|
+
|
|
598
|
+
| 参数 | 说明 | 适用命令 |
|
|
599
|
+
|------|------|----------|
|
|
600
|
+
| `--description, -d` | 仓库描述 | repo-create, repo-update |
|
|
601
|
+
| `--visibility, -v` | 可见性:`public`、`internal`、`private` | repo-create, repo-update |
|
|
602
|
+
| `--license` | 许可证(可多次使用) | repo-create, repo-update |
|
|
603
|
+
| `--tasks` | 任务类型(可多次使用) | repo-create, repo-update |
|
|
604
|
+
| `--languages` | 语言(可多次使用) | repo-create, repo-update |
|
|
605
|
+
| `--tags` | 标签(可多次使用) | repo-create, repo-update |
|
|
606
|
+
| `--frameworks` | 框架(可多次使用) | repo-create, repo-update |
|
|
607
|
+
| `--base-model` | 基础模型(可多次使用) | repo-create, repo-update |
|
|
608
|
+
| `--relationship` | 与基础模型的关系 | repo-create, repo-update |
|
|
609
|
+
| `--yes, -y` | 跳过确认提示 | repo-delete, branch-delete |
|
|
610
|
+
|
|
611
|
+
#### 分支管理参数
|
|
612
|
+
|
|
613
|
+
| 参数 | 说明 | 适用命令 |
|
|
614
|
+
|------|------|----------|
|
|
615
|
+
| `--from, -f` | 基于哪个分支创建(默认:main) | branch-create |
|
|
616
|
+
| `--yes, -y` | 跳过确认提示 | branch-delete |
|
|
617
|
+
|
|
468
618
|
### 使用环境变量
|
|
469
619
|
|
|
470
620
|
可以通过环境变量设置认证信息,避免每次输入:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
xiaoshiai_hub/__init__.py,sha256=2hnhTGad1GPTzRstfwZhAF2204Pl2VF8YHD8HQkAYGY,1469
|
|
2
|
+
xiaoshiai_hub/auth.py,sha256=Pv0P6f76flOefYmALyehuMu2Klyc-u_gtXlJYJR4eMY,4105
|
|
3
|
+
xiaoshiai_hub/cli.py,sha256=E2xWvybOz6Tr8bSk8pp-QZVHoCyQfnmCYg7442aq4-Y,29790
|
|
4
|
+
xiaoshiai_hub/client.py,sha256=3ASE_KuV-S0CmDvutvmMpY3GU2Hdrui2WAfwHs3iEkw,18775
|
|
5
|
+
xiaoshiai_hub/download.py,sha256=9Uido7cJjGVd6ERDKu_xNMPliarPdZVVb8hkLgYfFcU,13613
|
|
6
|
+
xiaoshiai_hub/envelope_crypto.py,sha256=zjrt5fc3ya86Y4N7_4OI5_NnmFpQ5HiHgoP31ioJRmw,8235
|
|
7
|
+
xiaoshiai_hub/exceptions.py,sha256=24QzgHWq_4bes07UkC3vGi2oT8SMH6Xu4FNlKt52QHo,672
|
|
8
|
+
xiaoshiai_hub/types.py,sha256=-h8EctZo_bRJALufjSTaxIWf2bqHz2ZDCy3SCPfucHs,2004
|
|
9
|
+
xiaoshiai_hub/upload.py,sha256=GKlnsxsIVuLhCWpymupya74TNgTP0s8T-gKTOR-RMgw,22371
|
|
10
|
+
xiaoshiai_hub-1.1.3.dist-info/licenses/LICENSE,sha256=tS28u6VpvqNisRWGeufp-XYQc6p194vOGARl3OIjidA,9110
|
|
11
|
+
xiaoshiai_hub-1.1.3.dist-info/METADATA,sha256=RCvKnYGmiU5upd5HXYkK0RzitGZT-Wsjvw_6CDoBa8M,21630
|
|
12
|
+
xiaoshiai_hub-1.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
+
xiaoshiai_hub-1.1.3.dist-info/entry_points.txt,sha256=fVh_IA1mbRWl7LEd4-RhENMaspSBa4Hxbsg8_HDWc6Y,48
|
|
14
|
+
xiaoshiai_hub-1.1.3.dist-info/top_level.txt,sha256=9AQDFb5Xn7RLQPdbk1aA0QpntbKhlhlT6Z_g-zUBtlM,14
|
|
15
|
+
xiaoshiai_hub-1.1.3.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
xiaoshiai_hub/__init__.py,sha256=2hnhTGad1GPTzRstfwZhAF2204Pl2VF8YHD8HQkAYGY,1469
|
|
2
|
-
xiaoshiai_hub/auth.py,sha256=Pv0P6f76flOefYmALyehuMu2Klyc-u_gtXlJYJR4eMY,4105
|
|
3
|
-
xiaoshiai_hub/cli.py,sha256=lBmIKlxmDk74WYttDCqF5fAuIKjvcIlETipWLyBUP4U,15292
|
|
4
|
-
xiaoshiai_hub/client.py,sha256=Ikq-eJz1kA5KA0_PbzAKhmnPOpZT_AubRIefcCOZYBk,12113
|
|
5
|
-
xiaoshiai_hub/download.py,sha256=9Uido7cJjGVd6ERDKu_xNMPliarPdZVVb8hkLgYfFcU,13613
|
|
6
|
-
xiaoshiai_hub/envelope_crypto.py,sha256=zjrt5fc3ya86Y4N7_4OI5_NnmFpQ5HiHgoP31ioJRmw,8235
|
|
7
|
-
xiaoshiai_hub/exceptions.py,sha256=24QzgHWq_4bes07UkC3vGi2oT8SMH6Xu4FNlKt52QHo,672
|
|
8
|
-
xiaoshiai_hub/types.py,sha256=ZC5elxqles8_ODl-fCSssOzm9q8_KjA9mGpiGgObgls,1915
|
|
9
|
-
xiaoshiai_hub/upload.py,sha256=Hb9KX-87YP7HyskHbQCtfgn11ARzzKzPX_i-fhRtZ0E,21372
|
|
10
|
-
xiaoshiai_hub-1.1.2.dist-info/licenses/LICENSE,sha256=tS28u6VpvqNisRWGeufp-XYQc6p194vOGARl3OIjidA,9110
|
|
11
|
-
xiaoshiai_hub-1.1.2.dist-info/METADATA,sha256=QEUR27oyHBY2bm-CIseIRQZzYFzWgtoMORZExQQeAEo,17454
|
|
12
|
-
xiaoshiai_hub-1.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
-
xiaoshiai_hub-1.1.2.dist-info/entry_points.txt,sha256=fVh_IA1mbRWl7LEd4-RhENMaspSBa4Hxbsg8_HDWc6Y,48
|
|
14
|
-
xiaoshiai_hub-1.1.2.dist-info/top_level.txt,sha256=9AQDFb5Xn7RLQPdbk1aA0QpntbKhlhlT6Z_g-zUBtlM,14
|
|
15
|
-
xiaoshiai_hub-1.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|