instance-repo 1.3.2.dev2__py3-none-any.whl → 1.3.2.dev4__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 +1 -1
- instance_repo/_routing.py +4 -4
- instance_repo/clients/datasets.py +11 -5
- instance_repo/clients/splits.py +23 -3
- instance_repo/clients/versions.py +25 -10
- instance_repo/repo.py +2 -1
- instance_repo/store/oss_discovery.py +215 -86
- {instance_repo-1.3.2.dev2.dist-info → instance_repo-1.3.2.dev4.dist-info}/METADATA +1 -1
- {instance_repo-1.3.2.dev2.dist-info → instance_repo-1.3.2.dev4.dist-info}/RECORD +12 -12
- {instance_repo-1.3.2.dev2.dist-info → instance_repo-1.3.2.dev4.dist-info}/WHEEL +0 -0
- {instance_repo-1.3.2.dev2.dist-info → instance_repo-1.3.2.dev4.dist-info}/entry_points.txt +0 -0
- {instance_repo-1.3.2.dev2.dist-info → instance_repo-1.3.2.dev4.dist-info}/top_level.txt +0 -0
instance_repo/__init__.py
CHANGED
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 =
|
|
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
|
|
54
|
-
|
|
55
|
-
|
|
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"
|
|
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` 单页对照)。
|
|
@@ -158,7 +161,8 @@ class DatasetsClient:
|
|
|
158
161
|
except TransportError as e:
|
|
159
162
|
if (e.biz_code == 92001 and fallback_to_oss
|
|
160
163
|
and self._content is not None):
|
|
161
|
-
return self._discover_detail(name, storage_env=environment
|
|
164
|
+
return self._discover_detail(name, storage_env=environment,
|
|
165
|
+
metadata_model=metadata_model)
|
|
162
166
|
raise
|
|
163
167
|
|
|
164
168
|
def update(self, name: str, *, display_name: str | None = None,
|
|
@@ -393,14 +397,16 @@ class DatasetsClient:
|
|
|
393
397
|
# ── OSS fallback discovery (private) ─────────────────────────────────
|
|
394
398
|
|
|
395
399
|
def _discover_detail(self, name: str, *,
|
|
396
|
-
storage_env: str | None = None
|
|
400
|
+
storage_env: str | None = None,
|
|
401
|
+
metadata_model: str | None = None) -> DatasetDetail:
|
|
397
402
|
from ..store.oss_discovery import (
|
|
398
403
|
parse_dataset_level, build_synthetic_dataset_detail,
|
|
399
404
|
_warn_if_truncated)
|
|
400
405
|
info = parse_dataset_level(
|
|
401
|
-
self._content, name, oss_prefix=self._oss_prefix
|
|
406
|
+
self._content, name, oss_prefix=self._oss_prefix,
|
|
407
|
+
metadata_model=metadata_model or self._metadata_model)
|
|
402
408
|
_warn_if_truncated(info, name, 500)
|
|
403
|
-
if not info.versions:
|
|
409
|
+
if not info.versions and not info.unversioned_splits:
|
|
404
410
|
from ..errors import DatasetNotFound
|
|
405
411
|
raise DatasetNotFound(f"dataset {name!r} not found in OSS")
|
|
406
412
|
return build_synthetic_dataset_detail(
|
instance_repo/clients/splits.py
CHANGED
|
@@ -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,
|
|
@@ -229,7 +229,7 @@ class VersionsClient:
|
|
|
229
229
|
except TransportError as e:
|
|
230
230
|
if (e.biz_code == 92001 and fallback_to_oss
|
|
231
231
|
and self._content is not None):
|
|
232
|
-
return self._discover_versions(dataset)
|
|
232
|
+
return self._discover_versions(dataset, metadata_model=metadata_model)
|
|
233
233
|
raise
|
|
234
234
|
|
|
235
235
|
def list_paged(self, dataset: str, *, status: str | None = None,
|
|
@@ -264,7 +264,8 @@ class VersionsClient:
|
|
|
264
264
|
if (e.biz_code == 92001 and fallback_to_oss
|
|
265
265
|
and self._content is not None):
|
|
266
266
|
return self._discover_versions_paged(
|
|
267
|
-
dataset, status, int(page), int(page_size)
|
|
267
|
+
dataset, status, int(page), int(page_size),
|
|
268
|
+
metadata_model=metadata_model)
|
|
268
269
|
raise
|
|
269
270
|
rows = data.get("versions", data) if isinstance(data, dict) else data
|
|
270
271
|
rows = rows if isinstance(rows, list) else []
|
|
@@ -295,7 +296,8 @@ class VersionsClient:
|
|
|
295
296
|
except TransportError as e:
|
|
296
297
|
if (e.biz_code == 92001 and fallback_to_oss
|
|
297
298
|
and self._content is not None):
|
|
298
|
-
return self._discover_version(dataset, version
|
|
299
|
+
return self._discover_version(dataset, version,
|
|
300
|
+
metadata_model=metadata_model)
|
|
299
301
|
raise
|
|
300
302
|
|
|
301
303
|
def status(self, dataset: str, version: str, *,
|
|
@@ -891,31 +893,37 @@ class VersionsClient:
|
|
|
891
893
|
|
|
892
894
|
# ── OSS fallback discovery (private) ─────────────────────────────────
|
|
893
895
|
|
|
894
|
-
def _discover_versions(self, dataset: str
|
|
896
|
+
def _discover_versions(self, dataset: str,
|
|
897
|
+
metadata_model: str | None = None) -> list:
|
|
895
898
|
from ..store.oss_discovery import (
|
|
896
899
|
parse_dataset_level, build_synthetic_version, _warn_if_truncated)
|
|
897
900
|
info = parse_dataset_level(
|
|
898
|
-
self._content, dataset, oss_prefix=self._oss_prefix
|
|
901
|
+
self._content, dataset, oss_prefix=self._oss_prefix,
|
|
902
|
+
metadata_model=metadata_model or self._discovery_mm())
|
|
899
903
|
_warn_if_truncated(info, dataset, 500)
|
|
900
|
-
|
|
904
|
+
# split_first 无版本布局(只有 split)也是发现成功,只是没有版本可列
|
|
905
|
+
if not info.versions and not info.unversioned_splits:
|
|
901
906
|
from ..errors import DatasetNotFound
|
|
902
907
|
raise DatasetNotFound(f"dataset {dataset!r} not found in OSS")
|
|
903
908
|
return [build_synthetic_version(dataset, v) for v in info.versions]
|
|
904
909
|
|
|
905
910
|
def _discover_versions_paged(self, dataset: str, status: str | None,
|
|
906
|
-
page: int, page_size: int
|
|
911
|
+
page: int, page_size: int,
|
|
912
|
+
metadata_model: str | None = None):
|
|
907
913
|
"""OSS 回退的 list_paged:全量探测后按 status 过滤 + 内存分页。"""
|
|
908
914
|
from ..store.oss_discovery import _in_memory_paged
|
|
909
|
-
items = self._discover_versions(dataset)
|
|
915
|
+
items = self._discover_versions(dataset, metadata_model=metadata_model)
|
|
910
916
|
if (status or "").strip():
|
|
911
917
|
items = [v for v in items if v.status == status.strip()]
|
|
912
918
|
return _in_memory_paged(items, int(page), int(page_size))
|
|
913
919
|
|
|
914
|
-
def _discover_version(self, dataset: str, version: str
|
|
920
|
+
def _discover_version(self, dataset: str, version: str,
|
|
921
|
+
metadata_model: str | None = None):
|
|
915
922
|
from ..store.oss_discovery import (
|
|
916
923
|
parse_dataset_level, build_synthetic_version, _warn_if_truncated)
|
|
917
924
|
info = parse_dataset_level(
|
|
918
|
-
self._content, dataset, oss_prefix=self._oss_prefix
|
|
925
|
+
self._content, dataset, oss_prefix=self._oss_prefix,
|
|
926
|
+
metadata_model=metadata_model or self._discovery_mm())
|
|
919
927
|
_warn_if_truncated(info, dataset, 500)
|
|
920
928
|
for v in info.versions:
|
|
921
929
|
if v.version == version:
|
|
@@ -924,6 +932,13 @@ class VersionsClient:
|
|
|
924
932
|
raise DatasetNotFound(
|
|
925
933
|
f"version {version!r} not found in OSS for dataset {dataset!r}")
|
|
926
934
|
|
|
935
|
+
def _discovery_mm(self) -> str:
|
|
936
|
+
"""OSS 回退探测的布局语义(split_first 默认;version_first 显式切换)。"""
|
|
937
|
+
from ..layout import normalize_metadata_model
|
|
938
|
+
return normalize_metadata_model(
|
|
939
|
+
getattr(self._p, "metadata_model", None)
|
|
940
|
+
if self._p is not None else None)
|
|
941
|
+
|
|
927
942
|
|
|
928
943
|
# 与 apiserver `DatasetVersionListItem`(pkg/server/dataset_service.go:315)对齐:
|
|
929
944
|
# 列表项带 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,
|
|
@@ -23,6 +23,7 @@ from ..models import (
|
|
|
23
23
|
DatasetInstance,
|
|
24
24
|
DatasetSplit,
|
|
25
25
|
DatasetVersion,
|
|
26
|
+
ManifestEntry,
|
|
26
27
|
PagedResult,
|
|
27
28
|
SplitManifest,
|
|
28
29
|
)
|
|
@@ -63,6 +64,14 @@ class OssVersionInfo:
|
|
|
63
64
|
default_instance_count: int = 0
|
|
64
65
|
|
|
65
66
|
|
|
67
|
+
@dataclass
|
|
68
|
+
class OssUnversionedSplit:
|
|
69
|
+
"""split_first 无版本布局:dataset 根下直接是 split 目录(无 version 层)。"""
|
|
70
|
+
name: str
|
|
71
|
+
has_assets: bool # 同层 {name}-assets/ 存在(split 级 content)
|
|
72
|
+
instance_count: int # {name}/ 下直接 .json 数
|
|
73
|
+
|
|
74
|
+
|
|
66
75
|
@dataclass
|
|
67
76
|
class OssDatasetLevelInfo:
|
|
68
77
|
"""Overall dataset structure discovered from OSS directory layout."""
|
|
@@ -70,6 +79,12 @@ class OssDatasetLevelInfo:
|
|
|
70
79
|
versions: list[OssVersionInfo]
|
|
71
80
|
total_instance_count: int
|
|
72
81
|
truncated: bool = False
|
|
82
|
+
# split_first 无版本布局下的 split(version="" 语义)
|
|
83
|
+
unversioned_splits: list[OssUnversionedSplit] = None # type: ignore[assignment]
|
|
84
|
+
|
|
85
|
+
def __post_init__(self):
|
|
86
|
+
if self.unversioned_splits is None:
|
|
87
|
+
self.unversioned_splits = []
|
|
73
88
|
|
|
74
89
|
|
|
75
90
|
# ---------------------------------------------------------------------------
|
|
@@ -185,11 +200,18 @@ def parse_dataset_level(
|
|
|
185
200
|
*,
|
|
186
201
|
oss_prefix: str = "swe/datasets",
|
|
187
202
|
max_versions: int = 500,
|
|
203
|
+
metadata_model: str = "split_first",
|
|
188
204
|
) -> OssDatasetLevelInfo:
|
|
189
205
|
"""Discover version/split structure from OSS directory layout.
|
|
190
206
|
|
|
191
|
-
Performs
|
|
192
|
-
|
|
207
|
+
Performs ``1 + min(N, max_versions)`` LIST calls where *N* is the number
|
|
208
|
+
of Level 1 entries; the per-group probes run concurrently (8-way).
|
|
209
|
+
|
|
210
|
+
语义消歧(``metadata_model``):
|
|
211
|
+
- ``split_first``(SDK 默认):level-1 目录 `X/` 下**只有 .json、无子目录**
|
|
212
|
+
时,`X` 是无版本 split(`{split}/{id}.json` 契约形态);含子目录时是
|
|
213
|
+
version(子目录为其 split)。
|
|
214
|
+
- ``version_first``(显式切换):level-1 目录一律视为 version(旧行为)。
|
|
193
215
|
|
|
194
216
|
Args:
|
|
195
217
|
content_store: :class:`~instance_repo.store.oss.OssContentStore` instance.
|
|
@@ -197,6 +219,8 @@ def parse_dataset_level(
|
|
|
197
219
|
oss_prefix: Root prefix inside the bucket (default ``"swe/datasets"``).
|
|
198
220
|
max_versions: Upper bound on the number of versions to probe. When
|
|
199
221
|
exceeded the result is truncated and ``truncated=True`` is set.
|
|
222
|
+
metadata_model: ``split_first``(默认)or ``version_first``;决定
|
|
223
|
+
level-1 目录的语义归属(见上)。
|
|
200
224
|
|
|
201
225
|
Returns:
|
|
202
226
|
:class:`OssDatasetLevelInfo` with the discovered version/split tree.
|
|
@@ -231,90 +255,104 @@ def parse_dataset_level(
|
|
|
231
255
|
f"dataset {dataset!r} not found in OSS under {root!r}"
|
|
232
256
|
)
|
|
233
257
|
|
|
234
|
-
# -- Group level 1 names into
|
|
258
|
+
# -- Group level 1 names into candidates -------------------------------
|
|
235
259
|
l1_groups = _group_level_names(level1_names)
|
|
236
260
|
|
|
237
|
-
if not l1_groups
|
|
261
|
+
if not l1_groups:
|
|
262
|
+
if level1_json_keys:
|
|
263
|
+
# 实例 .json 直接位于 dataset 根下(无 version/split 层)是**非法布局**:
|
|
264
|
+
# paths.py 契约要求 metadata 至少带 version 层(version_first)或
|
|
265
|
+
# split 层(split_first)。不兼容识别,明确报错引导上传方修正。
|
|
266
|
+
raise DatasetNotFound(
|
|
267
|
+
f"dataset {dataset!r} exists in OSS but has no recognizable "
|
|
268
|
+
f"version/split structure: {len(level1_json_keys)} .json file(s) "
|
|
269
|
+
f"directly under {root!r} (expected {root!r}{{version}}/[{{split}}/] "
|
|
270
|
+
f"layout per paths.py contract)")
|
|
238
271
|
raise DatasetNotFound(
|
|
239
272
|
f"dataset {dataset!r} exists in OSS but has no recognizable "
|
|
240
273
|
f"version/split structure under {root!r}"
|
|
241
274
|
)
|
|
242
275
|
|
|
243
276
|
# Sort for deterministic output.
|
|
244
|
-
|
|
277
|
+
names = sorted(l1_groups.keys())
|
|
245
278
|
|
|
246
279
|
# -- Truncation protection ---------------------------------------------
|
|
247
280
|
truncated = False
|
|
248
|
-
if len(
|
|
249
|
-
|
|
281
|
+
if len(names) > max_versions:
|
|
282
|
+
names = names[:max_versions]
|
|
250
283
|
truncated = True
|
|
251
284
|
|
|
252
|
-
|
|
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
|
|
285
|
+
split_first = (metadata_model or "split_first") != "version_first"
|
|
262
286
|
|
|
263
|
-
|
|
287
|
+
# -- Level 2: probe each candidate (concurrent) ------------------------
|
|
288
|
+
# 每个 group 的完整分析(level-2 列举 + 逐 split 计数)都在 probe 内完成,
|
|
289
|
+
# 使大 version 数数据集的探测全程并行,避免串行逐 split 计数拖慢整链。
|
|
290
|
+
def probe(name: str):
|
|
291
|
+
"""分析单个 level-1 group,返回 OssVersionInfo / OssUnversionedSplit。"""
|
|
292
|
+
grp = l1_groups[name]
|
|
293
|
+
group_dir = f"{root}{name}/"
|
|
264
294
|
try:
|
|
265
|
-
level2_names = content_store.
|
|
295
|
+
level2_names, level2_keys = content_store.list_level(
|
|
296
|
+
group_dir, list_only=True)
|
|
266
297
|
except Exception: # noqa: BLE001
|
|
267
|
-
level2_names = []
|
|
268
|
-
|
|
298
|
+
level2_names, level2_keys = [], []
|
|
269
299
|
l2_groups = _group_level_names(level2_names)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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.
|
|
300
|
+
direct_json = _direct_json_keys(level2_keys, group_dir)
|
|
301
|
+
subdirs = [g for g in l2_groups.values() if g.has_dir or g.has_assets]
|
|
302
|
+
|
|
303
|
+
# split_first 无版本布局:`{name}/` 下只有 .json、无子目录 →
|
|
304
|
+
# 无版本 split(contract: {split}/{id}.json + {split}-assets/)。
|
|
305
|
+
if split_first and grp.has_dir and not subdirs and direct_json:
|
|
306
|
+
return OssUnversionedSplit(
|
|
307
|
+
name=name,
|
|
308
|
+
has_assets=grp.has_assets,
|
|
309
|
+
instance_count=len(direct_json),
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# version 路径(version_first 或含子目录的 split_first 形态)。
|
|
313
|
+
splits: list[str] = sorted(g.name for g in subdirs)
|
|
314
|
+
instance_count = len(direct_json)
|
|
292
315
|
for s_name in splits:
|
|
293
|
-
split_dir = f"{
|
|
316
|
+
split_dir = f"{group_dir}{s_name}/"
|
|
294
317
|
try:
|
|
295
318
|
_s_dirs, s_keys = content_store.list_level(
|
|
296
319
|
split_dir, list_only=True)
|
|
297
|
-
|
|
298
|
-
instance_count += len(s_json)
|
|
320
|
+
instance_count += len(_direct_json_keys(s_keys, split_dir))
|
|
299
321
|
except Exception: # noqa: BLE001
|
|
300
322
|
pass # best-effort
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
versions.append(OssVersionInfo(
|
|
305
|
-
version=v_name,
|
|
306
|
-
has_three_layer_content=has_three_layer_content,
|
|
323
|
+
return OssVersionInfo(
|
|
324
|
+
version=name,
|
|
325
|
+
has_three_layer_content=grp.has_assets,
|
|
307
326
|
splits=splits,
|
|
308
|
-
has_default_instances=
|
|
327
|
+
has_default_instances=len(direct_json) > 0,
|
|
309
328
|
instance_count=instance_count,
|
|
310
|
-
default_instance_count=
|
|
311
|
-
)
|
|
329
|
+
default_instance_count=len(direct_json),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
try:
|
|
333
|
+
from ..concurrency import map_concurrent
|
|
334
|
+
results = map_concurrent(probe, names, concurrency=16)
|
|
335
|
+
except Exception: # noqa: BLE001 - 并发不可用时退化为串行
|
|
336
|
+
results = [probe(n) for n in names]
|
|
337
|
+
|
|
338
|
+
versions: list[OssVersionInfo] = []
|
|
339
|
+
unversioned_splits: list[OssUnversionedSplit] = []
|
|
340
|
+
total_instance_count = 0
|
|
341
|
+
|
|
342
|
+
for entry in results:
|
|
343
|
+
if isinstance(entry, OssUnversionedSplit):
|
|
344
|
+
unversioned_splits.append(entry)
|
|
345
|
+
total_instance_count += entry.instance_count
|
|
346
|
+
else:
|
|
347
|
+
versions.append(entry)
|
|
348
|
+
total_instance_count += entry.instance_count
|
|
312
349
|
|
|
313
350
|
return OssDatasetLevelInfo(
|
|
314
351
|
dataset=dataset,
|
|
315
352
|
versions=versions,
|
|
316
353
|
total_instance_count=total_instance_count,
|
|
317
354
|
truncated=truncated,
|
|
355
|
+
unversioned_splits=unversioned_splits,
|
|
318
356
|
)
|
|
319
357
|
|
|
320
358
|
|
|
@@ -323,9 +361,10 @@ def _warn_if_truncated(info: OssDatasetLevelInfo, dataset: str, max_versions: in
|
|
|
323
361
|
if info.truncated:
|
|
324
362
|
import logging
|
|
325
363
|
logging.getLogger(__name__).warning(
|
|
326
|
-
"dataset %r has more than %d
|
|
364
|
+
"dataset %r has more than %d level-1 entries; OSS discovery results "
|
|
327
365
|
"are truncated (showing %d of >%d)",
|
|
328
|
-
dataset, max_versions,
|
|
366
|
+
dataset, max_versions,
|
|
367
|
+
len(info.versions) + len(info.unversioned_splits), max_versions)
|
|
329
368
|
|
|
330
369
|
|
|
331
370
|
# ---------------------------------------------------------------------------
|
|
@@ -389,6 +428,10 @@ def build_synthetic_dataset_detail(
|
|
|
389
428
|
if v.has_default_instances:
|
|
390
429
|
all_splits.add("default")
|
|
391
430
|
total_instances += v.instance_count
|
|
431
|
+
# split_first 无版本布局:level-1 目录即 split
|
|
432
|
+
for us in info.unversioned_splits:
|
|
433
|
+
all_splits.add(us.name)
|
|
434
|
+
total_instances += us.instance_count
|
|
392
435
|
|
|
393
436
|
available_envs: list[str] = [storage_env] if storage_env else []
|
|
394
437
|
|
|
@@ -450,10 +493,11 @@ def build_synthetic_split(
|
|
|
450
493
|
instance_count: int = 0,
|
|
451
494
|
) -> DatasetSplit:
|
|
452
495
|
"""Build a :class:`DatasetSplit` from OSS discovery (``registered=False``)."""
|
|
496
|
+
base = f"swe/datasets/{dataset}" + (f"/{version}" if version else "")
|
|
453
497
|
if split_name == "default":
|
|
454
|
-
storage_path = f"
|
|
498
|
+
storage_path = f"{base}/"
|
|
455
499
|
else:
|
|
456
|
-
storage_path = f"
|
|
500
|
+
storage_path = f"{base}/{split_name}/"
|
|
457
501
|
|
|
458
502
|
return DatasetSplit(
|
|
459
503
|
dataset=dataset,
|
|
@@ -468,6 +512,16 @@ def build_synthetic_split(
|
|
|
468
512
|
)
|
|
469
513
|
|
|
470
514
|
|
|
515
|
+
def _metadata_prefix(dataset: str, version: str, split: str | None,
|
|
516
|
+
oss_prefix: str) -> str:
|
|
517
|
+
"""拼接 metadata 列举前缀;version 为空时表示 split_first 无版本布局。"""
|
|
518
|
+
l1, l2 = P.split_dataset(dataset)
|
|
519
|
+
base = f"{oss_prefix}/{l1}/{l2}" + (f"/{version}" if version else "")
|
|
520
|
+
if split and split != "default":
|
|
521
|
+
return f"{base}/{split}/"
|
|
522
|
+
return f"{base}/"
|
|
523
|
+
|
|
524
|
+
|
|
471
525
|
def build_synthetic_instances(
|
|
472
526
|
content_store,
|
|
473
527
|
dataset: str,
|
|
@@ -475,20 +529,16 @@ def build_synthetic_instances(
|
|
|
475
529
|
split: str | None = None,
|
|
476
530
|
*,
|
|
477
531
|
oss_prefix: str = "swe/datasets",
|
|
532
|
+
read_content: bool = False,
|
|
478
533
|
) -> list[DatasetInstance]:
|
|
479
534
|
"""List and build :class:`DatasetInstance` objects from OSS metadata JSONs.
|
|
480
535
|
|
|
481
536
|
For each ``{instance_id}.json`` found directly under the target prefix, a
|
|
482
|
-
``DatasetInstance`` is constructed.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
left at their defaults (best-effort).
|
|
537
|
+
``DatasetInstance`` is constructed. ``read_content=True`` 时尝试读 JSON
|
|
538
|
+
回填 tags/difficulty/docker_image;默认 False——发现模式(list-only STS)
|
|
539
|
+
无 GetObject,逐实例 GET 必然失败,跳过可省 N 次无谓的 STS 签发与下载。
|
|
486
540
|
"""
|
|
487
|
-
|
|
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}/"
|
|
541
|
+
metadata_prefix = _metadata_prefix(dataset, version, split, oss_prefix)
|
|
492
542
|
|
|
493
543
|
all_keys = content_store.list_objects(metadata_prefix, list_only=True)
|
|
494
544
|
json_keys = _direct_json_keys(all_keys, metadata_prefix)
|
|
@@ -503,20 +553,74 @@ def build_synthetic_instances(
|
|
|
503
553
|
split=split or "default",
|
|
504
554
|
registered=False,
|
|
505
555
|
)
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
inst.docker_image = data.get("docker_image")
|
|
514
|
-
inst.extend_meta = data.get("extend_meta", {})
|
|
556
|
+
if read_content:
|
|
557
|
+
data = _read_json_object(content_store, key)
|
|
558
|
+
if data:
|
|
559
|
+
inst.tags = data.get("tags", {})
|
|
560
|
+
inst.difficulty = data.get("difficulty")
|
|
561
|
+
inst.docker_image = data.get("docker_image")
|
|
562
|
+
inst.extend_meta = data.get("extend_meta", {})
|
|
515
563
|
instances.append(inst)
|
|
516
564
|
|
|
517
565
|
return instances
|
|
518
566
|
|
|
519
567
|
|
|
568
|
+
def _instance_dir(dataset: str, version: str, split: str | None,
|
|
569
|
+
instance_id: str, suffix: str, oss_prefix: str) -> str:
|
|
570
|
+
"""拼接实例的 -assets/-report 目录;version 为空表示无版本 split 布局。"""
|
|
571
|
+
l1, l2 = P.split_dataset(dataset)
|
|
572
|
+
base = f"{oss_prefix}/{l1}/{l2}" + (f"/{version}" if version else "")
|
|
573
|
+
if split and split != "default":
|
|
574
|
+
return f"{base}/{split}-{suffix}/{instance_id}/"
|
|
575
|
+
return f"{base}-{suffix}/{instance_id}/"
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _format_from_key(key: str) -> str:
|
|
579
|
+
if key.endswith(".jsonl"):
|
|
580
|
+
return "jsonl"
|
|
581
|
+
if key.endswith(".json"):
|
|
582
|
+
return "json"
|
|
583
|
+
if key.endswith(".tgz") or key.endswith(".tar.gz"):
|
|
584
|
+
return "tgz"
|
|
585
|
+
return ""
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _instance_manifest(
|
|
589
|
+
content_store,
|
|
590
|
+
dataset: str,
|
|
591
|
+
version: str,
|
|
592
|
+
instance_id: str,
|
|
593
|
+
split: str | None,
|
|
594
|
+
metadata_key: str,
|
|
595
|
+
*,
|
|
596
|
+
oss_prefix: str,
|
|
597
|
+
) -> list[ManifestEntry]:
|
|
598
|
+
"""用 LIST 枚举实例的文件结构(metadata + {split}-assets + {split}-report)。
|
|
599
|
+
|
|
600
|
+
发现模式(list-only STS)无 GetObject,读不到 metadata JSON 内容,但目录
|
|
601
|
+
结构可通过 LIST 完整呈现——恰好满足「查看实例的目录文件结构」需求。
|
|
602
|
+
"""
|
|
603
|
+
keys = [metadata_key]
|
|
604
|
+
for suffix in ("assets", "report"):
|
|
605
|
+
try:
|
|
606
|
+
keys.extend(k for k in content_store.list_objects(
|
|
607
|
+
_instance_dir(dataset, version, split, instance_id,
|
|
608
|
+
suffix, oss_prefix),
|
|
609
|
+
list_only=True) if not k.endswith("/"))
|
|
610
|
+
except Exception: # noqa: BLE001
|
|
611
|
+
pass # best-effort:无 assets/report 目录时忽略
|
|
612
|
+
bucket = getattr(content_store, "bucket_name", "") or ""
|
|
613
|
+
return [
|
|
614
|
+
ManifestEntry(
|
|
615
|
+
file_path=f"oss://{bucket}/{k}" if bucket else k,
|
|
616
|
+
content_type="file",
|
|
617
|
+
content_format=_format_from_key(k),
|
|
618
|
+
object_path=k,
|
|
619
|
+
)
|
|
620
|
+
for k in keys
|
|
621
|
+
]
|
|
622
|
+
|
|
623
|
+
|
|
520
624
|
def build_synthetic_instance(
|
|
521
625
|
content_store,
|
|
522
626
|
dataset: str,
|
|
@@ -525,13 +629,30 @@ def build_synthetic_instance(
|
|
|
525
629
|
split: str | None = None,
|
|
526
630
|
*,
|
|
527
631
|
oss_prefix: str = "swe/datasets",
|
|
632
|
+
read_content: bool = False,
|
|
633
|
+
include_files: bool = True,
|
|
528
634
|
) -> DatasetInstance | None:
|
|
529
635
|
"""Build a single :class:`DatasetInstance` by exact key lookup in OSS.
|
|
530
636
|
|
|
531
|
-
Returns ``None`` if the metadata JSON does not exist.
|
|
637
|
+
Returns ``None`` if the metadata JSON does not exist. ``read_content``
|
|
638
|
+
语义同 :func:`build_synthetic_instances`(发现模式默认跳过 JSON 回填)。
|
|
639
|
+
|
|
640
|
+
``include_files``(默认 True)用 LIST 枚举实例文件结构(metadata + assets
|
|
641
|
+
+ report 目录)回填 ``manifest``——文件名为真实值,digest/大小留空
|
|
642
|
+
(list-only STS 无 GetObject)。仅 instance get 单条路径开启,避免列表
|
|
643
|
+
路径 N 倍 LIST 开销。
|
|
532
644
|
"""
|
|
533
|
-
|
|
534
|
-
|
|
645
|
+
if version:
|
|
646
|
+
key = P.metadata_key(
|
|
647
|
+
dataset, version, instance_id, split, prefix=oss_prefix)
|
|
648
|
+
else:
|
|
649
|
+
# split_first 无版本布局:{split}/{id}.json(split=None/default 不合
|
|
650
|
+
# 此形态,调用方须传真实 split 名)
|
|
651
|
+
l1, l2 = P.split_dataset(dataset)
|
|
652
|
+
base = f"{oss_prefix}/{l1}/{l2}"
|
|
653
|
+
key = (f"{base}/{split}/{instance_id}.json"
|
|
654
|
+
if split and split != "default"
|
|
655
|
+
else f"{base}/{instance_id}.json")
|
|
535
656
|
|
|
536
657
|
# 存在性判定用 LIST(list_only=True 仅需 ListObjects)而非 HEAD——
|
|
537
658
|
# list-only STS 无 oss:GetObject,object_exists 的 HEAD 会在 STS 签发或
|
|
@@ -551,11 +672,19 @@ def build_synthetic_instance(
|
|
|
551
672
|
split=split or "default",
|
|
552
673
|
registered=False,
|
|
553
674
|
)
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
675
|
+
if include_files:
|
|
676
|
+
try:
|
|
677
|
+
inst.manifest = _instance_manifest(
|
|
678
|
+
content_store, dataset, version, instance_id, split, key,
|
|
679
|
+
oss_prefix=oss_prefix)
|
|
680
|
+
except Exception: # noqa: BLE001
|
|
681
|
+
pass # best-effort:manifest 枚举失败不阻断实例返回
|
|
682
|
+
if read_content:
|
|
683
|
+
data = _read_json_object(content_store, key)
|
|
684
|
+
if data:
|
|
685
|
+
inst.tags = data.get("tags", {})
|
|
686
|
+
inst.difficulty = data.get("difficulty")
|
|
687
|
+
inst.docker_image = data.get("docker_image")
|
|
688
|
+
inst.extend_meta = data.get("extend_meta", {})
|
|
560
689
|
|
|
561
690
|
return inst
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
instance_repo/__init__.py,sha256=
|
|
2
|
-
instance_repo/_routing.py,sha256=
|
|
1
|
+
instance_repo/__init__.py,sha256=lAyyofR-xkJ6HHVt7KUbO5Ar3_hFdKTJxyAK13u7hnE,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=
|
|
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=
|
|
26
|
+
instance_repo/clients/datasets.py,sha256=snSqr4tHVzOCj4HnQlGHoKmqtz1HrxEoMNoja4yrSuI,24497
|
|
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=
|
|
32
|
-
instance_repo/clients/versions.py,sha256=
|
|
31
|
+
instance_repo/clients/splits.py,sha256=o8ygCVAMrxECNojzfUdaHQtuwL4Imzm7ghNKWPxG-VE,28255
|
|
32
|
+
instance_repo/clients/versions.py,sha256=IOzGckKjQGTWC4w1Zqg12hN6bdzX2gClOZdgNCHdeyA,53639
|
|
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=
|
|
38
|
+
instance_repo/store/oss_discovery.py,sha256=gFZmHkpl1136hsXGL2CEBcHEbnpbTz3kAt2OZA71U-E,25894
|
|
39
39
|
instance_repo/store/registry.py,sha256=1R2GqXsuDZ8wc3IGpncF9yL6IHk-FeiwUlZN7v2ndSc,10837
|
|
40
|
-
instance_repo-1.3.2.
|
|
41
|
-
instance_repo-1.3.2.
|
|
42
|
-
instance_repo-1.3.2.
|
|
43
|
-
instance_repo-1.3.2.
|
|
44
|
-
instance_repo-1.3.2.
|
|
40
|
+
instance_repo-1.3.2.dev4.dist-info/METADATA,sha256=CY7IFFYrk54y2DSipSc5lSGMsFgGQfZiHfdh2ykx518,14227
|
|
41
|
+
instance_repo-1.3.2.dev4.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
42
|
+
instance_repo-1.3.2.dev4.dist-info/entry_points.txt,sha256=tn6jgwYChSx7eAqw9YNed_JFYYgoRtE7WkFn8xqa8Rw,53
|
|
43
|
+
instance_repo-1.3.2.dev4.dist-info/top_level.txt,sha256=mblK0Bkwwp6jCZioS6q3sSuFFs8NR9DKTJ0DJpIRDmY,14
|
|
44
|
+
instance_repo-1.3.2.dev4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|