@h-ai/vecdb 0.1.0-alpha.32 → 0.1.0-alpha.34
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 +12 -1
- package/dist/index.d.ts +106 -3
- package/dist/index.js +425 -84
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { core, ok, err } from '@h-ai/core';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { setTimeout } from 'timers/promises';
|
|
3
5
|
import { createHash } from 'crypto';
|
|
4
6
|
|
|
5
7
|
// src/vecdb-config.ts
|
|
@@ -112,10 +114,31 @@ var QdrantConfigSchema = z.object({
|
|
|
112
114
|
metric: DistanceMetricSchema.optional(),
|
|
113
115
|
...OperationLogConfigFieldSchema
|
|
114
116
|
});
|
|
117
|
+
var ChromaConfigSchema = z.object({
|
|
118
|
+
type: z.literal("chroma"),
|
|
119
|
+
/** 本地持久化路径(嵌入式模式:提供且无 url 时自动拉起本地服务) */
|
|
120
|
+
path: z.string().optional(),
|
|
121
|
+
/** 已有 Chroma 服务 URL(提供时直连,不自动拉起进程) */
|
|
122
|
+
url: z.string().url().optional(),
|
|
123
|
+
/** 主机(默认 localhost) */
|
|
124
|
+
host: z.string().default("localhost"),
|
|
125
|
+
/** 端口(默认 8000) */
|
|
126
|
+
port: z.number().int().min(1).max(65535).default(8e3),
|
|
127
|
+
/** API Key(可选,连接受保护的 Chroma 服务时使用) */
|
|
128
|
+
apiKey: z.string().optional(),
|
|
129
|
+
/** 拉起本地服务的可执行命令(默认 'chroma',可指定绝对路径) */
|
|
130
|
+
serverCommand: z.string().default("chroma"),
|
|
131
|
+
/** 拉起本地服务的就绪等待超时(毫秒,默认 30000) */
|
|
132
|
+
startupTimeout: z.number().int().positive().default(3e4),
|
|
133
|
+
/** 距离度量(默认 cosine) */
|
|
134
|
+
metric: DistanceMetricSchema.optional(),
|
|
135
|
+
...OperationLogConfigFieldSchema
|
|
136
|
+
});
|
|
115
137
|
var VecdbConfigSchema = z.discriminatedUnion("type", [
|
|
116
138
|
LancedbConfigSchema,
|
|
117
139
|
PgvectorConfigSchema,
|
|
118
|
-
QdrantConfigSchema
|
|
140
|
+
QdrantConfigSchema,
|
|
141
|
+
ChromaConfigSchema
|
|
119
142
|
]);
|
|
120
143
|
var VecdbErrorInfo = {
|
|
121
144
|
CONNECTION_FAILED: "001:500",
|
|
@@ -302,8 +325,295 @@ function createBaseVectorOps(ctx, driver) {
|
|
|
302
325
|
};
|
|
303
326
|
}
|
|
304
327
|
|
|
305
|
-
// src/providers/vecdb-provider-
|
|
306
|
-
var logger = core.logger.child({ module: "vecdb", scope: "
|
|
328
|
+
// src/providers/vecdb-provider-chroma.ts
|
|
329
|
+
var logger = core.logger.child({ module: "vecdb", scope: "chroma" });
|
|
330
|
+
var DIMENSION_METADATA_KEY = "hai_dimension";
|
|
331
|
+
var SPACE_METADATA_KEY = "hnsw:space";
|
|
332
|
+
var HEARTBEAT_POLL_INTERVAL = 300;
|
|
333
|
+
function isChromaModule(value) {
|
|
334
|
+
return typeof value === "object" && value !== null && typeof value.ChromaClient === "function";
|
|
335
|
+
}
|
|
336
|
+
var NOOP_EMBEDDING_FUNCTION = {
|
|
337
|
+
name: "hai-noop",
|
|
338
|
+
async generate() {
|
|
339
|
+
throw new Error("Chroma embedding function should not be called: vectors are provided explicitly");
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
function toChromaSpace(metric) {
|
|
343
|
+
if (metric === "euclidean")
|
|
344
|
+
return "l2";
|
|
345
|
+
if (metric === "dot")
|
|
346
|
+
return "ip";
|
|
347
|
+
return "cosine";
|
|
348
|
+
}
|
|
349
|
+
function fromChromaSpace(space) {
|
|
350
|
+
if (space === "l2")
|
|
351
|
+
return "euclidean";
|
|
352
|
+
if (space === "ip")
|
|
353
|
+
return "dot";
|
|
354
|
+
return "cosine";
|
|
355
|
+
}
|
|
356
|
+
function distanceToScore(distance, metric) {
|
|
357
|
+
if (metric === "cosine")
|
|
358
|
+
return Math.max(0, Math.min(1, 1 - distance));
|
|
359
|
+
return 1 / (1 + Math.max(0, distance));
|
|
360
|
+
}
|
|
361
|
+
function buildWhere(filter) {
|
|
362
|
+
if (!filter)
|
|
363
|
+
return void 0;
|
|
364
|
+
const entries = Object.entries(filter);
|
|
365
|
+
if (entries.length === 0)
|
|
366
|
+
return void 0;
|
|
367
|
+
if (entries.length === 1)
|
|
368
|
+
return { [entries[0][0]]: entries[0][1] };
|
|
369
|
+
return { $and: entries.map(([key, value]) => ({ [key]: value })) };
|
|
370
|
+
}
|
|
371
|
+
function createChromaProvider() {
|
|
372
|
+
let client = null;
|
|
373
|
+
let config = null;
|
|
374
|
+
let serverProcess = null;
|
|
375
|
+
function isMissingModuleError(error) {
|
|
376
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
377
|
+
return message.includes("chromadb");
|
|
378
|
+
}
|
|
379
|
+
async function loadChromaModule() {
|
|
380
|
+
try {
|
|
381
|
+
const mod = await import('chromadb');
|
|
382
|
+
if (!isChromaModule(mod)) {
|
|
383
|
+
const error = new Error("chromadb driver module does not export ChromaClient");
|
|
384
|
+
logger.error("Failed to load Chroma driver", { error: error.message });
|
|
385
|
+
return err(HaiVecdbError.DRIVER_NOT_FOUND, vecdbM("vecdb_driverNotFound", { params: { driver: "chromadb" } }), error);
|
|
386
|
+
}
|
|
387
|
+
return ok(mod);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
if (isMissingModuleError(error)) {
|
|
390
|
+
logger.error("Failed to load Chroma driver", { error: error instanceof Error ? error.message : String(error) });
|
|
391
|
+
return err(HaiVecdbError.DRIVER_NOT_FOUND, vecdbM("vecdb_driverNotFound", { params: { driver: "chromadb" } }), error);
|
|
392
|
+
}
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function startEmbeddedServer(chromaConfig) {
|
|
397
|
+
const args = ["run", "--path", chromaConfig.path ?? "./data/chroma", "--host", chromaConfig.host, "--port", String(chromaConfig.port)];
|
|
398
|
+
logger.info("Starting embedded Chroma server", { command: chromaConfig.serverCommand, host: chromaConfig.host, port: chromaConfig.port });
|
|
399
|
+
const proc = spawn(chromaConfig.serverCommand, args, { stdio: "ignore" });
|
|
400
|
+
proc.on("error", (error) => {
|
|
401
|
+
logger.error("Chroma server process error", { error: error.message });
|
|
402
|
+
});
|
|
403
|
+
serverProcess = proc;
|
|
404
|
+
}
|
|
405
|
+
function stopEmbeddedServer() {
|
|
406
|
+
if (!serverProcess)
|
|
407
|
+
return;
|
|
408
|
+
try {
|
|
409
|
+
serverProcess.kill();
|
|
410
|
+
} catch (error) {
|
|
411
|
+
logger.warn("Failed to stop Chroma server", { error: error instanceof Error ? error.message : String(error) });
|
|
412
|
+
}
|
|
413
|
+
serverProcess = null;
|
|
414
|
+
}
|
|
415
|
+
async function waitForHeartbeat(chromaClient, timeout) {
|
|
416
|
+
const deadline = Date.now() + timeout;
|
|
417
|
+
let lastError;
|
|
418
|
+
while (Date.now() < deadline) {
|
|
419
|
+
try {
|
|
420
|
+
await chromaClient.heartbeat();
|
|
421
|
+
return;
|
|
422
|
+
} catch (error) {
|
|
423
|
+
lastError = error;
|
|
424
|
+
await setTimeout(HEARTBEAT_POLL_INTERVAL);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
throw new Error(`Chroma heartbeat timed out after ${timeout}ms: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
|
|
428
|
+
}
|
|
429
|
+
async function getCollection(name) {
|
|
430
|
+
return client.getCollection({ name, embeddingFunction: NOOP_EMBEDDING_FUNCTION });
|
|
431
|
+
}
|
|
432
|
+
function readDimension(collection) {
|
|
433
|
+
const value = collection.metadata?.[DIMENSION_METADATA_KEY];
|
|
434
|
+
return typeof value === "number" ? value : 0;
|
|
435
|
+
}
|
|
436
|
+
function validateDimension(expected, actual) {
|
|
437
|
+
if (expected === 0 || expected === actual)
|
|
438
|
+
return null;
|
|
439
|
+
return err(
|
|
440
|
+
HaiVecdbError.DIMENSION_MISMATCH,
|
|
441
|
+
vecdbM("vecdb_dimensionMismatch", { params: { expected: String(expected), actual: String(actual) } })
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
const ctx = { isConnected: () => client !== null, logger, operationLog: () => config?.operationLog };
|
|
445
|
+
const collectionDriver = {
|
|
446
|
+
async create(name, options) {
|
|
447
|
+
logger.debug("Creating collection", { name, dimension: options.dimension, metric: options.metric });
|
|
448
|
+
const existing = await client.listCollections();
|
|
449
|
+
const exists = existing.some((item) => (typeof item === "string" ? item : item.name) === name);
|
|
450
|
+
if (exists)
|
|
451
|
+
return err(HaiVecdbError.COLLECTION_ALREADY_EXISTS, vecdbM("vecdb_collectionAlreadyExists", { params: { name } }));
|
|
452
|
+
const metric = options.metric ?? config?.metric ?? "cosine";
|
|
453
|
+
await client.getOrCreateCollection({
|
|
454
|
+
name,
|
|
455
|
+
metadata: { [SPACE_METADATA_KEY]: toChromaSpace(metric), [DIMENSION_METADATA_KEY]: options.dimension },
|
|
456
|
+
embeddingFunction: NOOP_EMBEDDING_FUNCTION
|
|
457
|
+
});
|
|
458
|
+
logger.info("Collection created", { name, dimension: options.dimension, metric });
|
|
459
|
+
return ok(void 0);
|
|
460
|
+
},
|
|
461
|
+
async drop(name) {
|
|
462
|
+
logger.debug("Dropping collection", { name });
|
|
463
|
+
const existing = await client.listCollections();
|
|
464
|
+
const exists = existing.some((item) => (typeof item === "string" ? item : item.name) === name);
|
|
465
|
+
if (!exists)
|
|
466
|
+
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name } }));
|
|
467
|
+
await client.deleteCollection({ name });
|
|
468
|
+
logger.info("Collection dropped", { name });
|
|
469
|
+
return ok(void 0);
|
|
470
|
+
},
|
|
471
|
+
async exists(name) {
|
|
472
|
+
logger.debug("Checking collection exists", { name });
|
|
473
|
+
const existing = await client.listCollections();
|
|
474
|
+
return ok(existing.some((item) => (typeof item === "string" ? item : item.name) === name));
|
|
475
|
+
},
|
|
476
|
+
async info(name) {
|
|
477
|
+
logger.debug("Getting collection info", { name });
|
|
478
|
+
const existing = await client.listCollections();
|
|
479
|
+
if (!existing.some((item) => (typeof item === "string" ? item : item.name) === name))
|
|
480
|
+
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name } }));
|
|
481
|
+
const collection = await getCollection(name);
|
|
482
|
+
const count = await collection.count();
|
|
483
|
+
return ok({
|
|
484
|
+
name,
|
|
485
|
+
dimension: readDimension(collection),
|
|
486
|
+
metric: fromChromaSpace(collection.metadata?.[SPACE_METADATA_KEY]),
|
|
487
|
+
count
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
async list() {
|
|
491
|
+
logger.debug("Listing collections");
|
|
492
|
+
const existing = await client.listCollections();
|
|
493
|
+
return ok(existing.map((item) => typeof item === "string" ? item : item.name));
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
function toWriteParams(documents) {
|
|
497
|
+
return {
|
|
498
|
+
ids: documents.map((doc) => doc.id),
|
|
499
|
+
embeddings: documents.map((doc) => doc.vector),
|
|
500
|
+
documents: documents.map((doc) => doc.content ?? null),
|
|
501
|
+
metadatas: documents.map((doc) => ({ ...doc.metadata, _content: doc.content ?? "" }))
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
const vectorDriver = {
|
|
505
|
+
async insert(collection, documents) {
|
|
506
|
+
logger.debug("Inserting vectors", { collection, count: documents.length });
|
|
507
|
+
const target = await getCollection(collection);
|
|
508
|
+
const expected = readDimension(target);
|
|
509
|
+
for (const doc of documents) {
|
|
510
|
+
const validation = validateDimension(expected, doc.vector.length);
|
|
511
|
+
if (validation)
|
|
512
|
+
return validation;
|
|
513
|
+
}
|
|
514
|
+
await target.add(toWriteParams(documents));
|
|
515
|
+
logger.info("Vectors inserted", { collection, count: documents.length });
|
|
516
|
+
return ok(void 0);
|
|
517
|
+
},
|
|
518
|
+
async upsert(collection, documents) {
|
|
519
|
+
logger.debug("Upserting vectors", { collection, count: documents.length });
|
|
520
|
+
const target = await getCollection(collection);
|
|
521
|
+
const expected = readDimension(target);
|
|
522
|
+
for (const doc of documents) {
|
|
523
|
+
const validation = validateDimension(expected, doc.vector.length);
|
|
524
|
+
if (validation)
|
|
525
|
+
return validation;
|
|
526
|
+
}
|
|
527
|
+
await target.upsert(toWriteParams(documents));
|
|
528
|
+
logger.info("Vectors upserted", { collection, count: documents.length });
|
|
529
|
+
return ok(void 0);
|
|
530
|
+
},
|
|
531
|
+
async delete(collection, ids) {
|
|
532
|
+
logger.debug("Deleting vectors", { collection, count: ids.length });
|
|
533
|
+
const target = await getCollection(collection);
|
|
534
|
+
await target.delete({ ids });
|
|
535
|
+
logger.info("Vectors deleted", { collection, count: ids.length });
|
|
536
|
+
return ok(void 0);
|
|
537
|
+
},
|
|
538
|
+
async search(collection, vector, options) {
|
|
539
|
+
const topK = options?.topK ?? 10;
|
|
540
|
+
const minScore = options?.minScore ?? 0;
|
|
541
|
+
logger.debug("Searching vectors", { collection, topK, hasFilter: !!options?.filter });
|
|
542
|
+
const target = await getCollection(collection);
|
|
543
|
+
const expected = readDimension(target);
|
|
544
|
+
const validation = validateDimension(expected, vector.length);
|
|
545
|
+
if (validation)
|
|
546
|
+
return validation;
|
|
547
|
+
const metric = fromChromaSpace(target.metadata?.[SPACE_METADATA_KEY]);
|
|
548
|
+
const response = await target.query({ queryEmbeddings: [vector], nResults: topK, where: buildWhere(options?.filter) });
|
|
549
|
+
const ids = response.ids[0] ?? [];
|
|
550
|
+
const distances = response.distances?.[0] ?? [];
|
|
551
|
+
const metadatas = response.metadatas?.[0] ?? [];
|
|
552
|
+
const results = [];
|
|
553
|
+
for (let i = 0; i < ids.length; i++) {
|
|
554
|
+
const score = distanceToScore(distances[i] ?? 0, metric);
|
|
555
|
+
if (score < minScore)
|
|
556
|
+
continue;
|
|
557
|
+
const metadata = metadatas[i] ?? {};
|
|
558
|
+
const { _content, ...rest } = metadata;
|
|
559
|
+
results.push({
|
|
560
|
+
id: ids[i],
|
|
561
|
+
score,
|
|
562
|
+
content: typeof _content === "string" && _content.length > 0 ? _content : void 0,
|
|
563
|
+
metadata: Object.keys(rest).length > 0 ? rest : void 0
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return ok(results);
|
|
567
|
+
},
|
|
568
|
+
async count(collection) {
|
|
569
|
+
logger.debug("Counting vectors", { collection });
|
|
570
|
+
const target = await getCollection(collection);
|
|
571
|
+
return ok(await target.count());
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
return {
|
|
575
|
+
name: "chroma",
|
|
576
|
+
async connect(cfg) {
|
|
577
|
+
if (cfg.type !== "chroma")
|
|
578
|
+
return err(HaiVecdbError.UNSUPPORTED_TYPE, vecdbM("vecdb_unsupportedType", { params: { type: cfg.type } }));
|
|
579
|
+
const chromaConfig = cfg;
|
|
580
|
+
const embedded = !!chromaConfig.path && !chromaConfig.url;
|
|
581
|
+
const loadResult = await loadChromaModule();
|
|
582
|
+
if (!loadResult.success)
|
|
583
|
+
return loadResult;
|
|
584
|
+
try {
|
|
585
|
+
if (embedded)
|
|
586
|
+
startEmbeddedServer(chromaConfig);
|
|
587
|
+
const clientParams = chromaConfig.url ? { path: chromaConfig.url } : { host: chromaConfig.host, port: chromaConfig.port };
|
|
588
|
+
if (chromaConfig.apiKey)
|
|
589
|
+
clientParams.auth = { provider: "token", credentials: chromaConfig.apiKey, tokenHeaderType: "AUTHORIZATION" };
|
|
590
|
+
const chromaClient = new loadResult.data.ChromaClient(clientParams);
|
|
591
|
+
await waitForHeartbeat(chromaClient, embedded ? chromaConfig.startupTimeout : 5e3);
|
|
592
|
+
client = chromaClient;
|
|
593
|
+
config = chromaConfig;
|
|
594
|
+
logger.info("Chroma connected", { embedded, host: chromaConfig.host, port: chromaConfig.port });
|
|
595
|
+
return ok(void 0);
|
|
596
|
+
} catch (error) {
|
|
597
|
+
stopEmbeddedServer();
|
|
598
|
+
logger.error("Failed to connect to Chroma", { error: error instanceof Error ? error.message : String(error) });
|
|
599
|
+
return err(HaiVecdbError.CONNECTION_FAILED, vecdbM("vecdb_connectionFailed", { params: { error: String(error) } }), error);
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
async close() {
|
|
603
|
+
stopEmbeddedServer();
|
|
604
|
+
client = null;
|
|
605
|
+
config = null;
|
|
606
|
+
logger.info("Chroma connection closed");
|
|
607
|
+
return ok(void 0);
|
|
608
|
+
},
|
|
609
|
+
isConnected() {
|
|
610
|
+
return client !== null;
|
|
611
|
+
},
|
|
612
|
+
collection: createBaseCollectionOps(ctx, collectionDriver),
|
|
613
|
+
vector: createBaseVectorOps(ctx, vectorDriver)
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
var logger2 = core.logger.child({ module: "vecdb", scope: "lancedb" });
|
|
307
617
|
function escapeLanceFilterValue(value) {
|
|
308
618
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
309
619
|
}
|
|
@@ -316,7 +626,7 @@ function createLancedbProvider() {
|
|
|
316
626
|
const mod = await import('@lancedb/lancedb');
|
|
317
627
|
return ok(mod);
|
|
318
628
|
} catch (error) {
|
|
319
|
-
|
|
629
|
+
logger2.error("Failed to load @lancedb/lancedb", { error });
|
|
320
630
|
return err(HaiVecdbError.DRIVER_NOT_FOUND, vecdbM("vecdb_driverNotFound", { params: { driver: "@lancedb/lancedb" } }), error);
|
|
321
631
|
}
|
|
322
632
|
}
|
|
@@ -363,10 +673,10 @@ function createLancedbProvider() {
|
|
|
363
673
|
}
|
|
364
674
|
return null;
|
|
365
675
|
}
|
|
366
|
-
const ctx = { isConnected: () => connection !== null, logger, operationLog: () => config?.operationLog };
|
|
676
|
+
const ctx = { isConnected: () => connection !== null, logger: logger2, operationLog: () => config?.operationLog };
|
|
367
677
|
const collectionDriver = {
|
|
368
678
|
async create(name, options) {
|
|
369
|
-
|
|
679
|
+
logger2.debug("Creating collection", { name, dimension: options.dimension, metric: options.metric });
|
|
370
680
|
const tableNames = await connection.tableNames();
|
|
371
681
|
if (tableNames.includes(name)) {
|
|
372
682
|
return err(HaiVecdbError.COLLECTION_ALREADY_EXISTS, vecdbM("vecdb_collectionAlreadyExists", { params: { name } }));
|
|
@@ -382,27 +692,27 @@ function createLancedbProvider() {
|
|
|
382
692
|
const table = await connection.createTable(name, [initRecord]);
|
|
383
693
|
await table.delete('id = "__init__"');
|
|
384
694
|
collectionMetas.set(name, { dimension, metric });
|
|
385
|
-
|
|
695
|
+
logger2.info("Collection created", { name, dimension, metric });
|
|
386
696
|
return ok(void 0);
|
|
387
697
|
},
|
|
388
698
|
async drop(name) {
|
|
389
|
-
|
|
699
|
+
logger2.debug("Dropping collection", { name });
|
|
390
700
|
const tableNames = await connection.tableNames();
|
|
391
701
|
if (!tableNames.includes(name)) {
|
|
392
702
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name } }));
|
|
393
703
|
}
|
|
394
704
|
await connection.dropTable(name);
|
|
395
705
|
collectionMetas.delete(name);
|
|
396
|
-
|
|
706
|
+
logger2.info("Collection dropped", { name });
|
|
397
707
|
return ok(void 0);
|
|
398
708
|
},
|
|
399
709
|
async exists(name) {
|
|
400
|
-
|
|
710
|
+
logger2.debug("Checking collection exists", { name });
|
|
401
711
|
const tableNames = await connection.tableNames();
|
|
402
712
|
return ok(tableNames.includes(name));
|
|
403
713
|
},
|
|
404
714
|
async info(name) {
|
|
405
|
-
|
|
715
|
+
logger2.debug("Getting collection info", { name });
|
|
406
716
|
const table = await openTable(name);
|
|
407
717
|
if (!table) {
|
|
408
718
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name } }));
|
|
@@ -417,14 +727,14 @@ function createLancedbProvider() {
|
|
|
417
727
|
});
|
|
418
728
|
},
|
|
419
729
|
async list() {
|
|
420
|
-
|
|
730
|
+
logger2.debug("Listing collections");
|
|
421
731
|
const tableNames = await connection.tableNames();
|
|
422
732
|
return ok(tableNames);
|
|
423
733
|
}
|
|
424
734
|
};
|
|
425
735
|
const vectorDriver = {
|
|
426
736
|
async insert(collection, documents) {
|
|
427
|
-
|
|
737
|
+
logger2.debug("Inserting vectors", { collection, count: documents.length });
|
|
428
738
|
const table = await openTable(collection);
|
|
429
739
|
if (!table) {
|
|
430
740
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name: collection } }));
|
|
@@ -440,11 +750,11 @@ function createLancedbProvider() {
|
|
|
440
750
|
metadata: JSON.stringify(doc.metadata ?? {})
|
|
441
751
|
}));
|
|
442
752
|
await table.add(records);
|
|
443
|
-
|
|
753
|
+
logger2.info("Vectors inserted", { collection, count: documents.length });
|
|
444
754
|
return ok(void 0);
|
|
445
755
|
},
|
|
446
756
|
async upsert(collection, documents) {
|
|
447
|
-
|
|
757
|
+
logger2.debug("Upserting vectors", { collection, count: documents.length });
|
|
448
758
|
const table = await openTable(collection);
|
|
449
759
|
if (!table) {
|
|
450
760
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name: collection } }));
|
|
@@ -462,27 +772,27 @@ function createLancedbProvider() {
|
|
|
462
772
|
if (typeof table.mergeInsert === "function") {
|
|
463
773
|
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(records);
|
|
464
774
|
} else {
|
|
465
|
-
|
|
775
|
+
logger2.warn("LanceDB mergeInsert is unavailable, falling back to delete/add upsert");
|
|
466
776
|
const ids = documents.map((d) => `"${escapeLanceFilterValue(d.id)}"`).join(", ");
|
|
467
777
|
await table.delete(`id IN (${ids})`);
|
|
468
778
|
await table.add(records);
|
|
469
779
|
}
|
|
470
|
-
|
|
780
|
+
logger2.info("Vectors upserted", { collection, count: documents.length });
|
|
471
781
|
return ok(void 0);
|
|
472
782
|
},
|
|
473
783
|
async delete(collection, ids) {
|
|
474
|
-
|
|
784
|
+
logger2.debug("Deleting vectors", { collection, count: ids.length });
|
|
475
785
|
const table = await openTable(collection);
|
|
476
786
|
if (!table) {
|
|
477
787
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name: collection } }));
|
|
478
788
|
}
|
|
479
789
|
const idList = ids.map((id) => `"${escapeLanceFilterValue(id)}"`).join(", ");
|
|
480
790
|
await table.delete(`id IN (${idList})`);
|
|
481
|
-
|
|
791
|
+
logger2.info("Vectors deleted", { collection, count: ids.length });
|
|
482
792
|
return ok(void 0);
|
|
483
793
|
},
|
|
484
794
|
async search(collection, vector, options) {
|
|
485
|
-
|
|
795
|
+
logger2.debug("Searching vectors", { collection, topK: options?.topK, hasFilter: !!options?.filter });
|
|
486
796
|
const table = await openTable(collection);
|
|
487
797
|
if (!table) {
|
|
488
798
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name: collection } }));
|
|
@@ -517,7 +827,7 @@ function createLancedbProvider() {
|
|
|
517
827
|
return ok(searchResults);
|
|
518
828
|
},
|
|
519
829
|
async count(collection) {
|
|
520
|
-
|
|
830
|
+
logger2.debug("Counting vectors", { collection });
|
|
521
831
|
const table = await openTable(collection);
|
|
522
832
|
if (!table) {
|
|
523
833
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name: collection } }));
|
|
@@ -557,10 +867,10 @@ function createLancedbProvider() {
|
|
|
557
867
|
} catch {
|
|
558
868
|
}
|
|
559
869
|
}));
|
|
560
|
-
|
|
870
|
+
logger2.info("LanceDB connected", { path: lanceConfig.path });
|
|
561
871
|
return ok(void 0);
|
|
562
872
|
} catch (error) {
|
|
563
|
-
|
|
873
|
+
logger2.error("Failed to connect to LanceDB", { error });
|
|
564
874
|
return err(HaiVecdbError.CONNECTION_FAILED, vecdbM("vecdb_connectionFailed", { params: { error: String(error) } }), error);
|
|
565
875
|
}
|
|
566
876
|
},
|
|
@@ -568,7 +878,7 @@ function createLancedbProvider() {
|
|
|
568
878
|
connection = null;
|
|
569
879
|
config = null;
|
|
570
880
|
collectionMetas.clear();
|
|
571
|
-
|
|
881
|
+
logger2.info("LanceDB connection closed");
|
|
572
882
|
return ok(void 0);
|
|
573
883
|
},
|
|
574
884
|
isConnected() {
|
|
@@ -578,7 +888,7 @@ function createLancedbProvider() {
|
|
|
578
888
|
vector: createBaseVectorOps(ctx, vectorDriver)
|
|
579
889
|
};
|
|
580
890
|
}
|
|
581
|
-
var
|
|
891
|
+
var logger3 = core.logger.child({ module: "vecdb", scope: "pgvector" });
|
|
582
892
|
function isPgModule(value) {
|
|
583
893
|
if (typeof value !== "object" || value === null) {
|
|
584
894
|
return false;
|
|
@@ -608,7 +918,7 @@ function createPgvectorProvider() {
|
|
|
608
918
|
return "<=>";
|
|
609
919
|
}
|
|
610
920
|
}
|
|
611
|
-
function
|
|
921
|
+
function distanceToScore2(distance) {
|
|
612
922
|
const metric = config?.metric ?? "cosine";
|
|
613
923
|
switch (metric) {
|
|
614
924
|
case "cosine":
|
|
@@ -630,13 +940,13 @@ function createPgvectorProvider() {
|
|
|
630
940
|
const mod = await import('pg');
|
|
631
941
|
if (!isPgModule(mod)) {
|
|
632
942
|
const error = new Error("pg driver module does not export Pool");
|
|
633
|
-
|
|
943
|
+
logger3.error("Failed to load pg driver", { error: error.message });
|
|
634
944
|
return err(HaiVecdbError.DRIVER_NOT_FOUND, vecdbM("vecdb_driverNotFound", { params: { driver: "pg" } }), error);
|
|
635
945
|
}
|
|
636
946
|
return ok(mod);
|
|
637
947
|
} catch (error) {
|
|
638
948
|
if (isMissingModuleError(error, "pg")) {
|
|
639
|
-
|
|
949
|
+
logger3.error("Failed to load pg driver", { error: error instanceof Error ? error.message : String(error) });
|
|
640
950
|
return err(HaiVecdbError.DRIVER_NOT_FOUND, vecdbM("vecdb_driverNotFound", { params: { driver: "pg" } }), error);
|
|
641
951
|
}
|
|
642
952
|
throw error;
|
|
@@ -677,14 +987,14 @@ function createPgvectorProvider() {
|
|
|
677
987
|
}
|
|
678
988
|
return null;
|
|
679
989
|
}
|
|
680
|
-
const ctx = { isConnected: () => pool !== null, logger:
|
|
990
|
+
const ctx = { isConnected: () => pool !== null, logger: logger3, operationLog: () => config?.operationLog };
|
|
681
991
|
const collectionDriver = {
|
|
682
992
|
async create(name, options) {
|
|
683
993
|
const table = tableName(name);
|
|
684
994
|
const dimension = options.dimension;
|
|
685
995
|
const metric = options.metric ?? config?.metric ?? "cosine";
|
|
686
996
|
const indexType = config?.indexType ?? "hnsw";
|
|
687
|
-
|
|
997
|
+
logger3.debug("Creating collection", { name, dimension, metric, indexType });
|
|
688
998
|
const checkResult = await pool.query(
|
|
689
999
|
`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $1)`,
|
|
690
1000
|
[table]
|
|
@@ -715,12 +1025,12 @@ function createPgvectorProvider() {
|
|
|
715
1025
|
WITH (lists = 100)
|
|
716
1026
|
`);
|
|
717
1027
|
}
|
|
718
|
-
|
|
1028
|
+
logger3.info("Collection created", { name, dimension, metric, indexType });
|
|
719
1029
|
return ok(void 0);
|
|
720
1030
|
},
|
|
721
1031
|
async drop(name) {
|
|
722
1032
|
const table = tableName(name);
|
|
723
|
-
|
|
1033
|
+
logger3.debug("Dropping collection", { name });
|
|
724
1034
|
const checkResult = await pool.query(
|
|
725
1035
|
`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $1)`,
|
|
726
1036
|
[table]
|
|
@@ -729,11 +1039,11 @@ function createPgvectorProvider() {
|
|
|
729
1039
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name } }));
|
|
730
1040
|
}
|
|
731
1041
|
await pool.query(`DROP TABLE ${quoteIdent(table)}`);
|
|
732
|
-
|
|
1042
|
+
logger3.info("Collection dropped", { name });
|
|
733
1043
|
return ok(void 0);
|
|
734
1044
|
},
|
|
735
1045
|
async exists(name) {
|
|
736
|
-
|
|
1046
|
+
logger3.debug("Checking collection exists", { name });
|
|
737
1047
|
const result = await pool.query(
|
|
738
1048
|
`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $1)`,
|
|
739
1049
|
[tableName(name)]
|
|
@@ -741,7 +1051,7 @@ function createPgvectorProvider() {
|
|
|
741
1051
|
return ok(result.rows[0].exists);
|
|
742
1052
|
},
|
|
743
1053
|
async info(name) {
|
|
744
|
-
|
|
1054
|
+
logger3.debug("Getting collection info", { name });
|
|
745
1055
|
const table = tableName(name);
|
|
746
1056
|
const dimensionResult = await getCollectionDimension(name);
|
|
747
1057
|
if (!dimensionResult.success) {
|
|
@@ -758,7 +1068,7 @@ function createPgvectorProvider() {
|
|
|
758
1068
|
},
|
|
759
1069
|
async list() {
|
|
760
1070
|
const prefix = config?.tablePrefix ?? "vec_";
|
|
761
|
-
|
|
1071
|
+
logger3.debug("Listing collections");
|
|
762
1072
|
const result = await pool.query(
|
|
763
1073
|
`SELECT table_name FROM information_schema.tables WHERE table_name LIKE $1 AND table_schema = 'public'`,
|
|
764
1074
|
[`${prefix}%`]
|
|
@@ -772,7 +1082,7 @@ function createPgvectorProvider() {
|
|
|
772
1082
|
const vectorDriver = {
|
|
773
1083
|
async insert(collection, documents) {
|
|
774
1084
|
const table = tableName(collection);
|
|
775
|
-
|
|
1085
|
+
logger3.debug("Inserting vectors", { collection, count: documents.length });
|
|
776
1086
|
const dimensionResult = await getCollectionDimension(collection);
|
|
777
1087
|
if (!dimensionResult.success) {
|
|
778
1088
|
return dimensionResult;
|
|
@@ -794,12 +1104,12 @@ function createPgvectorProvider() {
|
|
|
794
1104
|
`INSERT INTO ${qi} (id, vector, content, metadata) VALUES ${values.join(", ")}`,
|
|
795
1105
|
params
|
|
796
1106
|
);
|
|
797
|
-
|
|
1107
|
+
logger3.info("Vectors inserted", { collection, count: documents.length });
|
|
798
1108
|
return ok(void 0);
|
|
799
1109
|
},
|
|
800
1110
|
async upsert(collection, documents) {
|
|
801
1111
|
const table = tableName(collection);
|
|
802
|
-
|
|
1112
|
+
logger3.debug("Upserting vectors", { collection, count: documents.length });
|
|
803
1113
|
const dimensionResult = await getCollectionDimension(collection);
|
|
804
1114
|
if (!dimensionResult.success) {
|
|
805
1115
|
return dimensionResult;
|
|
@@ -825,15 +1135,15 @@ function createPgvectorProvider() {
|
|
|
825
1135
|
metadata = EXCLUDED.metadata`,
|
|
826
1136
|
params
|
|
827
1137
|
);
|
|
828
|
-
|
|
1138
|
+
logger3.info("Vectors upserted", { collection, count: documents.length });
|
|
829
1139
|
return ok(void 0);
|
|
830
1140
|
},
|
|
831
1141
|
async delete(collection, ids) {
|
|
832
1142
|
const table = tableName(collection);
|
|
833
|
-
|
|
1143
|
+
logger3.debug("Deleting vectors", { collection, count: ids.length });
|
|
834
1144
|
const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ");
|
|
835
1145
|
await pool.query(`DELETE FROM ${quoteIdent(table)} WHERE id IN (${placeholders})`, ids);
|
|
836
|
-
|
|
1146
|
+
logger3.info("Vectors deleted", { collection, count: ids.length });
|
|
837
1147
|
return ok(void 0);
|
|
838
1148
|
},
|
|
839
1149
|
async search(collection, vector, options) {
|
|
@@ -841,7 +1151,7 @@ function createPgvectorProvider() {
|
|
|
841
1151
|
const topK = options?.topK ?? 10;
|
|
842
1152
|
const minScore = options?.minScore ?? 0;
|
|
843
1153
|
const op = distanceOp();
|
|
844
|
-
|
|
1154
|
+
logger3.debug("Searching vectors", { collection, topK, hasFilter: !!options?.filter });
|
|
845
1155
|
const dimensionResult = await getCollectionDimension(collection);
|
|
846
1156
|
if (!dimensionResult.success) {
|
|
847
1157
|
return dimensionResult;
|
|
@@ -874,7 +1184,7 @@ function createPgvectorProvider() {
|
|
|
874
1184
|
filterParams
|
|
875
1185
|
);
|
|
876
1186
|
const searchResults = result.rows.map((row) => {
|
|
877
|
-
const score =
|
|
1187
|
+
const score = distanceToScore2(row.distance);
|
|
878
1188
|
return {
|
|
879
1189
|
id: row.id,
|
|
880
1190
|
score,
|
|
@@ -886,7 +1196,7 @@ function createPgvectorProvider() {
|
|
|
886
1196
|
},
|
|
887
1197
|
async count(collection) {
|
|
888
1198
|
const table = tableName(collection);
|
|
889
|
-
|
|
1199
|
+
logger3.debug("Counting vectors", { collection });
|
|
890
1200
|
const result = await pool.query(`SELECT COUNT(*) as count FROM ${quoteIdent(table)}`);
|
|
891
1201
|
return ok(Number.parseInt(String(result.rows[0].count), 10));
|
|
892
1202
|
}
|
|
@@ -914,10 +1224,10 @@ function createPgvectorProvider() {
|
|
|
914
1224
|
pool = new Pool(poolConfig);
|
|
915
1225
|
await pool.query("CREATE EXTENSION IF NOT EXISTS vector");
|
|
916
1226
|
config = pgConfig;
|
|
917
|
-
|
|
1227
|
+
logger3.info("pgvector connected", { host: pgConfig.host, database: pgConfig.database });
|
|
918
1228
|
return ok(void 0);
|
|
919
1229
|
} catch (error) {
|
|
920
|
-
|
|
1230
|
+
logger3.error("Failed to connect to pgvector", { error: error instanceof Error ? error.message : String(error) });
|
|
921
1231
|
return err(HaiVecdbError.CONNECTION_FAILED, vecdbM("vecdb_connectionFailed", { params: { error: error instanceof Error ? error.message : String(error) } }), error);
|
|
922
1232
|
}
|
|
923
1233
|
},
|
|
@@ -928,10 +1238,10 @@ function createPgvectorProvider() {
|
|
|
928
1238
|
}
|
|
929
1239
|
pool = null;
|
|
930
1240
|
config = null;
|
|
931
|
-
|
|
1241
|
+
logger3.info("pgvector connection closed");
|
|
932
1242
|
return ok(void 0);
|
|
933
1243
|
} catch (error) {
|
|
934
|
-
|
|
1244
|
+
logger3.error("Failed to close pgvector connection", { error: error instanceof Error ? error.message : String(error) });
|
|
935
1245
|
return err(HaiVecdbError.CONNECTION_FAILED, vecdbM("vecdb_closeFailed", { params: { error: String(error) } }), error);
|
|
936
1246
|
}
|
|
937
1247
|
},
|
|
@@ -954,7 +1264,7 @@ function sanitizeUrl(url) {
|
|
|
954
1264
|
return "(invalid url)";
|
|
955
1265
|
}
|
|
956
1266
|
}
|
|
957
|
-
var
|
|
1267
|
+
var logger4 = core.logger.child({ module: "vecdb", scope: "qdrant" });
|
|
958
1268
|
function isQdrantModule(value) {
|
|
959
1269
|
if (typeof value !== "object" || value === null) {
|
|
960
1270
|
return false;
|
|
@@ -990,13 +1300,13 @@ function createQdrantProvider() {
|
|
|
990
1300
|
const mod = await import('@qdrant/js-client-rest');
|
|
991
1301
|
if (!isQdrantModule(mod)) {
|
|
992
1302
|
const error = new Error("@qdrant/js-client-rest driver module does not export QdrantClient");
|
|
993
|
-
|
|
1303
|
+
logger4.error("Failed to load Qdrant driver", { error: error.message });
|
|
994
1304
|
return err(HaiVecdbError.DRIVER_NOT_FOUND, vecdbM("vecdb_driverNotFound", { params: { driver: "@qdrant/js-client-rest" } }), error);
|
|
995
1305
|
}
|
|
996
1306
|
return ok(mod);
|
|
997
1307
|
} catch (error) {
|
|
998
1308
|
if (isMissingModuleError(error, "@qdrant/js-client-rest")) {
|
|
999
|
-
|
|
1309
|
+
logger4.error("Failed to load Qdrant driver", { error: error instanceof Error ? error.message : String(error) });
|
|
1000
1310
|
return err(HaiVecdbError.DRIVER_NOT_FOUND, vecdbM("vecdb_driverNotFound", { params: { driver: "@qdrant/js-client-rest" } }), error);
|
|
1001
1311
|
}
|
|
1002
1312
|
throw error;
|
|
@@ -1038,10 +1348,10 @@ function createQdrantProvider() {
|
|
|
1038
1348
|
})
|
|
1039
1349
|
);
|
|
1040
1350
|
}
|
|
1041
|
-
const ctx = { isConnected: () => client !== null, logger:
|
|
1351
|
+
const ctx = { isConnected: () => client !== null, logger: logger4, operationLog: () => config?.operationLog };
|
|
1042
1352
|
const collectionDriver = {
|
|
1043
1353
|
async create(name, options) {
|
|
1044
|
-
|
|
1354
|
+
logger4.debug("Creating collection", { name, dimension: options.dimension, metric: options.metric });
|
|
1045
1355
|
const collections = await client.getCollections();
|
|
1046
1356
|
const exists = collections.collections.some((c) => c.name === name);
|
|
1047
1357
|
if (exists) {
|
|
@@ -1054,21 +1364,21 @@ function createQdrantProvider() {
|
|
|
1054
1364
|
distance: toQdrantDistance(metric)
|
|
1055
1365
|
}
|
|
1056
1366
|
});
|
|
1057
|
-
|
|
1367
|
+
logger4.info("Collection created", { name, dimension: options.dimension, metric });
|
|
1058
1368
|
return ok(void 0);
|
|
1059
1369
|
},
|
|
1060
1370
|
async drop(name) {
|
|
1061
|
-
|
|
1371
|
+
logger4.debug("Dropping collection", { name });
|
|
1062
1372
|
const infoResult = await getCollectionInfo(name);
|
|
1063
1373
|
if (!infoResult.success) {
|
|
1064
1374
|
return infoResult;
|
|
1065
1375
|
}
|
|
1066
1376
|
await client.deleteCollection(name);
|
|
1067
|
-
|
|
1377
|
+
logger4.info("Collection dropped", { name });
|
|
1068
1378
|
return ok(void 0);
|
|
1069
1379
|
},
|
|
1070
1380
|
async exists(name) {
|
|
1071
|
-
|
|
1381
|
+
logger4.debug("Checking collection exists", { name });
|
|
1072
1382
|
try {
|
|
1073
1383
|
await client.getCollection(name);
|
|
1074
1384
|
return ok(true);
|
|
@@ -1080,7 +1390,7 @@ function createQdrantProvider() {
|
|
|
1080
1390
|
}
|
|
1081
1391
|
},
|
|
1082
1392
|
async info(name) {
|
|
1083
|
-
|
|
1393
|
+
logger4.debug("Getting collection info", { name });
|
|
1084
1394
|
const infoResult = await getCollectionInfo(name);
|
|
1085
1395
|
if (!infoResult.success) {
|
|
1086
1396
|
return infoResult;
|
|
@@ -1098,7 +1408,7 @@ function createQdrantProvider() {
|
|
|
1098
1408
|
});
|
|
1099
1409
|
},
|
|
1100
1410
|
async list() {
|
|
1101
|
-
|
|
1411
|
+
logger4.debug("Listing collections");
|
|
1102
1412
|
const collections = await client.getCollections();
|
|
1103
1413
|
const names = collections.collections.map((c) => c.name);
|
|
1104
1414
|
return ok(names);
|
|
@@ -1106,7 +1416,7 @@ function createQdrantProvider() {
|
|
|
1106
1416
|
};
|
|
1107
1417
|
const vectorDriver = {
|
|
1108
1418
|
async insert(collection, documents) {
|
|
1109
|
-
|
|
1419
|
+
logger4.debug("Inserting vectors", { collection, count: documents.length });
|
|
1110
1420
|
const dimensionResult = await getCollectionDimension(collection);
|
|
1111
1421
|
if (!dimensionResult.success) {
|
|
1112
1422
|
return dimensionResult;
|
|
@@ -1127,11 +1437,11 @@ function createQdrantProvider() {
|
|
|
1127
1437
|
}
|
|
1128
1438
|
}));
|
|
1129
1439
|
await client.upsert(collection, { points });
|
|
1130
|
-
|
|
1440
|
+
logger4.info("Vectors inserted", { collection, count: documents.length });
|
|
1131
1441
|
return ok(void 0);
|
|
1132
1442
|
},
|
|
1133
1443
|
async upsert(collection, documents) {
|
|
1134
|
-
|
|
1444
|
+
logger4.debug("Upserting vectors", { collection, count: documents.length });
|
|
1135
1445
|
const dimensionResult = await getCollectionDimension(collection);
|
|
1136
1446
|
if (!dimensionResult.success) {
|
|
1137
1447
|
return dimensionResult;
|
|
@@ -1152,20 +1462,20 @@ function createQdrantProvider() {
|
|
|
1152
1462
|
}
|
|
1153
1463
|
}));
|
|
1154
1464
|
await client.upsert(collection, { points });
|
|
1155
|
-
|
|
1465
|
+
logger4.info("Vectors upserted", { collection, count: documents.length });
|
|
1156
1466
|
return ok(void 0);
|
|
1157
1467
|
},
|
|
1158
1468
|
async delete(collection, ids) {
|
|
1159
|
-
|
|
1469
|
+
logger4.debug("Deleting vectors", { collection, count: ids.length });
|
|
1160
1470
|
const uuids = ids.map(hashToUuid);
|
|
1161
1471
|
await client.delete(collection, { points: uuids });
|
|
1162
|
-
|
|
1472
|
+
logger4.info("Vectors deleted", { collection, count: ids.length });
|
|
1163
1473
|
return ok(void 0);
|
|
1164
1474
|
},
|
|
1165
1475
|
async search(collection, vector, options) {
|
|
1166
1476
|
const topK = options?.topK ?? 10;
|
|
1167
1477
|
const minScore = options?.minScore ?? 0;
|
|
1168
|
-
|
|
1478
|
+
logger4.debug("Searching vectors", { collection, topK, hasFilter: !!options?.filter });
|
|
1169
1479
|
const dimensionResult = await getCollectionDimension(collection);
|
|
1170
1480
|
if (!dimensionResult.success) {
|
|
1171
1481
|
return dimensionResult;
|
|
@@ -1202,7 +1512,7 @@ function createQdrantProvider() {
|
|
|
1202
1512
|
return ok(results);
|
|
1203
1513
|
},
|
|
1204
1514
|
async count(collection) {
|
|
1205
|
-
|
|
1515
|
+
logger4.debug("Counting vectors", { collection });
|
|
1206
1516
|
const infoResult = await getCollectionInfo(collection);
|
|
1207
1517
|
if (!infoResult.success) {
|
|
1208
1518
|
return infoResult;
|
|
@@ -1230,17 +1540,17 @@ function createQdrantProvider() {
|
|
|
1230
1540
|
await qdrantClient.getCollections();
|
|
1231
1541
|
client = qdrantClient;
|
|
1232
1542
|
config = qdrantConfig;
|
|
1233
|
-
|
|
1543
|
+
logger4.info("Qdrant connected", { url: sanitizeUrl(qdrantConfig.url) });
|
|
1234
1544
|
return ok(void 0);
|
|
1235
1545
|
} catch (error) {
|
|
1236
|
-
|
|
1546
|
+
logger4.error("Failed to connect to Qdrant", { error: error instanceof Error ? error.message : String(error) });
|
|
1237
1547
|
return err(HaiVecdbError.CONNECTION_FAILED, vecdbM("vecdb_connectionFailed", { params: { error: String(error) } }), error);
|
|
1238
1548
|
}
|
|
1239
1549
|
},
|
|
1240
1550
|
async close() {
|
|
1241
1551
|
client = null;
|
|
1242
1552
|
config = null;
|
|
1243
|
-
|
|
1553
|
+
logger4.info("Qdrant connection closed");
|
|
1244
1554
|
return ok(void 0);
|
|
1245
1555
|
},
|
|
1246
1556
|
isConnected() {
|
|
@@ -1252,7 +1562,7 @@ function createQdrantProvider() {
|
|
|
1252
1562
|
}
|
|
1253
1563
|
|
|
1254
1564
|
// src/vecdb-main.ts
|
|
1255
|
-
var
|
|
1565
|
+
var logger5 = core.logger.child({ module: "vecdb", scope: "main" });
|
|
1256
1566
|
var currentProvider = null;
|
|
1257
1567
|
var currentConfig = null;
|
|
1258
1568
|
var initInProgress = false;
|
|
@@ -1264,6 +1574,8 @@ function createProvider(config) {
|
|
|
1264
1574
|
return createPgvectorProvider();
|
|
1265
1575
|
case "qdrant":
|
|
1266
1576
|
return createQdrantProvider();
|
|
1577
|
+
case "chroma":
|
|
1578
|
+
return createChromaProvider();
|
|
1267
1579
|
}
|
|
1268
1580
|
}
|
|
1269
1581
|
var notInitialized = core.module.createNotInitializedKit(
|
|
@@ -1281,22 +1593,22 @@ var vecdb = {
|
|
|
1281
1593
|
*/
|
|
1282
1594
|
async init(config) {
|
|
1283
1595
|
if (initInProgress) {
|
|
1284
|
-
|
|
1596
|
+
logger5.warn("Vecdb init already in progress, skipping concurrent call");
|
|
1285
1597
|
return err(HaiVecdbError.CONFIG_ERROR, vecdbM("vecdb_initInProgress"));
|
|
1286
1598
|
}
|
|
1287
1599
|
initInProgress = true;
|
|
1288
1600
|
try {
|
|
1289
1601
|
if (currentProvider) {
|
|
1290
|
-
|
|
1602
|
+
logger5.warn("Vecdb module is already initialized, reinitializing");
|
|
1291
1603
|
const closeResult = await vecdb.close();
|
|
1292
1604
|
if (!closeResult.success) {
|
|
1293
1605
|
return closeResult;
|
|
1294
1606
|
}
|
|
1295
1607
|
}
|
|
1296
|
-
|
|
1608
|
+
logger5.info("Initializing vecdb module");
|
|
1297
1609
|
const parseResult = VecdbConfigSchema.safeParse(config);
|
|
1298
1610
|
if (!parseResult.success) {
|
|
1299
|
-
|
|
1611
|
+
logger5.error("Vecdb config validation failed", { error: parseResult.error.message });
|
|
1300
1612
|
return err(HaiVecdbError.CONFIG_ERROR, vecdbM("vecdb_configError", { params: { error: parseResult.error.message } }), parseResult.error);
|
|
1301
1613
|
}
|
|
1302
1614
|
const parsed = parseResult.data;
|
|
@@ -1304,7 +1616,7 @@ var vecdb = {
|
|
|
1304
1616
|
const provider = createProvider(parsed);
|
|
1305
1617
|
const connectResult = await provider.connect(parsed);
|
|
1306
1618
|
if (!connectResult.success) {
|
|
1307
|
-
|
|
1619
|
+
logger5.error("Vecdb module initialization failed", {
|
|
1308
1620
|
code: connectResult.error.code,
|
|
1309
1621
|
message: connectResult.error.message
|
|
1310
1622
|
});
|
|
@@ -1312,10 +1624,10 @@ var vecdb = {
|
|
|
1312
1624
|
}
|
|
1313
1625
|
currentProvider = provider;
|
|
1314
1626
|
currentConfig = parsed;
|
|
1315
|
-
|
|
1627
|
+
logger5.info("Vecdb module initialized", { type: parsed.type });
|
|
1316
1628
|
return ok(void 0);
|
|
1317
1629
|
} catch (error) {
|
|
1318
|
-
|
|
1630
|
+
logger5.error("Vecdb module initialization failed", { error });
|
|
1319
1631
|
return err(HaiVecdbError.CONNECTION_FAILED, vecdbM("vecdb_initFailed", { params: { error: error instanceof Error ? error.message : String(error) } }), error);
|
|
1320
1632
|
}
|
|
1321
1633
|
} finally {
|
|
@@ -1342,6 +1654,35 @@ var vecdb = {
|
|
|
1342
1654
|
get config() {
|
|
1343
1655
|
return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null;
|
|
1344
1656
|
},
|
|
1657
|
+
/** 获取底层后端的原始连接信息(含凭证,未初始化时为 null;仅供同进程内集成使用) */
|
|
1658
|
+
get rawConnection() {
|
|
1659
|
+
if (!currentConfig)
|
|
1660
|
+
return null;
|
|
1661
|
+
if (currentConfig.type === "qdrant")
|
|
1662
|
+
return { type: "qdrant", url: currentConfig.url, apiKey: currentConfig.apiKey };
|
|
1663
|
+
if (currentConfig.type === "pgvector") {
|
|
1664
|
+
return {
|
|
1665
|
+
type: "pgvector",
|
|
1666
|
+
connectionString: currentConfig.url,
|
|
1667
|
+
host: currentConfig.host,
|
|
1668
|
+
port: currentConfig.port,
|
|
1669
|
+
database: currentConfig.database,
|
|
1670
|
+
user: currentConfig.user,
|
|
1671
|
+
password: currentConfig.password
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
if (currentConfig.type === "chroma") {
|
|
1675
|
+
return {
|
|
1676
|
+
type: "chroma",
|
|
1677
|
+
url: currentConfig.url,
|
|
1678
|
+
host: currentConfig.host,
|
|
1679
|
+
port: currentConfig.port,
|
|
1680
|
+
apiKey: currentConfig.apiKey,
|
|
1681
|
+
path: currentConfig.path
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
return { type: currentConfig.type, path: currentConfig.path };
|
|
1685
|
+
},
|
|
1345
1686
|
/** 检查是否已初始化 */
|
|
1346
1687
|
get isInitialized() {
|
|
1347
1688
|
return currentProvider !== null && currentProvider.isConnected();
|
|
@@ -1354,20 +1695,20 @@ var vecdb = {
|
|
|
1354
1695
|
async close() {
|
|
1355
1696
|
if (!currentProvider) {
|
|
1356
1697
|
currentConfig = null;
|
|
1357
|
-
|
|
1698
|
+
logger5.info("Vecdb module already closed, skipping");
|
|
1358
1699
|
return ok(void 0);
|
|
1359
1700
|
}
|
|
1360
|
-
|
|
1701
|
+
logger5.info("Closing vecdb module");
|
|
1361
1702
|
try {
|
|
1362
1703
|
const closeResult = await currentProvider.close();
|
|
1363
1704
|
if (!closeResult.success) {
|
|
1364
|
-
|
|
1705
|
+
logger5.error("Vecdb module close failed", { code: closeResult.error.code, message: closeResult.error.message });
|
|
1365
1706
|
return closeResult;
|
|
1366
1707
|
}
|
|
1367
|
-
|
|
1708
|
+
logger5.info("Vecdb module closed");
|
|
1368
1709
|
return ok(void 0);
|
|
1369
1710
|
} catch (error) {
|
|
1370
|
-
|
|
1711
|
+
logger5.error("Vecdb module close failed", { error });
|
|
1371
1712
|
return err(HaiVecdbError.CONNECTION_FAILED, vecdbM("vecdb_closeFailed", { params: { error: error instanceof Error ? error.message : String(error) } }), error);
|
|
1372
1713
|
} finally {
|
|
1373
1714
|
currentProvider = null;
|