Commit graph

23 commits

Author SHA1 Message Date
Nicolò Boschi
43b3efc494
perf: replace window-function retrieval with UNION ALL + per-bank HNSW indexes (#541)
* perf: replace window-function retrieval with UNION ALL + per-bank HNSW indexes

The previous retrieve_semantic_bm25_combined() used ROW_NUMBER() OVER (PARTITION
BY fact_type ...) which forced a full sequential scan — pgvector cannot use HNSW
indexes when a window function partitions on the same column as the ORDER BY.

Changes:
- retrieval.py: rewrite to UNION ALL of per-fact_type subqueries; each arm has
  its own ORDER BY embedding <=> $1 LIMIT n, enabling partial HNSW index scans.
  Semantic arms over-fetch 5x (min 100) for HNSW approximation; trimmed in Python.
- memory_engine.py: set hnsw.ef_search=200 at pool init (persistent per-connection,
  no per-query SET/RESET overhead).
- bank_utils.py: add create_bank_hnsw_indexes / drop_bank_hnsw_indexes for
  per-(bank_id, fact_type) partial HNSW index lifecycle management.
- fact_storage.py / bank_utils.py: create per-bank indexes on fresh bank insert.
- memory_engine.py delete_bank: drop per-bank indexes via DELETE...RETURNING to
  avoid a separate round-trip.
- Migration a3b4c5d6e7f8: add interim fact_type-only partial indexes.
- Migration d5e6f7a8b9c0: add internal_id UUID UNIQUE to banks, replace
  fact_type-only indexes with per-(bank, fact_type) partial HNSW indexes, drop
  the global idx_memory_units_embedding that competed with them.

Why per-(bank, fact_type) not just per-fact_type:
The idx_memory_units_bank_id B-tree index always wins over fact_type-only partial
indexes when bank_id appears in the WHERE clause. Including bank_id in the partial
index predicate removes the B-tree from consideration and lets the planner choose
HNSW. The global HNSW index must also be dropped to avoid competing for the larger
fact_type partitions (world, observation).

* refactor: collapse two HNSW migrations into one

* refactor: generate bank internal_id in Python before insert

Instead of relying on DEFAULT gen_random_uuid() and RETURNING internal_id,
generate the UUID in application code before the INSERT. This means we
always know the value upfront and can call create_bank_hnsw_indexes
immediately without needing a DB round-trip to retrieve the assigned ID.

Also adds tests for HNSW index lifecycle and retrieve_semantic_bm25_combined.

* fix: correct migration and prevent global HNSW index recreation

Migration fixes:
- Add text() wrappers for raw SQL in d5e6f7a8b9c0 (SQLAlchemy 2.0 compat)
- Drop stale fact_type-only partial indexes (idx_mu_emb_world/observation/experience)
  that may exist from prior migrations on the same DB

migrations.py fix:
- Skip global HNSW index creation when per-bank partial HNSW indexes already
  exist on memory_units (idx_mu_emb_* pattern). Without this, the post-migration
  vector index check detects no %embedding% named index and recreates the global
  idx_memory_units_embedding, which defeats the per-bank index strategy.

Verified with EXPLAIN ANALYZE on 66K-row bank: all three fact_type arms use
their per-bank HNSW index scan (idx_mu_emb_worl/expr/obsv_<uid16>).

* fix: use correct embeddings.encode() in test
2026-03-11 12:09:50 +01:00
Nicolò Boschi
7942f181c2
fix(performance): improve recall and retain performance on large banks (#469) 2026-03-03 13:35:22 +01:00
Nicolò Boschi
9f0c031df7
fix: improve memory footprint of recall (#423) 2026-02-23 11:30:16 +01:00
Nicolò Boschi
d871c3009d
feat: support timescale pg_textsearch as text search extension (#359)
* feat: support timescale pg_textsearch as text search extension

* refactor: deduplicate text search query in retrieve_semantic_bm25_combined

Instead of maintaining 3 complete query copies (native, vchord, pg_textsearch),
now we:
- Build backend-specific parts (score_expr, order_by, where_filter)
- Use a single query template with injected backend-specific parts

This makes maintenance easier - changes to the semantic CTE or overall structure
only need to be made once.
2026-02-12 17:38:13 +01:00
Nicolò Boschi
71e408c27b
chore: remove dead code (#356)
* chore: remove dead code

* chore: remove dead code

* feat: support litellm-sdk for reranker endpoint
2026-02-12 14:44:00 +01:00
Nicolò Boschi
c029807add
feat: support for other text and vector search pg extensions (#355)
* feat: support for other text and vector search pg extensions

* test: increase timeout for test_batch_chunking_behavior to account for VectorChord BM25 tokenization overhead

* feat: support for other text and vector search pg extensions
2026-02-12 14:13:04 +01:00
Nicolò Boschi
16b85a4faa
chore: drop unused access_count column (#178) 2026-01-20 10:36:02 +01:00
Nicolò Boschi
20c8f8b06a
feat: add memory tags (#152)
* feat: add memory tags

* feat: add memory tags

* support tags

* support tags
2026-01-13 18:28:40 +01:00
Nicolò Boschi
59913086be
fix: batch queries on recall (#149)
* fix: batch queries on recall

* fix: batch queries on recall
2026-01-13 13:20:22 +01:00
Nicolò Boschi
7935b0accd
fix: improve mpfp retrieval (#146)
* fix: improve mpfp retrieval

* fix: improve mpfp retrieval

* fix: improve embeddings service performances

* fix: improve embeddings service performances

* fix: improve embeddings service performances

* fix: improve embeddings service performances
2026-01-12 18:58:05 +01:00
Nicolò Boschi
eb2702bcba
misc: performance improvements (#140)
* misc: performance improvements

* misc: performance improvements

* misc: performance improvements
2026-01-09 14:47:20 +01:00
Nicolò Boschi
2a0c490c9e
feat: extensions (#54) 2025-12-22 11:05:23 +01:00
Nicolò Boschi
9394cf92f2
fix: doc build and lint files (#34)
* fix doc build

* fix doc build
2025-12-16 13:49:09 +01:00
Nicolò Boschi
7445cef7b7
feat: add optional graph retriever MPFP (#26)
* feat: add optional graph retriever MPFP

* feat: add optional graph retriever MPFP
2025-12-12 16:58:50 +01:00
Nicolò Boschi
922164e25c fix recall trace visualization 2025-12-12 14:38:37 +01:00
Nicolò Boschi
3bb0a58ded
Increase graph neighbor limit and benchmark improvements (#18)
* Improve LongMemEval benchmark with structured prompts and better options

- Add --context-format option with 'json' (original) and 'structured' modes
- Structured format groups facts with source chunks for better LLM comprehension
- Add detailed instructions for date calculations, relative time handling, and abstention
- Add --source-results flag to read failed questions from a different file
- Allow --category to be combined with --max-instances for sampling
- Fix Gemini structured output by passing response_schema parameter
- Add retry logic for empty Gemini responses with block reason logging
- Add judge prompt comparison documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix recall in benchmarks

* Improve LongMemEval prompt and Gemini error handling

- Add JSONDecodeError retry for Gemini truncated responses
- Increase max_tokens to 32768 for thinking models
- Add counting/disambiguation guidance to structured prompt
- Add "when in doubt, undercount" and overlap detection rules

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add connection error retry and preference question guidance

- Add APIConnectionError retry for OpenAI client (server disconnects)
- Add recommendation/preference question guidance to structured prompt
- Instruct model to build on user's existing tools/experiences

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Make reasoning optional

* Seed for LLM through Groq

* fix entity and observations

* Increase graph retrieval neighbor limit for expanded entities

Doubled the neighbor limit multiplier from 10 to 20 in graph retrieval.
With expanded entity extraction (now including objects and concepts like
"kitchen"), facts share more common entities, causing the previous limit
to arbitrarily exclude relevant results. This fix ensures better recall
for questions about related items (e.g., kitchen items).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Expand entity extraction to include objects and concepts

Updated entity extraction prompt to include:
- Specific objects (coffee maker, toaster, car, laptop, kitchen)
- Abstract concepts/themes (friendship, career growth, loss, celebration)
- Places and organizations (IKEA, Goodwill, New York)

This enables better fact linking through shared entities. For example,
kitchen appliances now share a "kitchen" entity, allowing graph traversal
to find related facts like "replaced coffee maker" when querying about
"kitchen items".

Works in conjunction with the increased neighbor limit to improve recall.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Chris Bartholomew <chris.bartholomew@vectorize.io>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: andrew <andrew.neeser@me.com>
2025-12-08 15:24:13 +01:00
Nicolò Boschi
902eed711d fix release script 2025-12-03 16:04:27 +01:00
Nicolò Boschi
050a3d2743
fix: ci and ui build (#9)
* fix ci

* fix ci and release
2025-12-03 15:47:53 +01:00
Nicolò Boschi
e1424357c4
fix: ci and ui improvements (#8)
* fix: ci and ui improvements

* fix ci

* fix ci
2025-12-03 15:08:39 +01:00
Nicolò Boschi
3e72984cd2 chunks 2025-11-29 16:34:13 +01:00
Nicolò Boschi
d099ef870d rename to new names 2025-11-27 16:22:16 +01:00
Nicolò Boschi
a3ad76d165 many improvements 2025-11-26 18:22:25 +01:00
Nicolò Boschi
b1ff2e8823
rename to hindsight (#2) 2025-11-25 19:28:26 +01:00
Renamed from memora/memora/search/retrieval.py (Browse further)