From baa6c758f54d9f653e791ef222fa2272a8683b4e Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Mon, 18 May 2026 22:18:23 +0800 Subject: [PATCH] fix(vector): get_collection no longer silently deletes collection on error; memory builder skips full rebuild when index hash matches --- paperforge/embedding/_chroma.py | 15 ++++----------- paperforge/memory/builder.py | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/paperforge/embedding/_chroma.py b/paperforge/embedding/_chroma.py index ccf68832..3cdec382 100644 --- a/paperforge/embedding/_chroma.py +++ b/paperforge/embedding/_chroma.py @@ -22,17 +22,10 @@ def get_collection(vault: Path): db_path = get_vector_db_path(vault) db_path.mkdir(parents=True, exist_ok=True) client = chroma.PersistentClient(path=str(db_path)) - try: - return client.get_or_create_collection( - name="paperforge_fulltext", - metadata={"hnsw:space": "cosine"}, - ) - except Exception: - client.delete_collection("paperforge_fulltext") - return client.create_collection( - name="paperforge_fulltext", - metadata={"hnsw:space": "cosine"}, - ) + return client.get_or_create_collection( + name="paperforge_fulltext", + metadata={"hnsw:space": "cosine"}, + ) def delete_paper_vectors(vault: Path, zotero_key: str) -> int: diff --git a/paperforge/memory/builder.py b/paperforge/memory/builder.py index d97580df..3367964e 100644 --- a/paperforge/memory/builder.py +++ b/paperforge/memory/builder.py @@ -129,7 +129,7 @@ def _import_correction_log(conn, vault: Path) -> int: def build_from_index(vault: Path) -> dict: """Read formal-library.json and build/rebuild paperforge.db. - + Returns a dict with counts for reporting. """ envelope = read_index(vault) @@ -137,7 +137,6 @@ def build_from_index(vault: Path) -> dict: raise FileNotFoundError( "Canonical index not found. Run paperforge sync --rebuild-index." ) - # Legacy format: bare list of entries (pre-envelope) if isinstance(envelope, list): items = envelope generated_at = "" @@ -147,6 +146,24 @@ def build_from_index(vault: Path) -> dict: canonical_hash = compute_hash(items) if isinstance(items, list) and items and isinstance(items[0], dict) else "" db_path = get_memory_db_path(vault) + # fast-path: if index hash matches, nothing changed + if canonical_hash and db_path.exists(): + try: + conn = get_connection(db_path, read_only=False) + cached = conn.execute("SELECT value FROM meta WHERE key='canonical_index_hash'").fetchone() + stored_version = get_schema_version(conn) + if cached and cached[0] == canonical_hash and stored_version == CURRENT_SCHEMA_VERSION: + papers_count = conn.execute("SELECT COUNT(*) FROM papers").fetchone()[0] + conn.close() + return { + "papers_indexed": papers_count, + "db_path": str(db_path), + "hash_match": True, + } + conn.close() + except Exception: + pass + conn = get_connection(db_path, read_only=False) try: stored_version = get_schema_version(conn)