seam-runtime 1.3.0__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.
- seam.py +152 -0
- seam_runtime/__init__.py +29 -0
- seam_runtime/agent_memory.py +145 -0
- seam_runtime/benchmark_baseline_policy.py +116 -0
- seam_runtime/benchmark_integrity.py +462 -0
- seam_runtime/benchmarks.py +2346 -0
- seam_runtime/bm25.py +69 -0
- seam_runtime/cli.py +2168 -0
- seam_runtime/context_views.py +195 -0
- seam_runtime/dashboard.py +3157 -0
- seam_runtime/doctor.py +166 -0
- seam_runtime/dsl.py +190 -0
- seam_runtime/evals.py +191 -0
- seam_runtime/external_memory_benchmarks.py +210 -0
- seam_runtime/holographic.py +566 -0
- seam_runtime/improvement.py +114 -0
- seam_runtime/installer.py +386 -0
- seam_runtime/lossless.py +975 -0
- seam_runtime/lx1.py +311 -0
- seam_runtime/mcp.py +454 -0
- seam_runtime/mcp_protocol.py +288 -0
- seam_runtime/mirl.py +376 -0
- seam_runtime/models.py +213 -0
- seam_runtime/nl.py +322 -0
- seam_runtime/nl_extract.py +205 -0
- seam_runtime/pack.py +300 -0
- seam_runtime/pgvector_bootstrap.py +230 -0
- seam_runtime/pool.py +169 -0
- seam_runtime/reconcile.py +43 -0
- seam_runtime/retrieval.py +414 -0
- seam_runtime/retrieval_orchestrator/README.md +41 -0
- seam_runtime/retrieval_orchestrator/__init__.py +33 -0
- seam_runtime/retrieval_orchestrator/adapters.py +444 -0
- seam_runtime/retrieval_orchestrator/merger.py +22 -0
- seam_runtime/retrieval_orchestrator/orchestrator.py +119 -0
- seam_runtime/retrieval_orchestrator/planner.py +87 -0
- seam_runtime/retrieval_orchestrator/types.py +176 -0
- seam_runtime/retry.py +175 -0
- seam_runtime/runtime.py +396 -0
- seam_runtime/self_improve.py +415 -0
- seam_runtime/server.py +1013 -0
- seam_runtime/skills/__init__.py +26 -0
- seam_runtime/skills/factory.py +180 -0
- seam_runtime/skills/skill_ir.py +104 -0
- seam_runtime/storage.py +1621 -0
- seam_runtime/surface_adapters.py +152 -0
- seam_runtime/symbols.py +249 -0
- seam_runtime/temporal.py +127 -0
- seam_runtime/tokenization.py +33 -0
- seam_runtime/transpile.py +21 -0
- seam_runtime/ui/__init__.py +12 -0
- seam_runtime/ui/animations.py +483 -0
- seam_runtime/ui/bars.py +244 -0
- seam_runtime/ui/logo.py +409 -0
- seam_runtime/ui/theme.py +192 -0
- seam_runtime/vector.py +166 -0
- seam_runtime/vector_adapters.py +248 -0
- seam_runtime/verify.py +144 -0
- seam_runtime/webui/branding/seam-glitch.png +0 -0
- seam_runtime/webui/branding/seam-mark-retro.svg +25 -0
- seam_runtime/webui/branding/seam-mark-terminal.svg +10 -0
- seam_runtime/webui/dashboard.html +6504 -0
- seam_runtime/webui/favicon.svg +1 -0
- seam_runtime/webui/icons.svg +24 -0
- seam_runtime/webui/seam-api.js +317 -0
- seam_runtime/webui/tweaks-panel.jsx +426 -0
- seam_runtime-1.3.0.dist-info/METADATA +637 -0
- seam_runtime-1.3.0.dist-info/RECORD +73 -0
- seam_runtime-1.3.0.dist-info/WHEEL +5 -0
- seam_runtime-1.3.0.dist-info/entry_points.txt +6 -0
- seam_runtime-1.3.0.dist-info/licenses/LICENSE +201 -0
- seam_runtime-1.3.0.dist-info/licenses/NOTICE +13 -0
- seam_runtime-1.3.0.dist-info/top_level.txt +2 -0
seam.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from seam_runtime.cli import run_cli
|
|
7
|
+
from seam_runtime.dsl import compile_dsl
|
|
8
|
+
from seam_runtime.lossless import (
|
|
9
|
+
LosslessArtifact,
|
|
10
|
+
LosslessBenchmarkResult,
|
|
11
|
+
ReadableCompressionArtifact,
|
|
12
|
+
ReadableQueryResult,
|
|
13
|
+
benchmark_text_lossless,
|
|
14
|
+
compress_text_lossless,
|
|
15
|
+
compress_text_readable,
|
|
16
|
+
decompress_text_lossless,
|
|
17
|
+
decompress_text_readable,
|
|
18
|
+
query_readable_compressed,
|
|
19
|
+
)
|
|
20
|
+
from seam_runtime.holographic import (
|
|
21
|
+
HolographicReader,
|
|
22
|
+
SurfaceArtifact,
|
|
23
|
+
SurfacePayload,
|
|
24
|
+
SurfaceQueryResult,
|
|
25
|
+
SurfaceVerification,
|
|
26
|
+
context_surface,
|
|
27
|
+
decode_surface,
|
|
28
|
+
encode_surface,
|
|
29
|
+
query_surface,
|
|
30
|
+
verify_surface,
|
|
31
|
+
)
|
|
32
|
+
from seam_runtime.mirl import IRBatch, MIRLRecord, Pack, RecordKind
|
|
33
|
+
from seam_runtime.models import HashEmbeddingModel, OpenAICompatibleEmbeddingModel
|
|
34
|
+
from seam_runtime.nl import compile_nl
|
|
35
|
+
from seam_runtime.pack import pack_records, unpack_exact_pack
|
|
36
|
+
from seam_runtime.runtime import SeamRuntime
|
|
37
|
+
from seam_runtime.verify import verify_ir
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def pack_ir(records, lens: str = "general", budget: int = 512, mode: str = "context") -> Pack:
|
|
41
|
+
if isinstance(records, IRBatch):
|
|
42
|
+
batch = records
|
|
43
|
+
else:
|
|
44
|
+
batch = IRBatch(list(records))
|
|
45
|
+
return pack_records(batch.records, lens=lens, budget=budget, mode=mode)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def decompile_ir(records, mode: str = "expanded") -> str:
|
|
49
|
+
if isinstance(records, IRBatch):
|
|
50
|
+
batch = records
|
|
51
|
+
else:
|
|
52
|
+
batch = IRBatch(list(records))
|
|
53
|
+
states = [record for record in batch.records if record.kind == RecordKind.STA]
|
|
54
|
+
claims = [record for record in batch.records if record.kind == RecordKind.CLM]
|
|
55
|
+
if states:
|
|
56
|
+
summary = "; ".join(f"{key}={value}" for key, value in states[0].attrs.get("fields", {}).items())
|
|
57
|
+
elif claims:
|
|
58
|
+
summary = "; ".join(f"{record.attrs.get('subject')} {record.attrs.get('predicate')} {record.attrs.get('object')}" for record in claims)
|
|
59
|
+
else:
|
|
60
|
+
summary = "No MIRL records available."
|
|
61
|
+
return summary if mode == "minimal" else f"MIRL summary: {summary}"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def render_ir(records) -> str:
|
|
65
|
+
if isinstance(records, IRBatch):
|
|
66
|
+
return records.to_text()
|
|
67
|
+
return IRBatch(list(records)).to_text()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def load_ir_lines(text: str) -> list[MIRLRecord]:
|
|
71
|
+
return IRBatch.from_text(text).records
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def unpack_pack(pack: Pack | str):
|
|
75
|
+
if isinstance(pack, Pack):
|
|
76
|
+
if pack.mode == "exact":
|
|
77
|
+
return unpack_exact_pack(pack).to_json()
|
|
78
|
+
return pack.payload
|
|
79
|
+
raise TypeError("unpack_pack now expects a Pack instance")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def lossless_compress(text: str, codec: str = "auto", transform: str = "auto", tokenizer: str = "auto") -> LosslessArtifact:
|
|
83
|
+
return compress_text_lossless(text, codec=codec, transform=transform, tokenizer=tokenizer)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def lossless_decompress(machine_text: str) -> str:
|
|
87
|
+
return decompress_text_lossless(machine_text)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def readable_compress(text: str, source_ref: str = "local://input", granularity: str = "auto", tokenizer: str = "auto") -> ReadableCompressionArtifact:
|
|
91
|
+
return compress_text_readable(text, source_ref=source_ref, granularity=granularity, tokenizer=tokenizer)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def readable_query(machine_text: str, query: str, limit: int = 5) -> ReadableQueryResult:
|
|
95
|
+
return query_readable_compressed(machine_text, query=query, limit=limit)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def readable_decompress(machine_text: str) -> str:
|
|
99
|
+
return decompress_text_readable(machine_text)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def surface_encode(payload: bytes, output_path: str | Path, mode: str = "rgb24", payload_format: str = "auto") -> SurfaceArtifact:
|
|
103
|
+
return encode_surface(payload, Path(output_path), mode=mode, payload_format=payload_format)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def surface_compile(text: str, output_path: str | Path, mode: str = "rgb24", source_ref: str = "local://input") -> SurfaceArtifact:
|
|
107
|
+
batch = compile_nl(text, source_ref=source_ref)
|
|
108
|
+
return encode_surface(batch.to_text().encode("utf-8"), Path(output_path), mode=mode, payload_format="MIRL", source_ref=source_ref)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def surface_decode(path: str | Path) -> SurfacePayload:
|
|
112
|
+
return decode_surface(Path(path))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def surface_verify(path: str | Path) -> SurfaceVerification:
|
|
116
|
+
return verify_surface(Path(path))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def surface_query(path: str | Path, query: str, limit: int = 5) -> SurfaceQueryResult:
|
|
120
|
+
return query_surface(Path(path), query=query, limit=limit)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def lossless_benchmark(
|
|
124
|
+
text: str,
|
|
125
|
+
codec: str = "auto",
|
|
126
|
+
transform: str = "auto",
|
|
127
|
+
min_token_savings: float = 0.30,
|
|
128
|
+
tokenizer: str = "auto",
|
|
129
|
+
) -> LosslessBenchmarkResult:
|
|
130
|
+
return benchmark_text_lossless(text, codec=codec, transform=transform, min_token_savings=min_token_savings, tokenizer=tokenizer)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def main() -> None:
|
|
134
|
+
run_cli()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def benchmark_main() -> None:
|
|
138
|
+
argv = sys.argv[1:]
|
|
139
|
+
if argv and argv[0] not in {"run", "show", "verify", "diff", "gate", "-h", "--help"} and Path(argv[0]).exists():
|
|
140
|
+
run_cli(["lossless-benchmark", *argv])
|
|
141
|
+
return
|
|
142
|
+
if not argv:
|
|
143
|
+
run_cli(["benchmark", "run"])
|
|
144
|
+
return
|
|
145
|
+
if argv[0] in {"run", "show", "verify", "diff", "gate"}:
|
|
146
|
+
run_cli(["benchmark", *argv])
|
|
147
|
+
return
|
|
148
|
+
run_cli(["benchmark", "run", *argv])
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if __name__ == "__main__":
|
|
152
|
+
main()
|
seam_runtime/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .mirl import (
|
|
2
|
+
Artifact,
|
|
3
|
+
IRBatch,
|
|
4
|
+
MIRLRecord,
|
|
5
|
+
Pack,
|
|
6
|
+
PersistReport,
|
|
7
|
+
ReconcileReport,
|
|
8
|
+
RecordKind,
|
|
9
|
+
SearchResult,
|
|
10
|
+
Status,
|
|
11
|
+
TraceGraph,
|
|
12
|
+
VerifyReport,
|
|
13
|
+
)
|
|
14
|
+
from .runtime import SeamRuntime
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Artifact",
|
|
18
|
+
"IRBatch",
|
|
19
|
+
"MIRLRecord",
|
|
20
|
+
"Pack",
|
|
21
|
+
"PersistReport",
|
|
22
|
+
"ReconcileReport",
|
|
23
|
+
"RecordKind",
|
|
24
|
+
"SearchResult",
|
|
25
|
+
"SeamRuntime",
|
|
26
|
+
"Status",
|
|
27
|
+
"TraceGraph",
|
|
28
|
+
"VerifyReport",
|
|
29
|
+
]
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Iterable
|
|
8
|
+
|
|
9
|
+
from .mirl import IRBatch, MIRLRecord, RecordKind
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class IngestReport:
|
|
14
|
+
document: dict[str, object]
|
|
15
|
+
stored_ids: list[str]
|
|
16
|
+
|
|
17
|
+
def to_dict(self) -> dict[str, object]:
|
|
18
|
+
return {"document": self.document, "stored_ids": list(self.stored_ids)}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def source_hash(text: str) -> str:
|
|
22
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def stable_document_id(source_ref: str, text: str) -> str:
|
|
26
|
+
digest = hashlib.sha256(f"{source_ref}\n{source_hash(text)}".encode("utf-8")).hexdigest()[:16]
|
|
27
|
+
return f"doc:{digest}"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def namespace_ingest_batch(batch: IRBatch, document_id: str) -> IRBatch:
|
|
31
|
+
suffix = document_id.split(":", 1)[1]
|
|
32
|
+
id_map = {record.id: _document_record_id(record.id, suffix) for record in batch.records}
|
|
33
|
+
records = []
|
|
34
|
+
for record in batch.records:
|
|
35
|
+
cloned = MIRLRecord.from_dict(record.to_dict())
|
|
36
|
+
cloned.id = id_map[record.id]
|
|
37
|
+
cloned.prov = [id_map.get(item, item) for item in cloned.prov]
|
|
38
|
+
cloned.evidence = [id_map.get(item, item) for item in cloned.evidence]
|
|
39
|
+
cloned.attrs = _rewrite_refs(cloned.attrs, id_map)
|
|
40
|
+
records.append(cloned)
|
|
41
|
+
return IRBatch(records)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def compact_memory_index(records: Iterable[MIRLRecord], query: str, scores: dict[str, float] | None = None) -> dict[str, object]:
|
|
45
|
+
scores = scores or {}
|
|
46
|
+
items = []
|
|
47
|
+
for record in records:
|
|
48
|
+
items.append(
|
|
49
|
+
{
|
|
50
|
+
"id": record.id,
|
|
51
|
+
"kind": record.kind.value,
|
|
52
|
+
"score": round(float(scores.get(record.id, 0.0)), 6),
|
|
53
|
+
"summary": _record_summary(record),
|
|
54
|
+
"refs": sorted(set(record.prov + record.evidence)),
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
return {"query": query, "results": items, "next": "Use `seam memory get <ids>` for full records."}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def full_memory_records(records: Iterable[MIRLRecord]) -> dict[str, object]:
|
|
61
|
+
return {"records": [record.to_dict() for record in records]}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def neighbor_timeline(batch: IRBatch, record_ids: list[str]) -> dict[str, object]:
|
|
65
|
+
by_id = batch.by_id()
|
|
66
|
+
selected = [by_id[record_id] for record_id in record_ids if record_id in by_id]
|
|
67
|
+
neighbors: dict[str, list[str]] = {}
|
|
68
|
+
for record in selected:
|
|
69
|
+
refs = set(record.prov + record.evidence)
|
|
70
|
+
for key in ("src", "dst", "target", "raw_id", "subject"):
|
|
71
|
+
value = record.attrs.get(key)
|
|
72
|
+
if isinstance(value, str):
|
|
73
|
+
refs.add(value)
|
|
74
|
+
obj = record.attrs.get("object")
|
|
75
|
+
if isinstance(obj, str):
|
|
76
|
+
refs.add(obj)
|
|
77
|
+
neighbors[record.id] = sorted(ref for ref in refs if ref in by_id)
|
|
78
|
+
ordered = sorted(selected, key=lambda item: (item.t0 or item.created_at, item.id))
|
|
79
|
+
return {
|
|
80
|
+
"ids": list(record_ids),
|
|
81
|
+
"timeline": [{"id": record.id, "kind": record.kind.value, "updated_at": record.updated_at} for record in ordered],
|
|
82
|
+
"neighbors": neighbors,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def render_memory_index(payload: dict[str, object]) -> str:
|
|
87
|
+
lines = [f"Memory search: {payload.get('query')}"]
|
|
88
|
+
results = payload.get("results", [])
|
|
89
|
+
if not results:
|
|
90
|
+
lines.append("(none)")
|
|
91
|
+
for index, item in enumerate(results, start=1):
|
|
92
|
+
lines.append(f"{index}. {item['id']} [{item['kind']}] score={item['score']:.3f}")
|
|
93
|
+
if item.get("summary"):
|
|
94
|
+
lines.append(f" {item['summary']}")
|
|
95
|
+
refs = item.get("refs") or []
|
|
96
|
+
if refs:
|
|
97
|
+
lines.append(f" refs={', '.join(refs)}")
|
|
98
|
+
lines.append(str(payload.get("next", "")))
|
|
99
|
+
return "\n".join(line for line in lines if line)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def render_memory_records(payload: dict[str, object]) -> str:
|
|
103
|
+
records = payload.get("records", [])
|
|
104
|
+
if not records:
|
|
105
|
+
return "No records found."
|
|
106
|
+
return "\n".join(json.dumps(record, sort_keys=True) for record in records)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def read_path_text(path: str) -> tuple[str, str]:
|
|
110
|
+
if path == "-":
|
|
111
|
+
import sys
|
|
112
|
+
|
|
113
|
+
return sys.stdin.read(), "stdin://seam"
|
|
114
|
+
source = Path(path)
|
|
115
|
+
return source.read_bytes().decode("utf-8"), str(source)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _record_summary(record: MIRLRecord) -> str:
|
|
119
|
+
attrs = record.attrs
|
|
120
|
+
if record.kind == RecordKind.CLM:
|
|
121
|
+
return f"{attrs.get('subject')} {attrs.get('predicate')} {attrs.get('object')}"
|
|
122
|
+
if record.kind == RecordKind.REL:
|
|
123
|
+
return f"{attrs.get('src')} {attrs.get('predicate')} {attrs.get('dst')}"
|
|
124
|
+
if record.kind == RecordKind.STA:
|
|
125
|
+
return f"{attrs.get('target')} {attrs.get('fields')}"
|
|
126
|
+
if record.kind == RecordKind.EVT:
|
|
127
|
+
return f"{attrs.get('actor')} {attrs.get('action')} {attrs.get('object')}"
|
|
128
|
+
return str(attrs)[:180]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _document_record_id(record_id: str, suffix: str) -> str:
|
|
132
|
+
head, sep, tail = record_id.partition(":")
|
|
133
|
+
if not sep:
|
|
134
|
+
return f"{record_id}:{suffix}"
|
|
135
|
+
return f"{head}:{suffix}:{tail}"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _rewrite_refs(value, id_map: dict[str, str]):
|
|
139
|
+
if isinstance(value, str):
|
|
140
|
+
return id_map.get(value, value)
|
|
141
|
+
if isinstance(value, list):
|
|
142
|
+
return [_rewrite_refs(item, id_map) for item in value]
|
|
143
|
+
if isinstance(value, dict):
|
|
144
|
+
return {key: _rewrite_refs(item, id_map) for key, item in value.items()}
|
|
145
|
+
return value
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""CI baseline-source policy for ``seam bench gate``.
|
|
2
|
+
|
|
3
|
+
Picks the most recent benchmark run reachable from the merge-base of HEAD
|
|
4
|
+
and origin/main, excluding any path under ``benchmarks/runs/holdout/``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import subprocess
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
BENCHMARK_RUNS_DIR = Path("benchmarks") / "runs"
|
|
15
|
+
HOLDOUT_PREFIX = str(BENCHMARK_RUNS_DIR / "holdout")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def resolve_baseline(
|
|
19
|
+
repo_root: Path | None = None,
|
|
20
|
+
current_run: Path | None = None,
|
|
21
|
+
) -> Path | None:
|
|
22
|
+
"""Return the best baseline bundle path, or None for a first-run.
|
|
23
|
+
|
|
24
|
+
Policy:
|
|
25
|
+
1. Find the merge-base of HEAD and origin/main.
|
|
26
|
+
2. List all JSON files under ``benchmarks/runs/``, newest first.
|
|
27
|
+
3. Exclude any path under ``benchmarks/runs/holdout/``.
|
|
28
|
+
4. Exclude *current_run* itself when provided.
|
|
29
|
+
5. Return the first run whose git SHA is reachable from the merge-base.
|
|
30
|
+
|
|
31
|
+
Returns None when no baseline exists — the caller treats this as
|
|
32
|
+
"first run" (no regression check, exit 0 with a note).
|
|
33
|
+
"""
|
|
34
|
+
root = repo_root or _git_root()
|
|
35
|
+
if root is None:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
merge_base = _merge_base(root)
|
|
39
|
+
if merge_base is None:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
runs_dir = root / BENCHMARK_RUNS_DIR
|
|
43
|
+
if not runs_dir.is_dir():
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
candidates = sorted(
|
|
47
|
+
runs_dir.glob("**/*.json"),
|
|
48
|
+
key=lambda p: p.stat().st_mtime,
|
|
49
|
+
reverse=True,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
for candidate in candidates:
|
|
53
|
+
if _is_holdout_run(candidate, root):
|
|
54
|
+
continue
|
|
55
|
+
if current_run is not None and candidate.resolve() == current_run.resolve():
|
|
56
|
+
continue
|
|
57
|
+
sha = _bundle_git_sha(candidate)
|
|
58
|
+
if sha is None:
|
|
59
|
+
continue
|
|
60
|
+
if _is_reachable(root, sha, merge_base):
|
|
61
|
+
return candidate
|
|
62
|
+
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _is_holdout_run(path: Path, repo_root: Path) -> bool:
|
|
67
|
+
try:
|
|
68
|
+
relative = path.resolve().relative_to((repo_root / BENCHMARK_RUNS_DIR).resolve())
|
|
69
|
+
except ValueError:
|
|
70
|
+
return False
|
|
71
|
+
return bool(relative.parts) and relative.parts[0] == "holdout"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _git_root() -> Path | None:
|
|
75
|
+
try:
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
78
|
+
capture_output=True, text=True, timeout=10,
|
|
79
|
+
)
|
|
80
|
+
except Exception:
|
|
81
|
+
return None
|
|
82
|
+
return Path(result.stdout.strip()) if result.returncode == 0 else None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _merge_base(repo_root: Path) -> str | None:
|
|
86
|
+
try:
|
|
87
|
+
result = subprocess.run(
|
|
88
|
+
["git", "merge-base", "HEAD", "origin/main"],
|
|
89
|
+
capture_output=True, text=True, timeout=10, cwd=str(repo_root),
|
|
90
|
+
)
|
|
91
|
+
except Exception:
|
|
92
|
+
return None
|
|
93
|
+
return result.stdout.strip() if result.returncode == 0 and result.stdout.strip() else None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _bundle_git_sha(path: Path) -> str | None:
|
|
97
|
+
try:
|
|
98
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
99
|
+
except Exception:
|
|
100
|
+
return None
|
|
101
|
+
if isinstance(payload, dict):
|
|
102
|
+
manifest = payload.get("manifest") or {}
|
|
103
|
+
return manifest.get("git_sha") or None
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _is_reachable(repo_root: Path, sha: str, merge_base: str) -> bool:
|
|
108
|
+
"""True if *sha* is an ancestor of *merge_base* (i.e. reachable from it)."""
|
|
109
|
+
try:
|
|
110
|
+
result = subprocess.run(
|
|
111
|
+
["git", "merge-base", "--is-ancestor", sha, merge_base],
|
|
112
|
+
capture_output=True, timeout=10, cwd=str(repo_root),
|
|
113
|
+
)
|
|
114
|
+
except Exception:
|
|
115
|
+
return False
|
|
116
|
+
return result.returncode == 0
|