Spaces:
Running
Running
Download benchmarks/longmemeval/cli.py from Leon4gr45/brain: direct link, hf CLI and curl.
- Browser
- Download file 15.1 kB
-
https://huggingface.co/spaces/Leon4gr45/brain/resolve/main/benchmarks/longmemeval/cli.py
- Command line
-
hf download hf://spaces/Leon4gr45/brain/benchmarks/longmemeval/cli.py
-
curl -L -o cli.py https://huggingface.co/spaces/Leon4gr45/brain/resolve/main/benchmarks/longmemeval/cli.py
15.1 kB
| from __future__ import annotations | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| from rich.console import Console | |
| from rich.table import Table | |
| from longmemeval.answer import answer_question | |
| from longmemeval.client import BrainAPIClient | |
| from longmemeval.config import VARIANT_FILES, Settings | |
| from longmemeval.dataset import ( | |
| brain_id_for, | |
| dataset_stats, | |
| download_dataset, | |
| get_question, | |
| iter_ingest_units, | |
| load_dataset, | |
| resolve_questions, | |
| resolve_variant_settings, | |
| ) | |
| from longmemeval.evaluate import evaluate_questions | |
| from longmemeval.ingest import ensure_run_dir, ingest_questions, write_manifest | |
| from longmemeval.metrics import selftest_metrics, tokenize | |
| from longmemeval.prompts import ANSWER_SYSTEM | |
| from longmemeval.provenance import build_provenance | |
| from longmemeval.report import print_report_table, write_report | |
| console = Console() | |
| def _settings_for_args(args: argparse.Namespace, settings: Settings) -> Settings: | |
| variant = getattr(args, "variant", None) | |
| return resolve_variant_settings(settings, variant) | |
| def cmd_download(args: argparse.Namespace, settings: Settings) -> int: | |
| settings = _settings_for_args(args, settings) | |
| path = download_dataset( | |
| settings.dataset_path, settings.dataset_url, variant=settings.variant | |
| ) | |
| console.print(f"[green]Dataset ready:[/green] {path} (variant={settings.variant})") | |
| return 0 | |
| def cmd_dataset_stats(args: argparse.Namespace, settings: Settings) -> int: | |
| settings = _settings_for_args(args, settings) | |
| if not settings.dataset_path.exists(): | |
| download_dataset( | |
| settings.dataset_path, settings.dataset_url, variant=settings.variant | |
| ) | |
| stats = dataset_stats(load_dataset(settings.dataset_path, variant=settings.variant)) | |
| table = Table(title=f"LongMemEval ({settings.variant})") | |
| table.add_column("Metric") | |
| table.add_column("Value") | |
| table.add_row("Questions", str(stats["n_questions"])) | |
| table.add_row("Abstention", str(stats["n_abstention"])) | |
| sess = stats["sessions_per_question"] | |
| table.add_row( | |
| "Sessions/question", | |
| f"min={sess['min']} max={sess['max']} mean={sess['mean']:.1f}", | |
| ) | |
| console.print(table) | |
| type_table = Table(title="By question type") | |
| type_table.add_column("Type") | |
| type_table.add_column("N", justify="right") | |
| for qtype, count in (stats.get("by_type") or {}).items(): | |
| type_table.add_row(qtype, str(count)) | |
| console.print(type_table) | |
| assert brain_id_for("e47b4ab3aa_abs") == "lmee47b4ab3aaabs" | |
| console.print("[dim]brain_id_for('e47b4ab3aa_abs') == 'lmee47b4ab3aaabs' OK[/dim]") | |
| return 0 | |
| def cmd_smoke(args: argparse.Namespace, settings: Settings) -> int: | |
| settings = _settings_for_args(args, settings) | |
| if not settings.dataset_path.exists(): | |
| download_dataset( | |
| settings.dataset_path, settings.dataset_url, variant=settings.variant | |
| ) | |
| questions = resolve_questions(settings, limit=max(1, args.limit)) | |
| entry = questions[0] | |
| question_id = str(entry["question_id"]) | |
| brain_id = args.brain or brain_id_for(question_id) | |
| units = iter_ingest_units(entry, limit_sessions=1) | |
| if not units: | |
| raise SystemExit("No haystack sessions to smoke-test") | |
| unit = units[0] | |
| with BrainAPIClient(settings) as client: | |
| submitted = client.ingest_text( | |
| unit["text"], | |
| brain_id, | |
| source_timestamp=unit.get("source_timestamp"), | |
| ) | |
| task_id = (submitted.data or {}).get("task_id") | |
| console.print(f"Queued ingest task_id={task_id} brain={brain_id}") | |
| waited = client.wait_for_task(task_id, brain_id, timeout_s=args.timeout) | |
| console.print(f"Task status: {(waited.data or {}).get('status')}") | |
| question = str(entry.get("question") or "What do you remember?") | |
| ctx = client.retrieve_context(question, brain_id) | |
| text_context = (ctx.data or {}).get("text_context") or "" | |
| console.print(f"text_context ({len(text_context)} chars): {text_context[:400]}") | |
| if not text_context.strip(): | |
| console.print("[yellow]Warning: empty text_context[/yellow]") | |
| return 1 | |
| console.print("[green]Smoke test passed[/green]") | |
| return 0 | |
| def cmd_ingest(args: argparse.Namespace, settings: Settings) -> int: | |
| settings = _settings_for_args(args, settings) | |
| if not settings.dataset_path.exists(): | |
| download_dataset( | |
| settings.dataset_path, settings.dataset_url, variant=settings.variant | |
| ) | |
| question_types = ( | |
| {t.strip() for t in args.question_type.split(",") if t.strip()} | |
| if args.question_type | |
| else None | |
| ) | |
| questions = resolve_questions( | |
| settings, | |
| question_ids=args.question_id or None, | |
| limit=args.limit, | |
| question_types=question_types, | |
| ) | |
| run_id, run_dir = ensure_run_dir(settings, args.run) | |
| write_manifest( | |
| run_dir, | |
| { | |
| "run_id": run_id, | |
| "command": "ingest", | |
| "variant": settings.variant, | |
| "n_questions": len(questions), | |
| "question_ids": [q["question_id"] for q in questions], | |
| "concurrency": args.concurrency, | |
| "limit": args.limit, | |
| "limit_sessions": args.limit_sessions, | |
| "dry_run": args.dry_run, | |
| "brainapi_url": settings.brainapi_url, | |
| **build_provenance(settings), | |
| }, | |
| ) | |
| ingest_questions( | |
| settings, | |
| questions, | |
| run_dir=run_dir, | |
| concurrency=args.concurrency, | |
| limit_sessions=args.limit_sessions, | |
| dry_run=args.dry_run, | |
| resume=not args.no_resume, | |
| task_timeout_s=args.timeout, | |
| ) | |
| console.print(f"[green]Run directory:[/green] {run_dir}") | |
| return 0 | |
| def cmd_answer_once(args: argparse.Namespace, settings: Settings) -> int: | |
| settings = _settings_for_args(args, settings) | |
| entry = get_question( | |
| load_dataset(settings.dataset_path, variant=settings.variant), | |
| args.question_id, | |
| ) | |
| brain_id = brain_id_for(str(entry["question_id"])) | |
| question = args.question or str(entry.get("question") or "") | |
| with BrainAPIClient(settings) as client: | |
| retrieved = client.retrieve_context(question, brain_id) | |
| result = answer_question( | |
| settings, | |
| question, | |
| retrieved.data or {}, | |
| question_date=str(entry.get("question_date") or "") or None, | |
| ) | |
| console.print(result.answer) | |
| console.print( | |
| f"[dim]model={result.model} latency_ms={result.latency_ms:.0f} " | |
| f"tokens={result.total_tokens}[/dim]" | |
| ) | |
| return 0 | |
| def cmd_selftest_metrics(args: argparse.Namespace, settings: Settings) -> int: | |
| errors = selftest_metrics() | |
| if errors: | |
| for err in errors: | |
| console.print(f"[red]FAIL[/red] {err}") | |
| return 1 | |
| console.print("[green]selftest-metrics passed[/green]") | |
| return 0 | |
| def cmd_evaluate(args: argparse.Namespace, settings: Settings) -> int: | |
| settings = _settings_for_args(args, settings) | |
| if not settings.dataset_path.exists(): | |
| download_dataset( | |
| settings.dataset_path, settings.dataset_url, variant=settings.variant | |
| ) | |
| question_types = ( | |
| {t.strip() for t in args.question_type.split(",") if t.strip()} | |
| if args.question_type | |
| else None | |
| ) | |
| questions = resolve_questions( | |
| settings, | |
| question_ids=args.question_id or None, | |
| limit=args.limit, | |
| question_types=question_types, | |
| ) | |
| run_id, run_dir = ensure_run_dir(settings, args.run) | |
| if settings.judge_shares_answer_family: | |
| console.print( | |
| f"[yellow]Warning: judge model ({settings.judge_model}) is the same " | |
| f"family as the answer model ({settings.answer_model}); judge accuracy " | |
| "carries self-preference bias.[/yellow]" | |
| ) | |
| write_manifest( | |
| run_dir, | |
| { | |
| "run_id": run_id, | |
| "command": "evaluate", | |
| "variant": settings.variant, | |
| "n_questions": len(questions), | |
| "question_ids": [q["question_id"] for q in questions], | |
| "concurrency": args.concurrency, | |
| "limit": args.limit, | |
| "brainapi_url": settings.brainapi_url, | |
| **build_provenance(settings), | |
| "historical_limit": args.historical_limit, | |
| "max_passages": args.max_passages, | |
| "max_facts": args.max_facts, | |
| "apply_fact_filter": not args.no_fact_filter, | |
| "use_ppr": args.use_ppr, | |
| "sufficiency_retry": args.sufficiency_retry, | |
| }, | |
| ) | |
| evaluate_questions( | |
| settings, | |
| questions, | |
| run_dir=run_dir, | |
| concurrency=args.concurrency, | |
| resume=not args.no_resume, | |
| historical_limit=args.historical_limit, | |
| max_passages=args.max_passages, | |
| max_facts=args.max_facts, | |
| apply_fact_filter=not args.no_fact_filter, | |
| use_ppr=args.use_ppr, | |
| sufficiency_retry=args.sufficiency_retry, | |
| ) | |
| report = write_report(run_dir) | |
| print_report_table(report) | |
| console.print(f"[green]Report:[/green] {run_dir / 'report.md'}") | |
| return 0 | |
| def cmd_report(args: argparse.Namespace, settings: Settings) -> int: | |
| run_dir = settings.runs_dir / args.run | |
| if not run_dir.exists(): | |
| raise SystemExit(f"Run directory not found: {run_dir}") | |
| report = write_report(run_dir) | |
| print_report_table(report) | |
| console.print(report["run_dir"] + "/report.md") | |
| if args.json: | |
| console.print_json(json.dumps(report)) | |
| return 0 | |
| def cmd_prompt_audit(args: argparse.Namespace, settings: Settings) -> int: | |
| settings = _settings_for_args(args, settings) | |
| if not settings.dataset_path.exists(): | |
| download_dataset( | |
| settings.dataset_path, settings.dataset_url, variant=settings.variant | |
| ) | |
| dataset = load_dataset(settings.dataset_path, variant=settings.variant) | |
| prompt_tokens = tokenize(ANSWER_SYSTEM) | |
| n = args.ngram | |
| prompt_ngrams = { | |
| tuple(prompt_tokens[i : i + n]) | |
| for i in range(0, max(0, len(prompt_tokens) - n + 1)) | |
| } | |
| hits: list[tuple[str, str]] = [] | |
| for entry in dataset: | |
| qid = str(entry.get("question_id")) | |
| gold_tokens = tokenize(str(entry.get("answer") or "")) | |
| for i in range(0, max(0, len(gold_tokens) - n + 1)): | |
| gram = tuple(gold_tokens[i : i + n]) | |
| if gram in prompt_ngrams: | |
| hits.append((qid, " ".join(gram))) | |
| break | |
| if hits: | |
| console.print( | |
| f"[red]FAIL[/red] answer prompt shares a {n}-gram with " | |
| f"{len(hits)} gold answers" | |
| ) | |
| for qid, gram in hits[:20]: | |
| console.print(f" {qid} {gram!r}") | |
| return 1 | |
| console.print( | |
| f"[green]prompt-audit passed[/green] no {n}-gram of the answer prompt " | |
| f"appears in any gold answer ({len(dataset)} questions)" | |
| ) | |
| return 0 | |
| def build_parser() -> argparse.ArgumentParser: | |
| parser = argparse.ArgumentParser( | |
| prog="python -m longmemeval", | |
| description="LongMemEval benchmark harness for BrainAPI", | |
| ) | |
| parser.add_argument( | |
| "--env-file", | |
| type=Path, | |
| default=None, | |
| help="Optional path to .env (defaults to benchmarks/.env)", | |
| ) | |
| sub = parser.add_subparsers(dest="command", required=True) | |
| def add_variant(p: argparse.ArgumentParser) -> None: | |
| p.add_argument( | |
| "--variant", | |
| choices=sorted(VARIANT_FILES), | |
| default=None, | |
| help="Dataset variant (default: s / BENCH_LME_VARIANT)", | |
| ) | |
| p_download = sub.add_parser("download", help="Download LongMemEval JSON") | |
| add_variant(p_download) | |
| p_download.set_defaults(func=cmd_download) | |
| p_stats = sub.add_parser("dataset-stats", help="Print dataset statistics") | |
| add_variant(p_stats) | |
| p_stats.set_defaults(func=cmd_dataset_stats) | |
| p_smoke = sub.add_parser("smoke", help="Smoke-test ingest + retrieve") | |
| add_variant(p_smoke) | |
| p_smoke.add_argument("--brain", default=None) | |
| p_smoke.add_argument("--limit", type=int, default=1) | |
| p_smoke.add_argument("--timeout", type=float, default=600.0) | |
| p_smoke.set_defaults(func=cmd_smoke) | |
| p_ingest = sub.add_parser("ingest", help="Ingest LongMemEval haystacks") | |
| add_variant(p_ingest) | |
| p_ingest.add_argument("--run", default=None, help="Existing or new run id") | |
| p_ingest.add_argument( | |
| "--question-id", | |
| action="append", | |
| help="Question id (repeatable). Default: all questions.", | |
| ) | |
| p_ingest.add_argument("--question-type", default=None, help="Comma-separated types") | |
| p_ingest.add_argument("--limit", type=int, default=None) | |
| p_ingest.add_argument("--limit-sessions", type=int, default=None) | |
| p_ingest.add_argument("--concurrency", type=int, default=2) | |
| p_ingest.add_argument("--dry-run", action="store_true") | |
| p_ingest.add_argument("--no-resume", action="store_true") | |
| p_ingest.add_argument("--timeout", type=float, default=900.0) | |
| p_ingest.set_defaults(func=cmd_ingest) | |
| p_answer = sub.add_parser("answer-once", help="Retrieve + answer one question") | |
| add_variant(p_answer) | |
| p_answer.add_argument("--question-id", required=True) | |
| p_answer.add_argument("--question", default=None) | |
| p_answer.set_defaults(func=cmd_answer_once) | |
| p_self = sub.add_parser("selftest-metrics", help="Run local metrics self-test") | |
| p_self.set_defaults(func=cmd_selftest_metrics) | |
| p_eval = sub.add_parser("evaluate", help="Answer and score LongMemEval QA") | |
| add_variant(p_eval) | |
| p_eval.add_argument("--run", default=None) | |
| p_eval.add_argument("--question-id", action="append") | |
| p_eval.add_argument("--question-type", default=None) | |
| p_eval.add_argument("--limit", type=int, default=None) | |
| p_eval.add_argument("--concurrency", type=int, default=2) | |
| p_eval.add_argument("--no-resume", action="store_true") | |
| p_eval.add_argument("--historical-limit", type=int, default=10) | |
| p_eval.add_argument("--max-passages", type=int, default=8) | |
| p_eval.add_argument("--max-facts", type=int, default=40) | |
| p_eval.add_argument("--no-fact-filter", action="store_true") | |
| p_eval.add_argument("--use-ppr", action="store_true") | |
| p_eval.add_argument("--sufficiency-retry", action="store_true") | |
| p_eval.set_defaults(func=cmd_evaluate) | |
| p_report = sub.add_parser("report", help="Rebuild report from a run directory") | |
| p_report.add_argument("--run", required=True) | |
| p_report.add_argument("--json", action="store_true") | |
| p_report.set_defaults(func=cmd_report) | |
| p_audit = sub.add_parser( | |
| "prompt-audit", | |
| help="Fail if the answer prompt shares an n-gram with any gold answer", | |
| ) | |
| add_variant(p_audit) | |
| p_audit.add_argument("--ngram", type=int, default=3) | |
| p_audit.set_defaults(func=cmd_prompt_audit) | |
| return parser | |
| def main(argv: list[str] | None = None) -> int: | |
| parser = build_parser() | |
| args = parser.parse_args(argv) | |
| settings = Settings.load(args.env_file) | |
| return args.func(args, settings) | |