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.
Files changed (42) hide show
  1. modelscope_api/__init__.py +1 -0
  2. modelscope_api/config.py +23 -0
  3. modelscope_api/data_models/__init__.py +3 -0
  4. modelscope_api/data_models/base.py +22 -0
  5. modelscope_api/data_models/collection/__init__.py +6 -0
  6. modelscope_api/data_models/collection/collection.py +110 -0
  7. modelscope_api/data_models/collection/collection_item.py +92 -0
  8. modelscope_api/data_models/magicube/__init__.py +5 -0
  9. modelscope_api/data_models/magicube/magicube_blance.py +31 -0
  10. modelscope_api/data_models/studio/__init__.py +13 -0
  11. modelscope_api/data_models/studio/base_image.py +26 -0
  12. modelscope_api/data_models/studio/environment_variable.py +38 -0
  13. modelscope_api/data_models/studio/hardware.py +87 -0
  14. modelscope_api/data_models/studio/logs.py +54 -0
  15. modelscope_api/data_models/studio/sdk.py +41 -0
  16. modelscope_api/data_models/studio/studio.py +207 -0
  17. modelscope_api/data_models/user/__init__.py +5 -0
  18. modelscope_api/data_models/user/user.py +47 -0
  19. modelscope_api/exceptions.py +59 -0
  20. modelscope_api/models/__init__.py +9 -0
  21. modelscope_api/models/_sub_client.py +71 -0
  22. modelscope_api/models/collection/__init__.py +8 -0
  23. modelscope_api/models/collection/collection.py +122 -0
  24. modelscope_api/models/collection/collection_client.py +132 -0
  25. modelscope_api/models/collection/collection_item.py +92 -0
  26. modelscope_api/models/collection/collection_item_client.py +247 -0
  27. modelscope_api/models/magicube/__init__.py +5 -0
  28. modelscope_api/models/magicube/magicube_client.py +46 -0
  29. modelscope_api/models/modelscope_client.py +206 -0
  30. modelscope_api/models/studio/__init__.py +7 -0
  31. modelscope_api/models/studio/environment_variable_client.py +107 -0
  32. modelscope_api/models/studio/studio.py +197 -0
  33. modelscope_api/models/studio/studio_client.py +237 -0
  34. modelscope_api/models/user/__init__.py +5 -0
  35. modelscope_api/models/user/user_client.py +46 -0
  36. modelscope_api/utils/regex.py +12 -0
  37. modelscope_api/utils/typing.py +12 -0
  38. modelscope_api-0.0.1.dist-info/METADATA +20 -0
  39. modelscope_api-0.0.1.dist-info/RECORD +42 -0
  40. modelscope_api-0.0.1.dist-info/WHEEL +5 -0
  41. modelscope_api-0.0.1.dist-info/licenses/LICENSE +21 -0
  42. 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,5 @@
1
+ """
2
+ 与用户(User)有关的数据模型。
3
+ """
4
+
5
+ from .user import UserInfo
@@ -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,8 @@
1
+ """
2
+ 与合集(Collection)有关的 API 模型。
3
+ """
4
+
5
+ from .collection import Collection
6
+ from .collection_client import CollectionClient
7
+ from .collection_item import CollectionItem
8
+ from .collection_item_client import CollectionItemClient
@@ -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)