-
Notifications
You must be signed in to change notification settings - Fork 12
docs: add MemoryBench quantitative results to README #294
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
Changes from all commits
6f190d6
84d8223
e664d0d
a630c62
77ea849
f3a76d7
c8d7904
c0e40e8
3e25c04
85cfba0
5e93943
ca90e77
a85d663
9dbb874
7f06b8e
0a6255f
9adea31
0b7e2bc
4df080a
3dcd8c3
a291ee7
bb2fbb4
e58c43c
cf8297c
4d9fa7a
e4c3511
37641a9
5676f31
f9c08a2
326910c
335493d
a1c0693
31d6bd7
ec9db2c
b723eba
6047d55
2d535e1
3f7dcb8
318b8c6
dec1f92
da4c4f8
acec0e2
8b8fee1
1773d04
830de71
ad1268f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,17 +1,27 @@ | ||||||
| <!-- markdownlint-disable MD001 MD041 --> | ||||||
|
|
||||||
| <h1 align="center">PaperBot</h1> | ||||||
| <h1 align="center">Oh, God! My idea comes true.</h1> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new project name 'Oh, God! My idea comes true.' is unconventional and may not convey a professional image for the project. While this might be a deliberate creative choice, consider reverting to 'PaperBot' or choosing another name that is more descriptive of the project's purpose to aid in its branding and adoption. |
||||||
|
|
||||||
| <h3 align="center"> | ||||||
| AI-powered research workflow: paper discovery → LLM analysis → scholar tracking → Paper2Code → multi-agent studio | ||||||
| </h3> | ||||||
|
Comment on lines
+3
to
7
|
||||||
|
|
||||||
| <p align="center"> | ||||||
| <a href="https://github.com/jerry609/PaperBot/actions"><img src="https://github.com/jerry609/PaperBot/actions/workflows/ci.yml/badge.svg" alt="CI"></a> | ||||||
| <a href="https://github.com/jerry609/PaperBot/issues/232"><img src="https://img.shields.io/badge/roadmap-Q1%2FQ2%202026-blue" alt="Roadmap"></a> | ||||||
| <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT"></a> | ||||||
| <img src="https://img.shields.io/badge/python-3.10%2B-blue" alt="Python 3.10+"> | ||||||
| <img src="https://img.shields.io/badge/Next.js-16-black" alt="Next.js 16"> | ||||||
| <a href="https://github.com/jerry609/PaperBot/actions"> | ||||||
| <img src="https://img.shields.io/github/actions/workflow/status/jerry609/PaperBot/ci.yml?branch=dev&label=CI&logo=github" alt="CI"> | ||||||
| </a> | ||||||
| <a href="https://github.com/jerry609/PaperBot/issues/232"> | ||||||
| <img src="https://img.shields.io/badge/roadmap-2026-blue?logo=roadmap" alt="Roadmap"> | ||||||
| </a> | ||||||
| <img src="https://img.shields.io/badge/version-0.1.0-007ec6?logo=github" alt="Version 0.1.0"> | ||||||
| <img src="https://img.shields.io/badge/license-MIT-green" alt="License"> | ||||||
| </p> | ||||||
|
|
||||||
| <p align="center"> | ||||||
| <img src="https://img.shields.io/badge/Python-3.10+-blue?logo=python&logoColor=white" alt="Python"> | ||||||
| <img src="https://img.shields.io/badge/Next.js-16-black?logo=nextdotjs" alt="Next.js"> | ||||||
| <img src="https://img.shields.io/badge/Platform-Win%20|%20Mac%20|%20Linux-lightgrey" alt="Platform"> | ||||||
| <img src="https://img.shields.io/badge/Downloads-xx-lightgrey?style=social&logo=icloud" alt="Downloads"> | ||||||
|
||||||
| <img src="https://img.shields.io/badge/Downloads-xx-lightgrey?style=social&logo=icloud" alt="Downloads"> | |
| <img src="https://img.shields.io/github/downloads/jerry609/PaperBot/total?style=social&logo=icloud" alt="GitHub all releases"> |
Copilot
AI
Mar 7, 2026
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.
PR metadata says this is “docs only”, but this PR includes substantial code, test, dependency, and CI changes (e.g., MemoryBench runner additions, new stores/models, workflow updates). Please update the PR title/description to match the actual scope so reviewers can triage appropriately.
| 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)) |
| 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. |
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 method of concatenating all API key variables to check if at least one is set is functional but can be difficult to read and debug. For better clarity and maintainability, I suggest checking each variable individually. This makes the condition's intent more explicit.