@melandlabs/memory-store 0.1.6 → 0.2.1
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/dist/{config-D6MzksfT.d.ts → config-BYfTzG3h.d.ts} +17 -0
- package/dist/contracts.d.ts +7 -0
- package/dist/contracts.js +0 -2
- package/dist/http.d.ts +1 -1
- package/dist/http.js +355 -67
- package/dist/index.d.ts +2 -2
- package/dist/index.js +287 -52
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +313 -60
- package/dist/policies/memory-graph-correction-policy.js +0 -2
- package/dist/policies/memory-graph-write-policy.js +0 -2
- package/dist/search/unified-search.d.ts +39 -3
- package/dist/search/unified-search.js +311 -56
- package/dist/server/cli-http.js +360 -66
- package/dist/server/cli-mcp.js +318 -59
- package/dist/server/cli-shared.d.ts +1 -1
- package/dist/server/cli-shared.js +0 -2
- package/dist/storage/chroma-memory-index.js +0 -2
- package/dist/storage/postgres-raw-message-factory.d.ts +1 -1
- package/dist/storage/postgres-raw-message-factory.js +0 -2
- package/dist/storage/raw-message-store.d.ts +9 -1
- package/dist/storage/raw-message-store.js +0 -2
- package/dist/storage/sqlite-raw-message-store.d.ts +15 -2
- package/dist/storage/sqlite-raw-message-store.js +8 -3
- package/dist/storage/sqlite-vector-index.d.ts +1 -1
- package/dist/storage/sqlite-vector-index.js +0 -2
- package/package.json +81 -93
- package/dist/contracts.js.map +0 -1
- package/dist/http.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mcp.js.map +0 -1
- package/dist/policies/memory-graph-correction-policy.js.map +0 -1
- package/dist/policies/memory-graph-write-policy.js.map +0 -1
- package/dist/search/unified-search.js.map +0 -1
- package/dist/server/cli-http.js.map +0 -1
- package/dist/server/cli-mcp.js.map +0 -1
- package/dist/server/cli-shared.js.map +0 -1
- package/dist/storage/chroma-memory-index.js.map +0 -1
- package/dist/storage/postgres-raw-message-factory.js.map +0 -1
- package/dist/storage/raw-message-store.js.map +0 -1
- package/dist/storage/sqlite-raw-message-store.js.map +0 -1
- package/dist/storage/sqlite-vector-index.js.map +0 -1
|
@@ -2,16 +2,24 @@ import '@melandlabs/ai/memory';
|
|
|
2
2
|
import { ChromaVectorStore } from '@melandlabs/rag';
|
|
3
3
|
import { mkdirSync } from 'fs';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
|
-
import {
|
|
5
|
+
import { dirname, join } from 'path';
|
|
6
6
|
import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __esm = (fn, res) => function __init() {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
};
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/contracts.ts
|
|
19
|
+
var init_contracts = __esm({
|
|
20
|
+
"src/contracts.ts"() {
|
|
21
|
+
}
|
|
22
|
+
});
|
|
15
23
|
function getBackend(keys) {
|
|
16
24
|
for (const key of keys) {
|
|
17
25
|
const value = process.env[key]?.trim().toLowerCase();
|
|
@@ -62,21 +70,51 @@ async function searchRawMessagesWithChroma(input) {
|
|
|
62
70
|
metadata: result.metadata ?? {}
|
|
63
71
|
})).slice(0, input.limit);
|
|
64
72
|
}
|
|
73
|
+
var DEFAULT_RAW_MESSAGES_COLLECTION, RAW_VECTOR_BACKEND_ENV_KEYS;
|
|
74
|
+
var init_chroma_memory_index = __esm({
|
|
75
|
+
"src/storage/chroma-memory-index.ts"() {
|
|
76
|
+
init_contracts();
|
|
77
|
+
DEFAULT_RAW_MESSAGES_COLLECTION = "opencontext_raw_messages";
|
|
78
|
+
RAW_VECTOR_BACKEND_ENV_KEYS = [
|
|
79
|
+
"RAW_MESSAGE_VECTOR_STORE_BACKEND",
|
|
80
|
+
"MEMORY_VECTOR_STORE_BACKEND",
|
|
81
|
+
"VECTOR_STORE_BACKEND"
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
});
|
|
65
85
|
|
|
66
86
|
// src/storage/postgres-raw-message-factory.ts
|
|
67
|
-
var factory = null;
|
|
68
87
|
async function resolvePostgresFactory(env) {
|
|
69
|
-
return null;
|
|
88
|
+
if (!factory) return null;
|
|
89
|
+
return await factory(env);
|
|
70
90
|
}
|
|
71
91
|
function hasPostgresFactory() {
|
|
72
92
|
return factory !== null;
|
|
73
93
|
}
|
|
74
|
-
var
|
|
75
|
-
var
|
|
94
|
+
var factory;
|
|
95
|
+
var init_postgres_raw_message_factory = __esm({
|
|
96
|
+
"src/storage/postgres-raw-message-factory.ts"() {
|
|
97
|
+
factory = null;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// src/storage/sqlite-raw-message-store.ts
|
|
102
|
+
var sqlite_raw_message_store_exports = {};
|
|
103
|
+
__export(sqlite_raw_message_store_exports, {
|
|
104
|
+
__resetSQLiteRawMessageManagerForTests: () => __resetSQLiteRawMessageManagerForTests,
|
|
105
|
+
closeSQLiteRawMessageManager: () => closeSQLiteRawMessageManager,
|
|
106
|
+
getSQLiteRawMessageManager: () => getSQLiteRawMessageManager,
|
|
107
|
+
isSQLiteRawMessageStorageAvailable: () => isSQLiteRawMessageStorageAvailable,
|
|
108
|
+
lexicalSearchRawMessages: () => lexicalSearchRawMessages,
|
|
109
|
+
resolveSQLiteRawMessageDbPath: () => resolveSQLiteRawMessageDbPath
|
|
110
|
+
});
|
|
76
111
|
function resolveSQLiteRawMessageDbPath() {
|
|
77
112
|
const fromEnv = process.env.MEMORY_STORE_DB_PATH?.trim();
|
|
78
113
|
return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_SQLITE_PATH;
|
|
79
114
|
}
|
|
115
|
+
function isSQLiteRawMessageStorageAvailable(_env) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
80
118
|
async function getSQLiteRawMessageManager(_env) {
|
|
81
119
|
if (!manager) {
|
|
82
120
|
const dbPath = resolveSQLiteRawMessageDbPath();
|
|
@@ -96,8 +134,35 @@ async function closeSQLiteRawMessageManager() {
|
|
|
96
134
|
await manager.close();
|
|
97
135
|
manager = null;
|
|
98
136
|
}
|
|
137
|
+
function __resetSQLiteRawMessageManagerForTests() {
|
|
138
|
+
manager = null;
|
|
139
|
+
}
|
|
140
|
+
async function lexicalSearchRawMessages(input) {
|
|
141
|
+
const mgr = await getSQLiteRawMessageManager();
|
|
142
|
+
if (typeof mgr.lexicalSearchMessages !== "function") {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
return mgr.lexicalSearchMessages(input);
|
|
146
|
+
}
|
|
147
|
+
var manager, DEFAULT_SQLITE_PATH;
|
|
148
|
+
var init_sqlite_raw_message_store = __esm({
|
|
149
|
+
"src/storage/sqlite-raw-message-store.ts"() {
|
|
150
|
+
init_chroma_memory_index();
|
|
151
|
+
manager = null;
|
|
152
|
+
DEFAULT_SQLITE_PATH = join(homedir(), ".opencontext", "memory", "store.db");
|
|
153
|
+
}
|
|
154
|
+
});
|
|
99
155
|
|
|
100
156
|
// src/storage/raw-message-store.ts
|
|
157
|
+
var raw_message_store_exports = {};
|
|
158
|
+
__export(raw_message_store_exports, {
|
|
159
|
+
closeRawMessageStore: () => closeRawMessageStore,
|
|
160
|
+
configureRawMessageStore: () => configureRawMessageStore,
|
|
161
|
+
createRawMessageStore: () => createRawMessageStore,
|
|
162
|
+
getRawMessageManager: () => getRawMessageManager,
|
|
163
|
+
getRawMessageStorageBackend: () => getRawMessageStorageBackend,
|
|
164
|
+
isRawMessageStorageAvailable: () => isRawMessageStorageAvailable
|
|
165
|
+
});
|
|
101
166
|
function resolveBackend(env) {
|
|
102
167
|
const explicit = process.env.OPENCONTEXT_MEMORY_STORE_BACKEND?.trim().toLowerCase();
|
|
103
168
|
if (explicit === "postgres") return "postgres";
|
|
@@ -107,7 +172,7 @@ function resolveEnv(_env) {
|
|
|
107
172
|
return {};
|
|
108
173
|
}
|
|
109
174
|
function createRawMessageStore(options = {}) {
|
|
110
|
-
resolveEnv(options.env);
|
|
175
|
+
const env = resolveEnv(options.env);
|
|
111
176
|
return {
|
|
112
177
|
getBackend() {
|
|
113
178
|
return resolveBackend();
|
|
@@ -117,7 +182,7 @@ function createRawMessageStore(options = {}) {
|
|
|
117
182
|
},
|
|
118
183
|
async getManager() {
|
|
119
184
|
if (resolveBackend() === "postgres") {
|
|
120
|
-
const pg = await resolvePostgresFactory();
|
|
185
|
+
const pg = await resolvePostgresFactory(env);
|
|
121
186
|
if (!pg) {
|
|
122
187
|
throw new Error(
|
|
123
188
|
"OPENCONTEXT_MEMORY_STORE_BACKEND=postgres but no Postgres factory is registered. Call registerPostgresFactory() at startup, or unset the env var to use the default SQLite backend."
|
|
@@ -132,14 +197,47 @@ function createRawMessageStore(options = {}) {
|
|
|
132
197
|
}
|
|
133
198
|
};
|
|
134
199
|
}
|
|
135
|
-
|
|
136
|
-
|
|
200
|
+
function configureRawMessageStore(config) {
|
|
201
|
+
moduleConfig = config;
|
|
202
|
+
moduleStore = createRawMessageStore({ env: config.env });
|
|
203
|
+
return moduleStore;
|
|
204
|
+
}
|
|
205
|
+
function getRawMessageStorageBackend() {
|
|
206
|
+
if (!moduleStore) {
|
|
207
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
208
|
+
}
|
|
209
|
+
return moduleStore.getBackend();
|
|
210
|
+
}
|
|
137
211
|
function isRawMessageStorageAvailable() {
|
|
138
212
|
if (!moduleStore) {
|
|
139
213
|
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
140
214
|
}
|
|
141
215
|
return moduleStore.isAvailable();
|
|
142
216
|
}
|
|
217
|
+
async function getRawMessageManager() {
|
|
218
|
+
if (!moduleStore) {
|
|
219
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
220
|
+
}
|
|
221
|
+
return moduleStore.getManager();
|
|
222
|
+
}
|
|
223
|
+
async function closeRawMessageStore() {
|
|
224
|
+
if (!moduleStore) return;
|
|
225
|
+
await moduleStore.close();
|
|
226
|
+
moduleStore = null;
|
|
227
|
+
}
|
|
228
|
+
var moduleStore, moduleConfig;
|
|
229
|
+
var init_raw_message_store = __esm({
|
|
230
|
+
"src/storage/raw-message-store.ts"() {
|
|
231
|
+
init_postgres_raw_message_factory();
|
|
232
|
+
init_sqlite_raw_message_store();
|
|
233
|
+
moduleStore = null;
|
|
234
|
+
moduleConfig = null;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// src/search/unified-search.ts
|
|
239
|
+
init_chroma_memory_index();
|
|
240
|
+
init_raw_message_store();
|
|
143
241
|
|
|
144
242
|
// src/search/utilities.ts
|
|
145
243
|
var DEFAULT_LIMIT = 10;
|
|
@@ -169,7 +267,14 @@ function clampUnifiedMemorySearchThreshold(threshold) {
|
|
|
169
267
|
}
|
|
170
268
|
return Math.min(1, Math.max(-1, parsed));
|
|
171
269
|
}
|
|
172
|
-
function mergeUnifiedMemorySearchResults(results, limit) {
|
|
270
|
+
function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
|
|
271
|
+
const strategy = normalizeUnifiedMemoryMergeStrategy(options.strategy);
|
|
272
|
+
if (strategy === "rrf") {
|
|
273
|
+
const lists = options.rankedLists ?? [
|
|
274
|
+
{ name: "memory-semantic", hits: results }
|
|
275
|
+
];
|
|
276
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, limit);
|
|
277
|
+
}
|
|
173
278
|
return [...results].sort((a, b) => {
|
|
174
279
|
const scoreDelta = b.similarity - a.similarity;
|
|
175
280
|
if (scoreDelta !== 0) {
|
|
@@ -178,6 +283,42 @@ function mergeUnifiedMemorySearchResults(results, limit) {
|
|
|
178
283
|
return a.type.localeCompare(b.type) || a.id.localeCompare(b.id);
|
|
179
284
|
}).slice(0, limit);
|
|
180
285
|
}
|
|
286
|
+
function normalizeUnifiedMemoryMergeStrategy(value) {
|
|
287
|
+
return value === "rrf" ? "rrf" : "similarity";
|
|
288
|
+
}
|
|
289
|
+
var DEFAULT_RRF_K = 60;
|
|
290
|
+
function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
291
|
+
if (!Array.isArray(lists) || lists.length === 0) {
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
const safeK = Number.isFinite(k) && k > 0 ? k : DEFAULT_RRF_K;
|
|
295
|
+
const scores = /* @__PURE__ */ new Map();
|
|
296
|
+
const order = [];
|
|
297
|
+
for (const list of lists) {
|
|
298
|
+
for (let index = 0; index < list.hits.length; index += 1) {
|
|
299
|
+
const hit = list.hits[index];
|
|
300
|
+
const key = `${hit.type}::${hit.id}`;
|
|
301
|
+
const contribution = 1 / (safeK + index + 1);
|
|
302
|
+
const existing = scores.get(key);
|
|
303
|
+
if (existing) {
|
|
304
|
+
existing.rrf += contribution;
|
|
305
|
+
} else {
|
|
306
|
+
scores.set(key, { hit, rrf: contribution });
|
|
307
|
+
order.push(key);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const ranked = order.map((key) => scores.get(key)).sort((a, b) => {
|
|
312
|
+
if (b.rrf !== a.rrf) {
|
|
313
|
+
return b.rrf - a.rrf;
|
|
314
|
+
}
|
|
315
|
+
return a.hit.type.localeCompare(b.hit.type) || a.hit.id.localeCompare(b.hit.id);
|
|
316
|
+
}).slice(0, limit);
|
|
317
|
+
return ranked.map(({ hit, rrf }) => ({
|
|
318
|
+
...hit,
|
|
319
|
+
metadata: { ...hit.metadata, rrfScore: rrf }
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
181
322
|
function toKnowledgeResult(result) {
|
|
182
323
|
return {
|
|
183
324
|
type: "knowledge",
|
|
@@ -209,6 +350,9 @@ function isRawMemorySemanticResult(result) {
|
|
|
209
350
|
}
|
|
210
351
|
|
|
211
352
|
// src/search/unified-search.ts
|
|
353
|
+
function deriveLexicalKeywords(query) {
|
|
354
|
+
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
355
|
+
}
|
|
212
356
|
function createUnifiedSearch(deps = {}) {
|
|
213
357
|
const logger = deps.logger ?? console;
|
|
214
358
|
async function searchUnifiedMemory(input) {
|
|
@@ -216,8 +360,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
216
360
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
217
361
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
218
362
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
363
|
+
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
|
|
219
364
|
const warnings = [];
|
|
220
|
-
const results = [];
|
|
221
365
|
if (!query) {
|
|
222
366
|
return {
|
|
223
367
|
query,
|
|
@@ -227,17 +371,17 @@ function createUnifiedSearch(deps = {}) {
|
|
|
227
371
|
warnings
|
|
228
372
|
};
|
|
229
373
|
}
|
|
374
|
+
let memorySubs;
|
|
230
375
|
if (sources.includes("memory")) {
|
|
231
376
|
if (isRawMessageStorageAvailable()) {
|
|
232
377
|
try {
|
|
233
|
-
|
|
234
|
-
results.push(...memoryHits);
|
|
378
|
+
memorySubs = await runMemorySource(input, limit, threshold, warnings);
|
|
235
379
|
} catch (error) {
|
|
236
380
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
237
381
|
warnings.push({
|
|
238
382
|
source: "memory",
|
|
239
383
|
code: "memory_search_failed",
|
|
240
|
-
message: error.message ?? "
|
|
384
|
+
message: `Memory search failed: ${error.message ?? "Unknown error"}. Using keyword search fallback.`
|
|
241
385
|
});
|
|
242
386
|
}
|
|
243
387
|
} else {
|
|
@@ -248,9 +392,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
248
392
|
});
|
|
249
393
|
}
|
|
250
394
|
}
|
|
395
|
+
const insightHits = [];
|
|
251
396
|
if (sources.includes("insights") && typeof deps.searchInsights === "function") {
|
|
252
397
|
try {
|
|
253
|
-
const
|
|
398
|
+
const hits = await deps.searchInsights({
|
|
254
399
|
userId: input.userId,
|
|
255
400
|
query: input.query,
|
|
256
401
|
limit,
|
|
@@ -259,8 +404,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
259
404
|
includeArchived: input.includeArchivedInsights,
|
|
260
405
|
authToken: input.authToken
|
|
261
406
|
});
|
|
262
|
-
for (const hit of
|
|
263
|
-
|
|
407
|
+
for (const hit of hits) {
|
|
408
|
+
insightHits.push({
|
|
264
409
|
type: "insight",
|
|
265
410
|
id: hit.id,
|
|
266
411
|
content: hit.content,
|
|
@@ -276,16 +421,11 @@ function createUnifiedSearch(deps = {}) {
|
|
|
276
421
|
message: error.message ?? "insights_search_failed"
|
|
277
422
|
});
|
|
278
423
|
}
|
|
279
|
-
} else if (sources.includes("insights"))
|
|
280
|
-
|
|
281
|
-
source: "insights",
|
|
282
|
-
code: "insights_search_not_configured",
|
|
283
|
-
message: "Insights search is not configured for this host."
|
|
284
|
-
});
|
|
285
|
-
}
|
|
424
|
+
} else if (sources.includes("insights")) ;
|
|
425
|
+
const knowledgeHits = [];
|
|
286
426
|
if (sources.includes("knowledge") && typeof deps.searchKnowledge === "function") {
|
|
287
427
|
try {
|
|
288
|
-
const
|
|
428
|
+
const hits = await deps.searchKnowledge({
|
|
289
429
|
userId: input.userId,
|
|
290
430
|
query: input.query,
|
|
291
431
|
options: {
|
|
@@ -295,8 +435,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
295
435
|
},
|
|
296
436
|
authToken: input.authToken
|
|
297
437
|
});
|
|
298
|
-
for (const hit of
|
|
299
|
-
|
|
438
|
+
for (const hit of hits) {
|
|
439
|
+
knowledgeHits.push(toKnowledgeResult(hit));
|
|
300
440
|
}
|
|
301
441
|
} catch (error) {
|
|
302
442
|
logger.warn?.("[memory-store] knowledge source failed:", error);
|
|
@@ -306,14 +446,14 @@ function createUnifiedSearch(deps = {}) {
|
|
|
306
446
|
message: error.message ?? "knowledge_search_failed"
|
|
307
447
|
});
|
|
308
448
|
}
|
|
309
|
-
} else if (sources.includes("knowledge"))
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
449
|
+
} else if (sources.includes("knowledge")) ;
|
|
450
|
+
const merged = mergeAcrossSources({
|
|
451
|
+
memorySubs,
|
|
452
|
+
insightHits,
|
|
453
|
+
knowledgeHits,
|
|
454
|
+
limit,
|
|
455
|
+
strategy: mergeStrategy
|
|
456
|
+
});
|
|
317
457
|
return {
|
|
318
458
|
query: input.query,
|
|
319
459
|
sources,
|
|
@@ -322,9 +462,35 @@ function createUnifiedSearch(deps = {}) {
|
|
|
322
462
|
warnings
|
|
323
463
|
};
|
|
324
464
|
}
|
|
325
|
-
async function runMemorySource(input, limit, threshold) {
|
|
465
|
+
async function runMemorySource(input, limit, threshold, warnings) {
|
|
326
466
|
if (typeof deps.embedQuery !== "function") {
|
|
327
|
-
|
|
467
|
+
warnings.push({
|
|
468
|
+
source: "memory",
|
|
469
|
+
code: "memory_lexical_search_fallback",
|
|
470
|
+
message: "Semantic search not configured, using keyword search as fallback"
|
|
471
|
+
});
|
|
472
|
+
const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
|
|
473
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
474
|
+
let lexical2 = [];
|
|
475
|
+
if (keywords.length > 0) {
|
|
476
|
+
try {
|
|
477
|
+
const filters2 = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
478
|
+
const results = await Promise.all(
|
|
479
|
+
filters2.map(
|
|
480
|
+
(botId) => lexicalSearchRawMessages2({
|
|
481
|
+
userId: input.userId,
|
|
482
|
+
keywords,
|
|
483
|
+
limit: Math.ceil(limit / filters2.length),
|
|
484
|
+
botId
|
|
485
|
+
})
|
|
486
|
+
)
|
|
487
|
+
);
|
|
488
|
+
lexical2 = results.flat().filter(Boolean).map(toMemoryResult);
|
|
489
|
+
} catch (error) {
|
|
490
|
+
logger.warn?.("[memory-store] Default lexical search failed:", error);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return { semantic: [], lexical: lexical2 };
|
|
328
494
|
}
|
|
329
495
|
const queryEmbedding = await deps.embedQuery({
|
|
330
496
|
userId: input.userId,
|
|
@@ -332,9 +498,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
332
498
|
authToken: input.authToken
|
|
333
499
|
});
|
|
334
500
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
501
|
+
let semantic = [];
|
|
335
502
|
if (isRawMessageChromaEnabled()) {
|
|
336
503
|
try {
|
|
337
|
-
|
|
504
|
+
semantic = (await Promise.all(
|
|
338
505
|
filters.map((filter) => {
|
|
339
506
|
const botId = "botId" in filter ? filter.botId : void 0;
|
|
340
507
|
return searchRawMessagesWithChroma({
|
|
@@ -349,18 +516,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
349
516
|
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
350
517
|
backend: "chroma",
|
|
351
518
|
dimensions: queryEmbedding.length,
|
|
352
|
-
count:
|
|
519
|
+
count: semantic.length
|
|
353
520
|
});
|
|
354
|
-
return chromaHits;
|
|
355
521
|
} catch (error) {
|
|
356
522
|
logger.warn?.(
|
|
357
523
|
"[memory-store] Chroma raw message search failed; falling back to database search:",
|
|
358
524
|
error
|
|
359
525
|
);
|
|
360
526
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const rows = (await Promise.all(
|
|
527
|
+
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
528
|
+
semantic = (await Promise.all(
|
|
364
529
|
filters.map(
|
|
365
530
|
(filter) => deps.searchRawMessagesAnn({
|
|
366
531
|
userId: input.userId,
|
|
@@ -371,18 +536,108 @@ function createUnifiedSearch(deps = {}) {
|
|
|
371
536
|
})
|
|
372
537
|
)
|
|
373
538
|
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
374
|
-
return rows;
|
|
375
539
|
}
|
|
376
|
-
|
|
540
|
+
if (semantic.length === 0) {
|
|
541
|
+
try {
|
|
542
|
+
const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
|
|
543
|
+
const manager2 = await getRawMessageManager2();
|
|
544
|
+
if (typeof manager2.searchMessagesSemantically === "function") {
|
|
545
|
+
const results = await manager2.searchMessagesSemantically({
|
|
546
|
+
userId: input.userId,
|
|
547
|
+
queryEmbedding,
|
|
548
|
+
limit
|
|
549
|
+
});
|
|
550
|
+
semantic = results.map((r) => ({
|
|
551
|
+
type: "memory",
|
|
552
|
+
id: r.id,
|
|
553
|
+
content: r.content,
|
|
554
|
+
similarity: r.similarity,
|
|
555
|
+
metadata: r.metadata ?? {}
|
|
556
|
+
}));
|
|
557
|
+
}
|
|
558
|
+
} catch (error) {
|
|
559
|
+
logger.warn?.("[memory-store] SQLite semantic search failed:", error);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
let lexical = [];
|
|
563
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
564
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
565
|
+
if (keywords.length > 0) {
|
|
566
|
+
try {
|
|
567
|
+
const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
568
|
+
lexical = (await Promise.all(
|
|
569
|
+
lexFilters.map(
|
|
570
|
+
(botId) => deps.searchRawMessagesLexical({
|
|
571
|
+
userId: input.userId,
|
|
572
|
+
keywords,
|
|
573
|
+
limit,
|
|
574
|
+
botId
|
|
575
|
+
})
|
|
576
|
+
)
|
|
577
|
+
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
578
|
+
...toMemoryResult(hit),
|
|
579
|
+
metadata: { ...hit.metadata, scoring: "bm25" }
|
|
580
|
+
}));
|
|
581
|
+
} catch (error) {
|
|
582
|
+
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
583
|
+
warnings.push({
|
|
584
|
+
source: "memory",
|
|
585
|
+
code: "memory_lexical_search_failed",
|
|
586
|
+
message: error.message ?? "memory_lexical_search_failed"
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
} else if (input.mergeStrategy === "rrf") {
|
|
591
|
+
warnings.push({
|
|
592
|
+
source: "memory",
|
|
593
|
+
code: "memory_lexical_search_not_configured",
|
|
594
|
+
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
return { semantic, lexical };
|
|
377
598
|
}
|
|
378
599
|
async function searchRawMemorySemantically(input) {
|
|
379
600
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
380
601
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
381
|
-
|
|
602
|
+
const sub = await runMemorySource(input, limit, threshold, []);
|
|
603
|
+
return mergeAcrossSources({
|
|
604
|
+
memorySubs: sub,
|
|
605
|
+
insightHits: [],
|
|
606
|
+
knowledgeHits: [],
|
|
607
|
+
limit,
|
|
608
|
+
strategy: normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy)
|
|
609
|
+
});
|
|
382
610
|
}
|
|
383
611
|
return { searchUnifiedMemory, searchRawMemorySemantically };
|
|
384
612
|
}
|
|
613
|
+
function mergeAcrossSources(input) {
|
|
614
|
+
const memorySubs = input.memorySubs ?? { semantic: [], lexical: [] };
|
|
615
|
+
const all = [
|
|
616
|
+
...memorySubs.semantic,
|
|
617
|
+
...memorySubs.lexical,
|
|
618
|
+
...input.insightHits,
|
|
619
|
+
...input.knowledgeHits
|
|
620
|
+
];
|
|
621
|
+
if (input.strategy !== "rrf") {
|
|
622
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
623
|
+
}
|
|
624
|
+
const lists = [];
|
|
625
|
+
if (memorySubs.semantic.length > 0) {
|
|
626
|
+
lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
627
|
+
}
|
|
628
|
+
if (memorySubs.lexical.length > 0) {
|
|
629
|
+
lists.push({ name: "memory-bm25", hits: memorySubs.lexical });
|
|
630
|
+
}
|
|
631
|
+
if (input.insightHits.length > 0) {
|
|
632
|
+
lists.push({ name: "insights", hits: input.insightHits });
|
|
633
|
+
}
|
|
634
|
+
if (input.knowledgeHits.length > 0) {
|
|
635
|
+
lists.push({ name: "knowledge", hits: input.knowledgeHits });
|
|
636
|
+
}
|
|
637
|
+
if (lists.length <= 1) {
|
|
638
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
639
|
+
}
|
|
640
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, input.limit);
|
|
641
|
+
}
|
|
385
642
|
|
|
386
|
-
export { clampUnifiedMemorySearchLimit, clampUnifiedMemorySearchThreshold, createUnifiedSearch, isRawMemorySemanticResult, mergeUnifiedMemorySearchResults, normalizeUnifiedMemorySearchSources, toKnowledgeResult, toMemoryResult };
|
|
387
|
-
//# sourceMappingURL=unified-search.js.map
|
|
388
|
-
//# sourceMappingURL=unified-search.js.map
|
|
643
|
+
export { clampUnifiedMemorySearchLimit, clampUnifiedMemorySearchThreshold, createUnifiedSearch, isRawMemorySemanticResult, mergeUnifiedMemorySearchResults, mergeUnifiedMemorySearchResultsRrf, normalizeUnifiedMemoryMergeStrategy, normalizeUnifiedMemorySearchSources, toKnowledgeResult, toMemoryResult };
|