ap-client 0.5.0.dev1__tar.gz → 0.5.1.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.5.0.dev1 → ap_client-0.5.1.dev0}/PKG-INFO +3 -4
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/akpool_commands.py +1 -3
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/api.py +16 -53
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/cli.py +10 -93
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/dataset_commands.py +40 -14
- ap_client-0.5.1.dev0/ap_client/dataset_register.py +320 -0
- ap_client-0.5.1.dev0/ap_client/exporter.py +934 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/instance_commands.py +4 -1
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/pyproject.toml +3 -4
- ap_client-0.5.0.dev1/ap_client/exporter.py +0 -729
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/__init__.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/config.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/fs_commands.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/irepo_sdk.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/managed_ak.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/profile_commands.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/split_publish.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/tbb.py +0 -0
- {ap_client-0.5.0.dev1 → ap_client-0.5.1.dev0}/ap_client/waiter.py +0 -0
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ap-client
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1.dev0
|
|
4
4
|
Summary: Agent Platform API Client & CLI
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: irepo-artifact==0.2.0
|
|
7
6
|
Requires-Dist: pyyaml>=6.0
|
|
8
7
|
Requires-Dist: requests>=2.28.0
|
|
9
8
|
Requires-Dist: rich>=13.0.0
|
|
10
9
|
Requires-Dist: typer>=0.9.0
|
|
11
10
|
Requires-Dist: websockets>=13.0
|
|
12
11
|
Provides-Extra: all
|
|
13
|
-
Requires-Dist: instance-repo[oss]
|
|
12
|
+
Requires-Dist: instance-repo[oss]==1.3.2.dev0; extra == 'all'
|
|
14
13
|
Provides-Extra: dataset
|
|
15
|
-
Requires-Dist: instance-repo[oss]
|
|
14
|
+
Requires-Dist: instance-repo[oss]==1.3.2.dev0; extra == 'dataset'
|
|
16
15
|
Description-Content-Type: text/markdown
|
|
17
16
|
|
|
18
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.
|
|
@@ -63,9 +63,7 @@ def _fmt(output_format: Optional[str]) -> str:
|
|
|
63
63
|
def _check_time_range(value: str) -> str:
|
|
64
64
|
normalized = str(value or "").strip().lower()
|
|
65
65
|
if not _TIME_RANGE_RE.fullmatch(normalized):
|
|
66
|
-
raise typer.BadParameter(
|
|
67
|
-
f"--time-range must be a duration like 30s/5m/1h, got {value!r}"
|
|
68
|
-
)
|
|
66
|
+
raise typer.BadParameter(f"--time-range must be a duration like 30s/5m/1h, got {value!r}")
|
|
69
67
|
return normalized
|
|
70
68
|
|
|
71
69
|
|
|
@@ -123,14 +123,6 @@ _UNSET: Any = object()
|
|
|
123
123
|
# when aggregation runs later.
|
|
124
124
|
EXTRA_AGG_OPS = ("avg", "sum")
|
|
125
125
|
|
|
126
|
-
# Others 占位:apiserver 词表不返回,CLI 展示与 get_meta_model("Others") 兜底用。
|
|
127
|
-
_OTHERS_MODEL = {
|
|
128
|
-
"name": "Others",
|
|
129
|
-
"series_name": "",
|
|
130
|
-
"source_id": "",
|
|
131
|
-
"series_id": "",
|
|
132
|
-
}
|
|
133
|
-
|
|
134
126
|
|
|
135
127
|
def _merge_meta_tags(
|
|
136
128
|
tags: Optional[list],
|
|
@@ -976,18 +968,19 @@ class APIClient:
|
|
|
976
968
|
# `dataset versions/instances`、`job create --dataset`)。
|
|
977
969
|
|
|
978
970
|
def list_meta_models(self) -> dict:
|
|
979
|
-
"""
|
|
980
|
-
|
|
971
|
+
"""从 meta-model 标签目录获取可用模型,保留 list 的 items 响应形状。"""
|
|
972
|
+
items = self.get_meta_tags("meta-model").get("items") or []
|
|
973
|
+
return {
|
|
974
|
+
"items": [
|
|
975
|
+
{**item, "name": item["value"]}
|
|
976
|
+
for item in items
|
|
977
|
+
if item.get("value") and item.get("is_enabled", True)
|
|
978
|
+
]
|
|
979
|
+
}
|
|
981
980
|
|
|
982
981
|
def get_meta_model(self, name: str) -> dict:
|
|
983
|
-
"""
|
|
984
|
-
|
|
985
|
-
apiserver 没有单模型详情接口,客户端从 list 结果过滤;找不到 raise
|
|
986
|
-
APIError 404,与原 cluster AP /meta/models/{name} 行为一致。
|
|
987
|
-
"""
|
|
988
|
-
if name == "Others":
|
|
989
|
-
return dict(_OTHERS_MODEL)
|
|
990
|
-
result = self._get_central("/meta-models")
|
|
982
|
+
"""从启用的 meta-model 标签值中按 value 或 source_id 查找。"""
|
|
983
|
+
result = self.list_meta_models()
|
|
991
984
|
for item in result.get("items") or []:
|
|
992
985
|
if item.get("name") == name or item.get("source_id") == name:
|
|
993
986
|
return item
|
|
@@ -1053,43 +1046,13 @@ class APIClient:
|
|
|
1053
1046
|
return result
|
|
1054
1047
|
|
|
1055
1048
|
def list_meta_job_types(self) -> dict:
|
|
1056
|
-
"""
|
|
1057
|
-
|
|
1058
|
-
仅取目录中 ``is_enabled`` 的值(缺失按启用解析)+ 末尾 ``Others``
|
|
1059
|
-
兜底行;``default`` 为 ``Eval-Eval``(在目录中时,否则空串)。
|
|
1060
|
-
目录请求失败或为空 → 回退本地常量(Eval-Eval + Others,fail-open),
|
|
1061
|
-
并在 stderr 打一行告警,词表降级可见但不阻塞命令。
|
|
1062
|
-
"""
|
|
1063
|
-
error: Exception | None = None
|
|
1064
|
-
try:
|
|
1065
|
-
items = self.get_meta_tags("meta-job-type").get("items") or []
|
|
1066
|
-
except Exception as exc: # fail-open:目录不可用不阻塞词表命令
|
|
1067
|
-
error = exc
|
|
1068
|
-
items = []
|
|
1069
|
-
if items:
|
|
1070
|
-
options = []
|
|
1071
|
-
for item in items:
|
|
1072
|
-
value = str(item.get("value") or "").strip()
|
|
1073
|
-
if value and item.get("is_enabled", True):
|
|
1074
|
-
options.append({"value": value})
|
|
1075
|
-
if options:
|
|
1076
|
-
default = "Eval-Eval" if any(o["value"] == "Eval-Eval" for o in options) else ""
|
|
1077
|
-
return {
|
|
1078
|
-
"meta_job_type": {
|
|
1079
|
-
"default": default,
|
|
1080
|
-
"options": [*options, {"value": "Others"}],
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
_write_stderr(
|
|
1084
|
-
[
|
|
1085
|
-
"warning: meta-job-type catalog unavailable or empty, "
|
|
1086
|
-
f"falling back to local constants (reason={error or 'empty catalog'})"
|
|
1087
|
-
]
|
|
1088
|
-
)
|
|
1049
|
+
"""从 meta-job-type 标签目录获取启用的任务类型,不补本地展示值。"""
|
|
1050
|
+
items = self.get_meta_tags("meta-job-type").get("items") or []
|
|
1089
1051
|
return {
|
|
1090
1052
|
"meta_job_type": {
|
|
1091
|
-
"
|
|
1092
|
-
|
|
1053
|
+
"options": [
|
|
1054
|
+
item for item in items if item.get("value") and item.get("is_enabled", True)
|
|
1055
|
+
],
|
|
1093
1056
|
}
|
|
1094
1057
|
}
|
|
1095
1058
|
|
|
@@ -15,7 +15,6 @@ import typer
|
|
|
15
15
|
from ap_client import __version__, get_client, get_config, tbb
|
|
16
16
|
from ap_client.akpool_commands import register as _register_akpool_commands
|
|
17
17
|
from ap_client.api import (
|
|
18
|
-
_OTHERS_MODEL,
|
|
19
18
|
APIError,
|
|
20
19
|
PaginationProtocolError,
|
|
21
20
|
_is_sensitive_key,
|
|
@@ -26,7 +25,7 @@ from ap_client.api import (
|
|
|
26
25
|
)
|
|
27
26
|
from ap_client.config import ENV_VAR_SPECS, ConfigurationError, _parse_bool, normalize_output_format
|
|
28
27
|
from ap_client.dataset_commands import register as _register_dataset_commands
|
|
29
|
-
from ap_client.exporter import export_group, export_job
|
|
28
|
+
from ap_client.exporter import export_group, export_job
|
|
30
29
|
from ap_client.fs_commands import register as _register_fs_commands
|
|
31
30
|
from ap_client.instance_commands import register as _register_instance_commands
|
|
32
31
|
from ap_client.managed_ak import (
|
|
@@ -2689,23 +2688,22 @@ def meta_model_list(
|
|
|
2689
2688
|
help="Output format: plain/table/json/yaml (default: AP_FORMAT or command default)",
|
|
2690
2689
|
),
|
|
2691
2690
|
):
|
|
2692
|
-
"""List meta
|
|
2691
|
+
"""List enabled meta-model tag values."""
|
|
2693
2692
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
2694
2693
|
result = get_client().list_meta_models()
|
|
2695
2694
|
items = result.get("items") or []
|
|
2696
|
-
options =
|
|
2695
|
+
options = items
|
|
2697
2696
|
if output_format in ("plain", "table"):
|
|
2698
2697
|
rows = [
|
|
2699
2698
|
{
|
|
2700
2699
|
"name": m.get("name") or "-",
|
|
2701
|
-
"
|
|
2700
|
+
"desc": m.get("desc_l1") or "-",
|
|
2702
2701
|
}
|
|
2703
2702
|
for m in options
|
|
2704
2703
|
]
|
|
2705
|
-
_print_plain_table(rows, [("name", "name"), ("
|
|
2704
|
+
_print_plain_table(rows, [("name", "name"), ("desc", "desc")])
|
|
2706
2705
|
else:
|
|
2707
|
-
#
|
|
2708
|
-
# default 已随 meta-models default 逻辑移除而下线)
|
|
2706
|
+
# JSON/YAML 保持 {meta_model:{options}};每项保留目录字段及 name 别名。
|
|
2709
2707
|
_print_formatted({"meta_model": {"options": options}}, output_format)
|
|
2710
2708
|
|
|
2711
2709
|
|
|
@@ -2796,21 +2794,20 @@ def meta_job_type_list(
|
|
|
2796
2794
|
help="Output format: plain/table/json/yaml (default: AP_FORMAT or command default)",
|
|
2797
2795
|
),
|
|
2798
2796
|
):
|
|
2799
|
-
"""List meta
|
|
2797
|
+
"""List enabled meta-job-type tag values."""
|
|
2800
2798
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
2801
2799
|
result = get_client().list_meta_job_types()
|
|
2802
2800
|
info = result.get("meta_job_type") or {}
|
|
2803
2801
|
options = info.get("options") or []
|
|
2804
|
-
default = info.get("default") or ""
|
|
2805
2802
|
if output_format in ("plain", "table"):
|
|
2806
2803
|
rows = [
|
|
2807
2804
|
{
|
|
2808
2805
|
"value": o.get("value") or "-",
|
|
2809
|
-
"
|
|
2806
|
+
"desc": o.get("desc_l1") or "-",
|
|
2810
2807
|
}
|
|
2811
2808
|
for o in options
|
|
2812
2809
|
]
|
|
2813
|
-
_print_plain_table(rows, [("value", "value"), ("
|
|
2810
|
+
_print_plain_table(rows, [("value", "value"), ("desc", "desc")])
|
|
2814
2811
|
else:
|
|
2815
2812
|
_print_formatted(result, output_format)
|
|
2816
2813
|
|
|
@@ -4879,47 +4876,10 @@ def job_artifacts(
|
|
|
4879
4876
|
_print_formatted(results, output_format)
|
|
4880
4877
|
|
|
4881
4878
|
|
|
4882
|
-
def _validate_export_timeout(value: float) -> float:
|
|
4883
|
-
import math
|
|
4884
|
-
|
|
4885
|
-
if not math.isfinite(value) or value <= 0:
|
|
4886
|
-
raise typer.BadParameter("Timeout must be a finite number greater than zero")
|
|
4887
|
-
return value
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
def _export_channel_reporter(emit):
|
|
4891
|
-
"""Report completed Jobs, including callbacks from Group worker threads."""
|
|
4892
|
-
from threading import Lock
|
|
4893
|
-
|
|
4894
|
-
from rich.text import Text
|
|
4895
|
-
|
|
4896
|
-
counts = {"oss": 0, "proxy": 0}
|
|
4897
|
-
lock = Lock()
|
|
4898
|
-
|
|
4899
|
-
def report(job_id, status, channel):
|
|
4900
|
-
with lock:
|
|
4901
|
-
suffix = ""
|
|
4902
|
-
if status == "downloaded" and channel in counts:
|
|
4903
|
-
counts[channel] += 1
|
|
4904
|
-
suffix = f" channel={channel}"
|
|
4905
|
-
emit(Text(f"{job_id} {status}{suffix}"))
|
|
4906
|
-
|
|
4907
|
-
return report, counts
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
4879
|
@job_app.command("export")
|
|
4911
4880
|
def job_export(
|
|
4912
|
-
job_id: str = typer.Argument(..., help="Job ID
|
|
4881
|
+
job_id: str = typer.Argument(..., help="Job ID"),
|
|
4913
4882
|
output: Optional[Path] = typer.Option(None, "--output", "-o", help="Export directory"),
|
|
4914
|
-
workers: int = typer.Option(
|
|
4915
|
-
1, "--workers", "-w", min=1, help="Number of concurrent export workers"
|
|
4916
|
-
),
|
|
4917
|
-
timeout: float = typer.Option(
|
|
4918
|
-
300,
|
|
4919
|
-
"--timeout",
|
|
4920
|
-
callback=_validate_export_timeout,
|
|
4921
|
-
help="SDK request timeout in seconds, not the total export duration",
|
|
4922
|
-
),
|
|
4923
4883
|
logs: bool = typer.Option(False, "--logs", help="Include container logs"),
|
|
4924
4884
|
events: bool = typer.Option(False, "--events", help="Include events"),
|
|
4925
4885
|
no_extract_artifacts: bool = typer.Option(
|
|
@@ -4929,50 +4889,17 @@ def job_export(
|
|
|
4929
4889
|
),
|
|
4930
4890
|
):
|
|
4931
4891
|
"""Export job artifacts to a local directory. Use --logs/--events to include logs and events."""
|
|
4932
|
-
ids = list(dict.fromkeys(value.strip() for value in job_id.split(",")))
|
|
4933
|
-
if not all(ids):
|
|
4934
|
-
raise typer.BadParameter("Job IDs must not be empty", param_hint="job_id")
|
|
4935
4892
|
client = get_client()
|
|
4936
|
-
if len(ids) > 1:
|
|
4937
|
-
report, channels = _export_channel_reporter(print)
|
|
4938
|
-
dest, summary = export_jobs(
|
|
4939
|
-
client,
|
|
4940
|
-
ids,
|
|
4941
|
-
output,
|
|
4942
|
-
workers=workers,
|
|
4943
|
-
result_callback=report,
|
|
4944
|
-
timeout=timeout,
|
|
4945
|
-
include_logs=logs,
|
|
4946
|
-
include_events=events,
|
|
4947
|
-
extract_artifacts=not no_extract_artifacts,
|
|
4948
|
-
)
|
|
4949
|
-
print(
|
|
4950
|
-
f"Jobs exported: downloaded={summary.downloaded} "
|
|
4951
|
-
f"skipped={summary.skipped} failed={len(summary.failures)}"
|
|
4952
|
-
)
|
|
4953
|
-
print(f"channels: oss={channels['oss']} proxy={channels['proxy']}")
|
|
4954
|
-
print(f" path: {dest}")
|
|
4955
|
-
for failed_id, error in summary.failures.items():
|
|
4956
|
-
print(f"[red]Failed job {failed_id}:[/] {error}")
|
|
4957
|
-
if summary.failures:
|
|
4958
|
-
raise typer.Exit(code=1)
|
|
4959
|
-
return
|
|
4960
|
-
job_id = ids[0]
|
|
4961
|
-
result = {}
|
|
4962
4893
|
dest = export_job(
|
|
4963
4894
|
client,
|
|
4964
4895
|
job_id,
|
|
4965
4896
|
output,
|
|
4966
|
-
result_callback=lambda jid, status, channel: result.update(status=status, channel=channel),
|
|
4967
|
-
timeout=timeout,
|
|
4968
4897
|
include_logs=logs,
|
|
4969
4898
|
include_events=events,
|
|
4970
4899
|
extract_artifacts=not no_extract_artifacts,
|
|
4971
4900
|
)
|
|
4972
4901
|
print(f"[green]Job exported:[/] {job_id}")
|
|
4973
4902
|
print(f" path: {dest}")
|
|
4974
|
-
if result.get("channel") in {"oss", "proxy"} and result.get("status") == "downloaded":
|
|
4975
|
-
print(f" channel: {result['channel']}")
|
|
4976
4903
|
|
|
4977
4904
|
|
|
4978
4905
|
@job_app.command("wait")
|
|
@@ -6447,12 +6374,6 @@ def group_export(
|
|
|
6447
6374
|
workers: int = typer.Option(
|
|
6448
6375
|
4, "--workers", "-w", min=1, help="Number of concurrent export workers"
|
|
6449
6376
|
),
|
|
6450
|
-
timeout: float = typer.Option(
|
|
6451
|
-
300,
|
|
6452
|
-
"--timeout",
|
|
6453
|
-
callback=_validate_export_timeout,
|
|
6454
|
-
help="SDK request timeout in seconds, not the total export duration",
|
|
6455
|
-
),
|
|
6456
6377
|
logs: bool = typer.Option(False, "--logs", help="Include container logs"),
|
|
6457
6378
|
events: bool = typer.Option(False, "--events", help="Include events"),
|
|
6458
6379
|
no_extract_artifacts: bool = typer.Option(
|
|
@@ -6513,15 +6434,12 @@ def group_export(
|
|
|
6513
6434
|
current_stage=current_stage,
|
|
6514
6435
|
)
|
|
6515
6436
|
|
|
6516
|
-
report, channels = _export_channel_reporter(progress.console.print)
|
|
6517
6437
|
dest, summary = export_group(
|
|
6518
6438
|
client,
|
|
6519
6439
|
group_id,
|
|
6520
6440
|
output,
|
|
6521
6441
|
progress_callback=_on_progress,
|
|
6522
6442
|
workers=workers,
|
|
6523
|
-
result_callback=report,
|
|
6524
|
-
timeout=timeout,
|
|
6525
6443
|
include_logs=logs,
|
|
6526
6444
|
include_events=events,
|
|
6527
6445
|
extract_artifacts=not no_extract_artifacts,
|
|
@@ -6534,7 +6452,6 @@ def group_export(
|
|
|
6534
6452
|
f" [green]{summary.downloaded}[/] jobs downloaded, "
|
|
6535
6453
|
f"[yellow]{summary.skipped}[/] jobs skipped (no artifact)"
|
|
6536
6454
|
)
|
|
6537
|
-
print(f"channels: oss={channels['oss']} proxy={channels['proxy']}")
|
|
6538
6455
|
if summary.skipped_job_names:
|
|
6539
6456
|
print(f" Skipped: {', '.join(summary.skipped_job_names)}")
|
|
6540
6457
|
|
|
@@ -50,18 +50,15 @@ def register(dataset_app: typer.Typer) -> None:
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def _register_dataset_commands(dataset_app: typer.Typer) -> None:
|
|
53
|
-
"""挂载 dataset
|
|
53
|
+
"""挂载 dataset 级命令。"""
|
|
54
|
+
from .dataset_register import dataset_register
|
|
54
55
|
|
|
55
|
-
实现方式:把命令写成本模块的模块级函数,再在这里用
|
|
56
|
-
``dataset_app.command("<name>")(<func>)`` 挂上去。``list`` 需要**替换**
|
|
57
|
-
``cli.py`` 里现存的 ossdata 版本(设计 §4.1),挂载时用同名 ``"list"`` 覆盖,
|
|
58
|
-
并同步删掉 ``cli.py`` 中的旧实现。
|
|
59
|
-
"""
|
|
60
56
|
dataset_app.command("list")(dataset_list)
|
|
61
57
|
dataset_app.command("get")(dataset_get)
|
|
62
58
|
dataset_app.command("create")(dataset_create)
|
|
63
59
|
dataset_app.command("update")(dataset_update)
|
|
64
60
|
dataset_app.command("claim")(dataset_claim)
|
|
61
|
+
dataset_app.command("register")(dataset_register)
|
|
65
62
|
|
|
66
63
|
|
|
67
64
|
# ---------------------------------------------------------------------------
|
|
@@ -86,6 +83,7 @@ _VERSION_COLUMNS: tuple[tuple[str, str], ...] = (
|
|
|
86
83
|
("splits", "SPLITS"),
|
|
87
84
|
("instances", "INSTANCES"),
|
|
88
85
|
("updated_at", "UPDATED_AT"),
|
|
86
|
+
("registered", "REGISTERED"),
|
|
89
87
|
)
|
|
90
88
|
|
|
91
89
|
#: 上游 §8.2 的默认列(设计 §5)。
|
|
@@ -94,6 +92,7 @@ _SPLIT_COLUMNS: tuple[tuple[str, str], ...] = (
|
|
|
94
92
|
("run_type", "RUN_TYPE"),
|
|
95
93
|
("instances", "INSTANCES"),
|
|
96
94
|
("updated_at", "UPDATED_AT"),
|
|
95
|
+
("registered", "REGISTERED"),
|
|
97
96
|
)
|
|
98
97
|
|
|
99
98
|
_ACCESS_COLUMNS: tuple[tuple[str, str], ...] = (
|
|
@@ -398,6 +397,7 @@ def _model_dict(record: Any) -> dict:
|
|
|
398
397
|
``{}`` / 缺省都不会出现在响应里),而 SDK 模型会把它们补成默认值;留着这些默认值会让
|
|
399
398
|
JSON 凭空多出字段(迁移前是原样透出服务端响应,契约必须一致)。
|
|
400
399
|
|
|
400
|
+
``registered`` 是显式注册状态,必须保留 ``false``。
|
|
401
401
|
只清洗**顶层**:嵌套结构(如 version 详情里的 ``splits[].is_default``)按模型原样输出,
|
|
402
402
|
服务端在那里确实是显式返回 ``false``。
|
|
403
403
|
"""
|
|
@@ -412,13 +412,24 @@ def _model_dict(record: Any) -> dict:
|
|
|
412
412
|
return {}
|
|
413
413
|
if not isinstance(payload, dict):
|
|
414
414
|
return {}
|
|
415
|
+
registered = getattr(record, "registered", None)
|
|
416
|
+
if isinstance(registered, bool):
|
|
417
|
+
payload = {**payload, "registered": registered}
|
|
415
418
|
return {
|
|
416
419
|
key: value
|
|
417
420
|
for key, value in payload.items()
|
|
418
|
-
if
|
|
421
|
+
if value is not None
|
|
422
|
+
and (key == "registered" or not (value is False or value == "" or value == [] or value == {}))
|
|
419
423
|
}
|
|
420
424
|
|
|
421
425
|
|
|
426
|
+
def _registered_dict(record: Any) -> dict:
|
|
427
|
+
"""Preserve SDK registration state; older metadata-only SDK reads imply true."""
|
|
428
|
+
payload = _model_dict(record)
|
|
429
|
+
payload.setdefault("registered", True)
|
|
430
|
+
return payload
|
|
431
|
+
|
|
432
|
+
|
|
422
433
|
def _api_biz_code(exc: BaseException) -> Optional[int]:
|
|
423
434
|
"""从 ``APIError`` 里取 apiserver 业务码(设计 §5.1),不 import 数值异常类。
|
|
424
435
|
|
|
@@ -814,6 +825,7 @@ def _version_row(record: dict) -> dict:
|
|
|
814
825
|
"splits": splits,
|
|
815
826
|
"instances": record.get("instance_count", ""),
|
|
816
827
|
"updated_at": record.get("updated_at") or "",
|
|
828
|
+
"registered": record.get("registered", True),
|
|
817
829
|
}
|
|
818
830
|
|
|
819
831
|
|
|
@@ -833,6 +845,7 @@ def _split_row(record: dict, detail: dict) -> dict:
|
|
|
833
845
|
"run_type": record.get("run_type") or detail.get("run_type") or "",
|
|
834
846
|
"instances": "" if instances is None else instances,
|
|
835
847
|
"updated_at": record.get("updated_at") or detail.get("updated_at") or "",
|
|
848
|
+
"registered": record.get("registered", detail.get("registered", True)),
|
|
836
849
|
}
|
|
837
850
|
|
|
838
851
|
|
|
@@ -1144,11 +1157,22 @@ def dataset_get(
|
|
|
1144
1157
|
try:
|
|
1145
1158
|
detail = repo.datasets.get_detail(dataset, **_env_kwargs(env))
|
|
1146
1159
|
except Exception as exc: # noqa: BLE001 - 统一走设计 §5.1 的映射
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1160
|
+
if _business_code(exc) not in (92001, 94021):
|
|
1161
|
+
raise _fail(
|
|
1162
|
+
f"Failed to query dataset {dataset}: {_reason(exc)}",
|
|
1163
|
+
f"Check the dataset name: ap dataset list --query {dataset}",
|
|
1164
|
+
) from exc
|
|
1165
|
+
detail = None
|
|
1166
|
+
if detail is None:
|
|
1167
|
+
l1, _, l2 = dataset.partition("/")
|
|
1168
|
+
rendered = {
|
|
1169
|
+
"dataset_name": dataset,
|
|
1170
|
+
"dataset_l1": l1,
|
|
1171
|
+
"dataset_l2": l2,
|
|
1172
|
+
"registered": False,
|
|
1173
|
+
}
|
|
1174
|
+
else:
|
|
1175
|
+
rendered = _registered_dict(detail)
|
|
1152
1176
|
_emit_detail(rendered, output_format, order=_DATASET_DETAIL_ORDER, always=("status",))
|
|
1153
1177
|
if output_format not in ("json", "yaml"):
|
|
1154
1178
|
_print_dataset_environments(repo, dataset, rendered)
|
|
@@ -1642,7 +1666,7 @@ def dataset_version_list(
|
|
|
1642
1666
|
f"Check the dataset name: ap dataset list --query {dataset}",
|
|
1643
1667
|
) from exc
|
|
1644
1668
|
|
|
1645
|
-
records = [
|
|
1669
|
+
records = [_registered_dict(record) for record in (getattr(result, "items", None) or [])]
|
|
1646
1670
|
rows = [_version_row(record) for record in records]
|
|
1647
1671
|
_emit(
|
|
1648
1672
|
{"dataset": dataset, "versions": records, "pagination": _pagination_envelope(result)},
|
|
@@ -1663,7 +1687,7 @@ def dataset_version_get(
|
|
|
1663
1687
|
output_format = _fmt(output_format)
|
|
1664
1688
|
resolved = _version_value(version)
|
|
1665
1689
|
detail = _fetch_version(dataset, resolved, stage="query", environment=environment)
|
|
1666
|
-
_emit_detail(detail, output_format, order=_VERSION_DETAIL_ORDER)
|
|
1690
|
+
_emit_detail(_registered_dict(detail), output_format, order=_VERSION_DETAIL_ORDER)
|
|
1667
1691
|
|
|
1668
1692
|
|
|
1669
1693
|
@dataset_version_app.command("create")
|
|
@@ -1810,6 +1834,7 @@ def dataset_split_get(
|
|
|
1810
1834
|
payload = dict(record)
|
|
1811
1835
|
payload.setdefault("dataset", dataset)
|
|
1812
1836
|
payload.setdefault("version", resolved_version)
|
|
1837
|
+
payload.setdefault("registered", detail.get("registered", True))
|
|
1813
1838
|
_emit_detail(payload, output_format)
|
|
1814
1839
|
return
|
|
1815
1840
|
known = ", ".join(_split_names(detail)) or "<none>"
|
|
@@ -1819,6 +1844,7 @@ def dataset_split_get(
|
|
|
1819
1844
|
payload = dict(record)
|
|
1820
1845
|
payload.setdefault("dataset", dataset)
|
|
1821
1846
|
payload.setdefault("version", "")
|
|
1847
|
+
payload.setdefault("registered", True)
|
|
1822
1848
|
_emit_detail(payload, output_format)
|
|
1823
1849
|
return
|
|
1824
1850
|
known = (
|