Files
ely-eye/bench/ingest_baseline.py
ZacharyZhang-NY 9cb46c18d3 Keep every file inside the rules the repo already wrote down
The 500-line rule and the one-line-comment rule were enforced only from
memory, so they drifted. bench/filesize_check.py now checks the budget on
every tracked source file, and it passes.

Split, with behaviour proven unchanged by comparing each moved function body
against HEAD byte for byte (53 training bodies, 21 benchmark bodies, 0
differences):

  db.py        503 -> 396   store_search.py     105
  bench/run.py 609 -> 351   query_set.py         80, report.py 211
  benchmarks.py 619 -> 424  benchmark_scoring.py 262
  training.py 1630 ->  94   training_{common,encode,metrics,datasets,lora,retrieval}

TrainingService stays as a thin facade for the CLI; the facade's parameter
lists are pinned against the module functions by inspect, which is what
catches a leaked self.

Deleted the eight top-level functions no file called, after fixing the scan
that had reported 201 false positives by skipping the defining file. The
v1/v2/v3 sweep found nothing to delete and says so: every remaining version
tag is an on-disk format marker, a migration step, a fixture name, or a
third-party model name.

Collapsed 39 multi-line comment blocks (86 lines) to one line each, keeping
the measured facts and error strings that justify the code.

269 tests pass; ruff clean; mypy 24 errors in 9 files against a 24-in-6
baseline, net zero; pyright 4, the same four HEAD measures.
2026-10-08 08:20:21 -04:00

421 lines
18 KiB
Python

"""Ingest the frozen benchmark sets through the unmodified pre-P0 IngestionService.
The baseline number must come from the code as it was before any P0 change, so this
script never edits the product: it drives IngestionService over every file the corpus
index names, one sharded SQLite per worker, then merges the shards into one database.
Shards are content-addressed on disk, so merging is a plain row copy.
Run: python -m bench.ingest_baseline [--workers 16] [--modality pdf]
"""
from __future__ import annotations
import argparse
import gc
import json
import os
import resource
import shutil
import sqlite3
import sys
import time
from collections.abc import Sequence
from concurrent.futures import ProcessPoolExecutor
from concurrent.futures.process import BrokenProcessPool
from pathlib import Path
from ely_eye.storage import ( # the product's own source-id rule
safe_source_id,
sha256_bytes,
)
from bench.sets_common import MODALITIES, SETS, read_jsonl, record_path
REPO = Path(__file__).resolve().parent.parent
RUNS = REPO / "bench" / "runs" / "baseline"
SHARDS = RUNS / "shards"
TABLES = ("sources", "atoms", "atom_fts", "cartridges", "cache_events", "evidence_edges")
# ONNX Runtime and torch reserve far more address space than they touch, so a 3 GiB ceiling killed OCR on large images ("Unable to allocate 582. MiB for an array with shape (5632, 9024, 3)") while resident memory stayed near 1.5 GiB.
WORKER_MEMORY_BYTES = 6 * 1024**3
MAX_POOL_ATTEMPTS = 5
DELETE_STATEMENTS = (
'DELETE FROM "evidence_edges"',
'DELETE FROM "cache_events"',
'DELETE FROM "atom_fts"',
'DELETE FROM "cartridges"',
'DELETE FROM "atoms"',
'DELETE FROM "sources"',
)
COPY_STATEMENTS = (
'INSERT OR IGNORE INTO "sources" SELECT * FROM shard."sources"',
'INSERT OR IGNORE INTO "atoms" SELECT * FROM shard."atoms"',
(
'INSERT OR IGNORE INTO "atom_fts" (atom_id, source, text) '
'SELECT DISTINCT atom_id, source, text FROM shard."atom_fts"'
),
'INSERT OR IGNORE INTO "cartridges" SELECT * FROM shard."cartridges"',
'INSERT OR IGNORE INTO "cache_events" SELECT * FROM shard."cache_events"',
'INSERT OR IGNORE INTO "evidence_edges" SELECT * FROM shard."evidence_edges"',
)
COUNT_STATEMENTS = {
"sources": 'SELECT count(*) FROM "sources"',
"atoms": 'SELECT count(*) FROM "atoms"',
"atom_fts": 'SELECT count(*) FROM "atom_fts"',
"cartridges": 'SELECT count(*) FROM "cartridges"',
"cache_events": 'SELECT count(*) FROM "cache_events"',
"evidence_edges": 'SELECT count(*) FROM "evidence_edges"',
}
def corpus_paths(modality: str) -> list[Path]:
"""Absolute path of every file one modality's corpus index names."""
records = list(read_jsonl(SETS / "corpus" / f"{modality}.jsonl"))
paths: list[Path] = []
seen: dict[Path, str] = {}
for record in records:
path = record_path(record).resolve()
if not path.is_file():
raise SystemExit(f"{modality}: corpus record {record['source_id']} names a missing file {path}")
previous = seen.get(path)
if previous is not None and previous != record["source_id"]:
raise SystemExit(f"{modality}: {path} is claimed by both {previous} and {record['source_id']}")
seen[path] = record["source_id"]
paths.append(path)
return paths
def source_map() -> dict[str, str]:
"""Map an absolute file path to the benchmark source id that owns it."""
mapping: dict[str, str] = {}
for modality in MODALITIES:
for record in read_jsonl(SETS / "corpus" / f"{modality}.jsonl"):
path = record_path(record).resolve()
mapping[str(path)] = record["source_id"]
return mapping
def shard_plan(modalities: Sequence[str], workers: int, limit: int | None) -> list[list[Path]]:
"""Round-robin files across workers so long videos do not pile up in one shard."""
plan: list[list[Path]] = [[] for _ in range(workers)]
index = 0
for modality in modalities:
paths = corpus_paths(modality)
for path in paths[:limit] if limit else paths:
plan[index % workers].append(path)
index += 1
return [shard for shard in plan if shard]
def _pin_inference_threads() -> None:
"""Pin every ONNX Runtime session to one thread; the model and its output are unchanged.
rapidocr-onnxruntime 1.2.3 exposes no thread knob and ONNX Runtime defaults to every
core, so each of 16 workers opened ~90 inference threads and the machine thrashed at a
load average above 400. Wrapping InferenceSession is the only lever the library gives.
"""
from typing import Any
import onnxruntime as ort
original = ort.InferenceSession
def pinned(model_path: str | bytes | os.PathLike[str], *args: Any, **kwargs: Any) -> ort.InferenceSession:
options = ort.SessionOptions()
options.intra_op_num_threads = 1
options.inter_op_num_threads = 1
# The CPU arena keeps freed blocks mapped, so a worker OCRing thousands of pages grows until it hits its ceiling on an 11 MiB array.
options.enable_cpu_mem_arena = False
kwargs["sess_options"] = options
return original(model_path, *args, **kwargs)
ort.InferenceSession = pinned # type: ignore[method-assign]
def _single_threaded() -> None:
"""One CPU thread per worker: 16 workers on 32 cores, no oversubscription.
OpenCV also defaults to every core, which is why cv2 is pinned alongside ONNX.
"""
for variable in ("OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "MKL_NUM_THREADS", "NUMEXPR_NUM_THREADS"):
os.environ[variable] = "1"
import cv2
cv2.setNumThreads(1)
_pin_inference_threads()
_cap_worker_memory()
def _cap_worker_memory() -> None:
"""Cap each worker at 3 GiB so one pathological file raises MemoryError instead of being OOM-killed.
A 16-worker run was killed by the kernel (anon-rss 10.0 GB) and took the whole process pool
down with it; a per-worker ceiling turns that into a recorded per-file failure.
"""
resource.setrlimit(resource.RLIMIT_AS, (WORKER_MEMORY_BYTES, WORKER_MEMORY_BYTES))
def ingest_shard(job: tuple[int, list[Path]]) -> dict:
"""Worker: ingest this shard's files into its own ELY_EYE_HOME, skipping finished ones."""
index, paths = job
_single_threaded()
started = time.monotonic()
home = SHARDS / f"shard-{index:02d}"
home.mkdir(parents=True, exist_ok=True)
ledger = home / "done.jsonl"
done = set()
if ledger.is_file():
for number, line in enumerate(ledger.read_text(encoding="utf-8").splitlines(), start=1):
if not line.strip():
continue
try:
done.add(json.loads(line)["path"])
except (json.JSONDecodeError, KeyError) as error:
raise SystemExit(f"{ledger}:{number}: unreadable resume ledger: {error}") from error
from ely_eye.config import Settings
from ely_eye.ingestion import IngestionService
service = IngestionService(settings=Settings(home=home))
ingested = 0
atoms = 0
failed = 0
for path in paths:
if str(path) in done:
continue
try:
response = service.ingest_path(path)
outcome = {"path": str(path), "sources": response.source_count, "atoms": response.atom_count}
atoms += response.atom_count
ingested += 1
except Exception as error: # noqa: BLE001 - a corpus sweep must record the file that broke it, not die with it
outcome = {"path": str(path), "error": f"{type(error).__name__}: {error}"}
failed += 1
print(f"shard-{index:02d} FAILED {path.name}: {outcome['error']}", file=sys.stderr, flush=True)
with ledger.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(outcome) + "\n")
if ingested % 25 == 0:
# ONNX Runtime keeps the address space it touched, so one shard of thousands of files has to hand the arena back.
gc.collect()
elapsed = time.monotonic() - started
print(f"shard-{index:02d} {ingested}/{len(paths)} files in {elapsed / 60:.1f} min", flush=True)
return {
"shard": index,
"files": len(paths),
"ingested_now": ingested,
"failed": failed,
"skipped": len(paths) - ingested - failed,
"atoms": atoms,
"minutes": round((time.monotonic() - started) / 60, 2),
}
def database_path(home: Path) -> Path:
from ely_eye.config import Settings
return Settings(home=home).db_path
def ledger_outcomes() -> tuple[set[str], set[str]]:
"""Return (ingested paths, failed paths) recorded across every shard ledger."""
ingested: set[str] = set()
failed: set[str] = set()
for ledger in sorted(SHARDS.glob("shard-*/done.jsonl")):
for number, line in enumerate(ledger.read_text(encoding="utf-8").splitlines(), start=1):
if not line.strip():
continue
try:
entry = json.loads(line)
path = entry["path"]
except (json.JSONDecodeError, KeyError) as error:
raise SystemExit(f"{ledger}:{number}: unreadable resume ledger: {error}") from error
(failed if "error" in entry else ingested).add(path)
return ingested, failed
def retry_failed() -> int:
"""Drop failed ledger entries so those files are attempted again, archiving what was dropped."""
archive = RUNS / "failures.jsonl"
dropped = 0
for ledger in sorted(SHARDS.glob("shard-*/done.jsonl")):
kept: list[str] = []
removed: list[dict] = []
for number, line in enumerate(ledger.read_text(encoding="utf-8").splitlines(), start=1):
if not line.strip():
continue
try:
entry = json.loads(line)
except json.JSONDecodeError as error:
raise SystemExit(f"{ledger}:{number}: unreadable resume ledger: {error}") from error
if "error" in entry:
removed.append(entry)
else:
kept.append(line)
if not removed:
continue
with archive.open("a", encoding="utf-8") as handle:
for entry in removed:
handle.write(json.dumps({"shard": ledger.parent.name, **entry}) + "\n")
ledger.write_text("\n".join(kept) + ("\n" if kept else ""), encoding="utf-8")
dropped += len(removed)
return dropped
def merge() -> dict:
"""Copy every shard's rows and content-addressed objects into the merged database."""
main_db = database_path(RUNS)
main_db.parent.mkdir(parents=True, exist_ok=True)
from ely_eye.config import Settings
from ely_eye.db import Database
Database(Settings(home=RUNS)).init()
connection = sqlite3.connect(main_db)
tables = [
row[0]
for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
]
missing = sorted(set(TABLES) - set(tables))
if missing:
raise SystemExit(f"{main_db}: schema is missing tables {missing}")
for statement in DELETE_STATEMENTS:
connection.execute(statement)
shard_dirs = sorted(path for path in SHARDS.glob("shard-*") if path.is_dir())
if not shard_dirs:
raise SystemExit(f"{SHARDS}: no shards to merge")
copied_objects = 0
for shard in shard_dirs:
shard_db = database_path(shard)
if not shard_db.is_file():
raise SystemExit(f"{shard}: no database at {shard_db}")
connection.execute("ATTACH DATABASE ? AS shard", (str(shard_db),))
for statement in COPY_STATEMENTS:
cursor = connection.cursor()
cursor.execute(statement)
cursor.close()
connection.commit()
connection.execute("DETACH DATABASE shard")
shard_objects = shard / "objects"
if shard_objects.is_dir():
for source in shard_objects.rglob("*"):
if not source.is_file():
continue
target = RUNS / "objects" / source.relative_to(shard_objects)
target.parent.mkdir(parents=True, exist_ok=True)
if not target.exists():
shutil.copy2(source, target)
copied_objects += 1
connection.commit()
# A file processed twice lands in two shards: atoms dedupe on atom_id but fts5 has no key and kept every copy (288,266 rows for 165,973 atoms).
connection.execute("DELETE FROM atom_fts")
connection.execute(
"INSERT INTO atom_fts (atom_id, source, text) SELECT atom_id, source, text FROM atoms"
)
connection.commit()
counts = {}
for table, statement in COUNT_STATEMENTS.items():
counts[table] = connection.execute(statement).fetchone()[0]
connection.close()
mapping = source_map()
done, failed = ledger_outcomes()
unknown = sorted((done | failed) - set(mapping))
if unknown:
raise SystemExit(f"{main_db}: {len(unknown)} ledger entries are not corpus files, first {unknown[:3]}")
rows = sqlite3.connect(main_db).execute("SELECT path, source_id FROM sources").fetchall()
unmapped = [path for path, _ in rows if path not in mapping]
if unmapped:
raise SystemExit(f"{main_db}: {len(unmapped)} sources are not named by the corpus index, first {unmapped[:3]}")
product_paths = {path for path, _ in rows}
product_ids = {source_id for _, source_id in rows}
# The product names a source by content hash, so byte-identical files collapse into one source row (500 duplicate LongMemEval sessions).
collapsed_input = sorted(path for path in done - product_paths if safe_source_id(Path(path), sha256_bytes(Path(path).read_bytes())) in product_ids)
unaccounted = sorted(done - product_paths - set(collapsed_input))
if unaccounted:
raise SystemExit(
f"{main_db}: {len(unaccounted)} ingested files produced no product source, first {unaccounted[:3]}"
)
collapsed: dict[str, list[str]] = {}
for path, source_id in rows:
collapsed.setdefault(source_id, []).append(mapping[path])
covered = {source_id for ids in collapsed.values() for source_id in ids}
total = set(mapping.values())
counts["corpus_files"] = len(mapping)
counts["files_ingested"] = len(done)
counts["files_failed"] = len(failed & set(mapping))
counts["files_not_ingested"] = len(set(mapping) - done - failed)
counts["benchmark_sources_total"] = len(total)
counts["benchmark_sources_covered"] = len(covered)
counts["product_sources"] = len(collapsed)
counts["duplicate_content_files"] = len(collapsed_input)
counts["shared_source_ids"] = sum(1 for ids in collapsed.values() if len(ids) > 1)
if counts["atom_fts"] != counts["atoms"]:
raise SystemExit(
f"{main_db}: atom_fts holds {counts['atom_fts']} rows for {counts['atoms']} atoms"
)
counts["uncovered_sources"] = len(total - covered)
if counts["files_not_ingested"] == 0 and covered != total:
# Two honest reasons: byte-identical inputs collapse into one content-hashed source, and files the product cannot ingest at all.
explained = counts["duplicate_content_files"] + counts["files_failed"]
if counts["uncovered_sources"] > explained:
raise SystemExit(
f"{main_db}: {counts['uncovered_sources']} benchmark sources have no product source but only "
f"{explained} are explained by {counts['duplicate_content_files']} duplicate-content files and "
f"{counts['files_failed']} failures"
)
counts["objects_copied"] = copied_objects
(RUNS / "path_to_source_id.json").write_text(json.dumps(mapping, indent=1, sort_keys=True), encoding="utf-8")
(RUNS / "ingest_report.json").write_text(json.dumps(counts, indent=1, sort_keys=True), encoding="utf-8")
return counts
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--workers", type=int, default=8)
parser.add_argument("--modality", action="append", choices=list(MODALITIES))
parser.add_argument("--limit", type=int, help="ingest at most this many files per modality (smoke runs)")
parser.add_argument("--merge-only", action="store_true")
parser.add_argument("--retry-failed", action="store_true", help="re-attempt files a previous pass recorded as failed")
args = parser.parse_args()
if args.retry_failed:
counts = merge() if args.merge_only else None
dropped = retry_failed()
print(f"== re-queued {dropped} failed files; their errors are archived in {RUNS / 'failures.jsonl'}")
if counts is not None:
print(json.dumps(counts, indent=1, sort_keys=True))
return 0
modalities = args.modality or list(MODALITIES)
if not args.merge_only:
plan = shard_plan(modalities, args.workers, args.limit)
print(f"== ingesting {sum(len(shard) for shard in plan)} files across {len(plan)} shards", flush=True)
for attempt in range(1, MAX_POOL_ATTEMPTS + 1):
try:
with ProcessPoolExecutor(max_workers=len(plan)) as pool:
for result in pool.map(ingest_shard, [(index, shard) for index, shard in enumerate(plan)]):
print(
f"shard {result['shard']:02d}: {result['ingested_now']} ingested, "
f"{result['failed']} failed, {result['skipped']} already done, {result['atoms']} atoms",
flush=True,
)
break
except BrokenProcessPool as error:
print(
f"== worker pool died (attempt {attempt}/{MAX_POOL_ATTEMPTS}): {error}; resuming from ledgers",
file=sys.stderr,
flush=True,
)
if attempt == MAX_POOL_ATTEMPTS:
raise SystemExit(f"worker pool died {MAX_POOL_ATTEMPTS} times; ledgers keep progress") from error
counts = merge()
print(json.dumps(counts, indent=1, sort_keys=True))
print(f"== merged baseline database at {database_path(RUNS)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())