-
Notifications
You must be signed in to change notification settings - Fork 12
feat: MemoryBench evaluation suite (Epic #283) #292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
6f190d6
fix: validate studio output dirs and add p2c module design docs
jerry609 84d8223
feat(PaperToContext): M3
CJBshuosi e664d0d
feat(p2c): implement ContextEngineBridge to inject user context into …
wen-placeholder a630c62
activate paper-scope memory read/write path. Raletd to #158
wen-placeholder 77ea849
fix(p2c): address Gemini code review issues from PR. Related to #157
wen-placeholder f3a76d7
fix(p2c): address Gemini code review issues. Related to #158
wen-placeholder c8d7904
fix(p2c): sanitize XML tag content to prevent tag-escape prompt injec…
wen-placeholder c0e40e8
feat(memory): add FTS5 full-text search and sqlite-vec hybrid search.…
wen-placeholder 3e25c04
feat(p2c): persist CodeMemory experiences to SQLite. Related to #162
wen-placeholder 85cfba0
address Gemini code review issues from PR #224 and #225
wen-placeholder 5e93943
fix(memory): address Gemini code review issues from #153 epic audit
wen-placeholder ca90e77
fix: harden repro experience isolation and wire persistence into repr…
wen-placeholder a85d663
feat(memory): introduce memory decay mechanism (#163)
jerry609 9dbb874
feat(memory): add cross-track batch search (#164)
jerry609 7f06b8e
feat(context): implement layered context loading (#165)
jerry609 0a6255f
refactor(memory): align decay with OpenClaw patterns
jerry609 9adea31
fix(context): isolate layer0 cache and touch batch hits (#236)
jerry609 0b7e2bc
feat(memory): upgrade batch retrieval with hybrid and MMR options (#237)
jerry609 4df080a
feat(context): add embedding fallback chain and token guard config (#…
jerry609 3dcd8c3
fix(memory): bound batch hybrid candidates by scope_ids
jerry609 a291ee7
docs: update README with macOS python3 tips
bb2fbb4
docs: update README with more badges
e58c43c
docs: update README with deleting extra badges
cf8297c
feat(search): add offline retrieval benchmark harness
jerry609 4d9fa7a
feat(context): add offline context-engine benchmark
jerry609 e4c3511
feat(memory): add scope isolation acceptance bench
jerry609 37641a9
feat(memory): add offline injection robustness detector
jerry609 5676f31
feat(memory): add offline performance benchmark harness
jerry609 f9c08a2
feat: add ROI benchmark for repro memory
jerry609 326910c
merge: integrate memorybench-284-retrieval-bench into epic-283 suite
jerry609 335493d
merge: integrate contextbench-286-context-extraction-bench into epic-…
jerry609 a1c0693
merge: integrate memorybench-285-scope-isolation into epic-283 suite
jerry609 31d6bd7
merge: integrate memorybench-287-injection-robustness into epic-283 s…
jerry609 ec9db2c
merge: integrate memorybench-288-performance-bench into epic-283 suite
jerry609 b723eba
merge: integrate p2cbench-289-roi-bench into epic-283 suite
jerry609 6047d55
docs: add MemoryBench epic completion report
jerry609 2d535e1
merge: integrate MemoryBench epic #283 into dev
jerry609 3f7dcb8
docs: add runtime memory benchmark report
jerry609 318b8c6
Docs: update README with our new name (#290)
Linjie-top dec1f92
Fix: show friendly error when backend is unreachable (#291)
Linjie-top da4c4f8
feat: improve memory ROI and effectiveness benchmarks
jerry609 acec0e2
fix: avoid importing missing data template in main
jerry609 8b8fee1
fix: stabilize live memory roi benchmark
jerry609 1773d04
feat: expand multi-session memory effectiveness benchmark
jerry609 830de71
feat: implement MemoryBench evaluation suite with 4 bench suites
jerry609 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| .PHONY: bench-roi | ||
|
|
||
| bench-roi: | ||
| @if [ -z "$$OPENAI_API_KEY$$ANTHROPIC_API_KEY$$OPENROUTER_API_KEY$$NVIDIA_MINIMAX_API_KEY$$NVIDIA_GLM_API_KEY" ]; then \ | ||
| echo "Missing API key: set OPENAI_API_KEY, ANTHROPIC_API_KEY, OPENROUTER_API_KEY, NVIDIA_MINIMAX_API_KEY, or NVIDIA_GLM_API_KEY"; \ | ||
| exit 1; \ | ||
| fi | ||
| PYTHONPATH=src python evals/memory/bench_roi.py --output evals/reports/memory_roi_benchmark.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| """memory_items FTS5 full-text index | ||
|
|
||
| Revision ID: 0019_memory_fts5 | ||
| Revises: b94c1a2be26e | ||
| Create Date: 2026-03-03 | ||
|
|
||
| Creates a SQLite FTS5 virtual table for memory_items.content and three triggers | ||
| (after insert / after delete / after update) to keep it in sync. | ||
|
|
||
| FTS5 provides BM25-ranked full-text search; falls back to LIKE on non-SQLite | ||
| databases (Postgres) where FTS5 is not available. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
| revision = "0019_memory_fts5" | ||
| down_revision = "b94c1a2be26e" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
| # --------------------------------------------------------------------------- # | ||
| # SQLite-only helpers # | ||
| # --------------------------------------------------------------------------- # | ||
|
|
||
| _CREATE_FTS = """\ | ||
| CREATE VIRTUAL TABLE IF NOT EXISTS memory_items_fts | ||
| USING fts5(content, tokenize='porter ascii'); | ||
| """ | ||
|
|
||
| _POPULATE_FTS = """\ | ||
| INSERT INTO memory_items_fts(rowid, content) | ||
| SELECT id, content FROM memory_items | ||
| WHERE deleted_at IS NULL; | ||
| """ | ||
|
|
||
| _TRIGGER_INSERT = """\ | ||
| CREATE TRIGGER IF NOT EXISTS memory_items_fts_ai | ||
| AFTER INSERT ON memory_items BEGIN | ||
| INSERT INTO memory_items_fts(rowid, content) VALUES (new.id, new.content); | ||
| END; | ||
| """ | ||
|
|
||
| _TRIGGER_DELETE = """\ | ||
| CREATE TRIGGER IF NOT EXISTS memory_items_fts_ad | ||
| AFTER DELETE ON memory_items BEGIN | ||
| DELETE FROM memory_items_fts WHERE rowid = old.id; | ||
| END; | ||
| """ | ||
|
|
||
| _TRIGGER_UPDATE = """\ | ||
| CREATE TRIGGER IF NOT EXISTS memory_items_fts_au | ||
| AFTER UPDATE OF content ON memory_items BEGIN | ||
| DELETE FROM memory_items_fts WHERE rowid = old.id; | ||
| INSERT INTO memory_items_fts(rowid, content) VALUES (new.id, new.content); | ||
| END; | ||
| """ | ||
|
|
||
| _DROP_TRIGGERS = """\ | ||
| DROP TRIGGER IF EXISTS memory_items_fts_ai; | ||
| DROP TRIGGER IF EXISTS memory_items_fts_ad; | ||
| DROP TRIGGER IF EXISTS memory_items_fts_au; | ||
| """ | ||
|
|
||
| _DROP_FTS = "DROP TABLE IF EXISTS memory_items_fts;" | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| bind = op.get_bind() | ||
| dialect = bind.dialect.name | ||
| if dialect != "sqlite": | ||
| # FTS5 is SQLite-only; Postgres uses pg_trgm / tsvector instead. | ||
| return | ||
|
|
||
| bind.execute(sa.text(_CREATE_FTS)) | ||
| bind.execute(sa.text(_POPULATE_FTS)) | ||
| bind.execute(sa.text(_TRIGGER_INSERT)) | ||
| bind.execute(sa.text(_TRIGGER_DELETE)) | ||
| bind.execute(sa.text(_TRIGGER_UPDATE)) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| bind = op.get_bind() | ||
| if bind.dialect.name != "sqlite": | ||
| return | ||
|
|
||
| bind.execute(sa.text(_DROP_TRIGGERS)) | ||
| bind.execute(sa.text(_DROP_FTS)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| """memory_items embedding column + sqlite-vec virtual table | ||
|
|
||
| Revision ID: 0020_memory_embedding | ||
| Revises: 0019_memory_fts5 | ||
| Create Date: 2026-03-03 | ||
|
|
||
| Phase B of issue #161: | ||
| - Adds `embedding BLOB` column to memory_items for storing float32 vectors. | ||
| - Creates `vec_items` sqlite-vec virtual table (SQLite + sqlite-vec only). | ||
| - Creates sync triggers to keep vec_items in step with memory_items.embedding. | ||
|
|
||
| Graceful degradation: if sqlite-vec is not installed the column migration still | ||
| runs; only the virtual table and triggers are skipped. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
| revision = "0020_memory_embedding" | ||
| down_revision = "0019_memory_fts5" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
| _EMBEDDING_DIM = 1536 # text-embedding-3-small | ||
|
|
||
| _ADD_COLUMN = "ALTER TABLE memory_items ADD COLUMN embedding BLOB" | ||
|
|
||
| _CREATE_VEC = f"""\ | ||
| CREATE VIRTUAL TABLE IF NOT EXISTS vec_items | ||
| USING vec0(embedding float[{_EMBEDDING_DIM}]) | ||
| """ | ||
|
|
||
| _POPULATE_VEC = """\ | ||
| INSERT OR IGNORE INTO vec_items(rowid, embedding) | ||
| SELECT id, embedding FROM memory_items | ||
| WHERE embedding IS NOT NULL AND deleted_at IS NULL | ||
| """ | ||
|
|
||
| _TRIGGER_VEC_INSERT = """\ | ||
| CREATE TRIGGER IF NOT EXISTS memory_items_vec_ai | ||
| AFTER INSERT ON memory_items | ||
| WHEN new.embedding IS NOT NULL | ||
| BEGIN | ||
| INSERT OR REPLACE INTO vec_items(rowid, embedding) VALUES (new.id, new.embedding); | ||
| END | ||
| """ | ||
|
|
||
| _TRIGGER_VEC_UPDATE = """\ | ||
| CREATE TRIGGER IF NOT EXISTS memory_items_vec_au | ||
| AFTER UPDATE OF embedding ON memory_items | ||
| BEGIN | ||
| DELETE FROM vec_items WHERE rowid = old.id; | ||
| INSERT OR IGNORE INTO vec_items(rowid, embedding) | ||
| SELECT new.id, new.embedding WHERE new.embedding IS NOT NULL; | ||
| END | ||
| """ | ||
|
|
||
| _TRIGGER_VEC_DELETE = """\ | ||
| CREATE TRIGGER IF NOT EXISTS memory_items_vec_ad | ||
| AFTER DELETE ON memory_items BEGIN | ||
| DELETE FROM vec_items WHERE rowid = old.id; | ||
| END | ||
| """ | ||
|
|
||
| _DROP_VEC_TRIGGERS = """\ | ||
| DROP TRIGGER IF EXISTS memory_items_vec_ai; | ||
| DROP TRIGGER IF EXISTS memory_items_vec_au; | ||
| DROP TRIGGER IF EXISTS memory_items_vec_ad; | ||
| """ | ||
| _DROP_VEC_TABLE = "DROP TABLE IF EXISTS vec_items" | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| bind = op.get_bind() | ||
| dialect = bind.dialect.name | ||
|
|
||
| # 1. Add embedding column (all dialects — column is used for BLOB storage). | ||
| try: | ||
| bind.execute(sa.text(_ADD_COLUMN)) | ||
| except Exception: | ||
| pass # Column already exists. | ||
|
|
||
| if dialect != "sqlite": | ||
| return # sqlite-vec is SQLite-only. | ||
|
|
||
| # 2. Load sqlite-vec extension (best-effort). | ||
| try: | ||
| import sqlite_vec # type: ignore | ||
| raw_conn = bind.connection.dbapi_connection # type: ignore | ||
| raw_conn.enable_load_extension(True) | ||
| sqlite_vec.load(raw_conn) | ||
| raw_conn.enable_load_extension(False) | ||
| except Exception: | ||
| return # sqlite-vec not installed — skip virtual table creation. | ||
|
|
||
| # 3. Create vec_items virtual table + triggers. | ||
| bind.execute(sa.text(_CREATE_VEC)) | ||
| bind.execute(sa.text(_POPULATE_VEC)) | ||
| bind.execute(sa.text(_TRIGGER_VEC_INSERT)) | ||
| bind.execute(sa.text(_TRIGGER_VEC_UPDATE)) | ||
| bind.execute(sa.text(_TRIGGER_VEC_DELETE)) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| bind = op.get_bind() | ||
|
|
||
| # Drop vec infrastructure (SQLite only; ignore errors). | ||
| if bind.dialect.name == "sqlite": | ||
| try: | ||
| bind.execute(sa.text(_DROP_VEC_TRIGGERS)) | ||
| bind.execute(sa.text(_DROP_VEC_TABLE)) | ||
| except Exception: | ||
| pass | ||
|
|
||
| # NOTE: SQLite doesn't support DROP COLUMN; leave the embedding column in place. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| """repro_code_experience table for CodeMemory persistence | ||
|
|
||
| Revision ID: 0021_repro_code_experience | ||
| Revises: 0020_memory_embedding | ||
| Create Date: 2026-03-03 | ||
|
|
||
| Issue #162: Persist CodeMemory experience data so it survives process restarts. | ||
| Creates the repro_code_experience table with indexes on paper_id and pack_id. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import sqlalchemy as sa | ||
| from alembic import op | ||
|
|
||
| revision = "0021_repro_code_experience" | ||
| down_revision = "0020_memory_embedding" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| op.create_table( | ||
| "repro_code_experience", | ||
| sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), | ||
| sa.Column("pack_id", sa.String(64), nullable=True), | ||
| sa.Column("paper_id", sa.String(256), nullable=True), | ||
| sa.Column("pattern_type", sa.String(32), nullable=False), | ||
| sa.Column("content", sa.Text(), nullable=False, server_default=""), | ||
| sa.Column("code_snippet", sa.Text(), nullable=True), | ||
| sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), | ||
| sa.PrimaryKeyConstraint("id"), | ||
| ) | ||
| op.create_index("ix_repro_code_experience_paper_id", "repro_code_experience", ["paper_id"]) | ||
| op.create_index("ix_repro_code_experience_pack_id", "repro_code_experience", ["pack_id"]) | ||
| op.create_index("ix_repro_code_experience_pattern_type", "repro_code_experience", ["pattern_type"]) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| op.drop_index("ix_repro_code_experience_pattern_type", table_name="repro_code_experience") | ||
| op.drop_index("ix_repro_code_experience_pack_id", table_name="repro_code_experience") | ||
| op.drop_index("ix_repro_code_experience_paper_id", table_name="repro_code_experience") | ||
| op.drop_table("repro_code_experience") |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
MEMORYBENCH_EPIC_283_COMPLETION.mdcompletion report links todocs/benchmark/MEMORYBENCH_RUNTIME_REPORT_2026-03-07.mdin the README (line 240), but this file does not appear to be included in the PR diff. The README references a non-existent documentation file, which would result in a broken link.