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.
Files changed (26) hide show
  1. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/PKG-INFO +4 -4
  2. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/api.py +71 -4
  3. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/cli.py +134 -218
  4. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/dataset_commands.py +136 -35
  5. ap_client-0.6.1.dev1/ap_client/dataset_name.py +53 -0
  6. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/dataset_register.py +20 -6
  7. ap_client-0.6.1.dev1/ap_client/dfs_commands.py +680 -0
  8. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/exporter.py +33 -2
  9. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/instance_commands.py +519 -52
  10. ap_client-0.6.1.dev1/ap_client/instance_options.py +94 -0
  11. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/irepo_sdk.py +70 -4
  12. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/managed_ak.py +44 -14
  13. ap_client-0.6.1.dev1/ap_client/ossutil_hash.py +329 -0
  14. ap_client-0.6.1.dev1/ap_client/ossutil_io.py +1230 -0
  15. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/profile_commands.py +128 -16
  16. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/repo_read_guard.py +6 -2
  17. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/split_publish.py +1 -1
  18. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/pyproject.toml +5 -4
  19. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/__init__.py +0 -0
  20. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/akpool_commands.py +0 -0
  21. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/config.py +0 -0
  22. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/fs_commands.py +0 -0
  23. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/manifest_view.py +0 -0
  24. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/share_api.py +0 -0
  25. {ap_client-0.6.0.dev2 → ap_client-0.6.1.dev1}/ap_client/tbb.py +0 -0
  26. {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.0.dev2
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.1
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.3.2; extra == 'all'
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.3.2; extra == 'dataset'
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;apiserver 按该 WorkSpace 的 workspace-admin/
3089
- 创建者/superuser 门控。鉴权走当前 API Key 解析出的调用者身份。
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