superlocalmemory 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +140 -0
- package/CHANGELOG.md +1749 -0
- package/LICENSE +21 -0
- package/README.md +600 -0
- package/bin/aider-smart +72 -0
- package/bin/slm +202 -0
- package/bin/slm-npm +73 -0
- package/bin/slm.bat +195 -0
- package/bin/slm.cmd +10 -0
- package/bin/superlocalmemoryv2:list +3 -0
- package/bin/superlocalmemoryv2:profile +3 -0
- package/bin/superlocalmemoryv2:recall +3 -0
- package/bin/superlocalmemoryv2:remember +3 -0
- package/bin/superlocalmemoryv2:reset +3 -0
- package/bin/superlocalmemoryv2:status +3 -0
- package/completions/slm.bash +58 -0
- package/completions/slm.zsh +76 -0
- package/configs/antigravity-mcp.json +13 -0
- package/configs/chatgpt-desktop-mcp.json +7 -0
- package/configs/claude-desktop-mcp.json +15 -0
- package/configs/codex-mcp.toml +13 -0
- package/configs/cody-commands.json +29 -0
- package/configs/continue-mcp.yaml +14 -0
- package/configs/continue-skills.yaml +26 -0
- package/configs/cursor-mcp.json +15 -0
- package/configs/gemini-cli-mcp.json +11 -0
- package/configs/jetbrains-mcp.json +11 -0
- package/configs/opencode-mcp.json +12 -0
- package/configs/perplexity-mcp.json +9 -0
- package/configs/vscode-copilot-mcp.json +12 -0
- package/configs/windsurf-mcp.json +16 -0
- package/configs/zed-mcp.json +12 -0
- package/docs/ARCHITECTURE.md +877 -0
- package/docs/CLI-COMMANDS-REFERENCE.md +425 -0
- package/docs/COMPETITIVE-ANALYSIS.md +210 -0
- package/docs/COMPRESSION-README.md +390 -0
- package/docs/GRAPH-ENGINE.md +503 -0
- package/docs/MCP-MANUAL-SETUP.md +720 -0
- package/docs/MCP-TROUBLESHOOTING.md +787 -0
- package/docs/PATTERN-LEARNING.md +363 -0
- package/docs/PROFILES-GUIDE.md +453 -0
- package/docs/RESET-GUIDE.md +353 -0
- package/docs/SEARCH-ENGINE-V2.2.0.md +748 -0
- package/docs/SEARCH-INTEGRATION-GUIDE.md +502 -0
- package/docs/UI-SERVER.md +254 -0
- package/docs/UNIVERSAL-INTEGRATION.md +432 -0
- package/docs/V2.2.0-OPTIONAL-SEARCH.md +666 -0
- package/docs/WINDOWS-INSTALL-README.txt +34 -0
- package/docs/WINDOWS-POST-INSTALL.txt +45 -0
- package/docs/example_graph_usage.py +148 -0
- package/hooks/memory-list-skill.js +130 -0
- package/hooks/memory-profile-skill.js +284 -0
- package/hooks/memory-recall-skill.js +109 -0
- package/hooks/memory-remember-skill.js +127 -0
- package/hooks/memory-reset-skill.js +274 -0
- package/install-skills.sh +436 -0
- package/install.ps1 +417 -0
- package/install.sh +755 -0
- package/mcp_server.py +585 -0
- package/package.json +94 -0
- package/requirements-core.txt +24 -0
- package/requirements.txt +10 -0
- package/scripts/postinstall.js +126 -0
- package/scripts/preuninstall.js +57 -0
- package/skills/slm-build-graph/SKILL.md +423 -0
- package/skills/slm-list-recent/SKILL.md +348 -0
- package/skills/slm-recall/SKILL.md +325 -0
- package/skills/slm-remember/SKILL.md +194 -0
- package/skills/slm-status/SKILL.md +363 -0
- package/skills/slm-switch-profile/SKILL.md +442 -0
- package/src/__pycache__/cache_manager.cpython-312.pyc +0 -0
- package/src/__pycache__/embedding_engine.cpython-312.pyc +0 -0
- package/src/__pycache__/graph_engine.cpython-312.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-312.pyc +0 -0
- package/src/__pycache__/hybrid_search.cpython-312.pyc +0 -0
- package/src/__pycache__/memory-profiles.cpython-312.pyc +0 -0
- package/src/__pycache__/memory-reset.cpython-312.pyc +0 -0
- package/src/__pycache__/memory_compression.cpython-312.pyc +0 -0
- package/src/__pycache__/memory_store_v2.cpython-312.pyc +0 -0
- package/src/__pycache__/migrate_v1_to_v2.cpython-312.pyc +0 -0
- package/src/__pycache__/pattern_learner.cpython-312.pyc +0 -0
- package/src/__pycache__/query_optimizer.cpython-312.pyc +0 -0
- package/src/__pycache__/search_engine_v2.cpython-312.pyc +0 -0
- package/src/__pycache__/setup_validator.cpython-312.pyc +0 -0
- package/src/__pycache__/tree_manager.cpython-312.pyc +0 -0
- package/src/cache_manager.py +520 -0
- package/src/embedding_engine.py +671 -0
- package/src/graph_engine.py +970 -0
- package/src/hnsw_index.py +626 -0
- package/src/hybrid_search.py +693 -0
- package/src/memory-profiles.py +518 -0
- package/src/memory-reset.py +485 -0
- package/src/memory_compression.py +999 -0
- package/src/memory_store_v2.py +1088 -0
- package/src/migrate_v1_to_v2.py +638 -0
- package/src/pattern_learner.py +898 -0
- package/src/query_optimizer.py +513 -0
- package/src/search_engine_v2.py +403 -0
- package/src/setup_validator.py +479 -0
- package/src/tree_manager.py +720 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# Search Engine Integration Guide
|
|
2
|
+
|
|
3
|
+
**Version:** 2.2.0
|
|
4
|
+
**Author:** Varun Pratap Bhardwaj
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
### 5-Minute Setup
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# 1. Install dependencies
|
|
14
|
+
pip install scikit-learn numpy
|
|
15
|
+
|
|
16
|
+
# 2. Test BM25 engine
|
|
17
|
+
python src/search_engine_v2.py
|
|
18
|
+
|
|
19
|
+
# 3. Test hybrid search
|
|
20
|
+
python src/hybrid_search.py "your search query"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Basic Usage
|
|
26
|
+
|
|
27
|
+
### Option 1: Use Existing API (No Changes)
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from memory_store_v2 import MemoryStoreV2
|
|
31
|
+
|
|
32
|
+
store = MemoryStoreV2()
|
|
33
|
+
|
|
34
|
+
# Add memories
|
|
35
|
+
store.add_memory("Python web development with Django", tags=['python', 'web'])
|
|
36
|
+
store.add_memory("JavaScript React frontend", tags=['javascript', 'react'])
|
|
37
|
+
|
|
38
|
+
# Search (uses existing TF-IDF + FTS)
|
|
39
|
+
results = store.search("Python web", limit=5)
|
|
40
|
+
|
|
41
|
+
for mem in results:
|
|
42
|
+
print(f"Score: {mem['score']:.3f}")
|
|
43
|
+
print(f"Content: {mem['content'][:100]}")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Result:** Works exactly as before. No changes required.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### Option 2: Use New Hybrid Search (Recommended)
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from pathlib import Path
|
|
54
|
+
from hybrid_search import HybridSearchEngine
|
|
55
|
+
|
|
56
|
+
# Initialize hybrid search
|
|
57
|
+
db_path = Path.home() / ".claude-memory" / "memory.db"
|
|
58
|
+
hybrid = HybridSearchEngine(db_path, enable_cache=True)
|
|
59
|
+
|
|
60
|
+
# Search with BM25 (fastest, keyword-focused)
|
|
61
|
+
results = hybrid.search("Python web", method="bm25", limit=5)
|
|
62
|
+
|
|
63
|
+
# Search with hybrid fusion (best relevance)
|
|
64
|
+
results = hybrid.search("Python web", method="hybrid", limit=5)
|
|
65
|
+
|
|
66
|
+
# Display results
|
|
67
|
+
for mem in results:
|
|
68
|
+
print(f"[{mem['id']}] Score: {mem['score']:.3f}")
|
|
69
|
+
print(f"Category: {mem.get('category', 'N/A')}")
|
|
70
|
+
print(f"Content: {mem['content'][:100]}...")
|
|
71
|
+
print()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Benefits:**
|
|
75
|
+
- 3x faster search
|
|
76
|
+
- Better relevance ranking
|
|
77
|
+
- Query optimization (spell correction)
|
|
78
|
+
- Result caching
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Integration Patterns
|
|
83
|
+
|
|
84
|
+
### Pattern 1: Drop-In Replacement
|
|
85
|
+
|
|
86
|
+
Replace existing search calls with hybrid search:
|
|
87
|
+
|
|
88
|
+
**Before:**
|
|
89
|
+
```python
|
|
90
|
+
results = store.search(query, limit=10)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**After:**
|
|
94
|
+
```python
|
|
95
|
+
results = hybrid.search(query, method="hybrid", limit=10)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Changes:** None to result format - both return same dictionary structure.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Pattern 2: Fallback Strategy
|
|
103
|
+
|
|
104
|
+
Use hybrid search with fallback:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
def smart_search(query, limit=10):
|
|
108
|
+
"""Search with hybrid engine, fallback to store."""
|
|
109
|
+
try:
|
|
110
|
+
# Try hybrid search first
|
|
111
|
+
return hybrid.search(query, method="hybrid", limit=limit)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
# Fallback to store search
|
|
114
|
+
print(f"Hybrid search failed: {e}")
|
|
115
|
+
return store.search(query, limit=limit)
|
|
116
|
+
|
|
117
|
+
results = smart_search("Python web")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### Pattern 3: Query-Adaptive Selection
|
|
123
|
+
|
|
124
|
+
Choose method based on query type:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
import re
|
|
128
|
+
|
|
129
|
+
def adaptive_search(query, limit=10):
|
|
130
|
+
"""Select search method based on query characteristics."""
|
|
131
|
+
|
|
132
|
+
# Detect query type
|
|
133
|
+
has_quotes = '"' in query
|
|
134
|
+
has_boolean = any(op in query.upper() for op in ['AND', 'OR', 'NOT'])
|
|
135
|
+
word_count = len(query.split())
|
|
136
|
+
|
|
137
|
+
# Select method
|
|
138
|
+
if has_quotes or has_boolean:
|
|
139
|
+
# Exact matching - use BM25
|
|
140
|
+
method = "bm25"
|
|
141
|
+
weights = None
|
|
142
|
+
elif word_count <= 2:
|
|
143
|
+
# Short query - use BM25 (keyword-focused)
|
|
144
|
+
method = "weighted"
|
|
145
|
+
weights = {'bm25': 0.7, 'semantic': 0.3, 'graph': 0.0}
|
|
146
|
+
else:
|
|
147
|
+
# Long query - use hybrid (balanced)
|
|
148
|
+
method = "hybrid"
|
|
149
|
+
weights = None
|
|
150
|
+
|
|
151
|
+
return hybrid.search(query, method=method, weights=weights, limit=limit)
|
|
152
|
+
|
|
153
|
+
# Examples
|
|
154
|
+
results = adaptive_search('"Python Django"') # Uses BM25
|
|
155
|
+
results = adaptive_search('Python') # Uses BM25-heavy
|
|
156
|
+
results = adaptive_search('how to build REST API') # Uses hybrid
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### Pattern 4: Progressive Enhancement
|
|
162
|
+
|
|
163
|
+
Use cache-aware search with progressive methods:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
def progressive_search(query, limit=10, use_cache=True):
|
|
167
|
+
"""Try fast methods first, fall back to comprehensive."""
|
|
168
|
+
|
|
169
|
+
# Try cache first
|
|
170
|
+
if use_cache:
|
|
171
|
+
cached = hybrid.cache.get(query, limit=limit)
|
|
172
|
+
if cached:
|
|
173
|
+
print(f"Cache hit! ({len(cached)} results)")
|
|
174
|
+
return cached
|
|
175
|
+
|
|
176
|
+
# Try BM25 first (fastest)
|
|
177
|
+
results = hybrid.search(query, method="bm25", limit=limit)
|
|
178
|
+
|
|
179
|
+
# If insufficient results, try hybrid
|
|
180
|
+
if len(results) < limit // 2:
|
|
181
|
+
print(f"BM25 returned only {len(results)} results, trying hybrid...")
|
|
182
|
+
results = hybrid.search(query, method="hybrid", limit=limit)
|
|
183
|
+
|
|
184
|
+
# Cache results
|
|
185
|
+
if use_cache:
|
|
186
|
+
hybrid.cache.put(query, results, limit=limit)
|
|
187
|
+
|
|
188
|
+
return results
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Advanced Integration
|
|
194
|
+
|
|
195
|
+
### Custom Weight Configuration
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
class SearchEngine:
|
|
199
|
+
"""Wrapper with custom search strategies."""
|
|
200
|
+
|
|
201
|
+
def __init__(self, db_path):
|
|
202
|
+
self.hybrid = HybridSearchEngine(db_path, enable_cache=True)
|
|
203
|
+
|
|
204
|
+
# Define search strategies
|
|
205
|
+
self.strategies = {
|
|
206
|
+
'keyword': {'bm25': 0.8, 'semantic': 0.2, 'graph': 0.0},
|
|
207
|
+
'semantic': {'bm25': 0.2, 'semantic': 0.5, 'graph': 0.3},
|
|
208
|
+
'balanced': {'bm25': 0.4, 'semantic': 0.3, 'graph': 0.3},
|
|
209
|
+
'graph': {'bm25': 0.1, 'semantic': 0.3, 'graph': 0.6},
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
def search(self, query, strategy='balanced', limit=10):
|
|
213
|
+
"""Search with named strategy."""
|
|
214
|
+
weights = self.strategies.get(strategy, self.strategies['balanced'])
|
|
215
|
+
return self.hybrid.search(
|
|
216
|
+
query,
|
|
217
|
+
method='weighted',
|
|
218
|
+
weights=weights,
|
|
219
|
+
limit=limit
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def keyword_search(self, query, limit=10):
|
|
223
|
+
"""Optimized for exact term matching."""
|
|
224
|
+
return self.search(query, strategy='keyword', limit=limit)
|
|
225
|
+
|
|
226
|
+
def semantic_search(self, query, limit=10):
|
|
227
|
+
"""Optimized for meaning-based search."""
|
|
228
|
+
return self.search(query, strategy='semantic', limit=limit)
|
|
229
|
+
|
|
230
|
+
def graph_search(self, query, limit=10):
|
|
231
|
+
"""Optimized for conceptual/related search."""
|
|
232
|
+
return self.search(query, strategy='graph', limit=limit)
|
|
233
|
+
|
|
234
|
+
# Usage
|
|
235
|
+
engine = SearchEngine(db_path)
|
|
236
|
+
|
|
237
|
+
# Different search modes
|
|
238
|
+
results = engine.keyword_search("Python Django REST")
|
|
239
|
+
results = engine.semantic_search("how to authenticate users")
|
|
240
|
+
results = engine.graph_search("related to performance optimization")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### Query Optimization Pipeline
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from query_optimizer import QueryOptimizer
|
|
249
|
+
|
|
250
|
+
class OptimizedSearchEngine:
|
|
251
|
+
"""Search engine with query preprocessing."""
|
|
252
|
+
|
|
253
|
+
def __init__(self, db_path):
|
|
254
|
+
self.hybrid = HybridSearchEngine(db_path)
|
|
255
|
+
self.optimizer = self.hybrid.optimizer
|
|
256
|
+
|
|
257
|
+
def search_with_correction(self, query, limit=10):
|
|
258
|
+
"""Search with automatic spell correction."""
|
|
259
|
+
|
|
260
|
+
# Optimize query
|
|
261
|
+
optimized = self.optimizer.optimize(
|
|
262
|
+
query,
|
|
263
|
+
enable_spell_correction=True,
|
|
264
|
+
enable_expansion=False
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
print(f"Original: {query}")
|
|
268
|
+
print(f"Optimized: {optimized}")
|
|
269
|
+
|
|
270
|
+
# Search with optimized query
|
|
271
|
+
results = self.hybrid.search(optimized, method="hybrid", limit=limit)
|
|
272
|
+
|
|
273
|
+
return results
|
|
274
|
+
|
|
275
|
+
def search_with_expansion(self, query, limit=10):
|
|
276
|
+
"""Search with query expansion."""
|
|
277
|
+
|
|
278
|
+
# Parse and expand
|
|
279
|
+
tokens = self.optimizer.optimize(
|
|
280
|
+
query,
|
|
281
|
+
enable_spell_correction=True,
|
|
282
|
+
enable_expansion=True,
|
|
283
|
+
max_expansions=2
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
print(f"Expanded query: {tokens}")
|
|
287
|
+
|
|
288
|
+
return self.hybrid.search(tokens, method="hybrid", limit=limit)
|
|
289
|
+
|
|
290
|
+
# Usage
|
|
291
|
+
engine = OptimizedSearchEngine(db_path)
|
|
292
|
+
|
|
293
|
+
# Auto-corrects "pythno" → "python"
|
|
294
|
+
results = engine.search_with_correction("pythno web devlopment")
|
|
295
|
+
|
|
296
|
+
# Expands "auth" → "auth authentication authorize"
|
|
297
|
+
results = engine.search_with_expansion("auth")
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Performance Optimization
|
|
303
|
+
|
|
304
|
+
### 1. Cache Configuration
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from cache_manager import CacheManager
|
|
308
|
+
|
|
309
|
+
# High-traffic application
|
|
310
|
+
cache = CacheManager(
|
|
311
|
+
max_size=1000, # Large cache
|
|
312
|
+
ttl_seconds=600, # 10 minute TTL
|
|
313
|
+
thread_safe=True # Enable for concurrent access
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
hybrid = HybridSearchEngine(db_path, cache_manager=cache)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 2. Method Selection for Speed
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
# Fastest: BM25 only (~15ms)
|
|
323
|
+
results = hybrid.search(query, method="bm25")
|
|
324
|
+
|
|
325
|
+
# Fast: BM25 + Semantic (~25ms)
|
|
326
|
+
results = hybrid.search(
|
|
327
|
+
query,
|
|
328
|
+
method="weighted",
|
|
329
|
+
weights={'bm25': 0.6, 'semantic': 0.4, 'graph': 0.0}
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# Comprehensive: All methods (~35ms)
|
|
333
|
+
results = hybrid.search(query, method="hybrid")
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 3. Limit Results Early
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
# Request fewer results for speed
|
|
340
|
+
results = hybrid.search(query, limit=5) # Fast
|
|
341
|
+
|
|
342
|
+
# Request more for completeness
|
|
343
|
+
results = hybrid.search(query, limit=50) # Slower
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### 4. Cache Warming
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
def warm_cache(common_queries):
|
|
350
|
+
"""Pre-populate cache with common queries."""
|
|
351
|
+
for query in common_queries:
|
|
352
|
+
hybrid.search(query, limit=10, use_cache=True)
|
|
353
|
+
|
|
354
|
+
stats = hybrid.cache.get_stats()
|
|
355
|
+
print(f"Cache warmed: {stats['current_size']} entries")
|
|
356
|
+
|
|
357
|
+
# Common queries
|
|
358
|
+
common_queries = [
|
|
359
|
+
"python web",
|
|
360
|
+
"javascript react",
|
|
361
|
+
"authentication",
|
|
362
|
+
"database optimization",
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
warm_cache(common_queries)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Testing Integration
|
|
371
|
+
|
|
372
|
+
### Unit Tests
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
import unittest
|
|
376
|
+
from pathlib import Path
|
|
377
|
+
import tempfile
|
|
378
|
+
import shutil
|
|
379
|
+
|
|
380
|
+
class TestSearchIntegration(unittest.TestCase):
|
|
381
|
+
|
|
382
|
+
def setUp(self):
|
|
383
|
+
"""Create temp database."""
|
|
384
|
+
self.temp_dir = tempfile.mkdtemp()
|
|
385
|
+
self.db_path = Path(self.temp_dir) / "test.db"
|
|
386
|
+
|
|
387
|
+
# Create store and add test data
|
|
388
|
+
from memory_store_v2 import MemoryStoreV2
|
|
389
|
+
store = MemoryStoreV2(db_path=self.db_path)
|
|
390
|
+
|
|
391
|
+
store.add_memory("Python web development", tags=['python', 'web'])
|
|
392
|
+
store.add_memory("JavaScript frontend", tags=['javascript'])
|
|
393
|
+
|
|
394
|
+
# Initialize hybrid search
|
|
395
|
+
self.hybrid = HybridSearchEngine(self.db_path)
|
|
396
|
+
|
|
397
|
+
def tearDown(self):
|
|
398
|
+
"""Cleanup temp database."""
|
|
399
|
+
shutil.rmtree(self.temp_dir)
|
|
400
|
+
|
|
401
|
+
def test_bm25_search(self):
|
|
402
|
+
"""Test BM25 search returns results."""
|
|
403
|
+
results = self.hybrid.search("Python", method="bm25", limit=5)
|
|
404
|
+
self.assertGreater(len(results), 0)
|
|
405
|
+
self.assertIn('score', results[0])
|
|
406
|
+
|
|
407
|
+
def test_hybrid_search(self):
|
|
408
|
+
"""Test hybrid search returns results."""
|
|
409
|
+
results = self.hybrid.search("Python", method="hybrid", limit=5)
|
|
410
|
+
self.assertGreater(len(results), 0)
|
|
411
|
+
|
|
412
|
+
def test_cache_hit(self):
|
|
413
|
+
"""Test cache working."""
|
|
414
|
+
# First query - cache miss
|
|
415
|
+
self.hybrid.search("Python", method="bm25")
|
|
416
|
+
|
|
417
|
+
# Second query - cache hit
|
|
418
|
+
self.hybrid.search("Python", method="bm25")
|
|
419
|
+
|
|
420
|
+
stats = self.hybrid.cache.get_stats()
|
|
421
|
+
self.assertGreater(stats['hits'], 0)
|
|
422
|
+
|
|
423
|
+
if __name__ == '__main__':
|
|
424
|
+
unittest.main()
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Migration Checklist
|
|
430
|
+
|
|
431
|
+
### From V2.1.0 to V2.2.0
|
|
432
|
+
|
|
433
|
+
- [ ] Install dependencies: `pip install scikit-learn numpy`
|
|
434
|
+
- [ ] Test existing search: `python -c "from memory_store_v2 import MemoryStoreV2; store = MemoryStoreV2(); print(store.search('test'))"`
|
|
435
|
+
- [ ] Test new BM25: `python src/search_engine_v2.py`
|
|
436
|
+
- [ ] Test hybrid search: `python src/hybrid_search.py "test query"`
|
|
437
|
+
- [ ] Run test suite: `python test_search_engine.py`
|
|
438
|
+
- [ ] Update application code (optional)
|
|
439
|
+
- [ ] Monitor performance improvements
|
|
440
|
+
- [ ] Adjust weights if needed
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## Troubleshooting
|
|
445
|
+
|
|
446
|
+
### Issue: Import Error
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
ImportError: No module named 'sklearn'
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**Solution:**
|
|
453
|
+
```bash
|
|
454
|
+
pip install scikit-learn numpy
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### Issue: Slow Search
|
|
458
|
+
|
|
459
|
+
**Check:**
|
|
460
|
+
1. Database size: `SELECT COUNT(*) FROM memories`
|
|
461
|
+
2. Cache stats: `hybrid.cache.get_stats()`
|
|
462
|
+
3. Search method: Try `method="bm25"` for speed
|
|
463
|
+
|
|
464
|
+
**Solutions:**
|
|
465
|
+
- Use BM25 only for keyword queries
|
|
466
|
+
- Enable caching
|
|
467
|
+
- Reduce result limit
|
|
468
|
+
|
|
469
|
+
### Issue: Poor Relevance
|
|
470
|
+
|
|
471
|
+
**Solutions:**
|
|
472
|
+
1. Try hybrid search: `method="hybrid"`
|
|
473
|
+
2. Adjust weights for query type
|
|
474
|
+
3. Enable query expansion for short queries
|
|
475
|
+
4. Use spell correction for typos
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## Best Practices
|
|
480
|
+
|
|
481
|
+
1. **Use hybrid search by default** - Best balance of speed and relevance
|
|
482
|
+
2. **Enable caching** - 30-50% performance improvement on repeated queries
|
|
483
|
+
3. **Choose method based on query** - Use adaptive_search pattern
|
|
484
|
+
4. **Monitor cache stats** - Optimize cache size based on hit rate
|
|
485
|
+
5. **Test different weights** - Fine-tune for your specific use case
|
|
486
|
+
6. **Keep vocabulary updated** - Rebuild index when adding many memories
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## Support
|
|
491
|
+
|
|
492
|
+
**Documentation:**
|
|
493
|
+
- [Search Engine V2.2.0](SEARCH-ENGINE-V2.2.0.md)
|
|
494
|
+
- [API Reference](API-REFERENCE.md)
|
|
495
|
+
- [Main README](../README.md)
|
|
496
|
+
|
|
497
|
+
**Issues:** [GitHub Issues](https://github.com/varun369/SuperLocalMemoryV2/issues)
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
**Created by:** Varun Pratap Bhardwaj
|
|
502
|
+
**License:** MIT
|