@199-bio/engram 0.1.0 → 0.2.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 +5 -4
- package/dist/index.js +65 -2
- package/dist/retrieval/hybrid.d.ts.map +1 -1
- package/dist/storage/database.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +65 -2
- package/src/retrieval/hybrid.ts +19 -3
- package/src/storage/database.ts +41 -30
package/README.md
CHANGED
|
@@ -31,18 +31,19 @@ Engram connects the dots—*avoid seafood restaurants, book Sarah a window seat,
|
|
|
31
31
|
### Install
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
npm install -g engram
|
|
34
|
+
npm install -g @199-bio/engram
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
### Add to Your MCP Client
|
|
38
38
|
|
|
39
|
-
**Claude Desktop** — add to
|
|
39
|
+
**Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
40
40
|
|
|
41
41
|
```json
|
|
42
42
|
{
|
|
43
43
|
"mcpServers": {
|
|
44
44
|
"engram": {
|
|
45
|
-
"command": "
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "@199-bio/engram"]
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
}
|
|
@@ -51,7 +52,7 @@ npm install -g engram-mcp
|
|
|
51
52
|
**Claude Code:**
|
|
52
53
|
|
|
53
54
|
```bash
|
|
54
|
-
claude mcp add engram -- engram
|
|
55
|
+
claude mcp add engram -- npx -y @199-bio/engram
|
|
55
56
|
```
|
|
56
57
|
|
|
57
58
|
**Other MCP clients** — point to the `engram` command. It speaks standard MCP over stdio.
|
package/dist/index.js
CHANGED
|
@@ -40,13 +40,13 @@ async function initialize() {
|
|
|
40
40
|
// ============ MCP Server ============
|
|
41
41
|
const server = new Server({
|
|
42
42
|
name: "engram",
|
|
43
|
-
version: "0.
|
|
43
|
+
version: "0.2.0",
|
|
44
44
|
}, {
|
|
45
45
|
capabilities: {
|
|
46
46
|
tools: {},
|
|
47
47
|
},
|
|
48
48
|
});
|
|
49
|
-
// Tool definitions
|
|
49
|
+
// Tool definitions with MCP 2025-06-18 annotations
|
|
50
50
|
const TOOLS = [
|
|
51
51
|
{
|
|
52
52
|
name: "remember",
|
|
@@ -73,6 +73,13 @@ const TOOLS = [
|
|
|
73
73
|
},
|
|
74
74
|
required: ["content"],
|
|
75
75
|
},
|
|
76
|
+
annotations: {
|
|
77
|
+
title: "Store Memory",
|
|
78
|
+
readOnlyHint: false,
|
|
79
|
+
destructiveHint: false,
|
|
80
|
+
idempotentHint: false,
|
|
81
|
+
openWorldHint: false,
|
|
82
|
+
},
|
|
76
83
|
},
|
|
77
84
|
{
|
|
78
85
|
name: "recall",
|
|
@@ -97,6 +104,13 @@ const TOOLS = [
|
|
|
97
104
|
},
|
|
98
105
|
required: ["query"],
|
|
99
106
|
},
|
|
107
|
+
annotations: {
|
|
108
|
+
title: "Search Memories",
|
|
109
|
+
readOnlyHint: true,
|
|
110
|
+
destructiveHint: false,
|
|
111
|
+
idempotentHint: true,
|
|
112
|
+
openWorldHint: false,
|
|
113
|
+
},
|
|
100
114
|
},
|
|
101
115
|
{
|
|
102
116
|
name: "forget",
|
|
@@ -111,6 +125,13 @@ const TOOLS = [
|
|
|
111
125
|
},
|
|
112
126
|
required: ["id"],
|
|
113
127
|
},
|
|
128
|
+
annotations: {
|
|
129
|
+
title: "Delete Memory",
|
|
130
|
+
readOnlyHint: false,
|
|
131
|
+
destructiveHint: true,
|
|
132
|
+
idempotentHint: true,
|
|
133
|
+
openWorldHint: false,
|
|
134
|
+
},
|
|
114
135
|
},
|
|
115
136
|
{
|
|
116
137
|
name: "create_entity",
|
|
@@ -130,6 +151,13 @@ const TOOLS = [
|
|
|
130
151
|
},
|
|
131
152
|
required: ["name", "type"],
|
|
132
153
|
},
|
|
154
|
+
annotations: {
|
|
155
|
+
title: "Create Entity",
|
|
156
|
+
readOnlyHint: false,
|
|
157
|
+
destructiveHint: false,
|
|
158
|
+
idempotentHint: true,
|
|
159
|
+
openWorldHint: false,
|
|
160
|
+
},
|
|
133
161
|
},
|
|
134
162
|
{
|
|
135
163
|
name: "observe",
|
|
@@ -153,6 +181,13 @@ const TOOLS = [
|
|
|
153
181
|
},
|
|
154
182
|
required: ["entity", "observation"],
|
|
155
183
|
},
|
|
184
|
+
annotations: {
|
|
185
|
+
title: "Add Observation",
|
|
186
|
+
readOnlyHint: false,
|
|
187
|
+
destructiveHint: false,
|
|
188
|
+
idempotentHint: false,
|
|
189
|
+
openWorldHint: false,
|
|
190
|
+
},
|
|
156
191
|
},
|
|
157
192
|
{
|
|
158
193
|
name: "relate",
|
|
@@ -175,6 +210,13 @@ const TOOLS = [
|
|
|
175
210
|
},
|
|
176
211
|
required: ["from", "to", "relation"],
|
|
177
212
|
},
|
|
213
|
+
annotations: {
|
|
214
|
+
title: "Create Relationship",
|
|
215
|
+
readOnlyHint: false,
|
|
216
|
+
destructiveHint: false,
|
|
217
|
+
idempotentHint: true,
|
|
218
|
+
openWorldHint: false,
|
|
219
|
+
},
|
|
178
220
|
},
|
|
179
221
|
{
|
|
180
222
|
name: "query_entity",
|
|
@@ -189,6 +231,13 @@ const TOOLS = [
|
|
|
189
231
|
},
|
|
190
232
|
required: ["entity"],
|
|
191
233
|
},
|
|
234
|
+
annotations: {
|
|
235
|
+
title: "Query Entity",
|
|
236
|
+
readOnlyHint: true,
|
|
237
|
+
destructiveHint: false,
|
|
238
|
+
idempotentHint: true,
|
|
239
|
+
openWorldHint: false,
|
|
240
|
+
},
|
|
192
241
|
},
|
|
193
242
|
{
|
|
194
243
|
name: "list_entities",
|
|
@@ -208,6 +257,13 @@ const TOOLS = [
|
|
|
208
257
|
},
|
|
209
258
|
},
|
|
210
259
|
},
|
|
260
|
+
annotations: {
|
|
261
|
+
title: "List Entities",
|
|
262
|
+
readOnlyHint: true,
|
|
263
|
+
destructiveHint: false,
|
|
264
|
+
idempotentHint: true,
|
|
265
|
+
openWorldHint: false,
|
|
266
|
+
},
|
|
211
267
|
},
|
|
212
268
|
{
|
|
213
269
|
name: "stats",
|
|
@@ -216,6 +272,13 @@ const TOOLS = [
|
|
|
216
272
|
type: "object",
|
|
217
273
|
properties: {},
|
|
218
274
|
},
|
|
275
|
+
annotations: {
|
|
276
|
+
title: "Get Statistics",
|
|
277
|
+
readOnlyHint: true,
|
|
278
|
+
destructiveHint: false,
|
|
279
|
+
idempotentHint: true,
|
|
280
|
+
openWorldHint: false,
|
|
281
|
+
},
|
|
219
282
|
},
|
|
220
283
|
];
|
|
221
284
|
// List available tools
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAGzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD
|
|
1
|
+
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAGzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAqHhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;OAEG;YACW,WAAW;IAezB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,cAAc,CAAC;IAChE,UAAU,EAAE,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3C,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAoB;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@199-bio/engram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Give Claude a perfect memory. Local-first MCP server with hybrid search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"node": ">=18.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
50
|
-
"better-sqlite3": "^11.
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.25.0",
|
|
50
|
+
"better-sqlite3": "^11.10.0",
|
|
51
51
|
"chrono-node": "^2.7.0",
|
|
52
52
|
"uuid": "^10.0.0",
|
|
53
|
-
"zod": "^3.
|
|
53
|
+
"zod": "^3.24.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/better-sqlite3": "^7.6.0",
|
package/src/index.ts
CHANGED
|
@@ -58,7 +58,7 @@ async function initialize(): Promise<void> {
|
|
|
58
58
|
const server = new Server(
|
|
59
59
|
{
|
|
60
60
|
name: "engram",
|
|
61
|
-
version: "0.
|
|
61
|
+
version: "0.2.0",
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
64
|
capabilities: {
|
|
@@ -67,7 +67,7 @@ const server = new Server(
|
|
|
67
67
|
}
|
|
68
68
|
);
|
|
69
69
|
|
|
70
|
-
// Tool definitions
|
|
70
|
+
// Tool definitions with MCP 2025-06-18 annotations
|
|
71
71
|
const TOOLS = [
|
|
72
72
|
{
|
|
73
73
|
name: "remember",
|
|
@@ -95,6 +95,13 @@ const TOOLS = [
|
|
|
95
95
|
},
|
|
96
96
|
required: ["content"],
|
|
97
97
|
},
|
|
98
|
+
annotations: {
|
|
99
|
+
title: "Store Memory",
|
|
100
|
+
readOnlyHint: false,
|
|
101
|
+
destructiveHint: false,
|
|
102
|
+
idempotentHint: false,
|
|
103
|
+
openWorldHint: false,
|
|
104
|
+
},
|
|
98
105
|
},
|
|
99
106
|
{
|
|
100
107
|
name: "recall",
|
|
@@ -120,6 +127,13 @@ const TOOLS = [
|
|
|
120
127
|
},
|
|
121
128
|
required: ["query"],
|
|
122
129
|
},
|
|
130
|
+
annotations: {
|
|
131
|
+
title: "Search Memories",
|
|
132
|
+
readOnlyHint: true,
|
|
133
|
+
destructiveHint: false,
|
|
134
|
+
idempotentHint: true,
|
|
135
|
+
openWorldHint: false,
|
|
136
|
+
},
|
|
123
137
|
},
|
|
124
138
|
{
|
|
125
139
|
name: "forget",
|
|
@@ -134,6 +148,13 @@ const TOOLS = [
|
|
|
134
148
|
},
|
|
135
149
|
required: ["id"],
|
|
136
150
|
},
|
|
151
|
+
annotations: {
|
|
152
|
+
title: "Delete Memory",
|
|
153
|
+
readOnlyHint: false,
|
|
154
|
+
destructiveHint: true,
|
|
155
|
+
idempotentHint: true,
|
|
156
|
+
openWorldHint: false,
|
|
157
|
+
},
|
|
137
158
|
},
|
|
138
159
|
{
|
|
139
160
|
name: "create_entity",
|
|
@@ -153,6 +174,13 @@ const TOOLS = [
|
|
|
153
174
|
},
|
|
154
175
|
required: ["name", "type"],
|
|
155
176
|
},
|
|
177
|
+
annotations: {
|
|
178
|
+
title: "Create Entity",
|
|
179
|
+
readOnlyHint: false,
|
|
180
|
+
destructiveHint: false,
|
|
181
|
+
idempotentHint: true,
|
|
182
|
+
openWorldHint: false,
|
|
183
|
+
},
|
|
156
184
|
},
|
|
157
185
|
{
|
|
158
186
|
name: "observe",
|
|
@@ -176,6 +204,13 @@ const TOOLS = [
|
|
|
176
204
|
},
|
|
177
205
|
required: ["entity", "observation"],
|
|
178
206
|
},
|
|
207
|
+
annotations: {
|
|
208
|
+
title: "Add Observation",
|
|
209
|
+
readOnlyHint: false,
|
|
210
|
+
destructiveHint: false,
|
|
211
|
+
idempotentHint: false,
|
|
212
|
+
openWorldHint: false,
|
|
213
|
+
},
|
|
179
214
|
},
|
|
180
215
|
{
|
|
181
216
|
name: "relate",
|
|
@@ -198,6 +233,13 @@ const TOOLS = [
|
|
|
198
233
|
},
|
|
199
234
|
required: ["from", "to", "relation"],
|
|
200
235
|
},
|
|
236
|
+
annotations: {
|
|
237
|
+
title: "Create Relationship",
|
|
238
|
+
readOnlyHint: false,
|
|
239
|
+
destructiveHint: false,
|
|
240
|
+
idempotentHint: true,
|
|
241
|
+
openWorldHint: false,
|
|
242
|
+
},
|
|
201
243
|
},
|
|
202
244
|
{
|
|
203
245
|
name: "query_entity",
|
|
@@ -212,6 +254,13 @@ const TOOLS = [
|
|
|
212
254
|
},
|
|
213
255
|
required: ["entity"],
|
|
214
256
|
},
|
|
257
|
+
annotations: {
|
|
258
|
+
title: "Query Entity",
|
|
259
|
+
readOnlyHint: true,
|
|
260
|
+
destructiveHint: false,
|
|
261
|
+
idempotentHint: true,
|
|
262
|
+
openWorldHint: false,
|
|
263
|
+
},
|
|
215
264
|
},
|
|
216
265
|
{
|
|
217
266
|
name: "list_entities",
|
|
@@ -231,6 +280,13 @@ const TOOLS = [
|
|
|
231
280
|
},
|
|
232
281
|
},
|
|
233
282
|
},
|
|
283
|
+
annotations: {
|
|
284
|
+
title: "List Entities",
|
|
285
|
+
readOnlyHint: true,
|
|
286
|
+
destructiveHint: false,
|
|
287
|
+
idempotentHint: true,
|
|
288
|
+
openWorldHint: false,
|
|
289
|
+
},
|
|
234
290
|
},
|
|
235
291
|
{
|
|
236
292
|
name: "stats",
|
|
@@ -239,6 +295,13 @@ const TOOLS = [
|
|
|
239
295
|
type: "object" as const,
|
|
240
296
|
properties: {},
|
|
241
297
|
},
|
|
298
|
+
annotations: {
|
|
299
|
+
title: "Get Statistics",
|
|
300
|
+
readOnlyHint: true,
|
|
301
|
+
destructiveHint: false,
|
|
302
|
+
idempotentHint: true,
|
|
303
|
+
openWorldHint: false,
|
|
304
|
+
},
|
|
242
305
|
},
|
|
243
306
|
];
|
|
244
307
|
|
package/src/retrieval/hybrid.ts
CHANGED
|
@@ -27,6 +27,9 @@ export class HybridSearch {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Search using all available methods and fuse results
|
|
30
|
+
*
|
|
31
|
+
* Strategy: BM25 gets candidates fast, ColBERT reranks for quality
|
|
32
|
+
* This is both FASTER (fewer ColBERT computations) and BETTER (combines keyword + semantic)
|
|
30
33
|
*/
|
|
31
34
|
async search(
|
|
32
35
|
query: string,
|
|
@@ -36,6 +39,7 @@ export class HybridSearch {
|
|
|
36
39
|
bm25Weight?: number;
|
|
37
40
|
semanticWeight?: number;
|
|
38
41
|
graphWeight?: number;
|
|
42
|
+
useReranking?: boolean; // Use ColBERT to rerank BM25 results
|
|
39
43
|
} = {}
|
|
40
44
|
): Promise<HybridSearchResult[]> {
|
|
41
45
|
const {
|
|
@@ -44,18 +48,30 @@ export class HybridSearch {
|
|
|
44
48
|
bm25Weight = 1.0,
|
|
45
49
|
semanticWeight = 1.0,
|
|
46
50
|
graphWeight = 0.5,
|
|
51
|
+
useReranking = true, // Default: reranking mode for better quality
|
|
47
52
|
} = options;
|
|
48
53
|
|
|
49
54
|
// Fetch more candidates than needed for fusion
|
|
50
55
|
const candidateLimit = Math.max(limit * 3, 30);
|
|
51
56
|
|
|
52
|
-
// Run
|
|
53
|
-
const [bm25Results,
|
|
57
|
+
// Run BM25 and graph search in parallel (fast)
|
|
58
|
+
const [bm25Results, graphMemoryIds] = await Promise.all([
|
|
54
59
|
this.searchBM25(query, candidateLimit),
|
|
55
|
-
this.searchSemantic(query, candidateLimit),
|
|
56
60
|
includeGraph ? this.searchGraph(query) : Promise.resolve([]),
|
|
57
61
|
]);
|
|
58
62
|
|
|
63
|
+
// For semantic: either rerank BM25 results (faster+better) or search full index
|
|
64
|
+
let semanticResults: Array<{ id: string; score: number }>;
|
|
65
|
+
if (useReranking && bm25Results.length > 0) {
|
|
66
|
+
// Rerank BM25 candidates with ColBERT - faster AND better quality
|
|
67
|
+
const docs = bm25Results.map(r => ({ id: r.id, content: this.db.getMemory(r.id)?.content || '' }));
|
|
68
|
+
const reranked = await this.retriever.rerank(query, docs, candidateLimit);
|
|
69
|
+
semanticResults = reranked.map(r => ({ id: r.id, score: r.score }));
|
|
70
|
+
} else {
|
|
71
|
+
// Full semantic search
|
|
72
|
+
semanticResults = await this.searchSemantic(query, candidateLimit);
|
|
73
|
+
}
|
|
74
|
+
|
|
59
75
|
// Fetch graph memories
|
|
60
76
|
const graphMemories = graphMemoryIds.length > 0
|
|
61
77
|
? graphMemoryIds.map(id => this.db.getMemory(id)).filter(Boolean) as Memory[]
|
package/src/storage/database.ts
CHANGED
|
@@ -47,6 +47,7 @@ export interface Relation {
|
|
|
47
47
|
|
|
48
48
|
export class EngramDatabase {
|
|
49
49
|
private db: Database.Database;
|
|
50
|
+
private stmtCache: Map<string, Database.Statement> = new Map();
|
|
50
51
|
|
|
51
52
|
constructor(dbPath: string) {
|
|
52
53
|
// Ensure directory exists
|
|
@@ -56,8 +57,15 @@ export class EngramDatabase {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
this.db = new Database(dbPath);
|
|
59
|
-
|
|
60
|
+
|
|
61
|
+
// Performance optimizations - all improve speed with no quality trade-off
|
|
62
|
+
this.db.pragma("journal_mode = WAL"); // Better concurrent access
|
|
63
|
+
this.db.pragma("synchronous = NORMAL"); // Faster writes, WAL provides safety
|
|
64
|
+
this.db.pragma("cache_size = -64000"); // 64MB cache (negative = KB)
|
|
65
|
+
this.db.pragma("mmap_size = 268435456"); // 256MB memory-mapped I/O
|
|
66
|
+
this.db.pragma("temp_store = MEMORY"); // Keep temp tables in RAM
|
|
60
67
|
this.db.pragma("foreign_keys = ON");
|
|
68
|
+
|
|
61
69
|
this.initialize();
|
|
62
70
|
}
|
|
63
71
|
|
|
@@ -165,8 +173,7 @@ export class EngramDatabase {
|
|
|
165
173
|
}
|
|
166
174
|
|
|
167
175
|
getMemory(id: string): Memory | null {
|
|
168
|
-
const
|
|
169
|
-
const row = stmt.get(id) as Record<string, unknown> | undefined;
|
|
176
|
+
const row = this.stmt("SELECT * FROM memories WHERE id = ?").get(id) as Record<string, unknown> | undefined;
|
|
170
177
|
return row ? this.rowToMemory(row) : null;
|
|
171
178
|
}
|
|
172
179
|
|
|
@@ -198,35 +205,27 @@ export class EngramDatabase {
|
|
|
198
205
|
}
|
|
199
206
|
|
|
200
207
|
touchMemory(id: string): void {
|
|
201
|
-
|
|
202
|
-
UPDATE memories
|
|
203
|
-
SET access_count = access_count + 1, last_accessed = CURRENT_TIMESTAMP
|
|
204
|
-
WHERE id = ?
|
|
205
|
-
`);
|
|
206
|
-
stmt.run(id);
|
|
208
|
+
this.stmt(`UPDATE memories SET access_count = access_count + 1, last_accessed = CURRENT_TIMESTAMP WHERE id = ?`).run(id);
|
|
207
209
|
}
|
|
208
210
|
|
|
209
211
|
getAllMemories(limit: number = 1000): Memory[] {
|
|
210
|
-
const
|
|
211
|
-
const rows = stmt.all(limit) as Record<string, unknown>[];
|
|
212
|
+
const rows = this.stmt("SELECT * FROM memories ORDER BY timestamp DESC LIMIT ?").all(limit) as Record<string, unknown>[];
|
|
212
213
|
return rows.map((row) => this.rowToMemory(row));
|
|
213
214
|
}
|
|
214
215
|
|
|
215
216
|
// ============ BM25 Search ============
|
|
216
217
|
|
|
217
218
|
searchBM25(query: string, limit: number = 20): Array<Memory & { score: number }> {
|
|
218
|
-
|
|
219
|
+
// Escape special FTS5 characters and format query
|
|
220
|
+
const escapedQuery = this.escapeFTS5Query(query);
|
|
221
|
+
const rows = this.stmt(`
|
|
219
222
|
SELECT m.*, bm25(memories_fts) as score
|
|
220
223
|
FROM memories_fts fts
|
|
221
224
|
JOIN memories m ON fts.rowid = m.rowid
|
|
222
225
|
WHERE memories_fts MATCH ?
|
|
223
226
|
ORDER BY score
|
|
224
227
|
LIMIT ?
|
|
225
|
-
`)
|
|
226
|
-
|
|
227
|
-
// Escape special FTS5 characters and format query
|
|
228
|
-
const escapedQuery = this.escapeFTS5Query(query);
|
|
229
|
-
const rows = stmt.all(escapedQuery, limit) as Array<Record<string, unknown>>;
|
|
228
|
+
`).all(escapedQuery, limit) as Array<Record<string, unknown>>;
|
|
230
229
|
|
|
231
230
|
return rows.map((row) => ({
|
|
232
231
|
...this.rowToMemory(row),
|
|
@@ -262,14 +261,12 @@ export class EngramDatabase {
|
|
|
262
261
|
}
|
|
263
262
|
|
|
264
263
|
getEntity(id: string): Entity | null {
|
|
265
|
-
const
|
|
266
|
-
const row = stmt.get(id) as Record<string, unknown> | undefined;
|
|
264
|
+
const row = this.stmt("SELECT * FROM entities WHERE id = ?").get(id) as Record<string, unknown> | undefined;
|
|
267
265
|
return row ? this.rowToEntity(row) : null;
|
|
268
266
|
}
|
|
269
267
|
|
|
270
268
|
findEntityByName(name: string): Entity | null {
|
|
271
|
-
const
|
|
272
|
-
const row = stmt.get(name) as Record<string, unknown> | undefined;
|
|
269
|
+
const row = this.stmt("SELECT * FROM entities WHERE LOWER(name) = LOWER(?)").get(name) as Record<string, unknown> | undefined;
|
|
273
270
|
return row ? this.rowToEntity(row) : null;
|
|
274
271
|
}
|
|
275
272
|
|
|
@@ -329,8 +326,7 @@ export class EngramDatabase {
|
|
|
329
326
|
}
|
|
330
327
|
|
|
331
328
|
getObservation(id: string): Observation | null {
|
|
332
|
-
const
|
|
333
|
-
const row = stmt.get(id) as Record<string, unknown> | undefined;
|
|
329
|
+
const row = this.stmt("SELECT * FROM observations WHERE id = ?").get(id) as Record<string, unknown> | undefined;
|
|
334
330
|
return row ? this.rowToObservation(row) : null;
|
|
335
331
|
}
|
|
336
332
|
|
|
@@ -369,8 +365,7 @@ export class EngramDatabase {
|
|
|
369
365
|
}
|
|
370
366
|
|
|
371
367
|
getRelation(id: string): Relation | null {
|
|
372
|
-
const
|
|
373
|
-
const row = stmt.get(id) as Record<string, unknown> | undefined;
|
|
368
|
+
const row = this.stmt("SELECT * FROM relations WHERE id = ?").get(id) as Record<string, unknown> | undefined;
|
|
374
369
|
return row ? this.rowToRelation(row) : null;
|
|
375
370
|
}
|
|
376
371
|
|
|
@@ -466,12 +461,16 @@ export class EngramDatabase {
|
|
|
466
461
|
relations: number;
|
|
467
462
|
observations: number;
|
|
468
463
|
} {
|
|
469
|
-
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
464
|
+
// Single query for all stats - much faster than 4 separate queries
|
|
465
|
+
const row = this.stmt(`
|
|
466
|
+
SELECT
|
|
467
|
+
(SELECT COUNT(*) FROM memories) as memories,
|
|
468
|
+
(SELECT COUNT(*) FROM entities) as entities,
|
|
469
|
+
(SELECT COUNT(*) FROM relations) as relations,
|
|
470
|
+
(SELECT COUNT(*) FROM observations) as observations
|
|
471
|
+
`).get() as { memories: number; entities: number; relations: number; observations: number };
|
|
473
472
|
|
|
474
|
-
return
|
|
473
|
+
return row;
|
|
475
474
|
}
|
|
476
475
|
|
|
477
476
|
// ============ Utilities ============
|
|
@@ -480,6 +479,18 @@ export class EngramDatabase {
|
|
|
480
479
|
this.db.close();
|
|
481
480
|
}
|
|
482
481
|
|
|
482
|
+
/**
|
|
483
|
+
* Get a cached prepared statement - avoids re-parsing SQL
|
|
484
|
+
*/
|
|
485
|
+
private stmt(sql: string): Database.Statement {
|
|
486
|
+
let cached = this.stmtCache.get(sql);
|
|
487
|
+
if (!cached) {
|
|
488
|
+
cached = this.db.prepare(sql);
|
|
489
|
+
this.stmtCache.set(sql, cached);
|
|
490
|
+
}
|
|
491
|
+
return cached;
|
|
492
|
+
}
|
|
493
|
+
|
|
483
494
|
private rowToMemory(row: Record<string, unknown>): Memory {
|
|
484
495
|
return {
|
|
485
496
|
id: row.id as string,
|