@naveenadi/mnemonic 0.1.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/README.md +93 -0
- package/SKILL.md +158 -0
- package/bin/mne +8 -0
- package/dist/chunker/index.d.ts +8 -0
- package/dist/chunker/index.d.ts.map +1 -0
- package/dist/chunker/index.js +157 -0
- package/dist/chunker/index.js.map +1 -0
- package/dist/cli/commands.d.ts +2 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +793 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mne.d.ts +3 -0
- package/dist/cli/mne.d.ts.map +1 -0
- package/dist/cli/mne.js +7 -0
- package/dist/cli/mne.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/factory.d.ts +15 -0
- package/dist/llm/factory.d.ts.map +1 -0
- package/dist/llm/factory.js +60 -0
- package/dist/llm/factory.js.map +1 -0
- package/dist/llm/index.d.ts +19 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +18 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/node-llama-cpp.d.ts +30 -0
- package/dist/llm/node-llama-cpp.d.ts.map +1 -0
- package/dist/llm/node-llama-cpp.js +143 -0
- package/dist/llm/node-llama-cpp.js.map +1 -0
- package/dist/llm/ollama.d.ts +19 -0
- package/dist/llm/ollama.d.ts.map +1 -0
- package/dist/llm/ollama.js +99 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +360 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/pi-extension/index.d.ts +3 -0
- package/dist/pi-extension/index.d.ts.map +1 -0
- package/dist/pi-extension/index.js +193 -0
- package/dist/pi-extension/index.js.map +1 -0
- package/dist/search/fts.d.ts +17 -0
- package/dist/search/fts.d.ts.map +1 -0
- package/dist/search/fts.js +110 -0
- package/dist/search/fts.js.map +1 -0
- package/dist/search/fusion.d.ts +33 -0
- package/dist/search/fusion.d.ts.map +1 -0
- package/dist/search/fusion.js +97 -0
- package/dist/search/fusion.js.map +1 -0
- package/dist/search/pipeline.d.ts +34 -0
- package/dist/search/pipeline.d.ts.map +1 -0
- package/dist/search/pipeline.js +216 -0
- package/dist/search/pipeline.js.map +1 -0
- package/dist/search/vector.d.ts +20 -0
- package/dist/search/vector.d.ts.map +1 -0
- package/dist/search/vector.js +70 -0
- package/dist/search/vector.js.map +1 -0
- package/dist/store/collections.d.ts +23 -0
- package/dist/store/collections.d.ts.map +1 -0
- package/dist/store/collections.js +99 -0
- package/dist/store/collections.js.map +1 -0
- package/dist/store/database.d.ts +22 -0
- package/dist/store/database.d.ts.map +1 -0
- package/dist/store/database.js +209 -0
- package/dist/store/database.js.map +1 -0
- package/dist/store/documents.d.ts +68 -0
- package/dist/store/documents.d.ts.map +1 -0
- package/dist/store/documents.js +273 -0
- package/dist/store/documents.js.map +1 -0
- package/dist/types.d.ts +193 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export class FTSSearch {
|
|
2
|
+
db;
|
|
3
|
+
constructor(db) {
|
|
4
|
+
this.db = db;
|
|
5
|
+
}
|
|
6
|
+
/** BM25 search via FTS5 */
|
|
7
|
+
search(query, options = {}) {
|
|
8
|
+
const limit = options.limit ?? 20;
|
|
9
|
+
const minScore = options.minScore ?? 0;
|
|
10
|
+
// FTS5 syntax: clean the query
|
|
11
|
+
const cleanQuery = this.cleanQuery(query);
|
|
12
|
+
if (!cleanQuery)
|
|
13
|
+
return [];
|
|
14
|
+
const collectionFilter = options.collection?.length
|
|
15
|
+
? options.collection.map((c) => `AND d.collection = '${c.replace(/'/g, "''")}'`).join(' ')
|
|
16
|
+
: '';
|
|
17
|
+
const sql = `
|
|
18
|
+
SELECT d.docid, d.collection, d.path, d.full_path, d.title, d.tags,
|
|
19
|
+
fts.rank as bm25_score,
|
|
20
|
+
snippet(documents_fts, 0, '<mark>', '</mark>', '...', 32) as snippet,
|
|
21
|
+
d.modified_at
|
|
22
|
+
FROM documents_fts fts
|
|
23
|
+
JOIN documents d ON d.id = fts.rowid
|
|
24
|
+
WHERE documents_fts MATCH ? ${collectionFilter}
|
|
25
|
+
ORDER BY rank
|
|
26
|
+
LIMIT ?
|
|
27
|
+
`;
|
|
28
|
+
try {
|
|
29
|
+
const rows = this.db.db.prepare(sql).all(cleanQuery, limit);
|
|
30
|
+
return rows
|
|
31
|
+
.filter((r) => {
|
|
32
|
+
const score = Math.abs(r.bm25_score);
|
|
33
|
+
return score >= minScore;
|
|
34
|
+
})
|
|
35
|
+
.map((r) => ({
|
|
36
|
+
docid: r.docid,
|
|
37
|
+
collection: r.collection,
|
|
38
|
+
path: `mne://${r.collection}/${r.path}`,
|
|
39
|
+
fullPath: r.full_path,
|
|
40
|
+
title: r.title,
|
|
41
|
+
score: Math.abs(r.bm25_score),
|
|
42
|
+
snippet: r.snippet.replace(/<mark>/g, '**').replace(/<\/mark>/g, '**'),
|
|
43
|
+
context: [],
|
|
44
|
+
tags: r.tags ? JSON.parse(r.tags) : [],
|
|
45
|
+
line: 1,
|
|
46
|
+
heading: '',
|
|
47
|
+
modifiedAt: r.modified_at ?? '',
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// FTS5 query syntax error — fall back to LIKE search
|
|
52
|
+
return this.fallbackSearch(cleanQuery, options);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Fallback to LIKE-based search when FTS5 syntax fails */
|
|
56
|
+
fallbackSearch(query, options) {
|
|
57
|
+
const limit = options.limit ?? 20;
|
|
58
|
+
const terms = query
|
|
59
|
+
.replace(/["*()]/g, '')
|
|
60
|
+
.split(/\s+/)
|
|
61
|
+
.filter(Boolean);
|
|
62
|
+
if (terms.length === 0)
|
|
63
|
+
return [];
|
|
64
|
+
const likeClauses = terms.map(() => '(d.title LIKE ? OR d.content LIKE ?)').join(' AND ');
|
|
65
|
+
const params = terms.flatMap((t) => [`%${t}%`, `%${t}%`]);
|
|
66
|
+
const collectionFilter = options.collection?.length
|
|
67
|
+
? options.collection.map((c) => `AND d.collection = '${c.replace(/'/g, "''")}'`).join(' ')
|
|
68
|
+
: '';
|
|
69
|
+
const sql = `
|
|
70
|
+
SELECT d.docid, d.collection, d.path, d.full_path, d.title, d.tags,
|
|
71
|
+
d.content, d.modified_at
|
|
72
|
+
FROM documents d
|
|
73
|
+
WHERE ${likeClauses} ${collectionFilter}
|
|
74
|
+
ORDER BY d.size DESC
|
|
75
|
+
LIMIT ?
|
|
76
|
+
`;
|
|
77
|
+
const rows = this.db.db.prepare(sql).all(...params, limit);
|
|
78
|
+
return rows.map((r) => ({
|
|
79
|
+
docid: r.docid,
|
|
80
|
+
collection: r.collection,
|
|
81
|
+
path: `mne://${r.collection}/${r.path}`,
|
|
82
|
+
fullPath: r.full_path,
|
|
83
|
+
title: r.title,
|
|
84
|
+
score: 0.5, // uniform score for LIKE results
|
|
85
|
+
snippet: r.content.slice(0, 200).replace(/\n/g, ' '),
|
|
86
|
+
context: [],
|
|
87
|
+
tags: r.tags ? JSON.parse(r.tags) : [],
|
|
88
|
+
line: 1,
|
|
89
|
+
heading: '',
|
|
90
|
+
modifiedAt: r.modified_at ?? '',
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
/** Clean a query for FTS5 syntax */
|
|
94
|
+
cleanQuery(query) {
|
|
95
|
+
// If already FTS5 syntax (has quotes, operators), keep as-is
|
|
96
|
+
if (/["*()OR AND NEAR]/.test(query))
|
|
97
|
+
return query;
|
|
98
|
+
// Convert to FTS5: escape special chars, wrap phrases in quotes
|
|
99
|
+
return query
|
|
100
|
+
.split(/\s+/)
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.map((term) => {
|
|
103
|
+
if (/^[a-zA-Z0-9_-]+$/.test(term))
|
|
104
|
+
return term;
|
|
105
|
+
return `"${term.replace(/"/g, '')}"`;
|
|
106
|
+
})
|
|
107
|
+
.join(' ');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=fts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fts.js","sourceRoot":"","sources":["../../src/search/fts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SearchResult } from '../types.js';
|
|
2
|
+
interface RankedResult {
|
|
3
|
+
result: SearchResult;
|
|
4
|
+
contributions: Array<{
|
|
5
|
+
source: 'fts' | 'vector' | 'hyde';
|
|
6
|
+
queryType: 'original' | 'expanded' | 'hyde';
|
|
7
|
+
query: string;
|
|
8
|
+
rank: number;
|
|
9
|
+
weight: number;
|
|
10
|
+
backendScore: number;
|
|
11
|
+
rrfContribution: number;
|
|
12
|
+
}>;
|
|
13
|
+
totalRRF: number;
|
|
14
|
+
bestRank: number;
|
|
15
|
+
}
|
|
16
|
+
/** Apply Reciprocal Rank Fusion to merge ranked result sets */
|
|
17
|
+
export declare function fuseResults(lists: Array<{
|
|
18
|
+
results: SearchResult[];
|
|
19
|
+
source: 'fts' | 'vector' | 'hyde';
|
|
20
|
+
queryType: 'original' | 'expanded' | 'hyde';
|
|
21
|
+
query: string;
|
|
22
|
+
weight: number;
|
|
23
|
+
}>, options?: {
|
|
24
|
+
topRankBonus?: boolean;
|
|
25
|
+
candidateLimit?: number;
|
|
26
|
+
}): {
|
|
27
|
+
results: SearchResult[];
|
|
28
|
+
ranked: Map<string, RankedResult>;
|
|
29
|
+
};
|
|
30
|
+
/** Apply position-aware blending between RRF and reranker scores */
|
|
31
|
+
export declare function blendWithRerank(rrfRanked: Map<string, RankedResult>, rerankScores: Map<string, number>, limit?: number): SearchResult[];
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=fusion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fusion.d.ts","sourceRoot":"","sources":["../../src/search/fusion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,aAAa,CAAC;AAI9D,UAAU,YAAY;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,aAAa,EAAE,KAAK,CAAC;QACnB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAClC,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;QAC5C,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC,CAAC;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CACzB,KAAK,EAAE,KAAK,CAAC;IACX,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,EACF,OAAO,CAAC,EAAE;IACR,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA;IAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CAAE,CAmEhE;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACpC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,KAAK,CAAC,EAAE,MAAM,GACb,YAAY,EAAE,CA0ChB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const RRF_K = 60;
|
|
2
|
+
/** Apply Reciprocal Rank Fusion to merge ranked result sets */
|
|
3
|
+
export function fuseResults(lists, options) {
|
|
4
|
+
const rankMap = new Map();
|
|
5
|
+
const candidateLimit = options?.candidateLimit ?? 30;
|
|
6
|
+
// Collect all results into rank map
|
|
7
|
+
for (const list of lists) {
|
|
8
|
+
const w = list.weight;
|
|
9
|
+
for (let i = 0; i < list.results.length; i++) {
|
|
10
|
+
const r = list.results[i];
|
|
11
|
+
const rank = i + 1;
|
|
12
|
+
const rrfScore = 1 / (RRF_K + rank);
|
|
13
|
+
if (!rankMap.has(r.docid)) {
|
|
14
|
+
rankMap.set(r.docid, {
|
|
15
|
+
result: r,
|
|
16
|
+
contributions: [],
|
|
17
|
+
totalRRF: 0,
|
|
18
|
+
bestRank: rank,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const entry = rankMap.get(r.docid);
|
|
22
|
+
const contribution = {
|
|
23
|
+
source: list.source,
|
|
24
|
+
queryType: list.queryType,
|
|
25
|
+
query: list.query,
|
|
26
|
+
rank,
|
|
27
|
+
weight: w,
|
|
28
|
+
backendScore: r.score,
|
|
29
|
+
rrfContribution: rrfScore * w,
|
|
30
|
+
};
|
|
31
|
+
entry.contributions.push(contribution);
|
|
32
|
+
entry.totalRRF += rrfScore * w;
|
|
33
|
+
entry.bestRank = Math.min(entry.bestRank, rank);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Apply top-rank bonus: docs ranked #1 in any list get +0.05, #2-3 get +0.02
|
|
37
|
+
if (options?.topRankBonus !== false) {
|
|
38
|
+
for (const [, entry] of rankMap) {
|
|
39
|
+
for (const c of entry.contributions) {
|
|
40
|
+
if (c.rank === 1 && c.weight >= 1) {
|
|
41
|
+
entry.totalRRF += 0.05;
|
|
42
|
+
}
|
|
43
|
+
else if (c.rank <= 3) {
|
|
44
|
+
entry.totalRRF += 0.02;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Sort by RRF score descending
|
|
50
|
+
const sorted = [...rankMap.entries()]
|
|
51
|
+
.sort((a, b) => b[1].totalRRF - a[1].totalRRF)
|
|
52
|
+
.slice(0, candidateLimit);
|
|
53
|
+
// Build results with scores
|
|
54
|
+
const results = [];
|
|
55
|
+
const ranked = new Map();
|
|
56
|
+
for (const [docid, entry] of sorted) {
|
|
57
|
+
entry.result.score = entry.totalRRF;
|
|
58
|
+
results.push(entry.result);
|
|
59
|
+
ranked.set(docid, entry);
|
|
60
|
+
}
|
|
61
|
+
return { results, ranked };
|
|
62
|
+
}
|
|
63
|
+
/** Apply position-aware blending between RRF and reranker scores */
|
|
64
|
+
export function blendWithRerank(rrfRanked, rerankScores, limit) {
|
|
65
|
+
const blended = [];
|
|
66
|
+
// Sort RRF entries by rank to determine position
|
|
67
|
+
const sortedRrf = [...rrfRanked.entries()].sort((a, b) => b[1].totalRRF - a[1].totalRRF);
|
|
68
|
+
for (let i = 0; i < sortedRrf.length; i++) {
|
|
69
|
+
const [docid, entry] = sortedRrf[i];
|
|
70
|
+
const rank = i + 1;
|
|
71
|
+
const rerankScore = rerankScores.get(docid);
|
|
72
|
+
let rrfWeight;
|
|
73
|
+
let rerankWeight;
|
|
74
|
+
if (rank <= 3) {
|
|
75
|
+
rrfWeight = 0.75;
|
|
76
|
+
rerankWeight = 0.25;
|
|
77
|
+
}
|
|
78
|
+
else if (rank <= 10) {
|
|
79
|
+
rrfWeight = 0.6;
|
|
80
|
+
rerankWeight = 0.4;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
rrfWeight = 0.4;
|
|
84
|
+
rerankWeight = 0.6;
|
|
85
|
+
}
|
|
86
|
+
const finalScore = rrfWeight * entry.totalRRF +
|
|
87
|
+
(rerankScore !== undefined ? rerankWeight * rerankScore : 0);
|
|
88
|
+
const result = { ...entry.result };
|
|
89
|
+
result.score = Math.min(1, Math.max(0, finalScore));
|
|
90
|
+
blended.push({ result, score: finalScore });
|
|
91
|
+
}
|
|
92
|
+
// Sort by blended score
|
|
93
|
+
blended.sort((a, b) => b.score - a.score);
|
|
94
|
+
const limitNum = limit ?? 10;
|
|
95
|
+
return blended.slice(0, limitNum).map((b) => b.result);
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=fusion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fusion.js","sourceRoot":"","sources":["../../src/search/fusion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MnemonicDB } from '../store/database.js';
|
|
2
|
+
import type { LLMBackend, SearchOptions, SearchResult } from '../types.js';
|
|
3
|
+
export declare class SearchPipeline {
|
|
4
|
+
private db;
|
|
5
|
+
private llm?;
|
|
6
|
+
private fts;
|
|
7
|
+
private vector;
|
|
8
|
+
private docs;
|
|
9
|
+
constructor(db: MnemonicDB, llm?: LLMBackend);
|
|
10
|
+
/** Set LLM backend after construction */
|
|
11
|
+
setLLM(llm: LLMBackend): void;
|
|
12
|
+
/** Main search entry point */
|
|
13
|
+
search(options: SearchOptions): Promise<SearchResult[]>;
|
|
14
|
+
/** Apply time decay and link boost post-processors */
|
|
15
|
+
private applyPostProcessors;
|
|
16
|
+
/** Apply exponential time decay based on modification date */
|
|
17
|
+
private applyTimeDecay;
|
|
18
|
+
/** Boost results that have more incoming links */
|
|
19
|
+
private applyLinkBoost;
|
|
20
|
+
/** Get default collection names */
|
|
21
|
+
private getDefaultCollections;
|
|
22
|
+
/** BM25-only search (fast, no LLM) */
|
|
23
|
+
searchLex(query: string, options?: {
|
|
24
|
+
collection?: string[];
|
|
25
|
+
limit?: number;
|
|
26
|
+
minScore?: number;
|
|
27
|
+
}): SearchResult[];
|
|
28
|
+
/** Vector-only search */
|
|
29
|
+
searchVector(query: string, options?: {
|
|
30
|
+
collection?: string[];
|
|
31
|
+
limit?: number;
|
|
32
|
+
}): Promise<SearchResult[]>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/search/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAK1F,qBAAa,cAAc;IAMvB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,GAAG,CAAC;IANd,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,IAAI,CAAgB;gBAGlB,EAAE,EAAE,UAAU,EACd,GAAG,CAAC,EAAE,UAAU;IAO1B,yCAAyC;IACzC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAI7B,8BAA8B;IACxB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAmI7D,sDAAsD;IACtD,OAAO,CAAC,mBAAmB;IAuB3B,8DAA8D;IAC9D,OAAO,CAAC,cAAc;IAyBtB,kDAAkD;IAClD,OAAO,CAAC,cAAc;IAwBtB,mCAAmC;IACnC,OAAO,CAAC,qBAAqB;IAO7B,sCAAsC;IACtC,SAAS,CACP,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GACrE,YAAY,EAAE;IAIjB,yBAAyB;IACnB,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAClD,OAAO,CAAC,YAAY,EAAE,CAAC;CAU3B"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { DocumentStore } from '../store/documents.js';
|
|
2
|
+
import { FTSSearch } from './fts.js';
|
|
3
|
+
import { VectorSearch } from './vector.js';
|
|
4
|
+
import { fuseResults, blendWithRerank } from './fusion.js';
|
|
5
|
+
export class SearchPipeline {
|
|
6
|
+
db;
|
|
7
|
+
llm;
|
|
8
|
+
fts;
|
|
9
|
+
vector;
|
|
10
|
+
docs;
|
|
11
|
+
constructor(db, llm) {
|
|
12
|
+
this.db = db;
|
|
13
|
+
this.llm = llm;
|
|
14
|
+
this.fts = new FTSSearch(db);
|
|
15
|
+
this.vector = new VectorSearch(db);
|
|
16
|
+
this.docs = new DocumentStore(db);
|
|
17
|
+
}
|
|
18
|
+
/** Set LLM backend after construction */
|
|
19
|
+
setLLM(llm) {
|
|
20
|
+
this.llm = llm;
|
|
21
|
+
}
|
|
22
|
+
/** Main search entry point */
|
|
23
|
+
async search(options) {
|
|
24
|
+
const { query, queries, intent, collection, limit, minScore, rerank } = options;
|
|
25
|
+
const expand = options.expand ?? true;
|
|
26
|
+
const useHyde = options.hyde ?? true;
|
|
27
|
+
const limitNum = limit ?? 10;
|
|
28
|
+
const candidateLimit = options.candidateLimit ?? 40;
|
|
29
|
+
// Resolve collection filter
|
|
30
|
+
const collectionFilter = collection
|
|
31
|
+
? (Array.isArray(collection) ? collection : [collection])
|
|
32
|
+
: undefined;
|
|
33
|
+
// If no filter, use default collections
|
|
34
|
+
const collections = collectionFilter ?? this.getDefaultCollections();
|
|
35
|
+
// Determine which queries to run
|
|
36
|
+
let expandedQueries = queries ?? [];
|
|
37
|
+
if (!queries && query) {
|
|
38
|
+
// Auto-expand query
|
|
39
|
+
if (expand && this.llm) {
|
|
40
|
+
const expansions = await this.llm.expandQuery(query, intent);
|
|
41
|
+
expandedQueries = [
|
|
42
|
+
{ type: 'lex', query },
|
|
43
|
+
{ type: 'vec', query },
|
|
44
|
+
...expansions.map((q) => ({ type: 'vec', query: q })),
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
expandedQueries = [
|
|
49
|
+
{ type: 'lex', query },
|
|
50
|
+
{ type: 'vec', query },
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
// Add HyDE if enabled
|
|
54
|
+
if (useHyde && this.llm) {
|
|
55
|
+
const hydeDoc = await this.llm.generateHyde(query, intent);
|
|
56
|
+
if (hydeDoc && hydeDoc !== query) {
|
|
57
|
+
expandedQueries.push({ type: 'hyde', query: hydeDoc });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Run all sub-queries against FTS and vector backends
|
|
62
|
+
const lists = [];
|
|
63
|
+
for (const eq of expandedQueries) {
|
|
64
|
+
let isOriginal = false;
|
|
65
|
+
if (!queries && eq.query === query && eq.type !== 'hyde') {
|
|
66
|
+
isOriginal = true;
|
|
67
|
+
}
|
|
68
|
+
const weight = isOriginal ? 2 : 1;
|
|
69
|
+
const queryType = eq.type === 'hyde' ? 'hyde' : (isOriginal ? 'original' : 'expanded');
|
|
70
|
+
const source = eq.type === 'lex' ? 'fts' : eq.type === 'hyde' ? 'hyde' : 'vector';
|
|
71
|
+
if (eq.type === 'lex') {
|
|
72
|
+
const results = this.fts.search(eq.query, {
|
|
73
|
+
collection: collections,
|
|
74
|
+
limit: candidateLimit,
|
|
75
|
+
minScore,
|
|
76
|
+
});
|
|
77
|
+
lists.push({ results, source: 'fts', queryType: queryType, query: eq.query, weight });
|
|
78
|
+
}
|
|
79
|
+
else if (eq.type === 'vec' && this.llm) {
|
|
80
|
+
try {
|
|
81
|
+
const [embedding] = await this.llm.embed([eq.query]);
|
|
82
|
+
const results = this.vector.search(embedding, {
|
|
83
|
+
collection: collections,
|
|
84
|
+
limit: candidateLimit,
|
|
85
|
+
minScore,
|
|
86
|
+
});
|
|
87
|
+
lists.push({ results, source: 'vector', queryType: queryType, query: eq.query, weight });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Embedding failed, skip
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (eq.type === 'hyde' && this.llm) {
|
|
94
|
+
try {
|
|
95
|
+
const [embedding] = await this.llm.embed([eq.query]);
|
|
96
|
+
const results = this.vector.search(embedding, {
|
|
97
|
+
collection: collections,
|
|
98
|
+
limit: candidateLimit,
|
|
99
|
+
minScore,
|
|
100
|
+
});
|
|
101
|
+
lists.push({ results, source: 'hyde', queryType: 'hyde', query: eq.query, weight });
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// HyDE embedding failed, skip
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (lists.length === 0)
|
|
109
|
+
return [];
|
|
110
|
+
// RRF Fusion
|
|
111
|
+
const { results: fused, ranked } = fuseResults(lists, {
|
|
112
|
+
topRankBonus: true,
|
|
113
|
+
candidateLimit,
|
|
114
|
+
});
|
|
115
|
+
if (fused.length === 0)
|
|
116
|
+
return [];
|
|
117
|
+
// LLM Reranking
|
|
118
|
+
if (rerank !== false && this.llm && fused.length > 1) {
|
|
119
|
+
try {
|
|
120
|
+
const docs = fused.map((r) => r.snippet || r.title);
|
|
121
|
+
const scores = await this.llm.rerank(query ?? '', docs);
|
|
122
|
+
const rerankScores = new Map();
|
|
123
|
+
for (let i = 0; i < fused.length; i++) {
|
|
124
|
+
rerankScores.set(fused[i].docid, scores[i] ?? 0);
|
|
125
|
+
}
|
|
126
|
+
const blended = blendWithRerank(ranked, rerankScores, limitNum);
|
|
127
|
+
// Apply time decay and link boost
|
|
128
|
+
return this.applyPostProcessors(blended, options);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Reranking failed, use fused results
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Without reranking, use fused results directly
|
|
135
|
+
const results = fused.slice(0, limitNum);
|
|
136
|
+
return this.applyPostProcessors(results, options);
|
|
137
|
+
}
|
|
138
|
+
/** Apply time decay and link boost post-processors */
|
|
139
|
+
applyPostProcessors(results, options) {
|
|
140
|
+
let processed = results;
|
|
141
|
+
if (options.decay) {
|
|
142
|
+
processed = this.applyTimeDecay(processed, options.decayHalfLifeDays ?? 30);
|
|
143
|
+
}
|
|
144
|
+
if (options.boostLinks) {
|
|
145
|
+
processed = this.applyLinkBoost(processed);
|
|
146
|
+
}
|
|
147
|
+
// Add context
|
|
148
|
+
processed = processed.map((r) => ({
|
|
149
|
+
...r,
|
|
150
|
+
context: this.docs.getContextChain(r.collection, r.path.replace(`mne://${r.collection}/`, '')),
|
|
151
|
+
}));
|
|
152
|
+
return processed;
|
|
153
|
+
}
|
|
154
|
+
/** Apply exponential time decay based on modification date */
|
|
155
|
+
applyTimeDecay(results, halfLifeDays) {
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
const halfLifeMs = halfLifeDays * 24 * 60 * 60 * 1000;
|
|
158
|
+
return results.map((r) => {
|
|
159
|
+
let decayFactor = 1;
|
|
160
|
+
if (r.modifiedAt) {
|
|
161
|
+
const modified = new Date(r.modifiedAt).getTime();
|
|
162
|
+
if (!isNaN(modified)) {
|
|
163
|
+
const ageMs = now - modified;
|
|
164
|
+
decayFactor = Math.pow(0.5, ageMs / halfLifeMs);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
...r,
|
|
169
|
+
score: r.score * (0.7 + 0.3 * decayFactor),
|
|
170
|
+
};
|
|
171
|
+
}).sort((a, b) => b.score - a.score);
|
|
172
|
+
}
|
|
173
|
+
/** Boost results that have more incoming links */
|
|
174
|
+
applyLinkBoost(results) {
|
|
175
|
+
const maxLinks = Math.max(1, ...results.map((r) => {
|
|
176
|
+
const count = this.db.db
|
|
177
|
+
.prepare('SELECT COUNT(*) as c FROM links WHERE target_docid = ?')
|
|
178
|
+
.get(r.docid);
|
|
179
|
+
return count.c;
|
|
180
|
+
}));
|
|
181
|
+
return results.map((r) => {
|
|
182
|
+
const count = this.db.db
|
|
183
|
+
.prepare('SELECT COUNT(*) as c FROM links WHERE target_docid = ?')
|
|
184
|
+
.get(r.docid);
|
|
185
|
+
const boost = 1 + 0.2 * (count.c / maxLinks);
|
|
186
|
+
return {
|
|
187
|
+
...r,
|
|
188
|
+
score: r.score * boost,
|
|
189
|
+
};
|
|
190
|
+
}).sort((a, b) => b.score - a.score);
|
|
191
|
+
}
|
|
192
|
+
/** Get default collection names */
|
|
193
|
+
getDefaultCollections() {
|
|
194
|
+
const rows = this.db.db
|
|
195
|
+
.prepare('SELECT name FROM collections WHERE include_by_default = 1')
|
|
196
|
+
.all();
|
|
197
|
+
return rows.map((r) => r.name);
|
|
198
|
+
}
|
|
199
|
+
/** BM25-only search (fast, no LLM) */
|
|
200
|
+
searchLex(query, options) {
|
|
201
|
+
return this.fts.search(query, options);
|
|
202
|
+
}
|
|
203
|
+
/** Vector-only search */
|
|
204
|
+
async searchVector(query, options) {
|
|
205
|
+
if (!this.llm)
|
|
206
|
+
return [];
|
|
207
|
+
try {
|
|
208
|
+
const [embedding] = await this.llm.embed([query]);
|
|
209
|
+
return this.vector.search(embedding, options);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/search/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MnemonicDB } from '../store/database.js';
|
|
2
|
+
import type { SearchResult } from '../types.js';
|
|
3
|
+
export declare class VectorSearch {
|
|
4
|
+
private db;
|
|
5
|
+
constructor(db: MnemonicDB);
|
|
6
|
+
/** Search by embedding (cosine similarity) */
|
|
7
|
+
search(embedding: number[], options?: {
|
|
8
|
+
collection?: string[];
|
|
9
|
+
limit?: number;
|
|
10
|
+
minScore?: number;
|
|
11
|
+
}): SearchResult[];
|
|
12
|
+
/** Store embeddings in the vector index */
|
|
13
|
+
storeVectors(vectors: Array<{
|
|
14
|
+
hash: string;
|
|
15
|
+
embedding: number[];
|
|
16
|
+
}>): void;
|
|
17
|
+
/** Delete vectors for a document */
|
|
18
|
+
deleteVectors(hash: string): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=vector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector.d.ts","sourceRoot":"","sources":["../../src/search/vector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,qBAAa,YAAY;IACX,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,UAAU;IAElC,8CAA8C;IAC9C,MAAM,CACJ,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,GAAE;QACP,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd,GACL,YAAY,EAAE;IA4DjB,2CAA2C;IAC3C,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,GAAG,IAAI;IAmBzE,oCAAoC;IACpC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAIlC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export class VectorSearch {
|
|
2
|
+
db;
|
|
3
|
+
constructor(db) {
|
|
4
|
+
this.db = db;
|
|
5
|
+
}
|
|
6
|
+
/** Search by embedding (cosine similarity) */
|
|
7
|
+
search(embedding, options = {}) {
|
|
8
|
+
const limit = options.limit ?? 20;
|
|
9
|
+
const minScore = options.minScore ?? 0;
|
|
10
|
+
// Check if vector table exists
|
|
11
|
+
if (!this.db.hasVectorIndex())
|
|
12
|
+
return [];
|
|
13
|
+
const collectionFilter = options.collection?.length
|
|
14
|
+
? options.collection.map((c) => `AND d.collection = '${c.replace(/'/g, "''")}'`).join(' ')
|
|
15
|
+
: '';
|
|
16
|
+
// Format embedding for vec0 query
|
|
17
|
+
const embeddingStr = `[${embedding.join(',')}]`;
|
|
18
|
+
const sql = `
|
|
19
|
+
SELECT d.docid, d.collection, d.path, d.full_path, d.title, d.tags,
|
|
20
|
+
v.distance, c.heading, c.pos, d.modified_at
|
|
21
|
+
FROM vectors_vec v
|
|
22
|
+
JOIN vectors v2 ON v.hash = v2.hash
|
|
23
|
+
JOIN chunks c ON c.docid = v2.hash AND c.seq = 0
|
|
24
|
+
JOIN documents d ON d.docid = v2.hash
|
|
25
|
+
WHERE v.embedding MATCH ? ${collectionFilter}
|
|
26
|
+
AND v.distance <= ${1 - minScore}
|
|
27
|
+
ORDER BY v.distance
|
|
28
|
+
LIMIT ?
|
|
29
|
+
`;
|
|
30
|
+
try {
|
|
31
|
+
const rows = this.db.db.prepare(sql).all(embeddingStr, limit);
|
|
32
|
+
return rows.map((r) => ({
|
|
33
|
+
docid: r.docid,
|
|
34
|
+
collection: r.collection,
|
|
35
|
+
path: `mne://${r.collection}/${r.path}`,
|
|
36
|
+
fullPath: r.full_path,
|
|
37
|
+
title: r.title,
|
|
38
|
+
score: 1 / (1 + r.distance), // Convert distance to similarity score
|
|
39
|
+
snippet: '',
|
|
40
|
+
context: [],
|
|
41
|
+
tags: r.tags ? JSON.parse(r.tags) : [],
|
|
42
|
+
line: r.pos,
|
|
43
|
+
heading: r.heading,
|
|
44
|
+
modifiedAt: r.modified_at ?? '',
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Store embeddings in the vector index */
|
|
52
|
+
storeVectors(vectors) {
|
|
53
|
+
const insertVec = this.db.db.prepare('INSERT OR REPLACE INTO vectors (hash, embedding) VALUES (?, ?)');
|
|
54
|
+
const insertVec0 = this.db.db.prepare('INSERT OR REPLACE INTO vectors_vec (hash, embedding) VALUES (?, ?)');
|
|
55
|
+
const tx = this.db.db.transaction(() => {
|
|
56
|
+
for (const v of vectors) {
|
|
57
|
+
const blob = Buffer.from(new Float32Array(v.embedding).buffer);
|
|
58
|
+
insertVec.run(v.hash, blob);
|
|
59
|
+
insertVec0.run(v.hash, JSON.stringify(v.embedding));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
tx();
|
|
63
|
+
}
|
|
64
|
+
/** Delete vectors for a document */
|
|
65
|
+
deleteVectors(hash) {
|
|
66
|
+
this.db.db.prepare('DELETE FROM vectors WHERE hash = ?').run(hash);
|
|
67
|
+
// vec0 cascades deletion automatically
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=vector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector.js","sourceRoot":"","sources":["../../src/search/vector.ts"],"names":[],"mappings":"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"}
|