ralph-hero-knowledge-index 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/.claude-plugin/plugin.json +21 -0
- package/.mcp.json +12 -0
- package/dist/db.d.ts +30 -0
- package/dist/db.js +73 -0
- package/dist/db.js.map +1 -0
- package/dist/embedder.d.ts +4 -0
- package/dist/embedder.js +24 -0
- package/dist/embedder.js.map +1 -0
- package/dist/hybrid-search.d.ts +13 -0
- package/dist/hybrid-search.js +85 -0
- package/dist/hybrid-search.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +18 -0
- package/dist/parser.js +39 -0
- package/dist/parser.js.map +1 -0
- package/dist/reindex.d.ts +1 -0
- package/dist/reindex.js +77 -0
- package/dist/reindex.js.map +1 -0
- package/dist/search.d.ts +23 -0
- package/dist/search.js +63 -0
- package/dist/search.js.map +1 -0
- package/dist/traverse.d.ts +22 -0
- package/dist/traverse.js +91 -0
- package/dist/traverse.js.map +1 -0
- package/dist/vector-search.d.ts +15 -0
- package/dist/vector-search.js +52 -0
- package/dist/vector-search.js.map +1 -0
- package/package.json +27 -0
- package/src/__tests__/db.test.ts +51 -0
- package/src/__tests__/hybrid-search.test.ts +112 -0
- package/src/__tests__/index.test.ts +8 -0
- package/src/__tests__/parser.test.ts +100 -0
- package/src/__tests__/search.test.ts +92 -0
- package/src/__tests__/traverse.test.ts +115 -0
- package/src/__tests__/vector-search.test.ts +66 -0
- package/src/db.ts +103 -0
- package/src/embedder.ts +37 -0
- package/src/hybrid-search.ts +102 -0
- package/src/index.ts +76 -0
- package/src/parser.ts +63 -0
- package/src/reindex.ts +89 -0
- package/src/search.ts +92 -0
- package/src/traverse.ts +130 -0
- package/src/vector-search.ts +64 -0
- package/tsconfig.json +17 -0
package/src/traverse.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { KnowledgeDB } from "./db.js";
|
|
2
|
+
|
|
3
|
+
export interface TraverseOptions {
|
|
4
|
+
type?: string;
|
|
5
|
+
depth?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface TraverseResult {
|
|
9
|
+
sourceId: string;
|
|
10
|
+
targetId: string;
|
|
11
|
+
type: string;
|
|
12
|
+
depth: number;
|
|
13
|
+
doc: { title: string; status: string | null; date: string | null } | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Traverser {
|
|
17
|
+
private readonly db: KnowledgeDB;
|
|
18
|
+
|
|
19
|
+
constructor(db: KnowledgeDB) {
|
|
20
|
+
this.db = db;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
traverse(fromId: string, options: TraverseOptions = {}): TraverseResult[] {
|
|
24
|
+
const { type, depth = 3 } = options;
|
|
25
|
+
const typeFilter = type ? "AND r.type = @type" : "";
|
|
26
|
+
|
|
27
|
+
const sql = `
|
|
28
|
+
WITH RECURSIVE chain AS (
|
|
29
|
+
SELECT r.source_id, r.target_id, r.type, 1 AS depth
|
|
30
|
+
FROM relationships r
|
|
31
|
+
WHERE r.source_id = @fromId ${typeFilter}
|
|
32
|
+
|
|
33
|
+
UNION ALL
|
|
34
|
+
|
|
35
|
+
SELECT r.source_id, r.target_id, r.type, c.depth + 1
|
|
36
|
+
FROM relationships r
|
|
37
|
+
JOIN chain c ON r.source_id = c.target_id
|
|
38
|
+
WHERE c.depth < @depth ${typeFilter}
|
|
39
|
+
)
|
|
40
|
+
SELECT
|
|
41
|
+
chain.source_id AS sourceId,
|
|
42
|
+
chain.target_id AS targetId,
|
|
43
|
+
chain.type,
|
|
44
|
+
chain.depth,
|
|
45
|
+
d.title AS docTitle,
|
|
46
|
+
d.status AS docStatus,
|
|
47
|
+
d.date AS docDate
|
|
48
|
+
FROM chain
|
|
49
|
+
LEFT JOIN documents d ON d.id = chain.target_id
|
|
50
|
+
ORDER BY chain.depth, chain.target_id
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const params: Record<string, unknown> = { fromId, depth };
|
|
54
|
+
if (type) params.type = type;
|
|
55
|
+
|
|
56
|
+
const rows = this.db.db.prepare(sql).all(params) as Array<{
|
|
57
|
+
sourceId: string;
|
|
58
|
+
targetId: string;
|
|
59
|
+
type: string;
|
|
60
|
+
depth: number;
|
|
61
|
+
docTitle: string | null;
|
|
62
|
+
docStatus: string | null;
|
|
63
|
+
docDate: string | null;
|
|
64
|
+
}>;
|
|
65
|
+
|
|
66
|
+
return rows.map((r) => ({
|
|
67
|
+
sourceId: r.sourceId,
|
|
68
|
+
targetId: r.targetId,
|
|
69
|
+
type: r.type,
|
|
70
|
+
depth: r.depth,
|
|
71
|
+
doc: r.docTitle != null
|
|
72
|
+
? { title: r.docTitle, status: r.docStatus, date: r.docDate }
|
|
73
|
+
: null,
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
traverseIncoming(toId: string, options: TraverseOptions = {}): TraverseResult[] {
|
|
78
|
+
const { type, depth = 3 } = options;
|
|
79
|
+
const typeFilter = type ? "AND r.type = @type" : "";
|
|
80
|
+
|
|
81
|
+
const sql = `
|
|
82
|
+
WITH RECURSIVE chain AS (
|
|
83
|
+
SELECT r.source_id, r.target_id, r.type, 1 AS depth
|
|
84
|
+
FROM relationships r
|
|
85
|
+
WHERE r.target_id = @toId ${typeFilter}
|
|
86
|
+
|
|
87
|
+
UNION ALL
|
|
88
|
+
|
|
89
|
+
SELECT r.source_id, r.target_id, r.type, c.depth + 1
|
|
90
|
+
FROM relationships r
|
|
91
|
+
JOIN chain c ON r.target_id = c.source_id
|
|
92
|
+
WHERE c.depth < @depth ${typeFilter}
|
|
93
|
+
)
|
|
94
|
+
SELECT
|
|
95
|
+
chain.source_id AS sourceId,
|
|
96
|
+
chain.target_id AS targetId,
|
|
97
|
+
chain.type,
|
|
98
|
+
chain.depth,
|
|
99
|
+
d.title AS docTitle,
|
|
100
|
+
d.status AS docStatus,
|
|
101
|
+
d.date AS docDate
|
|
102
|
+
FROM chain
|
|
103
|
+
LEFT JOIN documents d ON d.id = chain.source_id
|
|
104
|
+
ORDER BY chain.depth, chain.target_id
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
const params: Record<string, unknown> = { toId, depth };
|
|
108
|
+
if (type) params.type = type;
|
|
109
|
+
|
|
110
|
+
const rows = this.db.db.prepare(sql).all(params) as Array<{
|
|
111
|
+
sourceId: string;
|
|
112
|
+
targetId: string;
|
|
113
|
+
type: string;
|
|
114
|
+
depth: number;
|
|
115
|
+
docTitle: string | null;
|
|
116
|
+
docStatus: string | null;
|
|
117
|
+
docDate: string | null;
|
|
118
|
+
}>;
|
|
119
|
+
|
|
120
|
+
return rows.map((r) => ({
|
|
121
|
+
sourceId: r.sourceId,
|
|
122
|
+
targetId: r.targetId,
|
|
123
|
+
type: r.type,
|
|
124
|
+
depth: r.depth,
|
|
125
|
+
doc: r.docTitle != null
|
|
126
|
+
? { title: r.docTitle, status: r.docStatus, date: r.docDate }
|
|
127
|
+
: null,
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as sqliteVec from "sqlite-vec";
|
|
2
|
+
import type { KnowledgeDB } from "./db.js";
|
|
3
|
+
|
|
4
|
+
export interface VectorResult {
|
|
5
|
+
id: string;
|
|
6
|
+
distance: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function float32ToBuffer(arr: Float32Array): Buffer {
|
|
10
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class VectorSearch {
|
|
14
|
+
private vecLoaded = false;
|
|
15
|
+
|
|
16
|
+
constructor(private knowledgeDb: KnowledgeDB) {}
|
|
17
|
+
|
|
18
|
+
private ensureVecLoaded(): void {
|
|
19
|
+
if (!this.vecLoaded) {
|
|
20
|
+
sqliteVec.load(this.knowledgeDb.db);
|
|
21
|
+
this.vecLoaded = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
createIndex(): void {
|
|
26
|
+
this.ensureVecLoaded();
|
|
27
|
+
this.knowledgeDb.db.exec(`
|
|
28
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS documents_vec USING vec0(
|
|
29
|
+
id TEXT PRIMARY KEY,
|
|
30
|
+
embedding float[384] distance_metric=cosine
|
|
31
|
+
)
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
dropIndex(): void {
|
|
36
|
+
this.knowledgeDb.db.exec("DROP TABLE IF EXISTS documents_vec");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
upsertEmbedding(id: string, embedding: Float32Array): void {
|
|
40
|
+
this.ensureVecLoaded();
|
|
41
|
+
const buf = float32ToBuffer(embedding);
|
|
42
|
+
this.knowledgeDb.db
|
|
43
|
+
.prepare("DELETE FROM documents_vec WHERE id = ?")
|
|
44
|
+
.run(id);
|
|
45
|
+
this.knowledgeDb.db
|
|
46
|
+
.prepare("INSERT INTO documents_vec (id, embedding) VALUES (?, ?)")
|
|
47
|
+
.run(id, buf);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
search(queryEmbedding: Float32Array, limit: number = 10): VectorResult[] {
|
|
51
|
+
this.ensureVecLoaded();
|
|
52
|
+
const buf = float32ToBuffer(queryEmbedding);
|
|
53
|
+
return this.knowledgeDb.db
|
|
54
|
+
.prepare(
|
|
55
|
+
`
|
|
56
|
+
SELECT id, distance
|
|
57
|
+
FROM documents_vec
|
|
58
|
+
WHERE embedding MATCH ? AND k = ?
|
|
59
|
+
ORDER BY distance
|
|
60
|
+
`
|
|
61
|
+
)
|
|
62
|
+
.all(buf, limit) as VectorResult[];
|
|
63
|
+
}
|
|
64
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"allowSyntheticDefaultImports": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"],
|
|
16
|
+
"exclude": ["node_modules", "dist", "src/__tests__"]
|
|
17
|
+
}
|