modelscope-api 0.0.1__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.
- modelscope_api/__init__.py +1 -0
- modelscope_api/config.py +23 -0
- modelscope_api/data_models/__init__.py +3 -0
- modelscope_api/data_models/base.py +22 -0
- modelscope_api/data_models/collection/__init__.py +6 -0
- modelscope_api/data_models/collection/collection.py +110 -0
- modelscope_api/data_models/collection/collection_item.py +92 -0
- modelscope_api/data_models/magicube/__init__.py +5 -0
- modelscope_api/data_models/magicube/magicube_blance.py +31 -0
- modelscope_api/data_models/studio/__init__.py +13 -0
- modelscope_api/data_models/studio/base_image.py +26 -0
- modelscope_api/data_models/studio/environment_variable.py +38 -0
- modelscope_api/data_models/studio/hardware.py +87 -0
- modelscope_api/data_models/studio/logs.py +54 -0
- modelscope_api/data_models/studio/sdk.py +41 -0
- modelscope_api/data_models/studio/studio.py +207 -0
- modelscope_api/data_models/user/__init__.py +5 -0
- modelscope_api/data_models/user/user.py +47 -0
- modelscope_api/exceptions.py +59 -0
- modelscope_api/models/__init__.py +9 -0
- modelscope_api/models/_sub_client.py +71 -0
- modelscope_api/models/collection/__init__.py +8 -0
- modelscope_api/models/collection/collection.py +122 -0
- modelscope_api/models/collection/collection_client.py +132 -0
- modelscope_api/models/collection/collection_item.py +92 -0
- modelscope_api/models/collection/collection_item_client.py +247 -0
- modelscope_api/models/magicube/__init__.py +5 -0
- modelscope_api/models/magicube/magicube_client.py +46 -0
- modelscope_api/models/modelscope_client.py +206 -0
- modelscope_api/models/studio/__init__.py +7 -0
- modelscope_api/models/studio/environment_variable_client.py +107 -0
- modelscope_api/models/studio/studio.py +197 -0
- modelscope_api/models/studio/studio_client.py +237 -0
- modelscope_api/models/user/__init__.py +5 -0
- modelscope_api/models/user/user_client.py +46 -0
- modelscope_api/utils/regex.py +12 -0
- modelscope_api/utils/typing.py +12 -0
- modelscope_api-0.0.1.dist-info/METADATA +20 -0
- modelscope_api-0.0.1.dist-info/RECORD +42 -0
- modelscope_api-0.0.1.dist-info/WHEEL +5 -0
- modelscope_api-0.0.1.dist-info/licenses/LICENSE +21 -0
- modelscope_api-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
创空间数据模型。
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import List, Optional, override
|
|
9
|
+
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
from pydantic.dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
from ...utils.regex import STUDIO_ID_PATTERN
|
|
14
|
+
from ...utils.typing import JsonObject
|
|
15
|
+
from ..base import BaseDataClass
|
|
16
|
+
from .sdk import SDKType
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StudioRuntimeStatus(StrEnum):
|
|
21
|
+
"""
|
|
22
|
+
创空间运行状态。
|
|
23
|
+
"""
|
|
24
|
+
INITIALIZED = "Initialized"
|
|
25
|
+
BUILDING = "Building"
|
|
26
|
+
BUILDFAILED = "BuildFailed"
|
|
27
|
+
DEPLOYING = "Deploying"
|
|
28
|
+
DEPLOYFAILED = "DeployFailed"
|
|
29
|
+
RUNNING = "Running"
|
|
30
|
+
STOPPING = "Stopping"
|
|
31
|
+
STOPPED = "Stopped"
|
|
32
|
+
DUPLICATING = "Duplicating"
|
|
33
|
+
SLEEPING = "Sleeping"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class StudioActiveConfig(BaseDataClass):
|
|
38
|
+
"""当前生效的运行时配置"""
|
|
39
|
+
hardware: Optional[str] = Field(
|
|
40
|
+
description="硬件配置,修改后需重新部署才能生效。",
|
|
41
|
+
default=None
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
base_image: Optional[str] = Field(
|
|
45
|
+
description="基础镜像,仅 Docker 类型不支持,建议选用最新版本,修改后需重新部署才能生效。",
|
|
46
|
+
default=None
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
sdk_type: Optional[SDKType] = Field(
|
|
50
|
+
description="SDK 类型,修改后需重新部署才能生效。",
|
|
51
|
+
default=None
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
sdk_version: Optional[str] = Field(
|
|
55
|
+
description="SDK 版本(仅 gradio 类型返回)",
|
|
56
|
+
default=None
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class StudioRuntimeInfo(BaseDataClass):
|
|
63
|
+
"""创空间运行时状态信息"""
|
|
64
|
+
status: StudioRuntimeStatus = Field(
|
|
65
|
+
description="运行状态"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
active_config: StudioActiveConfig = Field(
|
|
69
|
+
description="当前生效的配置详情",
|
|
70
|
+
default_factory=StudioActiveConfig
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
created_at: Optional[str] = Field(
|
|
74
|
+
description="部署时间(ISO 8601 格式)",
|
|
75
|
+
default=None
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
error_message: Optional[str] = Field(
|
|
79
|
+
description="失败信息(仅在错误状态时返回)",
|
|
80
|
+
default=None
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class StudioVisibility(StrEnum):
|
|
86
|
+
"""
|
|
87
|
+
创空间可见性。
|
|
88
|
+
"""
|
|
89
|
+
# 代码和体验都公开
|
|
90
|
+
PUBLIC = "public"
|
|
91
|
+
|
|
92
|
+
# 体验公开,代码仓库不可见
|
|
93
|
+
PROTECTED = "protected"
|
|
94
|
+
|
|
95
|
+
# 都不公开
|
|
96
|
+
PRIVATE = "private"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class StudioInfo(BaseDataClass):
|
|
101
|
+
"""
|
|
102
|
+
描述魔搭社区创空间(Studio)的完整信息模型。
|
|
103
|
+
|
|
104
|
+
该模型用于表示创空间的元数据、配置、运行时状态等所有公开属性,
|
|
105
|
+
适用于 API 响应或内部数据传递。
|
|
106
|
+
"""
|
|
107
|
+
id: str = Field(
|
|
108
|
+
description="Studio ID (owner/repo_name)",
|
|
109
|
+
pattern=STUDIO_ID_PATTERN.pattern
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
repo_name: str = Field(
|
|
113
|
+
description="仓库名称,是创空间的唯一标识。"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
display_name: Optional[str] = Field(
|
|
117
|
+
description="中文或友好的显示名称,默认将使用英文名称。",
|
|
118
|
+
default=None
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
owner: str = Field(
|
|
122
|
+
description="所有者(包括组织、个人)"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
description: Optional[str] = Field(
|
|
126
|
+
description="描述",
|
|
127
|
+
default=None
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
cover_image: Optional[str] = Field(
|
|
131
|
+
description="封面图片的 URL 地址",
|
|
132
|
+
default=None
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
created_at: Optional[str] = Field(
|
|
136
|
+
description="部署时间(ISO 8601 格式)",
|
|
137
|
+
default=None
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
likes: int = Field(
|
|
141
|
+
description="获赞数量",
|
|
142
|
+
ge=0
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
view_count: int = Field(
|
|
146
|
+
description="访问量",
|
|
147
|
+
ge=0
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
tags: List[str] = Field(
|
|
151
|
+
description="关联的标签列表,用于分类和搜索",
|
|
152
|
+
default_factory=list
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
visibility: StudioVisibility = Field(
|
|
156
|
+
description="创空间可见性"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
last_modified: Optional[str] = Field(
|
|
160
|
+
description="最后修改时间(ISO 8601 格式)",
|
|
161
|
+
default=None
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
sdk_type: Optional[SDKType] = Field(
|
|
165
|
+
description="使用的 SDK 类型,如 gradio、streamlit",
|
|
166
|
+
default=None
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
sdk_version: Optional[str] = Field(
|
|
170
|
+
description="SDK 版本,仅对 Gradio 类型生效",
|
|
171
|
+
default=None
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
hardware: Optional[str] = Field(
|
|
175
|
+
description="硬件配置,修改后需重新部署才能生效。",
|
|
176
|
+
default=None
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
base_image: Optional[str] = Field(
|
|
180
|
+
description="基础镜像,仅 Docker 类型不支持。",
|
|
181
|
+
default=None
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
license: Optional[str] = Field(
|
|
185
|
+
description="许可证",
|
|
186
|
+
default=None
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
host: str = Field(
|
|
190
|
+
description="API 访问地址(base url)"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
mcp_support: bool = Field(
|
|
194
|
+
description="是否支持 MCP"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
runtime: StudioRuntimeInfo = Field(
|
|
198
|
+
description="运行时状态与配置的嵌套信息"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@override
|
|
203
|
+
@classmethod
|
|
204
|
+
def from_json(cls, data: JsonObject) -> StudioInfo:
|
|
205
|
+
if "created_at" not in data:
|
|
206
|
+
data["created_at"] = data.get("runtime", {}).get("created_at")
|
|
207
|
+
return super().from_json(data)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
用户数据模型。
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
from pydantic.dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from ..base import BaseDataClass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class UserInfo(BaseDataClass):
|
|
16
|
+
"""
|
|
17
|
+
用户信息
|
|
18
|
+
"""
|
|
19
|
+
username: str = Field(
|
|
20
|
+
description="用户在平台上的唯一用户名。"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
nickname: Optional[str] = Field(
|
|
24
|
+
description="用户自定义的昵称。",
|
|
25
|
+
default=None
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
description: Optional[str] = Field(
|
|
29
|
+
description="个人介绍。文档节点 JSON 结构。",
|
|
30
|
+
default=None
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
email: Optional[str] = Field(
|
|
34
|
+
description="邮箱。",
|
|
35
|
+
default=None
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
avatar_url: Optional[str] = Field(
|
|
39
|
+
description="头像的 URL。",
|
|
40
|
+
default=None
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __post_init__(self) -> None:
|
|
45
|
+
# nickname 为空时,使用 username 填充
|
|
46
|
+
if self.nickname is None:
|
|
47
|
+
object.__setattr__(self, "nickname", self.username)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
异常类
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, ClassVar, Dict, Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BusinessException(Exception):
|
|
9
|
+
"""
|
|
10
|
+
业务异常基类,所有业务错误都继承自这里。
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
code: 错误代码
|
|
14
|
+
message: 错误消息
|
|
15
|
+
details: 附加详情(可选)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# 默认消息
|
|
19
|
+
DEFAULT_MESSAGE : ClassVar[str] = "Business Error"
|
|
20
|
+
|
|
21
|
+
# 默认错误代码
|
|
22
|
+
DEFAULT_CODE : ClassVar[str] = "ERROR"
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
message: Optional[str] = None,
|
|
27
|
+
*,
|
|
28
|
+
code: Optional[str] = None,
|
|
29
|
+
details: Optional[Dict[str, Any]] = None
|
|
30
|
+
):
|
|
31
|
+
self.message : str = self.DEFAULT_MESSAGE if (message is None) else message
|
|
32
|
+
self.code : str = self.DEFAULT_CODE if (code is None) else code
|
|
33
|
+
self.details : Dict[str, Any] = details or {}
|
|
34
|
+
|
|
35
|
+
super().__init__(self.message)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UpstreamException(BusinessException):
|
|
39
|
+
"""
|
|
40
|
+
上游服务端异常。
|
|
41
|
+
"""
|
|
42
|
+
DEFAULT_MESSAGE: ClassVar[str] = "UpstreamException"
|
|
43
|
+
DEFAULT_CODE: ClassVar[str] = "UPSTREAM EXCEPTION"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ParseException(UpstreamException):
|
|
47
|
+
"""
|
|
48
|
+
解析上游的响应失败。
|
|
49
|
+
"""
|
|
50
|
+
DEFAULT_MESSAGE: ClassVar[str] = "ParseException"
|
|
51
|
+
DEFAULT_CODE: ClassVar[str] = "PARSE EXCEPTION"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ModelScopeException(BusinessException):
|
|
55
|
+
"""
|
|
56
|
+
ModelScope 业务错误。
|
|
57
|
+
"""
|
|
58
|
+
DEFAULT_MESSAGE: ClassVar[str] = "ModelScopeError"
|
|
59
|
+
DEFAULT_CODE: ClassVar[str] = "MODELSCOPE ERROR"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
封装了 API 的模型,
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .collection import Collection, CollectionClient, CollectionItem, CollectionItemClient
|
|
6
|
+
from .magicube import MagicubeClient
|
|
7
|
+
from .modelscope_client import ModelScopeClient
|
|
8
|
+
from .studio import EnvironmentVariableClient, Studio, StudioClient
|
|
9
|
+
from .user import UserClient
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
集成封装子路由的 API。
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional, Union, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from yarl import URL
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .modelscope_client import ModelScopeClient, JsonObject
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SubClient:
|
|
16
|
+
"""
|
|
17
|
+
集成封装子路由的 API。
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
super_client: Union[ModelScopeClient, SubClient],
|
|
23
|
+
*,
|
|
24
|
+
prefix: str
|
|
25
|
+
):
|
|
26
|
+
self.super_client = super_client
|
|
27
|
+
self.prefix = prefix
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def openapi_url(self) -> URL:
|
|
32
|
+
"""
|
|
33
|
+
The URL of OpenAPI sub interface.
|
|
34
|
+
|
|
35
|
+
Returns like:
|
|
36
|
+
"https://modelscope.cn/openapi/v1/studios"
|
|
37
|
+
"""
|
|
38
|
+
return self.super_client.openapi_url / self.prefix
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_openapi_url(self, subpath: Optional[str] = None) -> URL:
|
|
42
|
+
"""
|
|
43
|
+
拼接完整的路由。
|
|
44
|
+
"""
|
|
45
|
+
if subpath is None:
|
|
46
|
+
return self.openapi_url
|
|
47
|
+
return self.openapi_url / subpath
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def request_openapi_data(
|
|
51
|
+
self,
|
|
52
|
+
subpath: Optional[str] = None,
|
|
53
|
+
**kwargs
|
|
54
|
+
) -> Optional[JsonObject | List[JsonObject]]:
|
|
55
|
+
"""
|
|
56
|
+
向 ModelScope OpenAPI 的子接口发送请求,并返回响应体中的 `data` 字段。
|
|
57
|
+
如果响应体中没有 `data` 字段,则返回整个响应体。
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
subpath: 在 `self.openapi_url` 之后要拼接的子路径。
|
|
61
|
+
不能以 `/` 开头。
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ParseException: 如果解析失败。
|
|
65
|
+
ModelScopeException: 如果请求失败。
|
|
66
|
+
"""
|
|
67
|
+
if subpath is None:
|
|
68
|
+
subpath = self.prefix
|
|
69
|
+
else:
|
|
70
|
+
subpath = f"{self.prefix}/{subpath}"
|
|
71
|
+
return await self.super_client.request_openapi_data(subpath=subpath, **kwargs)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
对单个合集的操作。
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Optional, TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from ...utils.regex import COLLECTION_SLUG_PATTERN
|
|
11
|
+
from ...data_models.collection import CollectionInfo, CollectionTheme, CollectionVisibility
|
|
12
|
+
from .._sub_client import SubClient
|
|
13
|
+
from .collection_item_client import CollectionItemClient
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from .collection_client import CollectionClient
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Collection(SubClient):
|
|
21
|
+
"""
|
|
22
|
+
单个合集。
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, collection_client: CollectionClient, slug: str):
|
|
26
|
+
self._slug = slug
|
|
27
|
+
assert COLLECTION_SLUG_PATTERN.fullmatch(self.slug), f"Invalid collection slug: {self.slug}"
|
|
28
|
+
super().__init__(
|
|
29
|
+
super_client=collection_client,
|
|
30
|
+
prefix=self.slug
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# 聚合子路由
|
|
34
|
+
self.items = CollectionItemClient(self)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def __str__(self) -> str:
|
|
38
|
+
return f"{type(self).__name__}<{self.slug}>"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ==== 只读属性 ====
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def slug(self) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Collection slug (owner/name)
|
|
47
|
+
"""
|
|
48
|
+
return self._slug
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def owner(self) -> str:
|
|
53
|
+
"""
|
|
54
|
+
拥有者(个人用户名或组织名)
|
|
55
|
+
"""
|
|
56
|
+
return self._slug.split("/")[0]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def name(self) -> str:
|
|
61
|
+
"""
|
|
62
|
+
合集英文名。
|
|
63
|
+
"""
|
|
64
|
+
return self._slug.split("/")[1]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ==== 合集操作 ====
|
|
68
|
+
|
|
69
|
+
async def get_info(self, **kwargs) -> CollectionInfo:
|
|
70
|
+
"""
|
|
71
|
+
获取当前 Collection 的详细信息。
|
|
72
|
+
|
|
73
|
+
visibility=public 时可以不认证;visibility=private 时必须认证。
|
|
74
|
+
"""
|
|
75
|
+
kwargs["method"] = "GET"
|
|
76
|
+
kwargs["subpath"] = None
|
|
77
|
+
data = await self.request_openapi_data(**kwargs)
|
|
78
|
+
return CollectionInfo.from_json(data)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def update(
|
|
82
|
+
self,
|
|
83
|
+
*,
|
|
84
|
+
title: Optional[str] = None,
|
|
85
|
+
description: Optional[str] = None,
|
|
86
|
+
owner: Optional[str] = None,
|
|
87
|
+
visibility: Optional[CollectionVisibility] = None,
|
|
88
|
+
theme: Optional[CollectionTheme] = None,
|
|
89
|
+
**kwargs
|
|
90
|
+
) -> CollectionInfo:
|
|
91
|
+
"""
|
|
92
|
+
更新 Collection 元数据。传入哪个字段即更新哪个字段,未传字段保持原值。
|
|
93
|
+
|
|
94
|
+
open/close 语义映射为 visibility(public/private)。
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
title: Collection 标题(最长 128 字符)。
|
|
98
|
+
description: Collection 描述(Markdown)。
|
|
99
|
+
owner: 所有者(用户名或组织名)。
|
|
100
|
+
visibility: 可见性:public / private。
|
|
101
|
+
theme: 主题标签,枚举值:Blue/Pink/Purple/Cyan。
|
|
102
|
+
"""
|
|
103
|
+
kwargs["method"] = "PATCH"
|
|
104
|
+
kwargs["subpath"] = None
|
|
105
|
+
kwargs.setdefault("json", {}).update({
|
|
106
|
+
"title": title,
|
|
107
|
+
"description": description,
|
|
108
|
+
"owner": owner,
|
|
109
|
+
"visibility": visibility,
|
|
110
|
+
"theme": theme,
|
|
111
|
+
})
|
|
112
|
+
data = await self.request_openapi_data(**kwargs)
|
|
113
|
+
self._slug = data["slug"]
|
|
114
|
+
self.prefix = self._slug
|
|
115
|
+
return await self.get_info()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def delete(self) -> None:
|
|
119
|
+
"""
|
|
120
|
+
删除当前合集。
|
|
121
|
+
"""
|
|
122
|
+
await self.super_client.delete_collection(self.slug)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
集成封装与合集(Collection)有关的 API。
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import List, Literal, Optional, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from ...data_models.collection import CollectionInfo, CollectionTheme, CollectionVisibility
|
|
10
|
+
from .._sub_client import SubClient
|
|
11
|
+
from .collection import Collection
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..modelscope_client import ModelScopeClient
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CollectionClient(SubClient):
|
|
19
|
+
"""
|
|
20
|
+
集成封装与合集(Collection)有关的 API。
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
modelscope_client: ModelScopeClient,
|
|
26
|
+
*,
|
|
27
|
+
prefix: str = "collections"
|
|
28
|
+
):
|
|
29
|
+
super().__init__(
|
|
30
|
+
super_client=modelscope_client,
|
|
31
|
+
prefix=prefix
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ==== 查询合集信息 ====
|
|
36
|
+
|
|
37
|
+
async def search_collection_infos(
|
|
38
|
+
self,
|
|
39
|
+
search: Optional[str] = None,
|
|
40
|
+
owner: Optional[str] = None,
|
|
41
|
+
*,
|
|
42
|
+
sort: Optional[Literal["default", "last_modified", "likes"]] = None,
|
|
43
|
+
page_number: int = 1,
|
|
44
|
+
page_size: int = 10,
|
|
45
|
+
**kwargs
|
|
46
|
+
) -> List[CollectionInfo]:
|
|
47
|
+
"""
|
|
48
|
+
获取 Collection 列表,支持搜索、按所有者过滤与排序、分页。
|
|
49
|
+
|
|
50
|
+
公开列表无需用户认证;查询私有 Collection 需要认证。
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
search: 针对标题、描述的子字符串搜索。
|
|
54
|
+
owner: 所有者过滤。
|
|
55
|
+
仅允许过滤当前 Token 所属用户自己的 Collection,过滤他人(或匿名使用)返回 403 OperationNotAllowed。
|
|
56
|
+
sort: 排序方式:default(默认综合)/ last_modified(最近更新)/ likes(喜欢数)。
|
|
57
|
+
page_number: 页码(≥ 1,默认 1)
|
|
58
|
+
page_size: 每页大小(1 ~ 50,默认 10)
|
|
59
|
+
"""
|
|
60
|
+
kwargs["method"] = "GET"
|
|
61
|
+
kwargs["subpath"] = None
|
|
62
|
+
kwargs.setdefault("params", {}).update({
|
|
63
|
+
"search": search,
|
|
64
|
+
"owner": owner,
|
|
65
|
+
"sort": sort,
|
|
66
|
+
"page_number": page_number,
|
|
67
|
+
"page_size": page_size,
|
|
68
|
+
})
|
|
69
|
+
data = await self.request_openapi_data(**kwargs)
|
|
70
|
+
collection_infos = list(map(
|
|
71
|
+
CollectionInfo.from_json,
|
|
72
|
+
data.get("collection_list") or []
|
|
73
|
+
))
|
|
74
|
+
return collection_infos
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ==== 创建合集 ====
|
|
78
|
+
|
|
79
|
+
def get_collection(self, collection_slug: str) -> Collection:
|
|
80
|
+
"""
|
|
81
|
+
构造一个 Collection 对象。
|
|
82
|
+
"""
|
|
83
|
+
return Collection(collection_client=self, slug=collection_slug)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def create_collection(
|
|
87
|
+
self,
|
|
88
|
+
title: str,
|
|
89
|
+
owner: Optional[str] = None,
|
|
90
|
+
*,
|
|
91
|
+
description: Optional[str] = None,
|
|
92
|
+
visibility: Optional[CollectionVisibility] = None,
|
|
93
|
+
theme: Optional[CollectionTheme] = None,
|
|
94
|
+
**kwargs
|
|
95
|
+
) -> Collection:
|
|
96
|
+
"""
|
|
97
|
+
创建一个新的 Collection。
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
title: Collection 标题(最长 128 字符)。
|
|
101
|
+
owner: 所有者(用户名或组织名)。默认使用当前登录的用户。
|
|
102
|
+
description: Collection 描述(Markdown)。
|
|
103
|
+
visibility: 可见性:public / private,默认 public。
|
|
104
|
+
theme: 主题标签,枚举值:Blue/Pink/Purple/Cyan。默认 Blue。
|
|
105
|
+
"""
|
|
106
|
+
# 补全 `owner` 参数
|
|
107
|
+
if not owner:
|
|
108
|
+
current_user_info = await self.super_client.user.get_current_user_info()
|
|
109
|
+
owner = current_user_info.username
|
|
110
|
+
|
|
111
|
+
kwargs["method"] = "POST"
|
|
112
|
+
kwargs["subpath"] = None
|
|
113
|
+
kwargs.setdefault("json", {}).update({
|
|
114
|
+
"title": title,
|
|
115
|
+
"owner": owner,
|
|
116
|
+
"description": description,
|
|
117
|
+
"visibility": visibility,
|
|
118
|
+
"theme": theme,
|
|
119
|
+
})
|
|
120
|
+
data = await self.request_openapi_data(**kwargs)
|
|
121
|
+
return self.get_collection(data["slug"])
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ==== 删除合集 ====
|
|
125
|
+
|
|
126
|
+
async def delete_collection(self, collection_slug: str, **kwargs) -> None:
|
|
127
|
+
"""
|
|
128
|
+
删除指定的 Collection。需具备 admin 权限的 Bearer Token。
|
|
129
|
+
"""
|
|
130
|
+
kwargs["method"] = "DELETE"
|
|
131
|
+
kwargs["subpath"] = collection_slug
|
|
132
|
+
await self.request_openapi_data(**kwargs)
|