@melandlabs/memory-store 0.2.0 → 0.2.2
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/http.js +211 -44
- package/dist/index.js +143 -29
- package/dist/mcp.js +169 -37
- package/dist/search/unified-search.js +166 -32
- package/dist/server/cli-http.js +236 -48
- package/dist/server/cli-mcp.js +194 -41
- package/dist/server/cli-shared.js +20 -5
- package/package.json +81 -93
package/dist/server/cli-http.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { serve } from '@hono/node-server';
|
|
3
|
-
import { Hono } from 'hono';
|
|
4
2
|
import { buildMemoryRecordEmbeddingDocument } from '@melandlabs/ai/memory';
|
|
5
3
|
import { ChromaVectorStore } from '@melandlabs/rag';
|
|
6
4
|
import { mkdirSync } from 'fs';
|
|
7
5
|
import { homedir } from 'os';
|
|
8
|
-
import {
|
|
6
|
+
import { dirname, join } from 'path';
|
|
9
7
|
import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
|
|
8
|
+
import { serve } from '@hono/node-server';
|
|
9
|
+
import { Hono } from 'hono';
|
|
10
|
+
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __esm = (fn, res) => function __init() {
|
|
14
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
+
};
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
10
20
|
|
|
11
21
|
// src/contracts.ts
|
|
12
22
|
function normalizeTimestampToMs(value) {
|
|
@@ -30,14 +40,10 @@ function rawMessageToMemoryRecord(message) {
|
|
|
30
40
|
embeddingContentHash: message.embeddingContentHash
|
|
31
41
|
};
|
|
32
42
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"RAW_MESSAGE_VECTOR_STORE_BACKEND",
|
|
38
|
-
"MEMORY_VECTOR_STORE_BACKEND",
|
|
39
|
-
"VECTOR_STORE_BACKEND"
|
|
40
|
-
];
|
|
43
|
+
var init_contracts = __esm({
|
|
44
|
+
"src/contracts.ts"() {
|
|
45
|
+
}
|
|
46
|
+
});
|
|
41
47
|
function getBackend(keys) {
|
|
42
48
|
for (const key of keys) {
|
|
43
49
|
const value = process.env[key]?.trim().toLowerCase();
|
|
@@ -140,21 +146,51 @@ async function searchRawMessagesWithChroma(input) {
|
|
|
140
146
|
metadata: result.metadata ?? {}
|
|
141
147
|
})).slice(0, input.limit);
|
|
142
148
|
}
|
|
149
|
+
var DEFAULT_RAW_MESSAGES_COLLECTION, RAW_VECTOR_BACKEND_ENV_KEYS;
|
|
150
|
+
var init_chroma_memory_index = __esm({
|
|
151
|
+
"src/storage/chroma-memory-index.ts"() {
|
|
152
|
+
init_contracts();
|
|
153
|
+
DEFAULT_RAW_MESSAGES_COLLECTION = "opencontext_raw_messages";
|
|
154
|
+
RAW_VECTOR_BACKEND_ENV_KEYS = [
|
|
155
|
+
"RAW_MESSAGE_VECTOR_STORE_BACKEND",
|
|
156
|
+
"MEMORY_VECTOR_STORE_BACKEND",
|
|
157
|
+
"VECTOR_STORE_BACKEND"
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
});
|
|
143
161
|
|
|
144
162
|
// src/storage/postgres-raw-message-factory.ts
|
|
145
|
-
var factory = null;
|
|
146
163
|
async function resolvePostgresFactory(env) {
|
|
147
|
-
return null;
|
|
164
|
+
if (!factory) return null;
|
|
165
|
+
return await factory(env);
|
|
148
166
|
}
|
|
149
167
|
function hasPostgresFactory() {
|
|
150
168
|
return factory !== null;
|
|
151
169
|
}
|
|
152
|
-
var
|
|
153
|
-
var
|
|
170
|
+
var factory;
|
|
171
|
+
var init_postgres_raw_message_factory = __esm({
|
|
172
|
+
"src/storage/postgres-raw-message-factory.ts"() {
|
|
173
|
+
factory = null;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// src/storage/sqlite-raw-message-store.ts
|
|
178
|
+
var sqlite_raw_message_store_exports = {};
|
|
179
|
+
__export(sqlite_raw_message_store_exports, {
|
|
180
|
+
__resetSQLiteRawMessageManagerForTests: () => __resetSQLiteRawMessageManagerForTests,
|
|
181
|
+
closeSQLiteRawMessageManager: () => closeSQLiteRawMessageManager,
|
|
182
|
+
getSQLiteRawMessageManager: () => getSQLiteRawMessageManager,
|
|
183
|
+
isSQLiteRawMessageStorageAvailable: () => isSQLiteRawMessageStorageAvailable,
|
|
184
|
+
lexicalSearchRawMessages: () => lexicalSearchRawMessages,
|
|
185
|
+
resolveSQLiteRawMessageDbPath: () => resolveSQLiteRawMessageDbPath
|
|
186
|
+
});
|
|
154
187
|
function resolveSQLiteRawMessageDbPath() {
|
|
155
188
|
const fromEnv = process.env.MEMORY_STORE_DB_PATH?.trim();
|
|
156
189
|
return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_SQLITE_PATH;
|
|
157
190
|
}
|
|
191
|
+
function isSQLiteRawMessageStorageAvailable(_env) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
158
194
|
async function getSQLiteRawMessageManager(_env) {
|
|
159
195
|
if (!manager) {
|
|
160
196
|
const dbPath = resolveSQLiteRawMessageDbPath();
|
|
@@ -174,8 +210,35 @@ async function closeSQLiteRawMessageManager() {
|
|
|
174
210
|
await manager.close();
|
|
175
211
|
manager = null;
|
|
176
212
|
}
|
|
213
|
+
function __resetSQLiteRawMessageManagerForTests() {
|
|
214
|
+
manager = null;
|
|
215
|
+
}
|
|
216
|
+
async function lexicalSearchRawMessages(input) {
|
|
217
|
+
const mgr = await getSQLiteRawMessageManager();
|
|
218
|
+
if (typeof mgr.lexicalSearchMessages !== "function") {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
return mgr.lexicalSearchMessages(input);
|
|
222
|
+
}
|
|
223
|
+
var manager, DEFAULT_SQLITE_PATH;
|
|
224
|
+
var init_sqlite_raw_message_store = __esm({
|
|
225
|
+
"src/storage/sqlite-raw-message-store.ts"() {
|
|
226
|
+
init_chroma_memory_index();
|
|
227
|
+
manager = null;
|
|
228
|
+
DEFAULT_SQLITE_PATH = join(homedir(), ".opencontext", "memory", "store.db");
|
|
229
|
+
}
|
|
230
|
+
});
|
|
177
231
|
|
|
178
232
|
// src/storage/raw-message-store.ts
|
|
233
|
+
var raw_message_store_exports = {};
|
|
234
|
+
__export(raw_message_store_exports, {
|
|
235
|
+
closeRawMessageStore: () => closeRawMessageStore,
|
|
236
|
+
configureRawMessageStore: () => configureRawMessageStore,
|
|
237
|
+
createRawMessageStore: () => createRawMessageStore,
|
|
238
|
+
getRawMessageManager: () => getRawMessageManager,
|
|
239
|
+
getRawMessageStorageBackend: () => getRawMessageStorageBackend,
|
|
240
|
+
isRawMessageStorageAvailable: () => isRawMessageStorageAvailable
|
|
241
|
+
});
|
|
179
242
|
function resolveBackend(env) {
|
|
180
243
|
const explicit = process.env.OPENCONTEXT_MEMORY_STORE_BACKEND?.trim().toLowerCase();
|
|
181
244
|
if (explicit === "postgres") return "postgres";
|
|
@@ -185,7 +248,7 @@ function resolveEnv(_env) {
|
|
|
185
248
|
return {};
|
|
186
249
|
}
|
|
187
250
|
function createRawMessageStore(options = {}) {
|
|
188
|
-
resolveEnv(options.env);
|
|
251
|
+
const env = resolveEnv(options.env);
|
|
189
252
|
return {
|
|
190
253
|
getBackend() {
|
|
191
254
|
return resolveBackend();
|
|
@@ -195,7 +258,7 @@ function createRawMessageStore(options = {}) {
|
|
|
195
258
|
},
|
|
196
259
|
async getManager() {
|
|
197
260
|
if (resolveBackend() === "postgres") {
|
|
198
|
-
const pg = await resolvePostgresFactory();
|
|
261
|
+
const pg = await resolvePostgresFactory(env);
|
|
199
262
|
if (!pg) {
|
|
200
263
|
throw new Error(
|
|
201
264
|
"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."
|
|
@@ -210,14 +273,47 @@ function createRawMessageStore(options = {}) {
|
|
|
210
273
|
}
|
|
211
274
|
};
|
|
212
275
|
}
|
|
213
|
-
|
|
214
|
-
|
|
276
|
+
function configureRawMessageStore(config) {
|
|
277
|
+
moduleConfig = config;
|
|
278
|
+
moduleStore = createRawMessageStore({ env: config.env });
|
|
279
|
+
return moduleStore;
|
|
280
|
+
}
|
|
281
|
+
function getRawMessageStorageBackend() {
|
|
282
|
+
if (!moduleStore) {
|
|
283
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
284
|
+
}
|
|
285
|
+
return moduleStore.getBackend();
|
|
286
|
+
}
|
|
215
287
|
function isRawMessageStorageAvailable() {
|
|
216
288
|
if (!moduleStore) {
|
|
217
289
|
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
218
290
|
}
|
|
219
291
|
return moduleStore.isAvailable();
|
|
220
292
|
}
|
|
293
|
+
async function getRawMessageManager() {
|
|
294
|
+
if (!moduleStore) {
|
|
295
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
296
|
+
}
|
|
297
|
+
return moduleStore.getManager();
|
|
298
|
+
}
|
|
299
|
+
async function closeRawMessageStore() {
|
|
300
|
+
if (!moduleStore) return;
|
|
301
|
+
await moduleStore.close();
|
|
302
|
+
moduleStore = null;
|
|
303
|
+
}
|
|
304
|
+
var moduleStore, moduleConfig;
|
|
305
|
+
var init_raw_message_store = __esm({
|
|
306
|
+
"src/storage/raw-message-store.ts"() {
|
|
307
|
+
init_postgres_raw_message_factory();
|
|
308
|
+
init_sqlite_raw_message_store();
|
|
309
|
+
moduleStore = null;
|
|
310
|
+
moduleConfig = null;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// src/search/unified-search.ts
|
|
315
|
+
init_chroma_memory_index();
|
|
316
|
+
init_raw_message_store();
|
|
221
317
|
|
|
222
318
|
// src/search/utilities.ts
|
|
223
319
|
var DEFAULT_LIMIT = 10;
|
|
@@ -361,7 +457,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
361
457
|
warnings.push({
|
|
362
458
|
source: "memory",
|
|
363
459
|
code: "memory_search_failed",
|
|
364
|
-
message: error.message ?? "
|
|
460
|
+
message: `Memory search failed: ${error.message ?? "Unknown error"}. Using keyword search fallback.`
|
|
365
461
|
});
|
|
366
462
|
}
|
|
367
463
|
} else {
|
|
@@ -401,13 +497,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
401
497
|
message: error.message ?? "insights_search_failed"
|
|
402
498
|
});
|
|
403
499
|
}
|
|
404
|
-
} else if (sources.includes("insights"))
|
|
405
|
-
warnings.push({
|
|
406
|
-
source: "insights",
|
|
407
|
-
code: "insights_search_not_configured",
|
|
408
|
-
message: "Insights search is not configured for this host."
|
|
409
|
-
});
|
|
410
|
-
}
|
|
500
|
+
} else if (sources.includes("insights")) ;
|
|
411
501
|
const knowledgeHits = [];
|
|
412
502
|
if (sources.includes("knowledge") && typeof deps.searchKnowledge === "function") {
|
|
413
503
|
try {
|
|
@@ -432,13 +522,7 @@ function createUnifiedSearch(deps = {}) {
|
|
|
432
522
|
message: error.message ?? "knowledge_search_failed"
|
|
433
523
|
});
|
|
434
524
|
}
|
|
435
|
-
} else if (sources.includes("knowledge"))
|
|
436
|
-
warnings.push({
|
|
437
|
-
source: "knowledge",
|
|
438
|
-
code: "knowledge_search_not_configured",
|
|
439
|
-
message: "Knowledge search is not configured for this host."
|
|
440
|
-
});
|
|
441
|
-
}
|
|
525
|
+
} else if (sources.includes("knowledge")) ;
|
|
442
526
|
const merged = mergeAcrossSources({
|
|
443
527
|
memorySubs,
|
|
444
528
|
insightHits,
|
|
@@ -456,7 +540,33 @@ function createUnifiedSearch(deps = {}) {
|
|
|
456
540
|
}
|
|
457
541
|
async function runMemorySource(input, limit, threshold, warnings) {
|
|
458
542
|
if (typeof deps.embedQuery !== "function") {
|
|
459
|
-
|
|
543
|
+
warnings.push({
|
|
544
|
+
source: "memory",
|
|
545
|
+
code: "memory_lexical_search_fallback",
|
|
546
|
+
message: "Semantic search not configured, using keyword search as fallback"
|
|
547
|
+
});
|
|
548
|
+
const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
|
|
549
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
550
|
+
let lexical2 = [];
|
|
551
|
+
if (keywords.length > 0) {
|
|
552
|
+
try {
|
|
553
|
+
const filters2 = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
554
|
+
const results = await Promise.all(
|
|
555
|
+
filters2.map(
|
|
556
|
+
(botId) => lexicalSearchRawMessages2({
|
|
557
|
+
userId: input.userId,
|
|
558
|
+
keywords,
|
|
559
|
+
limit: Math.ceil(limit / filters2.length),
|
|
560
|
+
botId
|
|
561
|
+
})
|
|
562
|
+
)
|
|
563
|
+
);
|
|
564
|
+
lexical2 = results.flat().filter(Boolean).map(toMemoryResult);
|
|
565
|
+
} catch (error) {
|
|
566
|
+
logger.warn?.("[memory-store] Default lexical search failed:", error);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return { semantic: [], lexical: lexical2 };
|
|
460
570
|
}
|
|
461
571
|
const queryEmbedding = await deps.embedQuery({
|
|
462
572
|
userId: input.userId,
|
|
@@ -503,6 +613,28 @@ function createUnifiedSearch(deps = {}) {
|
|
|
503
613
|
)
|
|
504
614
|
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
505
615
|
}
|
|
616
|
+
if (semantic.length === 0) {
|
|
617
|
+
try {
|
|
618
|
+
const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
|
|
619
|
+
const manager2 = await getRawMessageManager2();
|
|
620
|
+
if (typeof manager2.searchMessagesSemantically === "function") {
|
|
621
|
+
const results = await manager2.searchMessagesSemantically({
|
|
622
|
+
userId: input.userId,
|
|
623
|
+
queryEmbedding,
|
|
624
|
+
limit
|
|
625
|
+
});
|
|
626
|
+
semantic = results.map((r) => ({
|
|
627
|
+
type: "memory",
|
|
628
|
+
id: r.id,
|
|
629
|
+
content: r.content,
|
|
630
|
+
similarity: r.similarity,
|
|
631
|
+
metadata: r.metadata ?? {}
|
|
632
|
+
}));
|
|
633
|
+
}
|
|
634
|
+
} catch (error) {
|
|
635
|
+
logger.warn?.("[memory-store] SQLite semantic search failed:", error);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
506
638
|
let lexical = [];
|
|
507
639
|
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
508
640
|
const keywords = deriveLexicalKeywords(input.query);
|
|
@@ -585,6 +717,8 @@ function mergeAcrossSources(input) {
|
|
|
585
717
|
}
|
|
586
718
|
|
|
587
719
|
// src/http.ts
|
|
720
|
+
init_chroma_memory_index();
|
|
721
|
+
init_raw_message_store();
|
|
588
722
|
async function embedMissingMessages(messages, deps) {
|
|
589
723
|
if (typeof deps.embedQuery !== "function") {
|
|
590
724
|
return messages;
|
|
@@ -619,7 +753,23 @@ async function startHttpServer(options = {}) {
|
|
|
619
753
|
const rawStore = createRawMessageStore({
|
|
620
754
|
env: options.env
|
|
621
755
|
});
|
|
622
|
-
const
|
|
756
|
+
const manager2 = await rawStore.getManager();
|
|
757
|
+
const search = createUnifiedSearch({
|
|
758
|
+
...options.unified,
|
|
759
|
+
searchRawMessagesLexical: async (input) => {
|
|
760
|
+
if (typeof manager2.lexicalSearchMessages === "function") {
|
|
761
|
+
const results = await manager2.lexicalSearchMessages(input);
|
|
762
|
+
return results.filter(Boolean).map((r) => ({
|
|
763
|
+
type: "memory",
|
|
764
|
+
id: r.id,
|
|
765
|
+
content: r.content,
|
|
766
|
+
similarity: r.similarity,
|
|
767
|
+
metadata: r.metadata ?? {}
|
|
768
|
+
}));
|
|
769
|
+
}
|
|
770
|
+
return [];
|
|
771
|
+
}
|
|
772
|
+
});
|
|
623
773
|
const app = new Hono();
|
|
624
774
|
app.get("/health", (c) => c.json({ ok: true, store: "memory", ts: Date.now() }));
|
|
625
775
|
app.post("/v1/search", async (c) => {
|
|
@@ -647,20 +797,20 @@ async function startHttpServer(options = {}) {
|
|
|
647
797
|
if (!Array.isArray(body.messages) || !body.userId) {
|
|
648
798
|
return c.json({ error: "userId and messages[] required" }, 400);
|
|
649
799
|
}
|
|
650
|
-
const
|
|
800
|
+
const manager3 = await rawStore.getManager();
|
|
651
801
|
const incoming = body.messages;
|
|
652
802
|
const messages = body.embedOnInsert === true ? await embedMissingMessages(
|
|
653
803
|
incoming.map((m) => ({ ...m, userId: m.userId ?? body.userId })),
|
|
654
804
|
options.unified ?? {}
|
|
655
805
|
) : incoming.map((m) => ({ ...m, userId: m.userId ?? body.userId }));
|
|
656
806
|
let result;
|
|
657
|
-
if (typeof
|
|
658
|
-
result = await
|
|
807
|
+
if (typeof manager3.upsertRawMessages === "function") {
|
|
808
|
+
result = await manager3.upsertRawMessages({
|
|
659
809
|
userId: body.userId,
|
|
660
810
|
messages
|
|
661
811
|
});
|
|
662
|
-
} else if (typeof
|
|
663
|
-
const ids = await
|
|
812
|
+
} else if (typeof manager3.storeMessages === "function") {
|
|
813
|
+
const ids = await manager3.storeMessages(messages);
|
|
664
814
|
result = { inserted: ids.length, ids };
|
|
665
815
|
} else {
|
|
666
816
|
return c.json(
|
|
@@ -675,14 +825,33 @@ async function startHttpServer(options = {}) {
|
|
|
675
825
|
} catch (error) {
|
|
676
826
|
console.warn("[memory-store/http] chroma upsert failed:", error);
|
|
677
827
|
}
|
|
828
|
+
try {
|
|
829
|
+
const messagesWithEmbeddings = messages.filter(
|
|
830
|
+
(m) => Array.isArray(m.embedding) && m.embedding.length > 0
|
|
831
|
+
);
|
|
832
|
+
if (messagesWithEmbeddings.length > 0) {
|
|
833
|
+
for (const message of messagesWithEmbeddings) {
|
|
834
|
+
if (typeof manager3.upsertVectorForMessage === "function") {
|
|
835
|
+
manager3.upsertVectorForMessage(message.messageId, message.embedding);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
console.log(
|
|
839
|
+
"[memory-store/http] Updated sqlite-vec vector table for",
|
|
840
|
+
messagesWithEmbeddings.length,
|
|
841
|
+
"message(s)"
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
} catch (error) {
|
|
845
|
+
console.warn("[memory-store/http] sqlite-vec vector update failed:", error);
|
|
846
|
+
}
|
|
678
847
|
return c.json({ ok: true, count: messages.length, result });
|
|
679
848
|
});
|
|
680
849
|
app.get("/v1/raw-messages/:id", async (c) => {
|
|
681
850
|
const id = c.req.param("id");
|
|
682
851
|
const userId = c.req.query("userId");
|
|
683
852
|
if (!userId) return c.json({ error: "userId query param required" }, 400);
|
|
684
|
-
const
|
|
685
|
-
const row = await
|
|
853
|
+
const manager3 = await rawStore.getManager();
|
|
854
|
+
const row = await manager3.getMessageById?.(id);
|
|
686
855
|
if (!row) return c.json({ error: "not found" }, 404);
|
|
687
856
|
return c.json({ message: row });
|
|
688
857
|
});
|
|
@@ -697,7 +866,11 @@ async function startHttpServer(options = {}) {
|
|
|
697
866
|
};
|
|
698
867
|
}
|
|
699
868
|
|
|
869
|
+
// src/server/cli-shared.ts
|
|
870
|
+
init_raw_message_store();
|
|
871
|
+
|
|
700
872
|
// src/storage/sqlite-vector-index.ts
|
|
873
|
+
init_sqlite_raw_message_store();
|
|
701
874
|
function getMemoryVectorStoreBackend() {
|
|
702
875
|
const backend = (process.env.INSIGHT_VECTOR_STORE_BACKEND || process.env.MEMORY_VECTOR_STORE_BACKEND || process.env.VECTOR_STORE_BACKEND || "sqlite-vec").toLowerCase();
|
|
703
876
|
return backend === "chroma" ? "chroma" : "sqlite-vec";
|
|
@@ -811,11 +984,26 @@ async function buildUnified(args) {
|
|
|
811
984
|
const wantsAiRag = args.embeddingProvider === "local" || args.memoryBackend === "chroma" || args.insightsBackend === "chroma" || args.knowledgeBackend === "chroma";
|
|
812
985
|
const aiRag = wantsAiRag ? await loadAiRag() : null;
|
|
813
986
|
if (args.embeddingProvider === "local") {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
987
|
+
try {
|
|
988
|
+
if (!aiRag) {
|
|
989
|
+
throw new Error("ai-rag modules not loaded");
|
|
990
|
+
}
|
|
991
|
+
const provider = new aiRag.LocalTransformersEmbeddingProvider({ modelName: args.embeddingModel });
|
|
992
|
+
unified.embedQuery = async ({ query }) => provider.embedQuery(query);
|
|
993
|
+
log(
|
|
994
|
+
`embedQuery wired via LocalTransformersEmbeddingProvider (model=${args.embeddingModel ?? "Xenova/all-MiniLM-L6-v2"})`
|
|
995
|
+
);
|
|
996
|
+
} catch (error) {
|
|
997
|
+
log(
|
|
998
|
+
`Warning: Failed to initialize LocalTransformersEmbeddingProvider: ${error.message}`
|
|
999
|
+
);
|
|
1000
|
+
log(
|
|
1001
|
+
"Semantic search will be disabled. The server will continue with keyword-only search."
|
|
1002
|
+
);
|
|
1003
|
+
log(
|
|
1004
|
+
"To fix: Ensure the model is downloaded or check your network connection to huggingface.co"
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
819
1007
|
} else if (args.embeddingProvider === "openrouter") {
|
|
820
1008
|
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
821
1009
|
if (!apiKey) {
|