instance-repo 1.0.9.dev3__tar.gz → 1.0.9.dev5__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 (47) hide show
  1. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/PKG-INFO +2 -3
  2. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/README.md +1 -2
  3. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/__init__.py +1 -1
  4. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/_routing.py +36 -0
  5. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/cli.py +12 -10
  6. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/splits.py +1 -6
  7. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/versions.py +28 -12
  8. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/models.py +3 -4
  9. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/retry.py +5 -1
  10. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/store/oss.py +23 -0
  11. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/transport.py +1 -1
  12. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo.egg-info/PKG-INFO +2 -3
  13. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/pyproject.toml +1 -1
  14. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/_site_defaults.py +0 -0
  15. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/bootstrap.py +0 -0
  16. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/cache.py +0 -0
  17. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/__init__.py +0 -0
  18. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/benchmarks.py +0 -0
  19. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/config.py +0 -0
  20. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/datasets.py +0 -0
  21. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/images.py +0 -0
  22. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/instances.py +0 -0
  23. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/reports.py +0 -0
  24. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/scaffold.py +0 -0
  25. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/clients/workflows.py +0 -0
  26. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/concurrency.py +0 -0
  27. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/content.py +0 -0
  28. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/endpoints.py +0 -0
  29. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/errors.py +0 -0
  30. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/image.py +0 -0
  31. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/layout.py +0 -0
  32. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/loader.py +0 -0
  33. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/paths.py +0 -0
  34. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/release.py +0 -0
  35. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/repo.py +0 -0
  36. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/store/__init__.py +0 -0
  37. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/store/acr.py +0 -0
  38. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/store/base.py +0 -0
  39. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/store/registry.py +0 -0
  40. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/tasktoml.py +0 -0
  41. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo/validate.py +0 -0
  42. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo.egg-info/SOURCES.txt +0 -0
  43. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo.egg-info/dependency_links.txt +0 -0
  44. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo.egg-info/entry_points.txt +0 -0
  45. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo.egg-info/requires.txt +0 -0
  46. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/instance_repo.egg-info/top_level.txt +0 -0
  47. {instance_repo-1.0.9.dev3 → instance_repo-1.0.9.dev5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instance-repo
3
- Version: 1.0.9.dev3
3
+ Version: 1.0.9.dev5
4
4
  Summary: InstanceRepo SDK — 评测 Instance 统一存储客户端(SDK-only;控制面由 apiserver 承载)
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -149,10 +149,9 @@ keys = repo.list_user_data(uid)
149
149
  irepo --help
150
150
  # 全局参数:--api-env / --api-base / --cluster / --storage-env / --metadata-model / --profile
151
151
  # 无 --api-key:凭据只从 AP_API_KEY(旧别名 INSTANCEREPO_TOKEN)读取,避免进 shell history
152
- # 20 个子命令:validate / push / push-many / deliver / pull / list / get
152
+ # 18 个子命令:validate / push / push-many / deliver / pull / list / get
153
153
  # publish / grant / feedback / report / whoami / user-data
154
154
  # repo-config / create / claim / version / image
155
- # split(list/get/set-run-type)/ dataset(list/get/update/access/revoke/benchmarks/workflows)
156
155
  irepo image ref --dataset alibaba/mybench --instance-id inst-1 --split test
157
156
  irepo image pull '<acr>/<ns>/mybench:test-inst-1' ./out.tar
158
157
  ```
@@ -136,10 +136,9 @@ keys = repo.list_user_data(uid)
136
136
  irepo --help
137
137
  # 全局参数:--api-env / --api-base / --cluster / --storage-env / --metadata-model / --profile
138
138
  # 无 --api-key:凭据只从 AP_API_KEY(旧别名 INSTANCEREPO_TOKEN)读取,避免进 shell history
139
- # 20 个子命令:validate / push / push-many / deliver / pull / list / get
139
+ # 18 个子命令:validate / push / push-many / deliver / pull / list / get
140
140
  # publish / grant / feedback / report / whoami / user-data
141
141
  # repo-config / create / claim / version / image
142
- # split(list/get/set-run-type)/ dataset(list/get/update/access/revoke/benchmarks/workflows)
143
142
  irepo image ref --dataset alibaba/mybench --instance-id inst-1 --split test
144
143
  irepo image pull '<acr>/<ns>/mybench:test-inst-1' ./out.tar
145
144
  ```
@@ -18,7 +18,7 @@ from .errors import (
18
18
  TransportError,
19
19
  )
20
20
 
21
- __version__ = "1.0.9.dev3"
21
+ __version__ = "1.0.9.dev5"
22
22
 
23
23
  __all__ = [
24
24
  "Repo", "load_instances",
@@ -45,6 +45,42 @@ def _split_scheme(endpoint: str) -> tuple[str, str]:
45
45
  return "", endpoint
46
46
 
47
47
 
48
+ def _probe_private_endpoint(endpoint: str, timeout: float = 3.0) -> bool:
49
+ """Quick TCP connect probe to decide whether a private endpoint is reachable.
50
+
51
+ Performs a raw TCP connect (no TLS handshake) to the endpoint host/port.
52
+ Returns True if the connect succeeds, False on any error. The default
53
+ timeout is 3 seconds — short enough to save 60+ seconds of waiting when
54
+ the private endpoint is unreachable, yet long enough for a normal TCP
55
+ handshake on a reachable host.
56
+ """
57
+ _, host = _split_scheme(endpoint)
58
+ # Determine port: default 443 for https, 80 for http.
59
+ scheme, _ = _split_scheme(endpoint)
60
+ default_port = 443 if scheme == "https://" else 80
61
+ # Parse host:port — handle IPv4 with optional port.
62
+ if ":" in host:
63
+ parts = host.rsplit(":", 1)
64
+ hostname = parts[0]
65
+ try:
66
+ port = int(parts[1])
67
+ except (ValueError, IndexError):
68
+ hostname = host
69
+ port = default_port
70
+ else:
71
+ hostname = host
72
+ port = default_port
73
+ # Strip any trailing path from hostname.
74
+ if "/" in hostname:
75
+ hostname = hostname.split("/", 1)[0]
76
+ try:
77
+ conn = socket.create_connection((hostname, port), timeout=timeout)
78
+ conn.close()
79
+ return True
80
+ except Exception: # noqa: BLE001 - any failure means unreachable
81
+ return False
82
+
83
+
48
84
  def _plan_oss_route(endpoint: str) -> _EndpointRoute:
49
85
  value = (endpoint or "").strip()
50
86
  scheme, host = _split_scheme(value)
@@ -870,17 +870,19 @@ def main(argv=None, *, repo: Repo | None = None, out=None) -> int:
870
870
  # 1.0.10:取版本完整详情(splits 明细 + manifest + 时间戳)。
871
871
  # split_first 分支时服务端聚合 legacy 投影。
872
872
  dv = r.versions.get(dataset, version, environment=args.environment)
873
- # versions.get 返回 DatasetVersion dataclass(字段访问,非 dict 取值)。
874
- print(f"version\t{dv.version}\tstatus={dv.status}"
875
- f"\tstorage_path={dv.storage_path or ''}", file=out)
876
- print(f"published_at={dv.published_at or ''}"
877
- f"\tlast_ingested_at={dv.last_ingested_at or ''}", file=out)
878
- print(f"dataset_version_id={dv.dataset_version_id or ''}", file=out)
879
- for s in dv.splits:
880
- print(f" split\t{s.get('name', '')}\tstatus={s.get('status', '')}"
881
- f"\toss_prefix={s.get('oss_prefix', '')}"
873
+ print(f"version\t{dv.get('version','')}\tstatus={dv.get('status','')}"
874
+ f"\tstorage_path={dv.get('storage_path','')}", file=out)
875
+ print(f"published_at={dv.get('published_at') or ''}"
876
+ f"\tlast_ingested_at={dv.get('last_ingested_at') or ''}",
877
+ file=out)
878
+ print(f"dataset_version_id={dv.get('dataset_version_id','')}",
879
+ file=out)
880
+ splits = dv.get("splits") or []
881
+ for s in splits:
882
+ print(f" split\t{s.get('name','')}\tstatus={s.get('status','')}"
883
+ f"\toss_prefix={s.get('oss_prefix','')}"
882
884
  f"\tinstance_count={s.get('instance_count') or 0}", file=out)
883
- manifest = dv.manifest or {}
885
+ manifest = dv.get("manifest") or {}
884
886
  print(f"manifest\tinstance_count={manifest.get('instance_count') or 0}"
885
887
  f"\tfile_count={manifest.get('file_count') or 0}"
886
888
  f"\tsize_bytes={manifest.get('size_bytes') or 0}", file=out)
@@ -224,14 +224,11 @@ class SplitsClient:
224
224
 
225
225
  def update(self, dataset: str, split: str | None = None, *,
226
226
  version: str | None = None, run_type: str = "",
227
- dataset_split_id: str | None = None,
228
- environment: str | None = None) -> DatasetSplit:
227
+ dataset_split_id: str | None = None) -> DatasetSplit:
229
228
  """更新 split 记录(``PATCH /datasets/splits/detail``)。
230
229
 
231
230
  服务端 body 仅接受 ``run_type``(**必填**:"train"/"eval",空串=清除);
232
231
  仅 online 环境(split 记录 pre 环境的写经 versions.update 走集合路由)。
233
- ``environment`` 缺省由 profile 派生(同 get/list);profile 派生为 pre
234
- 而目标 split 记录在 online 集合时,须显式传 ``environment="online"``。
235
232
 
236
233
  寻址二选一:``dataset_split_id``(优先,split-first 精确记录 id)或
237
234
  ``dataset``+``split``(+可选 ``version``)name 三元组。
@@ -251,8 +248,6 @@ class SplitsClient:
251
248
  raise SchemaError(
252
249
  f"invalid run_type {rt!r}; must be one of "
253
250
  f"{', '.join(RUN_TYPE_VALUES)} (empty string to clear)")
254
- if (env := self._env(environment)):
255
- q += f"&environment={quote(env, safe='')}"
256
251
  data = self._t.patch(f"{self._BASE}/detail?{q}", {"run_type": rt})
257
252
  return DatasetSplit.from_dict(data) if isinstance(data, dict) else DatasetSplit()
258
253
 
@@ -209,8 +209,9 @@ class VersionsClient:
209
209
  out[iid] = img
210
210
  return out
211
211
 
212
- def list(self, dataset: str) -> list[DatasetVersion]:
213
- extra = self._version_extra_q()
212
+ def list(self, dataset: str, *, environment: str | None = None,
213
+ metadata_model: str | None = None) -> list[DatasetVersion]:
214
+ extra = self._version_extra_q(environment, metadata_model)
214
215
  data = self._t.get(
215
216
  f"{self._BASE}?dataset_name={quote(dataset, safe='')}{extra}")
216
217
  rows = data.get("versions", data) if isinstance(data, dict) else data
@@ -264,8 +265,10 @@ class VersionsClient:
264
265
  data = self._t.get(f"{self._BASE}/detail?{q}")
265
266
  return DatasetVersion.from_dict_full(data) if isinstance(data, dict) else DatasetVersion(version=version, dataset=dataset)
266
267
 
267
- def status(self, dataset: str, version: str) -> str:
268
- extra = self._version_extra_q()
268
+ def status(self, dataset: str, version: str, *,
269
+ environment: str | None = None,
270
+ metadata_model: str | None = None) -> str:
271
+ extra = self._version_extra_q(environment, metadata_model)
269
272
  data = self._t.get(
270
273
  f"{self._BASE}/detail?dataset_name={quote(dataset, safe='')}"
271
274
  f"&version={quote(version, safe='')}{extra}")
@@ -382,7 +385,9 @@ class VersionsClient:
382
385
  def update(self, dataset: str, version: str, *, status: str | None = None,
383
386
  splits: list[str] | None = None, run_type: str | None = None,
384
387
  split_run_types=None,
385
- manifest: dict | None = None) -> None:
388
+ manifest: dict | None = None,
389
+ environment: str | None = None,
390
+ metadata_model: str | None = None) -> None:
386
391
  """通用版本更新(``PATCH /apis/v1/datasets/versions/detail``)。
387
392
 
388
393
  只发送非 None 字段。apiserver 侧 ``run_type`` / ``split_run_types`` / ``splits``
@@ -398,6 +403,8 @@ class VersionsClient:
398
403
  manifest : 版本 manifest 整体替换(``{instance_count, file_count,
399
404
  size_bytes, validation_status, validation_message}``
400
405
  子集均可)——服务端支持但此前 SDK 未暴露(1.0.10 补)。
406
+ environment : 显式 environment 覆盖 profile 派生值(如发布后操作 online)。
407
+ metadata_model : 显式 metadata_model 覆盖 profile 派生值。
401
408
  """
402
409
  norm_srt = _normalize_split_run_types(split_run_types) if split_run_types else None
403
410
  # 三选一互斥(对齐 apiserver:run_type / split_run_types / splits 两两互斥)。
@@ -427,10 +434,13 @@ class VersionsClient:
427
434
  body["manifest"] = manifest
428
435
  if not body:
429
436
  raise SchemaError("update requires at least one field to change")
430
- self._patch_detail(dataset, version, body)
437
+ self._patch_detail(dataset, version, body,
438
+ environment=environment, metadata_model=metadata_model)
431
439
 
432
440
  def set_run_type(self, dataset: str, version: str, *,
433
- run_type: str | None = None, split_run_types=None) -> None:
441
+ run_type: str | None = None, split_run_types=None,
442
+ environment: str | None = None,
443
+ metadata_model: str | None = None) -> None:
434
444
  """便捷入口:仅给 version/split 打 run_type 标签(train/eval)。
435
445
 
436
446
  version 级与 split 级二选一:传 ``run_type`` 打整个版本(无 logical split 的版本),
@@ -440,14 +450,20 @@ class VersionsClient:
440
450
  raise SchemaError(
441
451
  "set_run_type requires run_type or split_run_types")
442
452
  self.update(dataset, version, run_type=run_type,
443
- split_run_types=split_run_types)
453
+ split_run_types=split_run_types,
454
+ environment=environment, metadata_model=metadata_model)
444
455
 
445
- def deprecate(self, dataset: str, version: str) -> None:
456
+ def deprecate(self, dataset: str, version: str, *,
457
+ environment: str | None = None,
458
+ metadata_model: str | None = None) -> None:
446
459
  """把版本置为 deprecated(``update(status="deprecated")`` 的薄封装)。"""
447
- self.update(dataset, version, status="deprecated")
460
+ self.update(dataset, version, status="deprecated",
461
+ environment=environment, metadata_model=metadata_model)
448
462
 
449
- def _patch_detail(self, dataset: str, version: str, body: dict) -> None:
450
- extra = self._version_extra_q()
463
+ def _patch_detail(self, dataset: str, version: str, body: dict, *,
464
+ environment: str | None = None,
465
+ metadata_model: str | None = None) -> None:
466
+ extra = self._version_extra_q(environment, metadata_model)
451
467
  self._t.patch(
452
468
  f"{self._BASE}/detail?dataset_name={quote(dataset, safe='')}"
453
469
  f"&version={quote(version, safe='')}{extra}",
@@ -124,7 +124,7 @@ class DatasetSeries:
124
124
  available_envs: list[str] = field(default_factory=list)
125
125
  # 服务端 `DatasetSeriesStatus`("" 正常 / "deprecated");列表项
126
126
  # `DatasetListItem.Status` 带 `omitempty`,正常态不出现。
127
- status: str = ""
127
+ # 与 class 开头 status 字段合并到此位置。
128
128
 
129
129
  def validate(self) -> None:
130
130
  if self.visibility not in VISIBILITY_VALUES:
@@ -139,12 +139,11 @@ class DatasetSeries:
139
139
  """create 请求体:剔除服务端派生/服务端分配的字段。
140
140
 
141
141
  teacher_direction / teacher_type 由 apiserver 从 benchmark 派生(传了也被忽略),
142
- dataset_id 由服务端分配;dataset_l1 / dataset_l2 由 dataset_name 推导。status
143
- 是服务端 DatasetSeriesStatus(deprecated 打标),create 不接受。都不发送,
142
+ dataset_id 由服务端分配;dataset_l1 / dataset_l2 由 dataset_name 推导。都不发送,
144
143
  避免让调用方误以为这些值能由客户端决定。
145
144
  """
146
145
  drop = {"teacher_direction", "teacher_type", "dataset_id",
147
- "dataset_l1", "dataset_l2", "status"}
146
+ "dataset_l1", "dataset_l2"}
148
147
  return {k: v for k, v in self.to_dict().items() if k not in drop}
149
148
 
150
149
 
@@ -22,6 +22,9 @@ _DEFAULT_SLEEP = time.sleep
22
22
  #: 可重试的 4xx:429=限流(退避后可成功)、408=请求超时(服务端主动断,重发安全)。
23
23
  RETRIABLE_4XX = frozenset({408, 429})
24
24
 
25
+ #: Tengine 等反向代理偶发返回的重定向状态码;SDK 应视为瞬时故障并重试。
26
+ REDIRECT_STATUSES = frozenset({301, 302, 307, 308})
27
+
25
28
 
26
29
  def _status_of(exc: BaseException) -> int | None:
27
30
  """尽力从异常里取 HTTP 状态码:TransportError.status / oss2.*.status / .code。"""
@@ -46,7 +49,8 @@ def is_transient(exc: BaseException) -> bool:
46
49
  status = _status_of(exc)
47
50
  if status is not None:
48
51
  return (status < 0 or 500 <= status <= 599
49
- or status in RETRIABLE_4XX)
52
+ or status in RETRIABLE_4XX
53
+ or status in REDIRECT_STATUSES)
50
54
  return False
51
55
 
52
56
 
@@ -15,6 +15,7 @@ from .._routing import (
15
15
  _OperationIntent,
16
16
  _is_reachability_error,
17
17
  _plan_oss_route,
18
+ _probe_private_endpoint,
18
19
  )
19
20
  from ..content import sha256_file, verify_file_digest
20
21
  from ..errors import DigestMismatch, PrivateNetworkRequired, TransportError
@@ -157,6 +158,9 @@ class OssContentStore(BaseContentStore):
157
158
  self._retry_attempts = retry_attempts
158
159
  self._cache_buckets = cache_buckets
159
160
  self._local = threading.local()
161
+ # Injectable TCP probe for private endpoint reachability. Defaults to
162
+ # the real raw-TCP-connect probe; tests may replace it with a stub.
163
+ self._probe_private_endpoint = _probe_private_endpoint
160
164
 
161
165
  @property
162
166
  def bucket_name(self) -> str:
@@ -251,6 +255,25 @@ class OssContentStore(BaseContentStore):
251
255
 
252
256
  private_endpoint = route.private_endpoint
253
257
  private_failure: BaseException | None = None
258
+
259
+ # Quick TCP probe to decide whether to attempt the private endpoint.
260
+ # If the route is standard (has a known public fallback) and the probe
261
+ # fails, skip the private attempt entirely to save 60+ seconds of
262
+ # waiting for a full timeout. Downloads are NOT affected — they
263
+ # always try private only (no public fallback).
264
+ if route.standard and route.public_endpoint is not None:
265
+ if not self._probe_private_endpoint(private_endpoint):
266
+ private_failure = ConnectionRefusedError(
267
+ f"TCP probe failed for {private_endpoint}")
268
+ try:
269
+ bucket = self._open_bucket(
270
+ sts, prefix, route.public_endpoint,
271
+ private_download=False)
272
+ return self._do(lambda: operation(bucket))
273
+ except Exception as public_exc: # noqa: BLE001
274
+ public_exc.private_route_failure = private_failure
275
+ raise
276
+
254
277
  try:
255
278
  bucket = self._open_bucket(
256
279
  sts, prefix, private_endpoint, private_download=False)
@@ -196,7 +196,7 @@ class Transport:
196
196
  raise SchemaError(
197
197
  "apiserver 根地址未配置;请通过 AP_BASE_URL 提供"
198
198
  "(旧别名 INSTANCEREPO_API_BASE 仍兼容;SDK 不内置任何域名,见部署文档)")
199
- retry = (method == "GET") if idempotent is None else idempotent
199
+ retry = True if idempotent is None else idempotent
200
200
  if not retry:
201
201
  return self._request_once(method, path, body)
202
202
  return retry_transient(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instance-repo
3
- Version: 1.0.9.dev3
3
+ Version: 1.0.9.dev5
4
4
  Summary: InstanceRepo SDK — 评测 Instance 统一存储客户端(SDK-only;控制面由 apiserver 承载)
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -149,10 +149,9 @@ keys = repo.list_user_data(uid)
149
149
  irepo --help
150
150
  # 全局参数:--api-env / --api-base / --cluster / --storage-env / --metadata-model / --profile
151
151
  # 无 --api-key:凭据只从 AP_API_KEY(旧别名 INSTANCEREPO_TOKEN)读取,避免进 shell history
152
- # 20 个子命令:validate / push / push-many / deliver / pull / list / get
152
+ # 18 个子命令:validate / push / push-many / deliver / pull / list / get
153
153
  # publish / grant / feedback / report / whoami / user-data
154
154
  # repo-config / create / claim / version / image
155
- # split(list/get/set-run-type)/ dataset(list/get/update/access/revoke/benchmarks/workflows)
156
155
  irepo image ref --dataset alibaba/mybench --instance-id inst-1 --split test
157
156
  irepo image pull '<acr>/<ns>/mybench:test-inst-1' ./out.tar
158
157
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "instance-repo"
7
- version = "1.0.9.dev3"
7
+ version = "1.0.9.dev5"
8
8
  description = "InstanceRepo SDK — 评测 Instance 统一存储客户端(SDK-only;控制面由 apiserver 承载)"
9
9
  requires-python = ">=3.10"
10
10
  readme = "README.md"