feat: hybrid search — FTS5 + sqlite-vec + RRF fusion (Phase 2a)#228
Merged
Conversation
- **Embedding pipeline** for Phase 2 hybrid search — notes are chunked, embedded via bge-small-en-v1.5 (q8, 384-dim), and stored in sqlite-vec with content-hash gating - **New modules:** `embedder.ts` (factory-closure, lazy ONNX model loading), `chunker.ts` (heading-aware splitting with paragraph sub-split), `plaintext.ts` (Obsidian markdown → plain text) - **Search index** gains vector tables (`note_chunks` + `note_vectors`), three-pass rebuild (Pass 3 = embedding), `embedNote` for incremental updates via file watcher - **`EMBEDDING_ENABLED`** env var (default: `true`) — when `false`, no model download, no vector tables, FTS5-only search Does **not** change `vault_search` behavior — that's PR \#3 (hybrid search with RRF fusion). - [x] 1130 tests pass (53 new: 10 embedder, 15 chunker, 15 plaintext, 8 search-index embedding, 5 config) - [x] TypeScript compiles clean (`tsc --noEmit`) - [x] Docker builds successfully - [x] Content-hash gating verified: re-embedding same content skips all chunks - [x] `EMBEDDING_ENABLED=false` verified: no vector tables created, embedNote is no-op - [x] removeNote cleans up vectors + chunks - [x] rebuildFromVault Pass 3 embeds all notes - [ ] Deploy to Lightsail via `phase2_deploy.yml` and verify search/memory/reads still work BREAKING CHANGE: Search index factory `createSearchIndex` gains an optional second parameter (`embedder`). Existing callers are unaffected. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JPDgHMKBvJ51V2YrrMYZET <!-- This is an auto-generated comment: release notes by coderabbit.ai --> * **New Features** * Added optional embedding-based search support, with a new setting to turn it on or off. * Notes are now chunked and embedded to improve search quality, while keeping basic search available as a fallback. * Updated container support to use a Debian-based image for broader compatibility. * **Documentation** * Updated setup and deployment guidance to explain the new search configuration option. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ately (#218) - **Server blocked for ~5 min on first startup** while embedding every note — FTS was ready but the server didn't accept requests until Pass 3 finished - `rebuildFromVault` now returns after FTS indexing (Passes 1+2) and fires embedding (Pass 3) in the background - Return type changes from `number` to `{ count, embedding }` — server ignores the promise, tests can await it - [x] 1131 tests pass - [x] TypeScript compiles clean - [x] Embedding test awaits the background promise to verify embeddings complete - [ ] Deploy and verify server responds to health check within seconds of startup (not minutes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JPDgHMKBvJ51V2YrrMYZET <!-- This is an auto-generated comment: release notes by coderabbit.ai --> * **New Features** * Added `EMBEDDING_ENABLED` feature flag (default: `true`) to disable the embedding pipeline and run search using FTS5 only. * Added `MEMORY_ENABLED` feature flag (default: `true`) to the local/remote compose setups. * **Bug Fixes** * Improved incremental indexing so unchanged content isn’t re-embedded and rebuilds preserve existing vector data. * Fixed missed re-embeds when an earlier embedding attempt fails, ensuring subsequent updates still proceed. * **Documentation / Tests** * Updated indexing documentation and environment-variable references; expanded search/indexing test coverage. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary - Add hybrid search (FTS5 + sqlite-vec vector + RRF fusion) to `vault_search` tool - Transparent upgrade: uses hybrid ranking when embeddings are available, falls back to FTS-only when they are not - Three new functions: `computeRrfScores` (pure RRF), `vectorSearch` (KNN), `hybridSearch` (orchestrator) - Updated `vault_search` tool description to reflect hybrid search capabilities ## Spike result 8/9 previously-failed queries fixed, 0 regressions, ~8ms latency (RRF-only mode). ## Test plan - [x] 9 `computeRrfScores` unit tests (table-driven: single/both systems, bonuses, custom k, sort order) - [x] 12 `hybridSearch` integration tests (fallback, hybrid ranking, filters, limits, dedup, callouts) - [x] All 1177 tests pass - [x] Build and lint clean - [ ] Deploy to Lightsail and run 9-query eval against live vault 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018ZbaHwP28ASYTpQnZk6MqM <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Search now uses a hybrid ranking approach that blends keyword and semantic matching for more relevant results. * Results can now fall back to keyword-only search when embeddings aren’t available. * Search snippets and result metadata are more complete for semantic matches, with improved relevance ordering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary - ARCHITECTURE.md: hybrid search flow, RRF algorithm, fallback behavior, `search_mode`, Mermaid diagrams - README.md: value prop → "Hybrid search", Phase 2 status → "In progress", "AI agent" terminology - AGENTS.md: search module description updated in structure tree - .devin/wiki.json: hybrid search functions and conditional description documented - DEPLOY.md: `EMBEDDING_ENABLED` row clarified - 3x .env.example: "for hybrid search" instead of "for vector search" ## Test plan - [x] Build and tests pass (1182) - [x] Docs-only changes — no code modified <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Search now supports hybrid ranking, combining keyword and semantic results for more relevant matches. * Search responses now include the search mode used, making results clearer. * **Bug Fixes** * Search falls back cleanly to keyword-only matching when embeddings are unavailable or disabled. * Timestamps in search results are now handled more reliably. * **Documentation** * Updated setup and product docs to reflect hybrid search behavior and embedding configuration options. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## What does this PR do? Updates README.md and ARCHITECTURE.md to reflect Phase 2a (hybrid search) as shipped — the final doc pass before merging `phase-2` into `main`. **README.md:** - Intro paragraph: `full-text search` → `hybrid search` in the bold callout - Roadmap table: split Phase 2 into Phase 2a (hybrid search, Complete) and Phase 2b (reranker, In progress) - How It Works mermaid diagram: `SQLite FTS5` → `SQLite FTS5 + sqlite-vec`, edge label updated to `FTS5 + vector` - Prose below diagram: accurately describes both persistence strategies (FTS rebuilds on startup, vectors persist with content-hash gating) and RRF fusion **ARCHITECTURE.md:** - Phasing section: `Phase 2 is in progress` → `Phase 2a` as shipped, with forward reference to Phase 2b (reranker) - Component diagram: moved sqlite-vec from a separate "Phase 2" subgraph with dotted arrows into the Docker Compose subgraph with solid arrows - Key Decisions table: two new rows — "Hybrid search over LightRAG" (grounded in the actual decision journey: LightRAG too heavy, qmd showed lightweight approach) and "RRF fusion (k=60)" (rank-based over score-based, validation results) - Search module decomposition: 5-module table (search-index, search-queries, search-helpers, fts-query, rrf) added before MCP Prompts section ## Type of change - [x] Documentation ## Checklist - [x] `npm test` passes - [x] `npm run lint` passes - [x] `npm run prettier:check` passes - [x] `npm run build` succeeds - [x] README or ARCHITECTURE.md updated (if user-facing behavior changed) > `npm test`, `npm run lint`, and `npm run build` require Node 24 (not available in this environment). Changes are docs-only — no code modified. --- _Generated by [Claude Code](https://claude.ai/code/session_01NP25qUqi8Phdro6EeMJjVo)_ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated the product and architecture documentation to reflect the current search experience and roadmap. * Clarified that search now uses a hybrid approach, with improved description of how results are ranked and refreshed. * Revised roadmap details to split the upcoming search work into two stages, including a later reranking phase. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. Ignoring alerts on:
|
Owner
Author
|
@SocketSecurity ignore npm/@huggingface/transformers@4.2.0 |
2 tasks
aliasunder
added a commit
that referenced
this pull request
Jun 30, 2026
## Summary - Folder inputs with trailing slashes (e.g. `"Projects/"`) produced a LIKE pattern with `//` (`Projects//%`) that silently returned zero results - Added `stripTrailingSlashes` helper in search-helpers.ts, applied at all 5 folder filter sites in search-queries.ts - 5 unit tests for the new helper Found by qodo in PR #228 review. ## Test plan - [x] `stripTrailingSlashes` unit tests (5 cases: single slash, multiple, no slash, internal slashes, bare name) - [x] Full search test suite passes (298 tests) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
vault_searchnow combines FTS5 keyword ranking with sqlite-vec vector similarity via Reciprocal Rank Fusion (RRF). Keywords stay precise on exact terms; vectors bridge vocabulary gaps. 30% of natural-language queries that failed on FTS-only now return relevant results.EMBEDDING_ENABLED=falsedisables the pipeline entirely. Each query falls back to FTS-only when vectors aren't available.search_moderesponse field tells clients which ranking was used.PRs included
Test plan
EMBEDDING_ENABLED=false🤖 Generated with Claude Code