ap-client 0.2.1__tar.gz → 0.2.2.dev0__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.2.1 → ap_client-0.2.2.dev0}/PKG-INFO +3 -3
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/api.py +254 -2
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/cli.py +24 -24
- ap_client-0.2.2.dev0/ap_client/dataset_commands.py +1381 -0
- ap_client-0.2.2.dev0/ap_client/fs_commands.py +674 -0
- ap_client-0.2.2.dev0/ap_client/instance_commands.py +551 -0
- ap_client-0.2.2.dev0/ap_client/irepo_sdk.py +326 -0
- ap_client-0.2.2.dev0/ap_client/split_publish.py +133 -0
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/pyproject.toml +3 -3
- ap_client-0.2.1/ap_client/irepo_commands.py +0 -131
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/__init__.py +0 -0
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/config.py +0 -0
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/exporter.py +0 -0
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/profile_commands.py +0 -0
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/tbb.py +0 -0
- {ap_client-0.2.1 → ap_client-0.2.2.dev0}/ap_client/waiter.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ap-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2.dev0
|
|
4
4
|
Summary: Agent Platform API Client & CLI
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: pyyaml>=6.0
|
|
@@ -9,9 +9,9 @@ Requires-Dist: rich>=13.0.0
|
|
|
9
9
|
Requires-Dist: typer>=0.9.0
|
|
10
10
|
Requires-Dist: websockets>=13.0
|
|
11
11
|
Provides-Extra: all
|
|
12
|
-
Requires-Dist: instance-repo[oss]>=0.
|
|
12
|
+
Requires-Dist: instance-repo[oss]>=1.0.9; extra == 'all'
|
|
13
13
|
Provides-Extra: dataset
|
|
14
|
-
Requires-Dist: instance-repo[oss]>=0.
|
|
14
|
+
Requires-Dist: instance-repo[oss]>=1.0.9; extra == 'dataset'
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
|
|
17
17
|
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.
|
|
@@ -293,6 +293,48 @@ def _secret_ws_params(workspace_id: Optional[str]) -> Optional[dict]:
|
|
|
293
293
|
return {"workspace_id": workspace_id} if workspace_id else None
|
|
294
294
|
|
|
295
295
|
|
|
296
|
+
def _unwrap_data(payload: Any) -> Any:
|
|
297
|
+
"""Unwrap the apiserver ``{code,message,data}`` envelope when present.
|
|
298
|
+
|
|
299
|
+
The dataset-domain detail/create/patch endpoints return a bare JSON body,
|
|
300
|
+
but the same handlers are occasionally wrapped by the generic success
|
|
301
|
+
envelope. Tolerate both so command code never has to branch: a mapping that
|
|
302
|
+
carries ``data`` alongside ``code``/``message`` is treated as an envelope,
|
|
303
|
+
anything else is returned verbatim.
|
|
304
|
+
"""
|
|
305
|
+
if (
|
|
306
|
+
isinstance(payload, dict)
|
|
307
|
+
and "data" in payload
|
|
308
|
+
and ("code" in payload or "message" in payload)
|
|
309
|
+
):
|
|
310
|
+
return payload["data"]
|
|
311
|
+
return payload
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _split_paged(payload: Any) -> tuple[list[dict], dict]:
|
|
315
|
+
"""Split a ``PagedSuccess`` envelope into ``(records, pagination)``.
|
|
316
|
+
|
|
317
|
+
``PagedSuccess`` is ``{code,message,data:[...],pagination:{total,page,page_size}}``.
|
|
318
|
+
A bare list (or a null ``data``) yields an empty pagination dict so callers
|
|
319
|
+
can render results without probing the response shape.
|
|
320
|
+
"""
|
|
321
|
+
if isinstance(payload, list):
|
|
322
|
+
return [item for item in payload if isinstance(item, dict)], {}
|
|
323
|
+
if not isinstance(payload, dict):
|
|
324
|
+
return [], {}
|
|
325
|
+
records = payload.get("data")
|
|
326
|
+
pagination = payload.get("pagination")
|
|
327
|
+
return (
|
|
328
|
+
[item for item in (records or []) if isinstance(item, dict)],
|
|
329
|
+
pagination if isinstance(pagination, dict) else {},
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _as_record_list(payload: Any) -> list[dict]:
|
|
334
|
+
"""Coerce a list-returning endpoint to ``list[dict]``, envelope or not."""
|
|
335
|
+
return _split_paged(payload)[0]
|
|
336
|
+
|
|
337
|
+
|
|
296
338
|
class APIClient:
|
|
297
339
|
"""Agent Platform API client."""
|
|
298
340
|
|
|
@@ -708,8 +750,35 @@ class APIClient:
|
|
|
708
750
|
return self._get(f"/templates/{quote(name)}", params=params)
|
|
709
751
|
|
|
710
752
|
def list_benchmarks(self) -> list:
|
|
711
|
-
"""List all
|
|
712
|
-
|
|
753
|
+
"""List all benchmarks from the central registry, preserving legacy IDs."""
|
|
754
|
+
benchmarks = []
|
|
755
|
+
page = 1
|
|
756
|
+
page_size = 200
|
|
757
|
+
# 与 CLI dataset list 的自动翻页同口径:100 页上限,防分页异常导致死循环。
|
|
758
|
+
max_pages = 100
|
|
759
|
+
while page <= max_pages:
|
|
760
|
+
payload = self._get(
|
|
761
|
+
"/apis/v1/benchmarks", params={"page": page, "page_size": page_size}
|
|
762
|
+
)
|
|
763
|
+
records, pagination = _split_paged(payload)
|
|
764
|
+
for record in records:
|
|
765
|
+
item = dict(record)
|
|
766
|
+
if "source_id" in item:
|
|
767
|
+
item.setdefault("id", item["source_id"])
|
|
768
|
+
benchmarks.append(item)
|
|
769
|
+
if not records or not pagination:
|
|
770
|
+
break
|
|
771
|
+
try:
|
|
772
|
+
total = int(pagination.get("total"))
|
|
773
|
+
except (TypeError, ValueError):
|
|
774
|
+
total = None
|
|
775
|
+
if total is not None:
|
|
776
|
+
if len(benchmarks) >= total:
|
|
777
|
+
break
|
|
778
|
+
elif len(records) < page_size:
|
|
779
|
+
break
|
|
780
|
+
page += 1
|
|
781
|
+
return benchmarks
|
|
713
782
|
|
|
714
783
|
def get_benchmark(self, name: str) -> dict:
|
|
715
784
|
"""Get a single benchmark by exact name."""
|
|
@@ -844,6 +913,189 @@ class APIClient:
|
|
|
844
913
|
"instance_ids": all_instance_ids,
|
|
845
914
|
}
|
|
846
915
|
|
|
916
|
+
# ==================== Dataset series (apiserver dataset domain) ====================
|
|
917
|
+
#
|
|
918
|
+
# 这一节对接 Go apiserver 的 dataset 领域(/apis/v1/datasets/...),与上面
|
|
919
|
+
# ossdata 目录的 /api/datasets 路由完全无关。方法名统一带 `_series`,
|
|
920
|
+
# 以免和 `ap job create --dataset` 依赖的 list_all_datasets /
|
|
921
|
+
# list_dataset_versions / list_all_dataset_instances 混淆。
|
|
922
|
+
#
|
|
923
|
+
# 列表端点返回 PagedSuccess envelope:
|
|
924
|
+
# {"code":0,"message":"ok","data":[...],"pagination":{"total","page","page_size"}}
|
|
925
|
+
# 详情/创建/更新端点返回裸 JSON body。
|
|
926
|
+
|
|
927
|
+
_DATASET_SERIES_BASE = "/apis/v1/datasets/series"
|
|
928
|
+
_DATASET_VERSIONS_BASE = "/apis/v1/datasets/versions"
|
|
929
|
+
_DATASET_INSTANCES_BASE = "/apis/v1/datasets/instances"
|
|
930
|
+
|
|
931
|
+
def list_dataset_series_page(
|
|
932
|
+
self,
|
|
933
|
+
*,
|
|
934
|
+
q: Optional[str] = None,
|
|
935
|
+
keyword: Optional[str] = None,
|
|
936
|
+
visibility: Optional[str] = None,
|
|
937
|
+
benchmark_id: Optional[str] = None,
|
|
938
|
+
include_deprecated: bool = False,
|
|
939
|
+
environment: Optional[str] = None,
|
|
940
|
+
page: int = 1,
|
|
941
|
+
page_size: int = 20,
|
|
942
|
+
) -> dict:
|
|
943
|
+
"""一页 dataset series(原始 PagedSuccess envelope)。
|
|
944
|
+
|
|
945
|
+
服务端没有 owner/mine/l1 过滤参数,调用方需要时只能在客户端做页内过滤。
|
|
946
|
+
"""
|
|
947
|
+
params: dict = {"page": page, "page_size": page_size}
|
|
948
|
+
if q:
|
|
949
|
+
params["q"] = q
|
|
950
|
+
if keyword:
|
|
951
|
+
params["keyword"] = keyword
|
|
952
|
+
if visibility:
|
|
953
|
+
params["visibility"] = visibility
|
|
954
|
+
if benchmark_id:
|
|
955
|
+
params["benchmark_id"] = benchmark_id
|
|
956
|
+
if include_deprecated:
|
|
957
|
+
params["include_deprecated"] = "true"
|
|
958
|
+
if environment:
|
|
959
|
+
params["environment"] = environment
|
|
960
|
+
return self._get(self._DATASET_SERIES_BASE, params=params)
|
|
961
|
+
|
|
962
|
+
def list_dataset_series(self, **kw) -> tuple[list[dict], dict]:
|
|
963
|
+
"""列出 dataset series,返回 ``(records, pagination)``。"""
|
|
964
|
+
return _split_paged(self.list_dataset_series_page(**kw))
|
|
965
|
+
|
|
966
|
+
def get_dataset_series(self, dataset_name: str, *, environment: Optional[str] = None) -> dict:
|
|
967
|
+
"""按名字取 dataset series 详情(含 dataset_id / claimable / stats)。"""
|
|
968
|
+
params: dict = {"dataset_name": dataset_name}
|
|
969
|
+
if environment:
|
|
970
|
+
params["environment"] = environment
|
|
971
|
+
return _unwrap_data(self._get(f"{self._DATASET_SERIES_BASE}/detail", params=params))
|
|
972
|
+
|
|
973
|
+
def update_dataset_series(self, dataset_name: str, body: dict) -> dict:
|
|
974
|
+
"""PATCH dataset series 元数据(visibility/status 需要 dataset-admin)。"""
|
|
975
|
+
return _unwrap_data(
|
|
976
|
+
self._request(
|
|
977
|
+
"PATCH",
|
|
978
|
+
f"{self._DATASET_SERIES_BASE}/detail",
|
|
979
|
+
params={"dataset_name": dataset_name},
|
|
980
|
+
json_body=body,
|
|
981
|
+
)
|
|
982
|
+
)
|
|
983
|
+
|
|
984
|
+
def list_claim_workspaces(self) -> list[dict]:
|
|
985
|
+
"""列出当前用户可用于认领的 workspace。"""
|
|
986
|
+
return _as_record_list(self._get("/apis/v1/datasets/claim-workspaces"))
|
|
987
|
+
|
|
988
|
+
# ---- dataset versions ----
|
|
989
|
+
|
|
990
|
+
def list_dataset_series_versions(
|
|
991
|
+
self,
|
|
992
|
+
dataset_name: str,
|
|
993
|
+
*,
|
|
994
|
+
status: Optional[str] = None,
|
|
995
|
+
page: int = 1,
|
|
996
|
+
page_size: int = 20,
|
|
997
|
+
environment: Optional[str] = None,
|
|
998
|
+
) -> tuple[list[dict], dict]:
|
|
999
|
+
"""列出某 dataset 的版本,返回 ``(records, pagination)``。列表项不含 splits 数组。"""
|
|
1000
|
+
params: dict = {"dataset_name": dataset_name, "page": page, "page_size": page_size}
|
|
1001
|
+
if status:
|
|
1002
|
+
params["status"] = status
|
|
1003
|
+
if environment:
|
|
1004
|
+
params["environment"] = environment
|
|
1005
|
+
return _split_paged(self._get(self._DATASET_VERSIONS_BASE, params=params))
|
|
1006
|
+
|
|
1007
|
+
def get_dataset_series_version(
|
|
1008
|
+
self,
|
|
1009
|
+
dataset_name: str,
|
|
1010
|
+
version: str,
|
|
1011
|
+
*,
|
|
1012
|
+
environment: Optional[str] = None,
|
|
1013
|
+
) -> dict:
|
|
1014
|
+
"""取单个版本详情(含 run_type / splits[] / manifest / published_at)。
|
|
1015
|
+
|
|
1016
|
+
``version=""`` 表示 split_first 的**无版本空间**,原样下发空串——绝不隐式取 latest。
|
|
1017
|
+
"""
|
|
1018
|
+
params: dict = {"dataset_name": dataset_name, "version": version}
|
|
1019
|
+
if environment:
|
|
1020
|
+
params["environment"] = environment
|
|
1021
|
+
return _unwrap_data(self._get(f"{self._DATASET_VERSIONS_BASE}/detail", params=params))
|
|
1022
|
+
|
|
1023
|
+
# ---- dataset instances (metadata only) ----
|
|
1024
|
+
|
|
1025
|
+
def list_dataset_series_instances(
|
|
1026
|
+
self,
|
|
1027
|
+
dataset_name: str,
|
|
1028
|
+
*,
|
|
1029
|
+
version: Optional[str] = None,
|
|
1030
|
+
split: Optional[str] = None,
|
|
1031
|
+
instance_id: Optional[str] = None,
|
|
1032
|
+
page: int = 1,
|
|
1033
|
+
page_size: int = 50,
|
|
1034
|
+
environment: Optional[str] = None,
|
|
1035
|
+
) -> tuple[list[dict], dict]:
|
|
1036
|
+
"""列出 instance 元数据,返回 ``(records, pagination)``。
|
|
1037
|
+
|
|
1038
|
+
``version=""`` / ``split=""`` 原样下发(无版本空间语义),``None`` 才省略该参数。
|
|
1039
|
+
"""
|
|
1040
|
+
params: dict = {"dataset_name": dataset_name, "page": page, "page_size": page_size}
|
|
1041
|
+
if version is not None:
|
|
1042
|
+
params["version"] = version
|
|
1043
|
+
if split is not None:
|
|
1044
|
+
params["split"] = split
|
|
1045
|
+
if instance_id:
|
|
1046
|
+
params["instance_id"] = instance_id
|
|
1047
|
+
if environment:
|
|
1048
|
+
params["environment"] = environment
|
|
1049
|
+
return _split_paged(self._get(self._DATASET_INSTANCES_BASE, params=params))
|
|
1050
|
+
|
|
1051
|
+
def get_dataset_series_instance(
|
|
1052
|
+
self,
|
|
1053
|
+
dataset_name: str,
|
|
1054
|
+
version: str,
|
|
1055
|
+
split: str,
|
|
1056
|
+
instance_id: str,
|
|
1057
|
+
*,
|
|
1058
|
+
environment: Optional[str] = None,
|
|
1059
|
+
) -> dict:
|
|
1060
|
+
"""取单个 instance 的元数据详情。``version=""`` 表示无版本空间。"""
|
|
1061
|
+
params: dict = {
|
|
1062
|
+
"dataset_name": dataset_name,
|
|
1063
|
+
"version": version,
|
|
1064
|
+
"split": split,
|
|
1065
|
+
"instance_id": instance_id,
|
|
1066
|
+
}
|
|
1067
|
+
if environment:
|
|
1068
|
+
params["environment"] = environment
|
|
1069
|
+
return _unwrap_data(self._get(f"{self._DATASET_INSTANCES_BASE}/detail", params=params))
|
|
1070
|
+
|
|
1071
|
+
def get_dataset_series_split(self, dataset_name: str, version: str, split: str) -> dict:
|
|
1072
|
+
"""Read the selected split's lifecycle without a version-level aggregate."""
|
|
1073
|
+
return _unwrap_data(
|
|
1074
|
+
self._get(
|
|
1075
|
+
"/apis/v1/datasets/splits/detail",
|
|
1076
|
+
params={"dataset_name": dataset_name, "version": version, "split": split},
|
|
1077
|
+
)
|
|
1078
|
+
)
|
|
1079
|
+
|
|
1080
|
+
def get_dataset_split_release(self, workflow_id: str) -> dict:
|
|
1081
|
+
"""Read a release workflow, including approval and per-step outcomes."""
|
|
1082
|
+
return _unwrap_data(self._get(f"/apis/v1/workflows/{quote(workflow_id, safe='')}"))
|
|
1083
|
+
|
|
1084
|
+
# ---- permissions ----
|
|
1085
|
+
|
|
1086
|
+
def get_my_resource_permissions(self, resource_type: str, resource_id: str) -> list[str]:
|
|
1087
|
+
"""当前用户在某资源上的 action 列表(``{"actions":[...]}``)。"""
|
|
1088
|
+
payload = self._get(
|
|
1089
|
+
"/apis/v1/me/resource-permissions",
|
|
1090
|
+
params={"resource_type": resource_type, "resource_id": resource_id},
|
|
1091
|
+
)
|
|
1092
|
+
payload = _unwrap_data(payload)
|
|
1093
|
+
if isinstance(payload, dict):
|
|
1094
|
+
actions = payload.get("actions")
|
|
1095
|
+
if isinstance(actions, list):
|
|
1096
|
+
return [str(action) for action in actions]
|
|
1097
|
+
return []
|
|
1098
|
+
|
|
847
1099
|
# ==================== Meta operations ====================
|
|
848
1100
|
|
|
849
1101
|
def list_meta_models(self) -> dict:
|
|
@@ -22,8 +22,10 @@ from ap_client.api import (
|
|
|
22
22
|
set_verbose_override,
|
|
23
23
|
)
|
|
24
24
|
from ap_client.config import ENV_VAR_SPECS, ConfigurationError, _parse_bool, normalize_output_format
|
|
25
|
+
from ap_client.dataset_commands import register as _register_dataset_commands
|
|
25
26
|
from ap_client.exporter import export_group, export_job
|
|
26
|
-
from ap_client.
|
|
27
|
+
from ap_client.fs_commands import register as _register_fs_commands
|
|
28
|
+
from ap_client.instance_commands import register as _register_instance_commands
|
|
27
29
|
from ap_client.profile_commands import profile_app
|
|
28
30
|
from ap_client.waiter import (
|
|
29
31
|
WaitTimeoutError,
|
|
@@ -144,8 +146,11 @@ app.add_typer(meta_job_type_app, name="meta-job-type")
|
|
|
144
146
|
app.add_typer(benchmark_app, name="benchmark")
|
|
145
147
|
app.add_typer(checkpoint_app, name="checkpoint")
|
|
146
148
|
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
+
# Dataset 领域(见 docs/ap-cli-dataset-refactor-design.md):
|
|
150
|
+
# ap dataset version/split/access 嵌套在 dataset 下;instance/fs 是一等资源。
|
|
151
|
+
_register_dataset_commands(dataset_app)
|
|
152
|
+
_register_instance_commands(app)
|
|
153
|
+
_register_fs_commands(app)
|
|
149
154
|
|
|
150
155
|
_PAI_RUNTIME_ENV_TAGS: tuple[tuple[str, str], ...] = (
|
|
151
156
|
("DLC_JOB_ID", "dlc_job_id"),
|
|
@@ -2015,24 +2020,17 @@ def template_fetch(
|
|
|
2015
2020
|
|
|
2016
2021
|
# ==================== Dataset operations ====================
|
|
2017
2022
|
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
2030
|
-
client = get_client()
|
|
2031
|
-
result = client.list_all_datasets(search)
|
|
2032
|
-
if output_format == "table":
|
|
2033
|
-
_print_records_table(result)
|
|
2034
|
-
else:
|
|
2035
|
-
_print_formatted(result, output_format)
|
|
2023
|
+
# 设计 §6 / 上游 §13.2:`ap dataset versions` 与 `ap dataset instances` 行为保持不变
|
|
2024
|
+
# (仍打 ossdata 路由,`ap job create` 的配套流程依赖它们),只追加固定弃用文案。
|
|
2025
|
+
# 文案**只写 stderr**——JSON/YAML 的 stdout 必须仍可被 json.loads 直接消费。
|
|
2026
|
+
_DATASET_VERSIONS_DEPRECATION = (
|
|
2027
|
+
"Warning: `ap dataset versions` is deprecated.\n"
|
|
2028
|
+
"Use `ap dataset version list --dataset <dataset>` instead."
|
|
2029
|
+
)
|
|
2030
|
+
_DATASET_INSTANCES_DEPRECATION = (
|
|
2031
|
+
"Warning: `ap dataset instances` is deprecated.\n"
|
|
2032
|
+
"Use `ap instance list --dataset <dataset> --split <split> [--version <version>]` instead."
|
|
2033
|
+
)
|
|
2036
2034
|
|
|
2037
2035
|
|
|
2038
2036
|
@dataset_app.command("versions")
|
|
@@ -2044,8 +2042,9 @@ def dataset_versions(
|
|
|
2044
2042
|
help="Output format: plain/table/json/yaml (default: AP_FORMAT or command default)",
|
|
2045
2043
|
),
|
|
2046
2044
|
):
|
|
2047
|
-
"""List dataset versions."""
|
|
2045
|
+
"""List dataset versions (deprecated: use 'ap dataset version list')."""
|
|
2048
2046
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
2047
|
+
_emit_progress(_DATASET_VERSIONS_DEPRECATION)
|
|
2049
2048
|
client = get_client()
|
|
2050
2049
|
versions = client.list_dataset_versions(dataset)
|
|
2051
2050
|
if output_format == "table":
|
|
@@ -2065,8 +2064,9 @@ def dataset_instances(
|
|
|
2065
2064
|
help="Output format: plain/table/json/yaml (default: AP_FORMAT or command default)",
|
|
2066
2065
|
),
|
|
2067
2066
|
):
|
|
2068
|
-
"""List dataset instances."""
|
|
2067
|
+
"""List dataset instances (deprecated: use 'ap instance list')."""
|
|
2069
2068
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
2069
|
+
_emit_progress(_DATASET_INSTANCES_DEPRECATION)
|
|
2070
2070
|
client = get_client()
|
|
2071
2071
|
result = client.list_all_dataset_instances(dataset_version)
|
|
2072
2072
|
if output_format == "table":
|
|
@@ -4779,7 +4779,7 @@ def benchmark_list(
|
|
|
4779
4779
|
help="Output format: plain/table/json/yaml (default: AP_FORMAT or command default)",
|
|
4780
4780
|
),
|
|
4781
4781
|
):
|
|
4782
|
-
"""List benchmarks from the
|
|
4782
|
+
"""List benchmarks from the central benchmark registry."""
|
|
4783
4783
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
4784
4784
|
client = get_client()
|
|
4785
4785
|
result = client.list_benchmarks()
|