Skip to content

Commit 7839bea

Browse files
committed
feat(skill): support create skill version
Hand-written change from the internal SDK repository. The original commit message is intentionally not mirrored; see the source repository for details. Sync-Source-Commit: 7e147e6ac4559876458d3321308a0e72d06d9414 Hand-Written-Reason: No Ark-APIs provenance marker; treated as a hand-written source commit. Release-Version: 0.8.0
1 parent 972f884 commit 7839bea

5 files changed

Lines changed: 223 additions & 0 deletions

File tree

‎src/arkruntime/resources/skills/skills.py‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ..._resource import AsyncAPIResource, SyncAPIResource
77
from ..._types import NOT_GIVEN, NotGiven
88
from ...types.skill.skill import Skill
9+
from ...types.skill.skill_version import SkillVersion
910

1011
__all__ = ["Skills", "AsyncSkills"]
1112

@@ -70,6 +71,35 @@ def retrieve(self, skill_id: str, *, extra_headers=None, extra_query=None, extra
7071
cast_to=Skill,
7172
)
7273

74+
def create_version(
75+
self,
76+
skill_id: str,
77+
*,
78+
files: FileArg,
79+
display_title: Optional[str] | NotGiven = NOT_GIVEN,
80+
extra_headers=None,
81+
extra_query=None,
82+
extra_body=None,
83+
timeout=None,
84+
) -> SkillVersion:
85+
if not skill_id:
86+
raise ValueError("skill_id is required")
87+
body: dict = {}
88+
if display_title is not NOT_GIVEN and display_title is not None:
89+
body["display_title"] = display_title
90+
return self._post(
91+
f"{_PREFIX}/{skill_id}/versions",
92+
body=body,
93+
files=_files_kwarg(files),
94+
options=make_request_options(
95+
extra_headers=_multipart_headers(extra_headers),
96+
extra_query=extra_query,
97+
extra_body=extra_body,
98+
timeout=timeout,
99+
),
100+
cast_to=SkillVersion,
101+
)
102+
73103

74104
class AsyncSkills(AsyncAPIResource):
75105
async def create(
@@ -110,3 +140,32 @@ async def retrieve(
110140
),
111141
cast_to=Skill,
112142
)
143+
144+
async def create_version(
145+
self,
146+
skill_id: str,
147+
*,
148+
files: FileArg,
149+
display_title: Optional[str] | NotGiven = NOT_GIVEN,
150+
extra_headers=None,
151+
extra_query=None,
152+
extra_body=None,
153+
timeout=None,
154+
) -> SkillVersion:
155+
if not skill_id:
156+
raise ValueError("skill_id is required")
157+
body: dict = {}
158+
if display_title is not NOT_GIVEN and display_title is not None:
159+
body["display_title"] = display_title
160+
return await self._post(
161+
f"{_PREFIX}/{skill_id}/versions",
162+
body=body,
163+
files=_files_kwarg(files),
164+
options=make_request_options(
165+
extra_headers=_multipart_headers(extra_headers),
166+
extra_query=extra_query,
167+
extra_body=extra_body,
168+
timeout=timeout,
169+
),
170+
cast_to=SkillVersion,
171+
)

‎src/arkruntime/types/skill/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
from __future__ import annotations
88

99
from .create_skill_request import CreateSkillRequest
10+
from .create_skill_version_request import CreateSkillVersionRequest
1011
from .skill import Skill
12+
from .skill_version import SkillVersion
1113

1214
__all__ = [
1315
"CreateSkillRequest",
16+
"CreateSkillVersionRequest",
1417
"Skill",
18+
"SkillVersion",
1519
]
1620

1721
# Hand-written extras (preserved across regen via Makefile rsync --exclude=*_shim.py).
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Generated by datamodel-code-generator from ark-apis typespec/openapi.
2+
# DO NOT EDIT — regenerate with `make gen-py-<api>` in ark-apis.
3+
#
4+
# Copyright (c) 2026 ByteDance Ltd. and/or its affiliates.
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
from __future__ import annotations
8+
9+
from typing import List, Optional
10+
11+
from typing_extensions import Annotated
12+
13+
from arkruntime._models import BaseModel
14+
from pydantic import Field
15+
16+
17+
class CreateSkillVersionRequest(BaseModel):
18+
display_title: Optional[str] = None
19+
"""
20+
Skill 版本展示名。
21+
"""
22+
files: Optional[List[object]] = None
23+
"""
24+
新版本技能包内容。可以是单个 `.zip`,也可以是一组带相对路径的松散文件;
25+
该字段名允许在同一次请求中重复出现以传多个文件。
26+
27+
与 `files[]` 语义等价,两者至少提供其一;同时提供时服务端会合并处理。
28+
"""
29+
files__: Annotated[Optional[List[object]], Field(alias="files[]")] = None
30+
"""
31+
`files` 的可重复字段名别名,语义完全等价。
32+
"""
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Generated by datamodel-code-generator from ark-apis typespec/openapi.
2+
# DO NOT EDIT — regenerate with `make gen-py-<api>` in ark-apis.
3+
#
4+
# Copyright (c) 2026 ByteDance Ltd. and/or its affiliates.
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
from __future__ import annotations
8+
9+
from typing import Literal, Optional
10+
11+
from arkruntime._models import BaseModel
12+
13+
14+
class SkillVersion(BaseModel):
15+
"""
16+
Skill 版本摘要。
17+
18+
CreateSkillVersion / ListSkillVersions / GetSkillVersion 返回该对象;主
19+
Skill 的 latest_version 会由服务端在新版本创建成功后推进。
20+
"""
21+
22+
id: str
23+
"""
24+
SkillVersion ID,形如 `skillver-...`。
25+
"""
26+
type: Literal["skill_version"]
27+
"""
28+
固定 `"skill_version"`。
29+
"""
30+
skill_id: str
31+
"""
32+
归属 Skill ID。
33+
"""
34+
version: str
35+
"""
36+
版本号,服务端按字符串形式自增,例如 `"1"`、`"2"`。
37+
"""
38+
display_title: str
39+
"""
40+
该版本的展示名。
41+
"""
42+
description: Optional[str] = None
43+
"""
44+
该版本的描述信息。
45+
"""
46+
created_at: str
47+
"""
48+
创建时间,RFC 3339 UTC 秒级精度。
49+
"""

‎tests/test_skills.py‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright (c) 2026 ByteDance Ltd. and/or its affiliates.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from __future__ import annotations
5+
6+
import pytest
7+
8+
from arkruntime.resources.skills.skills import AsyncSkills, Skills
9+
10+
11+
class _SyncClient:
12+
def __init__(self) -> None:
13+
self.request: dict[str, object] | None = None
14+
15+
def post(self, *args: object, **kwargs: object) -> dict[str, object]:
16+
self.request = {"args": args, **kwargs}
17+
return {}
18+
19+
def post_without_retry(self, *args: object, **kwargs: object) -> dict[str, object]:
20+
return self.post(*args, **kwargs)
21+
22+
def get(self, *args: object, **kwargs: object) -> dict[str, object]:
23+
return {}
24+
25+
def delete(self, *args: object, **kwargs: object) -> dict[str, object]:
26+
return {}
27+
28+
def get_api_list(self, *args: object, **kwargs: object) -> list[object]:
29+
return []
30+
31+
32+
class _AsyncClient:
33+
def __init__(self) -> None:
34+
self.request: dict[str, object] | None = None
35+
36+
async def post(self, *args: object, **kwargs: object) -> dict[str, object]:
37+
self.request = {"args": args, **kwargs}
38+
return {}
39+
40+
async def post_without_retry(self, *args: object, **kwargs: object) -> dict[str, object]:
41+
return await self.post(*args, **kwargs)
42+
43+
async def get(self, *args: object, **kwargs: object) -> dict[str, object]:
44+
return {}
45+
46+
async def delete(self, *args: object, **kwargs: object) -> dict[str, object]:
47+
return {}
48+
49+
async def get_api_list(self, *args: object, **kwargs: object) -> list[object]:
50+
return []
51+
52+
53+
def test_create_version_builds_multipart_request() -> None:
54+
client = _SyncClient()
55+
Skills(client).create_version(
56+
"skill-1",
57+
files=("skill.zip", b"zip-bytes"),
58+
display_title="Readiness Skill v2",
59+
)
60+
61+
assert client.request is not None
62+
assert client.request["args"] == ("/skills/skill-1/versions",)
63+
assert client.request["body"] == {"display_title": "Readiness Skill v2"}
64+
assert client.request["files"] == {"files": ("skill.zip", b"zip-bytes")}
65+
66+
67+
@pytest.mark.asyncio
68+
async def test_async_create_version_builds_multipart_request() -> None:
69+
client = _AsyncClient()
70+
await AsyncSkills(client).create_version(
71+
"skill-1",
72+
files=("skill.zip", b"zip-bytes"),
73+
display_title="Readiness Skill v2",
74+
)
75+
76+
assert client.request is not None
77+
assert client.request["args"] == ("/skills/skill-1/versions",)
78+
assert client.request["body"] == {"display_title": "Readiness Skill v2"}
79+
assert client.request["files"] == {"files": ("skill.zip", b"zip-bytes")}

0 commit comments

Comments
 (0)