You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: implement BM25 lexical search and hybrid retrieval capabilities
- Add a segmentation-free tokenizer for mixed Chinese/English text to support BM25 indexing.
- Introduce vector math helpers for dot product, norm, and cosine similarity calculations.
- Extend IVectorStore interface to include optional lexical search functionality.
- Implement LocalVectorStore to support BM25 indexing and hybrid retrieval.
- Update settings to include options for hybrid retrieval and MMR (Maximal Marginal Relevance) configurations.
- Add tests for BM25, tokenization, vector math, and hybrid recall mechanisms.
- Introduce a Vitest configuration for running tests in a Node environment with stubs for Obsidian.
Copy file name to clipboardExpand all lines: CLAUDE.md
+22-9Lines changed: 22 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,9 +9,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
9
9
|`npm run dev`| Watch mode build via esbuild; outputs `main.js`|
10
10
|`npm run build`| Type-check (`tsc --noEmit`) + production esbuild bundle (minified) |
11
11
|`npm run lint`| Run ESLint (typescript-eslint + eslint-plugin-obsidianmd) |
12
+
|`npm test`| Run the vitest unit suite (`vitest run`) |
13
+
|`npm run test:watch`| Run vitest in watch mode |
12
14
|`npm version <patch/minor/major>`| Bump version in `manifest.json` and `versions.json`|
13
15
14
-
There are no unit tests. Testing is manual: build, copy `main.js` + `manifest.json` + `styles.css` to `<Vault>/.obsidian/plugins/obsidian-copilot/`, reload Obsidian, and enable the plugin.
16
+
Unit tests live in `tests/` and run under vitest. They cover the dependency-free
plus a synthetic `recall@k` benchmark (`tests/hybridRecall.test.ts`) that
19
+
demonstrates hybrid + MMR retrieval beating pure top-K cosine. Tests only import
20
+
modules that do **not** depend on `obsidian`, so they need no DOM/electron env.
21
+
22
+
End-to-end testing is still manual: build, copy `main.js` + `manifest.json` +
23
+
`styles.css` to `<Vault>/.obsidian/plugins/simple-rag-plugin/` (the `id` in
24
+
`manifest.json`), reload Obsidian, and enable the plugin.
15
25
16
26
## High-Level Architecture
17
27
@@ -25,13 +35,16 @@ This is an Obsidian community plugin that adds an AI chat sidebar with RAG (Retr
25
35
- Providers support a "thinking depth" (reasoning budget tokens) mapped via `ThinkingDepth`.
26
36
27
37
2.**RAG Layer** (`src/rag/`)
28
-
-**Chunking**: Pluggable strategies — `DelimiterChunker`, `LengthChunker`, and `AIChunker` (uses an LLM call to semantically split text).
29
-
-**Embedding**: `EmbeddingService` calls an OpenAI-compatible `/embeddings` endpoint with batching.
38
+
-**Chunking**: Pluggable strategies resolved in `ChunkingResolver`— `DelimiterChunker`, `ParagraphChunker`, `LengthChunker`, `MarkdownHeadingChunker`, `MarkdownQAChunker`. `AIChunker` (LLM-based semantic split) exists but is **not** currently wired into the resolver — see the dead-code note below.
39
+
-**Embedding**: `EmbeddingService` calls an OpenAI-compatible `/embeddings` endpoint with batching, bounded concurrency, and retry/backoff (honoring `Retry-After`).
30
40
-**Vector Store**: `IVectorStore` interface with two implementations:
31
-
-`LocalVectorStore` — persists to `.copilot/vectors.json` inside the vault.
32
-
-`RemoteVectorStore` — connects to Pinecone, Qdrant, or Weaviate.
41
+
-`LocalVectorStore` — persists to `.copilot/vectors.json` inside the vault; also serves BM25 lexical search (`lexicalSearch`) for hybrid retrieval.
42
+
-`PgvectorStore` — Postgres + the `pgvector` extension (cosine distance, HNSW index when available). Vector-only; no lexical search yet, so hybrid degrades gracefully to vector + MMR.
> **Dead code**: `AIChunker` is implemented but unreferenced — it needs async chunking plumbing and an `AIService` dependency threaded through `createChunker`. Decide to wire it (as an `ai` strategy) or delete it; don't leave it dangling.
35
48
36
49
3.**Indexing** (`src/indexer/VaultIndexer.ts`)
37
50
- Incremental full-vault indexing that skips unchanged files by mtime.
@@ -43,16 +56,16 @@ This is an Obsidian community plugin that adds an AI chat sidebar with RAG (Retr
43
56
- Two chat modes:
44
57
-**Vault mode**: Uses `RAGPipeline.retrieve()` to inject relevant note chunks as context.
45
58
-**Note mode**: Lets the user attach specific files or URLs; no vault search.
46
-
- Settings are split across four tabs: General, Models, Embedding, VectorDB.
59
+
- Settings are split across five tabs: General (incl. RAG + advanced retrieval), Models, Embedding, VectorDB, and LightRAG.
47
60
48
61
### Plugin Lifecycle (`src/main.ts`)
49
62
50
63
`main.ts` should stay minimal. It:
51
64
1. Loads persisted data (`settings` + `conversations`) via `loadData()`.
52
-
2. Wires services together in `initServices()`: `AIService` → `EmbeddingService` + `RerankService` + `LocalVectorStore`+ `IVectorStore` → `RAGPipeline` → `VaultIndexer`.
65
+
2. Wires services together in `initServices()`: `AIService` → `EmbeddingService` + `RerankService` + (`LocalVectorStore`| `PgvectorStore`) as `IVectorStore` → `RAGPipeline` → `VaultIndexer`, plus `LightRAGClient` + `LightRAGSyncService`.
53
66
3. Registers the `ChatView`, commands, and settings tab.
54
67
4. On settings update, calls `reinitServices()`; if the vector store backend changed, it rebuilds the pipeline and indexer from scratch.
55
-
5. On `onunload`, saves the local vector store and detaches the chat view.
68
+
5. On `onunload`, saves the local vector store, stops LightRAG sync, closes the pgvector pool, and detaches the chat view.
0 commit comments