instance-repo 1.3.2.dev2__py3-none-any.whl → 1.3.2.dev3__py3-none-any.whl

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.
instance_repo/__init__.py CHANGED
@@ -18,7 +18,7 @@ from .errors import (
18
18
  TransportError,
19
19
  )
20
20
 
21
- __version__ = "1.3.2.dev2"
21
+ __version__ = "1.3.2.dev3"
22
22
 
23
23
  __all__ = [
24
24
  "Repo", "load_instances",
instance_repo/_routing.py CHANGED
@@ -45,14 +45,14 @@ 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:
48
+ def _probe_private_endpoint(endpoint: str, timeout: float = 0.5) -> bool:
49
49
  """Quick TCP connect probe to decide whether a private endpoint is reachable.
50
50
 
51
51
  Performs a raw TCP connect (no TLS handshake) to the endpoint host/port.
52
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.
53
+ timeout is 0.5 seconds — 内网 TCP 握手通常在几十毫秒内完成,0.5s 足够
54
+ 判定可达性,同时把公网环境下私网不可达的探测开销从 3s 压到 0.5s
55
+ (结果按 store 缓存 5 分钟,仅端点变更或缓存过期才重探)。
56
56
  """
57
57
  _, host = _split_scheme(endpoint)
58
58
  # Determine port: default 443 for https, 80 for http.
@@ -16,10 +16,13 @@ class DatasetsClient:
16
16
  _BASE = "/apis/v1/datasets/series"
17
17
 
18
18
  def __init__(self, transport, *, content_store=None,
19
- oss_prefix: str = "swe/datasets") -> None:
19
+ oss_prefix: str = "swe/datasets",
20
+ metadata_model: str | None = None) -> None:
20
21
  self._t = transport
21
22
  self._content = content_store
22
23
  self._oss_prefix = oss_prefix
24
+ # OSS 回退探测的布局语义(split_first 默认;version_first 显式切换)
25
+ self._metadata_model = metadata_model or "split_first"
23
26
 
24
27
  def list(self, l1: str | None = None) -> list[DatasetSeries]:
25
28
  """列出 dataset(便利语义:自动翻页取全量,见 :meth:`list_paged` 单页对照)。
@@ -398,9 +401,10 @@ class DatasetsClient:
398
401
  parse_dataset_level, build_synthetic_dataset_detail,
399
402
  _warn_if_truncated)
400
403
  info = parse_dataset_level(
401
- self._content, name, oss_prefix=self._oss_prefix)
404
+ self._content, name, oss_prefix=self._oss_prefix,
405
+ metadata_model=self._metadata_model)
402
406
  _warn_if_truncated(info, name, 500)
403
- if not info.versions:
407
+ if not info.versions and not info.unversioned_splits:
404
408
  from ..errors import DatasetNotFound
405
409
  raise DatasetNotFound(f"dataset {name!r} not found in OSS")
406
410
  return build_synthetic_dataset_detail(
@@ -264,16 +264,23 @@ class SplitsClient:
264
264
  注意:回退场景(dataset 未注册)下 ``version_scope`` 是 MongoDB 语义,
265
265
  OSS 探测无从区分 versioned/unversioned——**忽略 scope 并返回全部探测结果**
266
266
  (G4:否则 ap CLI 无 --version 的 split list 恒空)。``version`` 非空时
267
- 仍精确匹配该 version。
267
+ 仍精确匹配该 version;version 为空时额外包含 split_first 无版本 split
268
+ (version="" 归属)。
268
269
  """
269
270
  from ..store.oss_discovery import (
270
271
  parse_dataset_level, build_synthetic_split,
271
272
  _in_memory_paged, _warn_if_truncated,
272
273
  )
273
274
  info = parse_dataset_level(
274
- self._content, dataset, oss_prefix=self._oss_prefix)
275
+ self._content, dataset, oss_prefix=self._oss_prefix,
276
+ metadata_model=self._discovery_mm())
275
277
  _warn_if_truncated(info, dataset, 500)
276
278
  splits: list[DatasetSplit] = []
279
+ # split_first 无版本布局:dataset 根下直接是 split 目录
280
+ if not version:
281
+ for us in info.unversioned_splits:
282
+ splits.append(build_synthetic_split(
283
+ dataset, "", us.name, instance_count=us.instance_count))
277
284
  for v in info.versions:
278
285
  if version and v.version != version:
279
286
  continue
@@ -295,8 +302,14 @@ class SplitsClient:
295
302
  parse_dataset_level, build_synthetic_split, _warn_if_truncated,
296
303
  )
297
304
  info = parse_dataset_level(
298
- self._content, dataset, oss_prefix=self._oss_prefix)
305
+ self._content, dataset, oss_prefix=self._oss_prefix,
306
+ metadata_model=self._discovery_mm())
299
307
  _warn_if_truncated(info, dataset, 500)
308
+ if not version:
309
+ for us in info.unversioned_splits:
310
+ if us.name == split_name:
311
+ return build_synthetic_split(
312
+ dataset, "", us.name, instance_count=us.instance_count)
300
313
  for v in info.versions:
301
314
  if version and v.version != version:
302
315
  continue
@@ -309,6 +322,13 @@ class SplitsClient:
309
322
  dataset, v.version, split_name)
310
323
  return None
311
324
 
325
+ def _discovery_mm(self) -> str:
326
+ """OSS 回退探测的布局语义(split_first 默认;version_first 显式切换)。"""
327
+ from ..layout import normalize_metadata_model
328
+ return normalize_metadata_model(
329
+ getattr(self._p, "metadata_model", None)
330
+ if self._p is not None else None)
331
+
312
332
  # ── 写 ──
313
333
 
314
334
  def _post_create(self, dataset: str, split: str, version: str,
@@ -895,9 +895,11 @@ class VersionsClient:
895
895
  from ..store.oss_discovery import (
896
896
  parse_dataset_level, build_synthetic_version, _warn_if_truncated)
897
897
  info = parse_dataset_level(
898
- self._content, dataset, oss_prefix=self._oss_prefix)
898
+ self._content, dataset, oss_prefix=self._oss_prefix,
899
+ metadata_model=self._discovery_mm())
899
900
  _warn_if_truncated(info, dataset, 500)
900
- if not info.versions:
901
+ # split_first 无版本布局(只有 split)也是发现成功,只是没有版本可列
902
+ if not info.versions and not info.unversioned_splits:
901
903
  from ..errors import DatasetNotFound
902
904
  raise DatasetNotFound(f"dataset {dataset!r} not found in OSS")
903
905
  return [build_synthetic_version(dataset, v) for v in info.versions]
@@ -915,7 +917,8 @@ class VersionsClient:
915
917
  from ..store.oss_discovery import (
916
918
  parse_dataset_level, build_synthetic_version, _warn_if_truncated)
917
919
  info = parse_dataset_level(
918
- self._content, dataset, oss_prefix=self._oss_prefix)
920
+ self._content, dataset, oss_prefix=self._oss_prefix,
921
+ metadata_model=self._discovery_mm())
919
922
  _warn_if_truncated(info, dataset, 500)
920
923
  for v in info.versions:
921
924
  if v.version == version:
@@ -924,6 +927,13 @@ class VersionsClient:
924
927
  raise DatasetNotFound(
925
928
  f"version {version!r} not found in OSS for dataset {dataset!r}")
926
929
 
930
+ def _discovery_mm(self) -> str:
931
+ """OSS 回退探测的布局语义(split_first 默认;version_first 显式切换)。"""
932
+ from ..layout import normalize_metadata_model
933
+ return normalize_metadata_model(
934
+ getattr(self._p, "metadata_model", None)
935
+ if self._p is not None else None)
936
+
927
937
 
928
938
  # 与 apiserver `DatasetVersionListItem`(pkg/server/dataset_service.go:315)对齐:
929
939
  # 列表项带 instance_count/split_count/updated_at/last_ingested_at,详情接口才带
instance_repo/repo.py CHANGED
@@ -84,7 +84,8 @@ class Repo:
84
84
  self.datasets = DatasetsClient(
85
85
  self.transport,
86
86
  content_store=self._content,
87
- oss_prefix=self.profile.oss_prefix)
87
+ oss_prefix=self.profile.oss_prefix,
88
+ metadata_model=self.profile.metadata_model)
88
89
  self.versions = VersionsClient(
89
90
  self.transport, self.profile,
90
91
  content_store=self._content,
@@ -63,6 +63,14 @@ class OssVersionInfo:
63
63
  default_instance_count: int = 0
64
64
 
65
65
 
66
+ @dataclass
67
+ class OssUnversionedSplit:
68
+ """split_first 无版本布局:dataset 根下直接是 split 目录(无 version 层)。"""
69
+ name: str
70
+ has_assets: bool # 同层 {name}-assets/ 存在(split 级 content)
71
+ instance_count: int # {name}/ 下直接 .json 数
72
+
73
+
66
74
  @dataclass
67
75
  class OssDatasetLevelInfo:
68
76
  """Overall dataset structure discovered from OSS directory layout."""
@@ -70,6 +78,12 @@ class OssDatasetLevelInfo:
70
78
  versions: list[OssVersionInfo]
71
79
  total_instance_count: int
72
80
  truncated: bool = False
81
+ # split_first 无版本布局下的 split(version="" 语义)
82
+ unversioned_splits: list[OssUnversionedSplit] = None # type: ignore[assignment]
83
+
84
+ def __post_init__(self):
85
+ if self.unversioned_splits is None:
86
+ self.unversioned_splits = []
73
87
 
74
88
 
75
89
  # ---------------------------------------------------------------------------
@@ -185,11 +199,18 @@ def parse_dataset_level(
185
199
  *,
186
200
  oss_prefix: str = "swe/datasets",
187
201
  max_versions: int = 500,
202
+ metadata_model: str = "split_first",
188
203
  ) -> OssDatasetLevelInfo:
189
204
  """Discover version/split structure from OSS directory layout.
190
205
 
191
- Performs at most ``1 + min(N, max_versions)`` LIST calls where *N* is the
192
- number of Level 1 entries.
206
+ Performs ``1 + min(N, max_versions)`` LIST calls where *N* is the number
207
+ of Level 1 entries; the per-group probes run concurrently (8-way).
208
+
209
+ 语义消歧(``metadata_model``):
210
+ - ``split_first``(SDK 默认):level-1 目录 `X/` 下**只有 .json、无子目录**
211
+ 时,`X` 是无版本 split(`{split}/{id}.json` 契约形态);含子目录时是
212
+ version(子目录为其 split)。
213
+ - ``version_first``(显式切换):level-1 目录一律视为 version(旧行为)。
193
214
 
194
215
  Args:
195
216
  content_store: :class:`~instance_repo.store.oss.OssContentStore` instance.
@@ -197,6 +218,8 @@ def parse_dataset_level(
197
218
  oss_prefix: Root prefix inside the bucket (default ``"swe/datasets"``).
198
219
  max_versions: Upper bound on the number of versions to probe. When
199
220
  exceeded the result is truncated and ``truncated=True`` is set.
221
+ metadata_model: ``split_first``(默认)or ``version_first``;决定
222
+ level-1 目录的语义归属(见上)。
200
223
 
201
224
  Returns:
202
225
  :class:`OssDatasetLevelInfo` with the discovered version/split tree.
@@ -231,90 +254,104 @@ def parse_dataset_level(
231
254
  f"dataset {dataset!r} not found in OSS under {root!r}"
232
255
  )
233
256
 
234
- # -- Group level 1 names into version candidates -----------------------
257
+ # -- Group level 1 names into candidates -------------------------------
235
258
  l1_groups = _group_level_names(level1_names)
236
259
 
237
- if not l1_groups and not level1_json_keys:
260
+ if not l1_groups:
261
+ if level1_json_keys:
262
+ # 实例 .json 直接位于 dataset 根下(无 version/split 层)是**非法布局**:
263
+ # paths.py 契约要求 metadata 至少带 version 层(version_first)或
264
+ # split 层(split_first)。不兼容识别,明确报错引导上传方修正。
265
+ raise DatasetNotFound(
266
+ f"dataset {dataset!r} exists in OSS but has no recognizable "
267
+ f"version/split structure: {len(level1_json_keys)} .json file(s) "
268
+ f"directly under {root!r} (expected {root!r}{{version}}/[{{split}}/] "
269
+ f"layout per paths.py contract)")
238
270
  raise DatasetNotFound(
239
271
  f"dataset {dataset!r} exists in OSS but has no recognizable "
240
272
  f"version/split structure under {root!r}"
241
273
  )
242
274
 
243
275
  # Sort for deterministic output.
244
- version_names = sorted(l1_groups.keys())
276
+ names = sorted(l1_groups.keys())
245
277
 
246
278
  # -- Truncation protection ---------------------------------------------
247
279
  truncated = False
248
- if len(version_names) > max_versions:
249
- version_names = version_names[:max_versions]
280
+ if len(names) > max_versions:
281
+ names = names[:max_versions]
250
282
  truncated = True
251
283
 
252
- # -- Level 2: probe each version ---------------------------------------
253
- versions: list[OssVersionInfo] = []
254
- total_instance_count = 0
255
-
256
- for v_name in version_names:
257
- grp = l1_groups[v_name]
258
- version_dir = f"{root}{v_name}/"
259
-
260
- # 3-layer content/report siblings at level 1.
261
- has_three_layer_content = grp.has_assets # {version}-assets/ exists
284
+ split_first = (metadata_model or "split_first") != "version_first"
262
285
 
263
- # Level 2 listing (subdirectories of {version}/).
286
+ # -- Level 2: probe each candidate (concurrent) ------------------------
287
+ # 每个 group 的完整分析(level-2 列举 + 逐 split 计数)都在 probe 内完成,
288
+ # 使大 version 数数据集的探测全程并行,避免串行逐 split 计数拖慢整链。
289
+ def probe(name: str):
290
+ """分析单个 level-1 group,返回 OssVersionInfo / OssUnversionedSplit。"""
291
+ grp = l1_groups[name]
292
+ group_dir = f"{root}{name}/"
264
293
  try:
265
- level2_names = content_store.list_prefixes(version_dir, list_only=True)
294
+ level2_names, level2_keys = content_store.list_level(
295
+ group_dir, list_only=True)
266
296
  except Exception: # noqa: BLE001
267
- level2_names = []
268
-
297
+ level2_names, level2_keys = [], []
269
298
  l2_groups = _group_level_names(level2_names)
270
-
271
- # Splits are level 2 groups that have a real directory (has_dir) or
272
- # an assets directory (4-layer content pattern).
273
- splits: list[str] = sorted(
274
- name for name, g in l2_groups.items()
275
- if g.has_dir or g.has_assets
276
- )
277
-
278
- # Detect default-split instances: .json files directly under {version}/.
279
- try:
280
- _level2_dirs, v_all_keys = content_store.list_level(
281
- version_dir, list_only=True)
282
- v_json_keys = _direct_json_keys(v_all_keys, version_dir)
283
- has_default_instances = len(v_json_keys) > 0
284
- default_instance_count = len(v_json_keys)
285
- instance_count = len(v_json_keys)
286
- except Exception: # noqa: BLE001
287
- has_default_instances = False
288
- default_instance_count = 0
289
- instance_count = 0
290
-
291
- # Count instances in each named split directory.
299
+ direct_json = _direct_json_keys(level2_keys, group_dir)
300
+ subdirs = [g for g in l2_groups.values() if g.has_dir or g.has_assets]
301
+
302
+ # split_first 无版本布局:`{name}/` 下只有 .json、无子目录 →
303
+ # 无版本 split(contract: {split}/{id}.json + {split}-assets/)。
304
+ if split_first and grp.has_dir and not subdirs and direct_json:
305
+ return OssUnversionedSplit(
306
+ name=name,
307
+ has_assets=grp.has_assets,
308
+ instance_count=len(direct_json),
309
+ )
310
+
311
+ # version 路径(version_first 或含子目录的 split_first 形态)。
312
+ splits: list[str] = sorted(g.name for g in subdirs)
313
+ instance_count = len(direct_json)
292
314
  for s_name in splits:
293
- split_dir = f"{version_dir}{s_name}/"
315
+ split_dir = f"{group_dir}{s_name}/"
294
316
  try:
295
317
  _s_dirs, s_keys = content_store.list_level(
296
318
  split_dir, list_only=True)
297
- s_json = _direct_json_keys(s_keys, split_dir)
298
- instance_count += len(s_json)
319
+ instance_count += len(_direct_json_keys(s_keys, split_dir))
299
320
  except Exception: # noqa: BLE001
300
321
  pass # best-effort
301
-
302
- total_instance_count += instance_count
303
-
304
- versions.append(OssVersionInfo(
305
- version=v_name,
306
- has_three_layer_content=has_three_layer_content,
322
+ return OssVersionInfo(
323
+ version=name,
324
+ has_three_layer_content=grp.has_assets,
307
325
  splits=splits,
308
- has_default_instances=has_default_instances,
326
+ has_default_instances=len(direct_json) > 0,
309
327
  instance_count=instance_count,
310
- default_instance_count=default_instance_count,
311
- ))
328
+ default_instance_count=len(direct_json),
329
+ )
330
+
331
+ try:
332
+ from ..concurrency import map_concurrent
333
+ results = map_concurrent(probe, names, concurrency=16)
334
+ except Exception: # noqa: BLE001 - 并发不可用时退化为串行
335
+ results = [probe(n) for n in names]
336
+
337
+ versions: list[OssVersionInfo] = []
338
+ unversioned_splits: list[OssUnversionedSplit] = []
339
+ total_instance_count = 0
340
+
341
+ for entry in results:
342
+ if isinstance(entry, OssUnversionedSplit):
343
+ unversioned_splits.append(entry)
344
+ total_instance_count += entry.instance_count
345
+ else:
346
+ versions.append(entry)
347
+ total_instance_count += entry.instance_count
312
348
 
313
349
  return OssDatasetLevelInfo(
314
350
  dataset=dataset,
315
351
  versions=versions,
316
352
  total_instance_count=total_instance_count,
317
353
  truncated=truncated,
354
+ unversioned_splits=unversioned_splits,
318
355
  )
319
356
 
320
357
 
@@ -323,9 +360,10 @@ def _warn_if_truncated(info: OssDatasetLevelInfo, dataset: str, max_versions: in
323
360
  if info.truncated:
324
361
  import logging
325
362
  logging.getLogger(__name__).warning(
326
- "dataset %r has more than %d versions; OSS discovery results "
363
+ "dataset %r has more than %d level-1 entries; OSS discovery results "
327
364
  "are truncated (showing %d of >%d)",
328
- dataset, max_versions, len(info.versions), max_versions)
365
+ dataset, max_versions,
366
+ len(info.versions) + len(info.unversioned_splits), max_versions)
329
367
 
330
368
 
331
369
  # ---------------------------------------------------------------------------
@@ -389,6 +427,10 @@ def build_synthetic_dataset_detail(
389
427
  if v.has_default_instances:
390
428
  all_splits.add("default")
391
429
  total_instances += v.instance_count
430
+ # split_first 无版本布局:level-1 目录即 split
431
+ for us in info.unversioned_splits:
432
+ all_splits.add(us.name)
433
+ total_instances += us.instance_count
392
434
 
393
435
  available_envs: list[str] = [storage_env] if storage_env else []
394
436
 
@@ -450,10 +492,11 @@ def build_synthetic_split(
450
492
  instance_count: int = 0,
451
493
  ) -> DatasetSplit:
452
494
  """Build a :class:`DatasetSplit` from OSS discovery (``registered=False``)."""
495
+ base = f"swe/datasets/{dataset}" + (f"/{version}" if version else "")
453
496
  if split_name == "default":
454
- storage_path = f"swe/datasets/{dataset}/{version}/"
497
+ storage_path = f"{base}/"
455
498
  else:
456
- storage_path = f"swe/datasets/{dataset}/{version}/{split_name}/"
499
+ storage_path = f"{base}/{split_name}/"
457
500
 
458
501
  return DatasetSplit(
459
502
  dataset=dataset,
@@ -468,6 +511,16 @@ def build_synthetic_split(
468
511
  )
469
512
 
470
513
 
514
+ def _metadata_prefix(dataset: str, version: str, split: str | None,
515
+ oss_prefix: str) -> str:
516
+ """拼接 metadata 列举前缀;version 为空时表示 split_first 无版本布局。"""
517
+ l1, l2 = P.split_dataset(dataset)
518
+ base = f"{oss_prefix}/{l1}/{l2}" + (f"/{version}" if version else "")
519
+ if split and split != "default":
520
+ return f"{base}/{split}/"
521
+ return f"{base}/"
522
+
523
+
471
524
  def build_synthetic_instances(
472
525
  content_store,
473
526
  dataset: str,
@@ -475,20 +528,16 @@ def build_synthetic_instances(
475
528
  split: str | None = None,
476
529
  *,
477
530
  oss_prefix: str = "swe/datasets",
531
+ read_content: bool = False,
478
532
  ) -> list[DatasetInstance]:
479
533
  """List and build :class:`DatasetInstance` objects from OSS metadata JSONs.
480
534
 
481
535
  For each ``{instance_id}.json`` found directly under the target prefix, a
482
- ``DatasetInstance`` is constructed. An attempt is made to read the JSON
483
- body to populate ``tags``, ``difficulty``, ``docker_image``, and
484
- ``extend_meta``; on failure the instance is returned with those fields
485
- left at their defaults (best-effort).
536
+ ``DatasetInstance`` is constructed. ``read_content=True`` 时尝试读 JSON
537
+ 回填 tags/difficulty/docker_image;默认 False——发现模式(list-only STS)
538
+ 无 GetObject,逐实例 GET 必然失败,跳过可省 N 次无谓的 STS 签发与下载。
486
539
  """
487
- l1, l2 = P.split_dataset(dataset)
488
- if split and split != "default":
489
- metadata_prefix = f"{oss_prefix}/{l1}/{l2}/{version}/{split}/"
490
- else:
491
- metadata_prefix = f"{oss_prefix}/{l1}/{l2}/{version}/"
540
+ metadata_prefix = _metadata_prefix(dataset, version, split, oss_prefix)
492
541
 
493
542
  all_keys = content_store.list_objects(metadata_prefix, list_only=True)
494
543
  json_keys = _direct_json_keys(all_keys, metadata_prefix)
@@ -503,15 +552,13 @@ def build_synthetic_instances(
503
552
  split=split or "default",
504
553
  registered=False,
505
554
  )
506
- # Best-effort: read JSON to populate optional fields. With list_only=True
507
- # STS, GetObject is not granted, so these reads will fail silently (returning
508
- # empty dicts) — the instance will still be listed but with empty optional fields.
509
- data = _read_json_object(content_store, key)
510
- if data:
511
- inst.tags = data.get("tags", {})
512
- inst.difficulty = data.get("difficulty")
513
- inst.docker_image = data.get("docker_image")
514
- inst.extend_meta = data.get("extend_meta", {})
555
+ if read_content:
556
+ data = _read_json_object(content_store, key)
557
+ if data:
558
+ inst.tags = data.get("tags", {})
559
+ inst.difficulty = data.get("difficulty")
560
+ inst.docker_image = data.get("docker_image")
561
+ inst.extend_meta = data.get("extend_meta", {})
515
562
  instances.append(inst)
516
563
 
517
564
  return instances
@@ -525,13 +572,24 @@ def build_synthetic_instance(
525
572
  split: str | None = None,
526
573
  *,
527
574
  oss_prefix: str = "swe/datasets",
575
+ read_content: bool = False,
528
576
  ) -> DatasetInstance | None:
529
577
  """Build a single :class:`DatasetInstance` by exact key lookup in OSS.
530
578
 
531
- Returns ``None`` if the metadata JSON does not exist.
579
+ Returns ``None`` if the metadata JSON does not exist. ``read_content``
580
+ 语义同 :func:`build_synthetic_instances`(发现模式默认跳过 JSON 回填)。
532
581
  """
533
- key = P.metadata_key(
534
- dataset, version, instance_id, split, prefix=oss_prefix)
582
+ if version:
583
+ key = P.metadata_key(
584
+ dataset, version, instance_id, split, prefix=oss_prefix)
585
+ else:
586
+ # split_first 无版本布局:{split}/{id}.json(split=None/default 不合
587
+ # 此形态,调用方须传真实 split 名)
588
+ l1, l2 = P.split_dataset(dataset)
589
+ base = f"{oss_prefix}/{l1}/{l2}"
590
+ key = (f"{base}/{split}/{instance_id}.json"
591
+ if split and split != "default"
592
+ else f"{base}/{instance_id}.json")
535
593
 
536
594
  # 存在性判定用 LIST(list_only=True 仅需 ListObjects)而非 HEAD——
537
595
  # list-only STS 无 oss:GetObject,object_exists 的 HEAD 会在 STS 签发或
@@ -551,11 +609,12 @@ def build_synthetic_instance(
551
609
  split=split or "default",
552
610
  registered=False,
553
611
  )
554
- data = _read_json_object(content_store, key)
555
- if data:
556
- inst.tags = data.get("tags", {})
557
- inst.difficulty = data.get("difficulty")
558
- inst.docker_image = data.get("docker_image")
559
- inst.extend_meta = data.get("extend_meta", {})
612
+ if read_content:
613
+ data = _read_json_object(content_store, key)
614
+ if data:
615
+ inst.tags = data.get("tags", {})
616
+ inst.difficulty = data.get("difficulty")
617
+ inst.docker_image = data.get("docker_image")
618
+ inst.extend_meta = data.get("extend_meta", {})
560
619
 
561
620
  return inst
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instance-repo
3
- Version: 1.3.2.dev2
3
+ Version: 1.3.2.dev3
4
4
  Summary: InstanceRepo SDK — 评测 Instance 统一存储客户端(SDK-only;控制面由 apiserver 承载)
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,5 +1,5 @@
1
- instance_repo/__init__.py,sha256=ae2kTZekBd48C9oAOBgdUsN2JST5lkCSA6M348urFGI,1422
2
- instance_repo/_routing.py,sha256=XbdKPF-Zh2sEkr4UyLC9HuiRcTLP-ssvhp6SCDC98YM,8326
1
+ instance_repo/__init__.py,sha256=DbN35U2M95tsrr3gORthAQ1V8maenGoEP1sikL-2-wI,1422
2
+ instance_repo/_routing.py,sha256=fX3oKJNca1XTD1pKK304eq0clf7I8iEdFkd2WFO3oV4,8414
3
3
  instance_repo/_site_defaults.py,sha256=ck7aHHTup8ibBo0l5MSR03XpTtxJ11hYo7oM2DzWs4A,2383
4
4
  instance_repo/artifacts.py,sha256=OOcSI1ehB_dw1ZNScjyhzghPqOdL534kQp_iI2Hz7IU,10659
5
5
  instance_repo/bootstrap.py,sha256=qtBZoq77zHUcri3EgUleTrXTDUQq3GU9cI2wzw_wly4,9658
@@ -15,7 +15,7 @@ instance_repo/loader.py,sha256=lWjhw0MjEIIxbZ5w6cjyaDIWkNVFDhjjNdu1lc5zRuo,474
15
15
  instance_repo/models.py,sha256=-DyP2DBu2vNecmASbyExfNu5nJkutNsqWInhlq8_ZMU,35987
16
16
  instance_repo/paths.py,sha256=EL0d613T0mnabiYCtZQBC50rZT3mWKUq-3TepPbTRec,8438
17
17
  instance_repo/release.py,sha256=Up96Qeh2GRH26BSAHVEjDFQnFCaEA-vIvSme3a8FQFo,13882
18
- instance_repo/repo.py,sha256=5D7jYLeaDSZLKnf548YlfXT9tUPNgdcXT2j31wzShpA,40305
18
+ instance_repo/repo.py,sha256=XDa-61in_7P-2SGIMDbNM1okbJYuJh6ODlQCdY1DajI,40361
19
19
  instance_repo/retry.py,sha256=8y-AX9qmnLqGwqRzVJ5PKqZt2fWyqUL2t2P0suSBAA4,3438
20
20
  instance_repo/tasktoml.py,sha256=b-tWfNiOHN9D-ixCbPpCLnZaaU9Q6LRGG0Wl4YN8r10,5152
21
21
  instance_repo/transport.py,sha256=2Vh2atYxmq5Hrh0mc-10yA3p8QUOQruAE_2f1i1uwWk,21995
@@ -23,22 +23,22 @@ instance_repo/validate.py,sha256=NMR-vFBIU_FQ0WKdBZuQ-ua4jtkAzvWwEjnAHyAFwDs,118
23
23
  instance_repo/clients/__init__.py,sha256=5V7Pe0b6nnMiwIhACA4ctQowovrL4iHTiHKKcjmsBQo,488
24
24
  instance_repo/clients/benchmarks.py,sha256=RzHY8VzDP1o2jT4M8gxxbcr3amuNr4mlSblZhhJqG_w,2778
25
25
  instance_repo/clients/config.py,sha256=UdfNDqeQnFaOBBAl-Aj-W2JvuouvJ11q-_gB4da9yBg,21209
26
- instance_repo/clients/datasets.py,sha256=0b94qr-UNXKuEg1zIi3dl1FKd0Rz0TokvlflNdeUL88,24052
26
+ instance_repo/clients/datasets.py,sha256=qjUzhnJNEeMmsukMVoChh7zoTgsZDgHMnKZDJmvylaQ,24343
27
27
  instance_repo/clients/images.py,sha256=8i0FXxAAL2mOUUTrVgDdWSYM2imJ5nOEXzpVRBa-G-k,4911
28
28
  instance_repo/clients/instances.py,sha256=IOAKl3MAlW3UcftX8b5cGIEdC5_1qfouaVSE_0-OAxE,58125
29
29
  instance_repo/clients/reports.py,sha256=4Z8bpcSi-mmfkQLKpMklelLI4mgHcnjfb0725bE2nkI,3001
30
30
  instance_repo/clients/scaffold.py,sha256=VLicNFnqKi2iYB-gjOkQ9wa1unNOJXO7UxBZlz7El5A,4998
31
- instance_repo/clients/splits.py,sha256=9hUZAiLCv0gZpxbEPvxtsJZ0qeGxSxaYl21ARF3sl58,27211
32
- instance_repo/clients/versions.py,sha256=jeLwV0K5h4lpeeEMPQMnv-BpzfRcm0jiqcGIa0Nl2RI,52658
31
+ instance_repo/clients/splits.py,sha256=o8ygCVAMrxECNojzfUdaHQtuwL4Imzm7ghNKWPxG-VE,28255
32
+ instance_repo/clients/versions.py,sha256=VH3sqEhPY_lbYDX5vQmtWkIFv8D5SWsd9UBmcF7P5rg,53222
33
33
  instance_repo/clients/workflows.py,sha256=6ftl1tuyEGx8BtMLSjfExP9_VcQhK4veo7ex1jDOdlc,3686
34
34
  instance_repo/store/__init__.py,sha256=fGwePNjKuojqx1yagSaY-svONE5pjaEz2BZmG0ISUZ0,674
35
35
  instance_repo/store/acr.py,sha256=OD7LH3GZY4HrrbEht86Mf3NfW_lh-8MhCTOGyX6gLT4,19468
36
36
  instance_repo/store/base.py,sha256=7feHdDXDvSkq60wXYvF6p8TK9KRj7YTLlESdgZ5MQHo,2622
37
37
  instance_repo/store/oss.py,sha256=-1dBPlUBLD0z-aI4GOuO6kwGyIgZX1zRvYCD-IPp-Zs,30672
38
- instance_repo/store/oss_discovery.py,sha256=4s8n2i8AhiNCKP2LoDaRgELEOLW3quodRgREhMM0I3g,20008
38
+ instance_repo/store/oss_discovery.py,sha256=RRgbo5eAXDJS6GeS0EW6n9j-BlGM6OD-ouvtVMoxjwU,23257
39
39
  instance_repo/store/registry.py,sha256=1R2GqXsuDZ8wc3IGpncF9yL6IHk-FeiwUlZN7v2ndSc,10837
40
- instance_repo-1.3.2.dev2.dist-info/METADATA,sha256=H2v_y6ot4xb5UX5sY5YwDfOgEIt96jdDLxPO8NQXVvg,14227
41
- instance_repo-1.3.2.dev2.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
42
- instance_repo-1.3.2.dev2.dist-info/entry_points.txt,sha256=tn6jgwYChSx7eAqw9YNed_JFYYgoRtE7WkFn8xqa8Rw,53
43
- instance_repo-1.3.2.dev2.dist-info/top_level.txt,sha256=mblK0Bkwwp6jCZioS6q3sSuFFs8NR9DKTJ0DJpIRDmY,14
44
- instance_repo-1.3.2.dev2.dist-info/RECORD,,
40
+ instance_repo-1.3.2.dev3.dist-info/METADATA,sha256=zUbd3l8k4Z9bPbzZ4MPqTaUAOmwF8YgRCOWU6SozuK0,14227
41
+ instance_repo-1.3.2.dev3.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
42
+ instance_repo-1.3.2.dev3.dist-info/entry_points.txt,sha256=tn6jgwYChSx7eAqw9YNed_JFYYgoRtE7WkFn8xqa8Rw,53
43
+ instance_repo-1.3.2.dev3.dist-info/top_level.txt,sha256=mblK0Bkwwp6jCZioS6q3sSuFFs8NR9DKTJ0DJpIRDmY,14
44
+ instance_repo-1.3.2.dev3.dist-info/RECORD,,