ap-client 0.6.0.dev2__tar.gz → 0.6.1.dev1__tar.gz
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.
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/PKG-INFO +4 -4
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/api.py +71 -4
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/cli.py +134 -218
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/dataset_commands.py +136 -35
- ap_client-0.6.1.dev1/ap_client/dataset_name.py +53 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/dataset_register.py +20 -6
- ap_client-0.6.1.dev1/ap_client/dfs_commands.py +680 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/exporter.py +33 -2
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/instance_commands.py +519 -52
- ap_client-0.6.1.dev1/ap_client/instance_options.py +94 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/irepo_sdk.py +70 -4
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/managed_ak.py +44 -14
- ap_client-0.6.1.dev1/ap_client/ossutil_hash.py +329 -0
- ap_client-0.6.1.dev1/ap_client/ossutil_io.py +1230 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/profile_commands.py +128 -16
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/repo_read_guard.py +6 -2
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/split_publish.py +1 -1
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/pyproject.toml +5 -4
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/__init__.py +0 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/akpool_commands.py +0 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/config.py +0 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/fs_commands.py +0 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/manifest_view.py +0 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/share_api.py +0 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/tbb.py +0 -0
- {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/waiter.py +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ap-client
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1.dev1
|
|
4
4
|
Summary: Agent Platform API Client & CLI
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: irepo-artifact==0.2.
|
|
6
|
+
Requires-Dist: irepo-artifact==0.2.2
|
|
7
7
|
Requires-Dist: pyyaml>=6.0
|
|
8
8
|
Requires-Dist: requests>=2.28.0
|
|
9
9
|
Requires-Dist: rich>=13.0.0
|
|
10
10
|
Requires-Dist: typer>=0.9.0
|
|
11
11
|
Requires-Dist: websockets>=13.0
|
|
12
12
|
Provides-Extra: all
|
|
13
|
-
Requires-Dist: instance-repo[oss]<2.0.0,>=1.
|
|
13
|
+
Requires-Dist: instance-repo[oss]<2.0.0,>=1.4.0.dev4; extra == 'all'
|
|
14
14
|
Provides-Extra: dataset
|
|
15
|
-
Requires-Dist: instance-repo[oss]<2.0.0,>=1.
|
|
15
|
+
Requires-Dist: instance-repo[oss]<2.0.0,>=1.4.0.dev4; extra == 'dataset'
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
|
|
18
18
|
A lightweight Python SDK and command line interface for Agent Platform. It provides helpers for configuring API access and managing templates, datasets, jobs, and groups.
|
|
@@ -1127,6 +1127,8 @@ class APIClient:
|
|
|
1127
1127
|
ak_ids: Optional[list[str]] = None,
|
|
1128
1128
|
priority: Optional[str] = None,
|
|
1129
1129
|
priority_weight: Optional[int] = None,
|
|
1130
|
+
transparency: Optional[Literal["whitebox", "blackbox"]] = None,
|
|
1131
|
+
idempotency_key: Optional[str] = None,
|
|
1130
1132
|
) -> dict:
|
|
1131
1133
|
"""Create a group with optional scheduling defaults for its Jobs.
|
|
1132
1134
|
|
|
@@ -1134,6 +1136,10 @@ class APIClient:
|
|
|
1134
1136
|
"""
|
|
1135
1137
|
if priority is not None and priority not in {"high", "medium"}:
|
|
1136
1138
|
raise ValueError("priority must be one of high/medium")
|
|
1139
|
+
if transparency is not None and transparency not in {"whitebox", "blackbox"}:
|
|
1140
|
+
raise ValueError("transparency must be whitebox or blackbox")
|
|
1141
|
+
if transparency == "blackbox" and not idempotency_key:
|
|
1142
|
+
raise ValueError("blackbox Group creation requires a reusable idempotency_key (UUID4)")
|
|
1137
1143
|
if ak_ids is not None and not isinstance(ak_ids, list):
|
|
1138
1144
|
raise ValueError("ak_ids must be a list")
|
|
1139
1145
|
references = list(ak_ids or [])
|
|
@@ -1142,6 +1148,10 @@ class APIClient:
|
|
|
1142
1148
|
raise ValueError("ak_ids entries must be nonempty strings")
|
|
1143
1149
|
references = list(dict.fromkeys(references))
|
|
1144
1150
|
body: dict = {}
|
|
1151
|
+
if transparency is not None:
|
|
1152
|
+
body["transparency"] = transparency
|
|
1153
|
+
if idempotency_key is not None:
|
|
1154
|
+
body["idempotency_key"] = idempotency_key
|
|
1145
1155
|
if references:
|
|
1146
1156
|
body["ak_ids"] = references
|
|
1147
1157
|
if name:
|
|
@@ -1263,9 +1273,11 @@ class APIClient:
|
|
|
1263
1273
|
ak_mode: str = "auto",
|
|
1264
1274
|
instance_range: Optional[str] = None,
|
|
1265
1275
|
profile: Optional[str] = None,
|
|
1276
|
+
transparency: Optional[Literal["whitebox", "blackbox"]] = None,
|
|
1266
1277
|
) -> dict:
|
|
1267
1278
|
"""Create a job."""
|
|
1268
1279
|
body = self.build_create_job_body(
|
|
1280
|
+
transparency=transparency,
|
|
1269
1281
|
template=template,
|
|
1270
1282
|
params=params,
|
|
1271
1283
|
params_list=params_list,
|
|
@@ -1377,8 +1389,11 @@ class APIClient:
|
|
|
1377
1389
|
ak_mode: str = "auto",
|
|
1378
1390
|
instance_range: Optional[str] = None,
|
|
1379
1391
|
profile: Optional[str] = None,
|
|
1392
|
+
transparency: Optional[Literal["whitebox", "blackbox"]] = None,
|
|
1380
1393
|
) -> dict:
|
|
1381
1394
|
"""Build the /jobs request body for job creation."""
|
|
1395
|
+
if transparency is not None and transparency not in {"whitebox", "blackbox"}:
|
|
1396
|
+
raise ValueError("transparency must be whitebox or blackbox")
|
|
1382
1397
|
if profile is not None and (profile_id is not None or profile_version is not None):
|
|
1383
1398
|
raise ValueError("profile cannot be combined with profile_id or profile_version")
|
|
1384
1399
|
_validate_credential_source_selection(
|
|
@@ -1503,8 +1518,23 @@ class APIClient:
|
|
|
1503
1518
|
if experiment is not None:
|
|
1504
1519
|
body["experiment"] = experiment
|
|
1505
1520
|
|
|
1521
|
+
if transparency is not None:
|
|
1522
|
+
body["transparency"] = transparency
|
|
1506
1523
|
return body
|
|
1507
1524
|
|
|
1525
|
+
def get_job_access_contact(self, job_id: str) -> dict:
|
|
1526
|
+
"""Discover the owner and grant contact without reading protected details."""
|
|
1527
|
+
result = self._get(f"/jobs/{quote(job_id, safe='')}", params={"access_contact": True})
|
|
1528
|
+
if (
|
|
1529
|
+
not isinstance(result, dict)
|
|
1530
|
+
or result.get("resource_type") != "job"
|
|
1531
|
+
or "authorization_contact" not in result
|
|
1532
|
+
):
|
|
1533
|
+
raise ConfigurationError(
|
|
1534
|
+
"Server does not support access contact queries; upgrade Agent-Platform first"
|
|
1535
|
+
)
|
|
1536
|
+
return result
|
|
1537
|
+
|
|
1508
1538
|
def get_job(
|
|
1509
1539
|
self,
|
|
1510
1540
|
job_id: str,
|
|
@@ -1630,6 +1660,19 @@ class APIClient:
|
|
|
1630
1660
|
|
|
1631
1661
|
# ==================== Group operations ====================
|
|
1632
1662
|
|
|
1663
|
+
def get_group_access_contact(self, group_id: str) -> dict:
|
|
1664
|
+
"""Discover the owner and grant contact without reading protected details."""
|
|
1665
|
+
result = self._get(f"/groups/{quote(group_id, safe='')}", params={"access_contact": True})
|
|
1666
|
+
if (
|
|
1667
|
+
not isinstance(result, dict)
|
|
1668
|
+
or result.get("resource_type") != "group"
|
|
1669
|
+
or "authorization_contact" not in result
|
|
1670
|
+
):
|
|
1671
|
+
raise ConfigurationError(
|
|
1672
|
+
"Server does not support access contact queries; upgrade Agent-Platform first"
|
|
1673
|
+
)
|
|
1674
|
+
return result
|
|
1675
|
+
|
|
1633
1676
|
def get_group(self, group_id: str, timeout: TimeoutType = None) -> dict:
|
|
1634
1677
|
"""Get group details."""
|
|
1635
1678
|
return self._get(f"/groups/{quote(group_id)}", timeout=timeout)
|
|
@@ -3046,6 +3089,23 @@ class APIClient:
|
|
|
3046
3089
|
return self._get(f"{self._PROFILES_BASE}/{quote(id_or_name)}/versions/{version}")
|
|
3047
3090
|
return self._get(f"{self._PROFILES_BASE}/{quote(id_or_name)}")
|
|
3048
3091
|
|
|
3092
|
+
def update_profile_job_meta(self, profile_id: str, patch: dict) -> dict:
|
|
3093
|
+
"""Patch live Job defaults without changing the Profile version.
|
|
3094
|
+
|
|
3095
|
+
Omitted fields stay unchanged. Set benchmark to None to clear it.
|
|
3096
|
+
Tags use {"append": [...], "remove": [...]} and are applied atomically.
|
|
3097
|
+
"""
|
|
3098
|
+
return self._request_central(
|
|
3099
|
+
"PATCH", f"/profiles/{quote(profile_id, safe='')}/job-meta", json_body=patch
|
|
3100
|
+
)
|
|
3101
|
+
|
|
3102
|
+
def replace_profile_job_meta(self, profile_id: str, job_meta: dict) -> dict:
|
|
3103
|
+
"""Replace all live Job defaults; {} clears benchmark and tags.
|
|
3104
|
+
|
|
3105
|
+
This does not change spec, Profile tags, version, verification or release.
|
|
3106
|
+
"""
|
|
3107
|
+
return self._put_central(f"/profiles/{quote(profile_id, safe='')}/job-meta", job_meta)
|
|
3108
|
+
|
|
3049
3109
|
def update_profile(
|
|
3050
3110
|
self,
|
|
3051
3111
|
profile_id: str,
|
|
@@ -3054,8 +3114,13 @@ class APIClient:
|
|
|
3054
3114
|
description: str = "",
|
|
3055
3115
|
account_pool: Optional[str] = None,
|
|
3056
3116
|
metadata: Optional[dict] = None,
|
|
3117
|
+
reason: Optional[str] = None,
|
|
3057
3118
|
) -> dict:
|
|
3058
|
-
"""Update a submission profile, preserving an omitted default Pool.
|
|
3119
|
+
"""Update a submission profile, preserving an omitted default Pool.
|
|
3120
|
+
|
|
3121
|
+
reason describes this update; omission never reuses a previous reason.
|
|
3122
|
+
The server validates whether it is required and its 500-character limit.
|
|
3123
|
+
"""
|
|
3059
3124
|
body: dict = {"name": name, "spec": spec}
|
|
3060
3125
|
if description:
|
|
3061
3126
|
body["description"] = description
|
|
@@ -3063,6 +3128,8 @@ class APIClient:
|
|
|
3063
3128
|
body["account_pool"] = account_pool
|
|
3064
3129
|
if metadata is not None:
|
|
3065
3130
|
body["metadata"] = metadata
|
|
3131
|
+
if reason is not None:
|
|
3132
|
+
body["reason"] = reason
|
|
3066
3133
|
return self._put(f"{self._PROFILES_BASE}/{quote(profile_id)}", body)
|
|
3067
3134
|
|
|
3068
3135
|
def delete_profile(self, profile_id: str) -> dict:
|
|
@@ -3085,8 +3152,8 @@ class APIClient:
|
|
|
3085
3152
|
) -> dict:
|
|
3086
3153
|
"""把某个 job/group 实例的访问权授予某 user/user_group(scope=resource 绑定)。
|
|
3087
3154
|
|
|
3088
|
-
workspace_id = 该实例所属 queue_id;
|
|
3089
|
-
|
|
3155
|
+
workspace_id = 该实例所属 queue_id;Job/Group Owner、Group Admin 或所属
|
|
3156
|
+
WorkSpace 的管理员/创建者及平台管理员可授权。鉴权走当前 API Key 解析出的调用者身份。
|
|
3090
3157
|
"""
|
|
3091
3158
|
body = {
|
|
3092
3159
|
"scope": "resource",
|
|
@@ -3352,7 +3419,7 @@ def _response_error_detail(resp: requests.Response) -> Any:
|
|
|
3352
3419
|
except Exception:
|
|
3353
3420
|
return resp.text
|
|
3354
3421
|
if isinstance(err_data, dict):
|
|
3355
|
-
return err_data.get("detail", resp.text)
|
|
3422
|
+
return err_data.get("detail", err_data.get("message", resp.text))
|
|
3356
3423
|
return err_data
|
|
3357
3424
|
|
|
3358
3425
|
|