|
| 1 | +# LongMemEval Benchmark for XMem Python |
| 2 | + |
| 3 | +This harness benchmarks the Python XMem service only. It targets the deployed |
| 4 | +Python API at `https://api.xmem.in` by default and does not run or compare the |
| 5 | +Go implementation. |
| 6 | + |
| 7 | +LongMemEval evaluates long-term conversational memory across multi-session |
| 8 | +recall, temporal reasoning, single-session recall, knowledge updates, and |
| 9 | +preference tracking. The harness follows the same broad structure used by |
| 10 | +open-source memory-layer benchmarks: load dataset records, ingest the haystack |
| 11 | +conversation history into an isolated user namespace, retrieve an answer for |
| 12 | +the benchmark question, write predictions, and compute lightweight local |
| 13 | +metrics for quick iteration. |
| 14 | + |
| 15 | +## Files |
| 16 | + |
| 17 | +- `dataset.py`: Loads JSON/JSONL LongMemEval records and converts sessions to |
| 18 | + XMem conversation-turn ingest payloads. |
| 19 | +- `client.py`: Async HTTP client for the Python XMem API. |
| 20 | +- `runner.py`: Benchmark orchestration, batching, polling, resume support, and |
| 21 | + output writing. |
| 22 | +- `metrics.py`: Local exact-match, contains, and token-F1 metrics plus summary |
| 23 | + aggregation. |
| 24 | +- `run.py`: CLI entrypoint. |
| 25 | + |
| 26 | +## Secrets |
| 27 | + |
| 28 | +Do not commit API keys or provider credentials. |
| 29 | + |
| 30 | +To generate XMem predictions, set an XMem API key: |
| 31 | + |
| 32 | +```bash |
| 33 | +export XMEM_API_KEY="..." |
| 34 | +``` |
| 35 | + |
| 36 | +Use `--api-key-env` if your local environment uses a different variable name. |
| 37 | + |
| 38 | +To score predictions with the official LongMemEval LLM-as-judge evaluator, set |
| 39 | +an OpenAI API key before running the evaluator: |
| 40 | + |
| 41 | +```bash |
| 42 | +export OPENAI_API_KEY="..." |
| 43 | +``` |
| 44 | + |
| 45 | +## Run a Smoke Check |
| 46 | + |
| 47 | +Validate dataset parsing and payload construction without calling the service: |
| 48 | + |
| 49 | +```bash |
| 50 | +python -m benchmarks.longmemeval.run \ |
| 51 | + --download \ |
| 52 | + --dry-run \ |
| 53 | + --limit 2 |
| 54 | +``` |
| 55 | + |
| 56 | +Validate all six official categories without requiring an API key: |
| 57 | + |
| 58 | +```bash |
| 59 | +python -m benchmarks.longmemeval.run_all_categories \ |
| 60 | + --download \ |
| 61 | + --dry-run |
| 62 | +``` |
| 63 | + |
| 64 | +If the dataset is already available locally: |
| 65 | + |
| 66 | +```bash |
| 67 | +python -m benchmarks.longmemeval.run \ |
| 68 | + --dataset-path benchmarks/longmemeval/data/longmemeval_s_cleaned.json \ |
| 69 | + --dry-run \ |
| 70 | + --limit 2 |
| 71 | +``` |
| 72 | + |
| 73 | +## Run Against the Python API |
| 74 | + |
| 75 | +```bash |
| 76 | +export XMEM_API_KEY="..." |
| 77 | + |
| 78 | +python -m benchmarks.longmemeval.run \ |
| 79 | + --download \ |
| 80 | + --api-base-url https://api.xmem.in \ |
| 81 | + --limit 10 \ |
| 82 | + --batch-size 25 \ |
| 83 | + --output-dir benchmarks/longmemeval/results/run-001 |
| 84 | +``` |
| 85 | + |
| 86 | +The runner writes: |
| 87 | + |
| 88 | +- `results.jsonl`: Full per-example benchmark records. |
| 89 | +- `predictions.jsonl`: Official prediction file with only `question_id` and |
| 90 | + `hypothesis`. |
| 91 | +- `summary.json`: Aggregate local metrics and latency. |
| 92 | + |
| 93 | +The local metrics are intended for fast development feedback. For publication |
| 94 | +quality reporting, run the generated `predictions.jsonl` through the official |
| 95 | +LongMemEval evaluation flow or an agreed LLM-as-judge rubric using the same |
| 96 | +model/settings across systems. |
| 97 | + |
| 98 | +The benchmark runner itself only needs `XMEM_API_KEY` because it generates XMem |
| 99 | +answers. The official/equivalent evaluator is a separate scoring step and needs |
| 100 | +`OPENAI_API_KEY` when using an OpenAI judge model. |
| 101 | + |
| 102 | +## Run All Official Categories |
| 103 | + |
| 104 | +The dataset has six `question_type` categories. Each example has a unique |
| 105 | +`question_id` and its own haystack sessions, and this runner isolates each |
| 106 | +question into a separate XMem user namespace. That makes category-level |
| 107 | +parallelism safe from memory leakage; the only practical constraint is API |
| 108 | +throughput and rate limiting. |
| 109 | + |
| 110 | +```bash |
| 111 | +export XMEM_API_KEY="..." |
| 112 | + |
| 113 | +python -m benchmarks.longmemeval.run_all_categories \ |
| 114 | + --dataset-path benchmarks/longmemeval/data/longmemeval_s_cleaned.json \ |
| 115 | + --api-base-url https://api.xmem.in \ |
| 116 | + --output-root benchmarks/longmemeval/results/full-six-categories \ |
| 117 | + --max-parallel-categories 6 |
| 118 | +``` |
| 119 | + |
| 120 | +The all-category runner prints live processed/left/ETA status and writes one |
| 121 | +official merged prediction file at: |
| 122 | + |
| 123 | +```text |
| 124 | +benchmarks/longmemeval/results/full-six-categories/predictions.jsonl |
| 125 | +``` |
| 126 | + |
| 127 | +Each category also gets a `runner.log` file under its output directory. If a |
| 128 | +category process fails, the launcher prints the failing category, exit code, log |
| 129 | +path, and the most recent child-process output. |
| 130 | + |
| 131 | +## Useful Options |
| 132 | + |
| 133 | +- `--limit N`: Run a small subset first. |
| 134 | +- `--offset N`: Skip the first N selected examples. |
| 135 | +- `--question-type TYPE`: Filter to one LongMemEval category. |
| 136 | +- `--skip-ingest`: Reuse already-ingested user namespaces and only retrieve. |
| 137 | +- `--no-resume`: Re-run examples even if they already exist in `results.jsonl`. |
| 138 | +- `--ingest-api-version v1`: Use synchronous batch ingestion instead of the |
| 139 | + default durable `/v2/memory/batch-ingest` path. |
| 140 | +- `--effort-level high`: Use high-effort XMem ingestion for long records. |
| 141 | +- `--dry-run`: Validate dataset/category setup without API calls. |
| 142 | +- `--verbose`: Print child runner output while the all-category launcher runs. |
| 143 | + |
| 144 | +## Expected Failures |
| 145 | + |
| 146 | +These errors are intentional and should be actionable: |
| 147 | + |
| 148 | +- `Dataset file not found`: run with `--download`, or pass `--dataset-path`. |
| 149 | +- `Missing API key`: set `XMEM_API_KEY`, or pass `--api-key-env` for a custom |
| 150 | + variable name. |
| 151 | +- Official evaluator authentication errors: set `OPENAI_API_KEY` before running |
| 152 | + the LongMemEval scoring step. |
| 153 | +- `Failed to download the LongMemEval dataset`: check network access, then retry |
| 154 | + or download the dataset manually. |
| 155 | +- `<category> failed with exit code ...`: inspect that category's `runner.log`. |
| 156 | + |
| 157 | +## Isolation Model |
| 158 | + |
| 159 | +Each example is ingested into a user id derived from: |
| 160 | + |
| 161 | +```text |
| 162 | +<user-prefix>-<question-id> |
| 163 | +``` |
| 164 | + |
| 165 | +This prevents facts from one benchmark question from leaking into another. Use a |
| 166 | +new `--user-prefix` for fully fresh runs. |
0 commit comments