ap-client 0.3.1.dev1__tar.gz → 0.3.1.dev2__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.3.1.dev1 → ap_client-0.3.1.dev2}/PKG-INFO +1 -1
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/api.py +0 -18
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/cli.py +29 -10
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/dataset_commands.py +134 -11
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/instance_commands.py +21 -10
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/irepo_sdk.py +10 -4
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/split_publish.py +4 -1
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/pyproject.toml +1 -1
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/__init__.py +0 -0
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/config.py +0 -0
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/exporter.py +0 -0
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/fs_commands.py +0 -0
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/managed_ak.py +0 -0
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/profile_commands.py +0 -0
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/tbb.py +0 -0
- {ap_client-0.3.1.dev1 → ap_client-0.3.1.dev2}/ap_client/waiter.py +0 -0
|
@@ -294,24 +294,6 @@ def _secret_ws_params(workspace_id: Optional[str]) -> Optional[dict]:
|
|
|
294
294
|
return {"workspace_id": workspace_id} if workspace_id else None
|
|
295
295
|
|
|
296
296
|
|
|
297
|
-
def _unwrap_data(payload: Any) -> Any:
|
|
298
|
-
"""Unwrap the apiserver ``{code,message,data}`` envelope when present.
|
|
299
|
-
|
|
300
|
-
The dataset-domain detail/create/patch endpoints return a bare JSON body,
|
|
301
|
-
but the same handlers are occasionally wrapped by the generic success
|
|
302
|
-
envelope. Tolerate both so command code never has to branch: a mapping that
|
|
303
|
-
carries ``data`` alongside ``code``/``message`` is treated as an envelope,
|
|
304
|
-
anything else is returned verbatim.
|
|
305
|
-
"""
|
|
306
|
-
if (
|
|
307
|
-
isinstance(payload, dict)
|
|
308
|
-
and "data" in payload
|
|
309
|
-
and ("code" in payload or "message" in payload)
|
|
310
|
-
):
|
|
311
|
-
return payload["data"]
|
|
312
|
-
return payload
|
|
313
|
-
|
|
314
|
-
|
|
315
297
|
def _split_paged(payload: Any) -> tuple[list[dict], dict]:
|
|
316
298
|
"""Split a ``PagedSuccess`` envelope into ``(records, pagination)``.
|
|
317
299
|
|
|
@@ -815,26 +815,45 @@ def _print_json_block(data: Any) -> None:
|
|
|
815
815
|
typer.echo(json.dumps(data, ensure_ascii=False, indent=2))
|
|
816
816
|
|
|
817
817
|
|
|
818
|
+
def _format_nested_detail(value: Any) -> str:
|
|
819
|
+
"""Nested dict/list for detail views: compact one-liner, pretty JSON when long.
|
|
820
|
+
|
|
821
|
+
A detail view must not silently drop data - the manifest in ``ap instance
|
|
822
|
+
get`` used to be cut mid-JSON by the one-liner preview.
|
|
823
|
+
"""
|
|
824
|
+
compact = json.dumps(value, ensure_ascii=False, separators=(",", ":"))
|
|
825
|
+
if len(compact) <= _PLAIN_VALUE_PREVIEW:
|
|
826
|
+
return compact
|
|
827
|
+
return json.dumps(value, ensure_ascii=False, indent=2)
|
|
828
|
+
|
|
829
|
+
|
|
818
830
|
def _print_detail(data: object) -> None:
|
|
819
831
|
"""Render a single object as an aligned key/value detail view.
|
|
820
832
|
|
|
821
833
|
Top-level scalars become key/value rows (``image`` fields shortened); nested
|
|
822
|
-
dict/list fields are shown as compact one-liners
|
|
834
|
+
dict/list fields are shown as compact one-liners, or pretty-printed across
|
|
835
|
+
multiple lines when the one-liner would not fit. Falls back to JSON for
|
|
823
836
|
non-dict payloads.
|
|
824
837
|
"""
|
|
825
838
|
if not isinstance(data, dict):
|
|
826
839
|
_print_json(data)
|
|
827
840
|
return
|
|
828
|
-
|
|
829
|
-
nested_rows: list[tuple[str, Any]] = []
|
|
841
|
+
rows: list[tuple[str, str]] = []
|
|
830
842
|
for key, value in data.items():
|
|
831
843
|
if isinstance(value, (dict, list)):
|
|
832
|
-
|
|
844
|
+
rows.append((key, _format_nested_detail(value)))
|
|
833
845
|
elif key == "image" and isinstance(value, str):
|
|
834
|
-
|
|
846
|
+
rows.append((key, _shorten_image(value)))
|
|
847
|
+
elif value is None:
|
|
848
|
+
continue
|
|
835
849
|
else:
|
|
836
|
-
|
|
837
|
-
|
|
850
|
+
rows.append((key, _format_plain_value(value, max_len=0)))
|
|
851
|
+
width = max((len(label) for label, _text in rows), default=0)
|
|
852
|
+
indent = " " * (width + 4)
|
|
853
|
+
for label, text in rows:
|
|
854
|
+
if "\n" in text:
|
|
855
|
+
text = text.replace("\n", f"\n{indent}")
|
|
856
|
+
typer.echo(f" {label.ljust(width)}: {text}")
|
|
838
857
|
|
|
839
858
|
|
|
840
859
|
def _print_template_get_plain(t: dict) -> None:
|
|
@@ -944,7 +963,7 @@ def _print_field_value(value: Any, output_format: str) -> None:
|
|
|
944
963
|
_print_formatted(value, "json" if output_format == "plain" else output_format)
|
|
945
964
|
|
|
946
965
|
|
|
947
|
-
def _format_plain_value(value: Any) -> str:
|
|
966
|
+
def _format_plain_value(value: Any, *, max_len: int = _PLAIN_VALUE_PREVIEW) -> str:
|
|
948
967
|
if value is None:
|
|
949
968
|
return ""
|
|
950
969
|
if isinstance(value, (dict, list)):
|
|
@@ -952,9 +971,9 @@ def _format_plain_value(value: Any) -> str:
|
|
|
952
971
|
else:
|
|
953
972
|
text = str(value)
|
|
954
973
|
text = text.replace("\r", "\\r").replace("\n", "\\n")
|
|
955
|
-
if len(text) <=
|
|
974
|
+
if not max_len or len(text) <= max_len:
|
|
956
975
|
return text
|
|
957
|
-
return f"{text[:
|
|
976
|
+
return f"{text[: max_len - 3]}..."
|
|
958
977
|
|
|
959
978
|
|
|
960
979
|
def _print_key_values(rows: list[tuple[str, Any]], *, skip_empty: bool = True) -> None:
|
|
@@ -17,6 +17,8 @@ import typer
|
|
|
17
17
|
if TYPE_CHECKING: # pragma: no cover - 仅用于类型标注,运行时不 import
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
|
|
20
|
+
from instance_repo import Repo
|
|
21
|
+
|
|
20
22
|
__all__ = [
|
|
21
23
|
"dataset_version_app",
|
|
22
24
|
"dataset_split_app",
|
|
@@ -420,7 +422,7 @@ _DATASET_CODE_HINTS: dict[int, str] = {
|
|
|
420
422
|
}
|
|
421
423
|
|
|
422
424
|
|
|
423
|
-
def _resolve_benchmark_id(repo:
|
|
425
|
+
def _resolve_benchmark_id(repo: "Repo", value: str) -> str:
|
|
424
426
|
"""``--benchmark`` 接受名称或 ``source_id``,统一解析成 ``source_id``(设计 §9)。
|
|
425
427
|
|
|
426
428
|
走 SDK ``benchmarks``:先按 source_id 点查,再按名称模糊搜一遍精确比对
|
|
@@ -539,7 +541,10 @@ def _fetch_version(
|
|
|
539
541
|
detail = repo.versions.get(dataset, version, **_env_kwargs(env))
|
|
540
542
|
except Exception as exc: # noqa: BLE001 - 统一走设计 §5.1 的映射
|
|
541
543
|
if version:
|
|
542
|
-
hint =
|
|
544
|
+
hint = (
|
|
545
|
+
f"确认版本名:ap dataset version list --dataset {dataset}"
|
|
546
|
+
";版本也可能在另一个元数据 environment,加 --environment online/pre 重试"
|
|
547
|
+
)
|
|
543
548
|
else:
|
|
544
549
|
hint = f"{_NO_VERSION_NOTE};要操作具体版本请加 --version <version>"
|
|
545
550
|
raise _fail(
|
|
@@ -634,7 +639,7 @@ def _split_names(detail: dict) -> list[str]:
|
|
|
634
639
|
return names
|
|
635
640
|
|
|
636
641
|
|
|
637
|
-
def _dataset_repo() ->
|
|
642
|
+
def _dataset_repo() -> "Repo":
|
|
638
643
|
"""dataset 写路径统一走 InstanceRepo SDK;未装 ``dataset`` extra 时退出 2。"""
|
|
639
644
|
from .irepo_sdk import SdkMissing, load_repo
|
|
640
645
|
|
|
@@ -645,6 +650,47 @@ def _dataset_repo() -> Any:
|
|
|
645
650
|
raise typer.Exit(2) from exc
|
|
646
651
|
|
|
647
652
|
|
|
653
|
+
def _dataset_available_envs(repo: "Repo", dataset: str) -> list[str]:
|
|
654
|
+
"""dataset 元数据分布的 environment 候选;详情读不到时退回固定枚举。"""
|
|
655
|
+
envs: list[str] = []
|
|
656
|
+
try:
|
|
657
|
+
detail = repo.datasets.get_detail(dataset)
|
|
658
|
+
except Exception: # noqa: BLE001 - 引导信息尽力而为,不打断主流程
|
|
659
|
+
detail = None
|
|
660
|
+
raw = detail.get("available_envs") if isinstance(detail, dict) else None
|
|
661
|
+
if isinstance(raw, list):
|
|
662
|
+
envs = [str(env).strip() for env in raw if str(env).strip()]
|
|
663
|
+
from .irepo_sdk import DATASET_ENVIRONMENTS
|
|
664
|
+
|
|
665
|
+
for fallback in DATASET_ENVIRONMENTS:
|
|
666
|
+
if fallback not in envs:
|
|
667
|
+
envs.append(fallback)
|
|
668
|
+
return envs
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _note_environment_distribution(repo: "Repo", dataset: str, probe, *, what: str) -> None:
|
|
672
|
+
"""空结果时逐 environment 探测数据分布,引导用户换 ``--environment`` 重试。
|
|
673
|
+
|
|
674
|
+
``probe(env)`` 返回该 environment 下的条数;读失败按 -1 计。全部为空/读失败
|
|
675
|
+
(dataset 真没数据)时不提示,避免噪声。
|
|
676
|
+
"""
|
|
677
|
+
counts: list[tuple[str, int]] = []
|
|
678
|
+
for env in _dataset_available_envs(repo, dataset):
|
|
679
|
+
try:
|
|
680
|
+
counts.append((env, int(probe(env))))
|
|
681
|
+
except Exception: # noqa: BLE001 - 旧 SDK 不接受 environment 等情况
|
|
682
|
+
counts.append((env, -1))
|
|
683
|
+
if not any(count > 0 for _env, count in counts):
|
|
684
|
+
return
|
|
685
|
+
shown = ", ".join(f"{env}={count if count >= 0 else '读失败'}" for env, count in counts)
|
|
686
|
+
with_data = [env for env, count in counts if count > 0]
|
|
687
|
+
suggest = with_data[0] if len(with_data) == 1 else f"<{'/'.join(with_data)}>"
|
|
688
|
+
_note(
|
|
689
|
+
f"{what}在本次查询的 environment 下没有数据;各 environment 探测:{shown}。"
|
|
690
|
+
f"数据可能在另一个元数据 environment,加 --environment {suggest} 重试"
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
|
|
648
694
|
def _patch_version(
|
|
649
695
|
dataset: str,
|
|
650
696
|
version: str,
|
|
@@ -1024,6 +1070,43 @@ def dataset_list(
|
|
|
1024
1070
|
_print_formatted(payload, output_format)
|
|
1025
1071
|
|
|
1026
1072
|
|
|
1073
|
+
#: ``dataset get`` 尾部 environment 摘要表的列。
|
|
1074
|
+
_ENVIRONMENT_SUMMARY_COLUMNS: tuple[tuple[str, str], ...] = (
|
|
1075
|
+
("environment", "ENVIRONMENT"),
|
|
1076
|
+
("versions", "VERSIONS"),
|
|
1077
|
+
("splits", "SPLITS"),
|
|
1078
|
+
("latest_version", "LATEST_VERSION"),
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
def _print_dataset_environments(repo: "Repo", dataset: str, detail: dict) -> None:
|
|
1083
|
+
"""``dataset get`` 尾部按 environment 汇总 version/split,告诉用户该去哪个 env 指定。
|
|
1084
|
+
|
|
1085
|
+
仅人读视图(plain/table)追加;``json``/``yaml`` 恒为服务端原样。单个 env 读失败
|
|
1086
|
+
(旧 SDK 不接受 environment、权限差异)显示 ``?``,不影响主输出。
|
|
1087
|
+
"""
|
|
1088
|
+
envs = [str(env).strip() for env in detail.get("available_envs") or [] if str(env).strip()]
|
|
1089
|
+
if not envs:
|
|
1090
|
+
return
|
|
1091
|
+
rows: list[dict] = []
|
|
1092
|
+
for env in envs:
|
|
1093
|
+
try:
|
|
1094
|
+
env_detail = _model_dict(repo.datasets.get_detail(dataset, environment=env))
|
|
1095
|
+
except Exception: # noqa: BLE001 - 摘要尽力而为
|
|
1096
|
+
rows.append({"environment": env, "versions": "?", "splits": "?", "latest_version": "?"})
|
|
1097
|
+
continue
|
|
1098
|
+
rows.append(
|
|
1099
|
+
{
|
|
1100
|
+
"environment": env,
|
|
1101
|
+
"versions": env_detail.get("version_count", ""),
|
|
1102
|
+
"splits": env_detail.get("split_count", ""),
|
|
1103
|
+
"latest_version": env_detail.get("latest_version") or "",
|
|
1104
|
+
}
|
|
1105
|
+
)
|
|
1106
|
+
typer.echo("\nEnvironments (add --environment <env> to target one)")
|
|
1107
|
+
_print_table(rows, _ENVIRONMENT_SUMMARY_COLUMNS, "plain")
|
|
1108
|
+
|
|
1109
|
+
|
|
1027
1110
|
def dataset_get(
|
|
1028
1111
|
dataset: str = typer.Argument(..., help="Dataset name"),
|
|
1029
1112
|
environment: Optional[str] = typer.Option(None, "--environment", help=_ENVIRONMENT_HELP),
|
|
@@ -1042,12 +1125,10 @@ def dataset_get(
|
|
|
1042
1125
|
f"dataset {dataset} 查询失败:{_reason(exc)}",
|
|
1043
1126
|
f"确认 dataset 名称:ap dataset list --query {dataset}",
|
|
1044
1127
|
) from exc
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
always=("status",),
|
|
1050
|
-
)
|
|
1128
|
+
rendered = _model_dict(detail)
|
|
1129
|
+
_emit_detail(rendered, output_format, order=_DATASET_DETAIL_ORDER, always=("status",))
|
|
1130
|
+
if output_format not in ("json", "yaml"):
|
|
1131
|
+
_print_dataset_environments(repo, dataset, rendered)
|
|
1051
1132
|
|
|
1052
1133
|
|
|
1053
1134
|
def dataset_create(
|
|
@@ -1534,6 +1615,29 @@ def dataset_version_list(
|
|
|
1534
1615
|
) from exc
|
|
1535
1616
|
|
|
1536
1617
|
records = [_model_dict(record) for record in (getattr(result, "items", None) or [])]
|
|
1618
|
+
if not records:
|
|
1619
|
+
# 空结果常见根因是元数据 environment 不匹配(version 落在另一个 env),
|
|
1620
|
+
# 探测一遍并引导切换,而不是只回一张空表。
|
|
1621
|
+
_note_environment_distribution(
|
|
1622
|
+
repo,
|
|
1623
|
+
dataset,
|
|
1624
|
+
lambda env: (
|
|
1625
|
+
_pagination_total(
|
|
1626
|
+
_pagination_envelope(
|
|
1627
|
+
repo.versions.list_paged(
|
|
1628
|
+
dataset,
|
|
1629
|
+
status=resolved_status or None,
|
|
1630
|
+
page=1,
|
|
1631
|
+
page_size=1,
|
|
1632
|
+
environment=env,
|
|
1633
|
+
)
|
|
1634
|
+
),
|
|
1635
|
+
fallback=0,
|
|
1636
|
+
)
|
|
1637
|
+
or 0
|
|
1638
|
+
),
|
|
1639
|
+
what="version",
|
|
1640
|
+
)
|
|
1537
1641
|
if records and all(
|
|
1538
1642
|
"split_count" not in record and "instance_count" not in record for record in records
|
|
1539
1643
|
):
|
|
@@ -1651,6 +1755,27 @@ def dataset_split_list(
|
|
|
1651
1755
|
rows = [_split_row(record, {}) for record in records]
|
|
1652
1756
|
if resolved_run_type:
|
|
1653
1757
|
rows = [row for row in rows if str(row["run_type"]).lower() == resolved_run_type]
|
|
1758
|
+
if not rows and not resolved_run_type:
|
|
1759
|
+
# 空结果常见根因是元数据 environment 不匹配,探测一遍并引导切换。
|
|
1760
|
+
probe_repo = _dataset_repo()
|
|
1761
|
+
if resolved_version:
|
|
1762
|
+
|
|
1763
|
+
def _probe(env: str) -> int:
|
|
1764
|
+
try:
|
|
1765
|
+
detail = probe_repo.versions.get(dataset, resolved_version, environment=env)
|
|
1766
|
+
except Exception: # noqa: BLE001 - 该 env 没有这个版本
|
|
1767
|
+
return -1
|
|
1768
|
+
return len(_split_records(_model_dict(detail)))
|
|
1769
|
+
|
|
1770
|
+
else:
|
|
1771
|
+
|
|
1772
|
+
def _probe(env: str) -> int:
|
|
1773
|
+
page = probe_repo.splits.list(
|
|
1774
|
+
dataset, version_scope="unversioned", page_size=1, environment=env
|
|
1775
|
+
)
|
|
1776
|
+
return _pagination_total(_pagination_envelope(page), fallback=0) or 0
|
|
1777
|
+
|
|
1778
|
+
_note_environment_distribution(probe_repo, dataset, _probe, what="split")
|
|
1654
1779
|
_emit(
|
|
1655
1780
|
{"dataset": dataset, "version": resolved_version, "splits": rows},
|
|
1656
1781
|
rows,
|
|
@@ -1878,8 +2003,6 @@ def dataset_split_update(
|
|
|
1878
2003
|
if split not in _split_names(detail):
|
|
1879
2004
|
known = ", ".join(_split_names(detail)) or "<无>"
|
|
1880
2005
|
hint = f"先创建:ap dataset split create {split} --dataset {dataset}(现有:{known})"
|
|
1881
|
-
if not resolved_version:
|
|
1882
|
-
hint = f"{hint};{_NO_VERSION_NOTE}"
|
|
1883
2006
|
raise _fail(
|
|
1884
2007
|
f"dataset {dataset} 版本 {_version_label(resolved_version)} split {split} "
|
|
1885
2008
|
f"更新失败:不存在",
|
|
@@ -237,14 +237,18 @@ def _sdk_read_kwargs(sdk_obj: Any, method: str, *, environment: str, metadata_mo
|
|
|
237
237
|
accepted.add(name)
|
|
238
238
|
|
|
239
239
|
kwargs: dict[str, str] = {}
|
|
240
|
+
dropped: list[str] = []
|
|
240
241
|
for key, value in (("environment", environment), ("metadata_model", metadata_model)):
|
|
241
242
|
# 空值不下发:CLI 不替 SDK 决定 metadata_model(见 dataset_commands._metadata_model)。
|
|
242
243
|
if key in accepted and value:
|
|
243
244
|
kwargs[key] = value
|
|
244
|
-
|
|
245
|
+
elif value:
|
|
246
|
+
dropped.append(key)
|
|
247
|
+
if dropped:
|
|
248
|
+
# 只有真正丢弃了调用方给的值才提示;无条件打印会暗示"想指定却被忽略"。
|
|
245
249
|
_note(
|
|
246
|
-
"已安装的 instance-repo 不支持按调用指定
|
|
247
|
-
"
|
|
250
|
+
"已安装的 instance-repo 不支持按调用指定 "
|
|
251
|
+
f"{'/'.join(dropped)},本次按 profile 派生值读取;需要精确指定请升级到 1.1.0 及以上"
|
|
248
252
|
)
|
|
249
253
|
return kwargs
|
|
250
254
|
|
|
@@ -467,9 +471,12 @@ def instance_get(
|
|
|
467
471
|
_emit_detail(_instance_record(record), output_format)
|
|
468
472
|
|
|
469
473
|
|
|
474
|
+
def _confirm_version_autocreate(repo, dataset: str, version: str, split: str, *, yes: bool) -> None:
|
|
475
|
+
"""version 不存在时 push 会触发服务端自动创建;要求二次确认(可 ``-y/--yes`` 跳过)。
|
|
470
476
|
|
|
471
|
-
|
|
472
|
-
|
|
477
|
+
``repo`` 由调用方构造并复用:``Repo.__init__`` 会做一次 best-effort 的
|
|
478
|
+
repo-config 自动发现(HTTP),确认与推送共用一个实例,不重复触网。
|
|
479
|
+
"""
|
|
473
480
|
import sys
|
|
474
481
|
|
|
475
482
|
from .dataset_commands import _env_kwargs, _environment, _version_label
|
|
@@ -478,7 +485,6 @@ def _confirm_version_autocreate(dataset: str, version: str, split: str, *, yes:
|
|
|
478
485
|
if not str(version or "").strip():
|
|
479
486
|
# 无版本空间:没有"版本会被自动创建"这回事(数据面由 ingest 处理)。
|
|
480
487
|
return
|
|
481
|
-
repo = _load_repo_or_exit()
|
|
482
488
|
try:
|
|
483
489
|
repo.versions.get(dataset, version, **_env_kwargs(_environment(None)))
|
|
484
490
|
except Exception as exc: # noqa: BLE001 - 统一走设计 §5.1 的映射
|
|
@@ -547,10 +553,9 @@ def instance_push(
|
|
|
547
553
|
# ② 版本不存在时 push 会在服务端自动创建;先确认,避免误建。
|
|
548
554
|
# (split 已发布不可写由**服务端**裁决——pre 环境里 ingest 产出的 split 记录状态
|
|
549
555
|
# 就是 published,客户端按它预判会把正常的二次上传全拦掉。)
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
# ③ 数据面推送。
|
|
556
|
+
# ③ 数据面推送(repo 只构造一次,确认与推送复用,见 _confirm_version_autocreate)。
|
|
553
557
|
repo = _load_repo_or_exit()
|
|
558
|
+
_confirm_version_autocreate(repo, dataset, resolved, split, yes=yes)
|
|
554
559
|
pushed: list[dict] = []
|
|
555
560
|
failed: list[dict] = []
|
|
556
561
|
unconfirmed: list[str] = []
|
|
@@ -603,7 +608,13 @@ def instance_push(
|
|
|
603
608
|
exc, what=f"dataset {dataset} 的 {len(targets)} 个 instance", stage="上传"
|
|
604
609
|
)
|
|
605
610
|
else:
|
|
606
|
-
|
|
611
|
+
results_list = list(results_many or [])
|
|
612
|
+
if len(results_list) != len(targets):
|
|
613
|
+
_note(
|
|
614
|
+
f"SDK 返回了 {len(results_list)}/{len(targets)} 个上传结果,摘要可能不完整;"
|
|
615
|
+
f"用 ap instance list --dataset {dataset} --split {split} 核对实际内容"
|
|
616
|
+
)
|
|
617
|
+
for path, result in zip(targets, results_list):
|
|
607
618
|
pushed.append(_push_row(path, result))
|
|
608
619
|
|
|
609
620
|
_print_push_summary(dataset, resolved, split, pushed, failed, unconfirmed, output_format)
|
|
@@ -41,7 +41,13 @@ from .config import Config, _parse_bool
|
|
|
41
41
|
if TYPE_CHECKING: # pragma: no cover - 仅供类型检查,运行时不 import SDK
|
|
42
42
|
from instance_repo import Repo
|
|
43
43
|
|
|
44
|
-
__all__ = [
|
|
44
|
+
__all__ = [
|
|
45
|
+
"SdkMissing",
|
|
46
|
+
"is_already_satisfied",
|
|
47
|
+
"load_repo",
|
|
48
|
+
"sdk_error_exit",
|
|
49
|
+
"update_version_detail",
|
|
50
|
+
]
|
|
45
51
|
|
|
46
52
|
#: 数据集元数据的环境枚举(服务端 ``dataset environment``)。
|
|
47
53
|
DATASET_ENVIRONMENTS = ("online", "pre")
|
|
@@ -57,7 +63,7 @@ _INSTALL_HINT = (
|
|
|
57
63
|
_LEGACY_SDK_NOTE = (
|
|
58
64
|
"note: installed instance-repo does not accept Repo(api_base=/cluster=); "
|
|
59
65
|
"falling back to INSTANCEREPO_* environment injection. "
|
|
60
|
-
"Upgrade with: pip install -U 'instance-repo[oss]>=1.0
|
|
66
|
+
"Upgrade with: pip install -U 'instance-repo[oss]>=1.1.0' "
|
|
61
67
|
"for cluster<->storage_env validation and symmetric read/write paths."
|
|
62
68
|
)
|
|
63
69
|
|
|
@@ -210,7 +216,7 @@ _BIZ_CODE_RULES: dict[int, tuple[str, int]] = {
|
|
|
210
216
|
),
|
|
211
217
|
94018: ("当前角色只读,写操作需要 writer 角色;让 admin 执行 ap dataset access grant", 1),
|
|
212
218
|
94019: ("dataset 标识冲突:检查 --dataset 的取值是否与已有 dataset 重名", 1),
|
|
213
|
-
94020: ("请求未带 dataset 标识(SDK 过旧),升级:pip install -U 'instance-repo[oss]>=1.0
|
|
219
|
+
94020: ("请求未带 dataset 标识(SDK 过旧),升级:pip install -U 'instance-repo[oss]>=1.1.0'", 1),
|
|
214
220
|
94021: ("该 dataset 不存在,先执行:ap dataset create <dataset> --benchmark <b>", 1),
|
|
215
221
|
94022: (
|
|
216
222
|
"无该用户存储空间的授权,让空间主人执行:ap fs access grant --user <你的工号> --role reader",
|
|
@@ -232,7 +238,7 @@ _CODE_RULES: dict[str, tuple[str, int]] = {
|
|
|
232
238
|
"E_DIGEST_MISMATCH": ("内容校验失败,重新拉取或重新打包该 instance 后再试", 1),
|
|
233
239
|
"E_IMMUTABLE": ("已发布的版本不可修改,创建新版本再操作", 1),
|
|
234
240
|
"E_FORBIDDEN": ("当前身份无权执行该操作,确认 AP_API_KEY 与 dataset 角色", 1),
|
|
235
|
-
"E_OWNER_REF_MISSING": ("缺少上游来源引用,升级:pip install -U 'instance-repo[oss]>=1.0
|
|
241
|
+
"E_OWNER_REF_MISSING": ("缺少上游来源引用,升级:pip install -U 'instance-repo[oss]>=1.1.0'", 1),
|
|
236
242
|
"E_LAYOUT": ("instance 目录结构不合规,先执行 ap instance validate <path> 定位问题", 1),
|
|
237
243
|
"E_SCHEMA": ("instance 元数据不符合 schema,先执行 ap instance validate <path> 定位问题", 1),
|
|
238
244
|
"E_CRED_EXPIRED": ("临时凭据已过期,重试该命令即可重新换取", 1),
|
|
@@ -44,7 +44,10 @@ def dataset_split_publish(
|
|
|
44
44
|
if not yes:
|
|
45
45
|
if not sys.stdin.isatty():
|
|
46
46
|
raise _fail("非交互发布需要确认", "加 --yes 确认将该 split 发布到 online", 2)
|
|
47
|
-
|
|
47
|
+
target = (
|
|
48
|
+
f"{dataset}/{resolved_version}/{split}" if resolved_version else f"{dataset}/{split}"
|
|
49
|
+
)
|
|
50
|
+
if not typer.confirm(f"Publish {target} to online?"):
|
|
48
51
|
raise _fail("发布已取消,未提交工作流", "确认后重跑", 1)
|
|
49
52
|
|
|
50
53
|
try:
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ap-client"
|
|
7
|
-
version = "0.3.1.
|
|
7
|
+
version = "0.3.1.dev2"
|
|
8
8
|
description = "Agent Platform API Client & CLI"
|
|
9
9
|
readme = { text = "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.", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|