Introduce deterministic practice-memory infrastructure that adapts sealed scout output into compact, retention-gated ExperienceRecords with family, hazard, and promotion-candidate summaries. No serving, corpus, pack, or report.json mutation — measurement-only adapter for future sprint reuse.
109 lines
No EOL
3.2 KiB
Python
109 lines
No EOL
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""GSM8K bounded experience flywheel — deterministic practice memory builder.
|
|
|
|
Reads sealed scout evidence and emits compact experience artifacts. Never
|
|
mutates serving, report.json, packs, teaching corpus, or sealed practice lanes
|
|
unless an explicit --out path is provided by the operator.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
_REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
if str(_REPO_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(_REPO_ROOT))
|
|
|
|
from evals.gsm8k_math.train_sample.v1.experience import (
|
|
build_experience_report,
|
|
load_compacted_from_report,
|
|
write_experience_json,
|
|
write_experience_jsonl,
|
|
)
|
|
from evals.gsm8k_math.train_sample.v1.runner import _CASES_PATH, _load_cases
|
|
from evals.gsm8k_math.train_sample.v1.scout import build_scout_summary
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(
|
|
description="Build bounded GSM8K experience flywheel artifact from scout"
|
|
)
|
|
parser.add_argument(
|
|
"--cases",
|
|
type=Path,
|
|
default=_CASES_PATH,
|
|
help="Path to cases.jsonl (default: train_sample)",
|
|
)
|
|
parser.add_argument(
|
|
"--limit",
|
|
type=int,
|
|
default=None,
|
|
help="Score only the first N cases (sorted by case_id)",
|
|
)
|
|
parser.add_argument(
|
|
"--prior",
|
|
type=Path,
|
|
default=None,
|
|
help="Optional prior experience report JSON for cross-run compaction",
|
|
)
|
|
parser.add_argument(
|
|
"--out",
|
|
type=Path,
|
|
default=None,
|
|
help="Optional JSON output path (never writes repo artifacts by default)",
|
|
)
|
|
parser.add_argument(
|
|
"--jsonl-out",
|
|
type=Path,
|
|
default=None,
|
|
help="Optional JSONL output path for compacted case records",
|
|
)
|
|
parser.add_argument(
|
|
"--include-raw",
|
|
action="store_true",
|
|
help="Include pre-compaction raw records in JSON output",
|
|
)
|
|
args = parser.parse_args(argv)
|
|
|
|
if not args.cases.exists():
|
|
print(f"ERROR: cases file not found: {args.cases}", file=sys.stderr)
|
|
return 1
|
|
|
|
cases = _load_cases(args.cases)
|
|
if args.limit is not None:
|
|
cases = sorted(cases, key=lambda c: c["case_id"])[: args.limit]
|
|
|
|
scout_summary = build_scout_summary(
|
|
cases,
|
|
cases_source=str(args.cases),
|
|
include_rows=True,
|
|
)
|
|
|
|
prior_compacted = None
|
|
if args.prior is not None:
|
|
if not args.prior.exists():
|
|
print(f"ERROR: prior report not found: {args.prior}", file=sys.stderr)
|
|
return 1
|
|
prior_payload = json.loads(args.prior.read_text(encoding="utf-8"))
|
|
prior_compacted = load_compacted_from_report(prior_payload)
|
|
|
|
report = build_experience_report(
|
|
scout_summary,
|
|
cases=cases,
|
|
prior_compacted=prior_compacted,
|
|
include_raw_records=args.include_raw,
|
|
)
|
|
print(json.dumps(report, indent=2, sort_keys=True))
|
|
|
|
if args.out is not None:
|
|
write_experience_json(report, args.out)
|
|
if args.jsonl_out is not None:
|
|
write_experience_jsonl(report, args.jsonl_out)
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main()) |