@lov3kaizen/agentsea-embeddings 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1650 @@
1
+ import {
2
+ importOptional
3
+ } from "./chunk-2TCNSTX3.mjs";
4
+ import {
5
+ batch
6
+ } from "./chunk-3KM32UQK.mjs";
7
+ import {
8
+ EmbeddingModel
9
+ } from "./chunk-QAITLJ2E.mjs";
10
+
11
+ // src/stores/BaseStore.ts
12
+ var BaseStore = class {
13
+ /** Store configuration */
14
+ config;
15
+ constructor(config) {
16
+ this.config = {
17
+ namespace: config.namespace ?? "default",
18
+ dimensions: config.dimensions,
19
+ metric: config.metric ?? "cosine",
20
+ ...config
21
+ };
22
+ }
23
+ /**
24
+ * Get namespace
25
+ */
26
+ get namespace() {
27
+ return this.config.namespace ?? "default";
28
+ }
29
+ /**
30
+ * Get dimensions
31
+ */
32
+ get dimensions() {
33
+ return this.config.dimensions;
34
+ }
35
+ /**
36
+ * Get distance metric
37
+ */
38
+ get metric() {
39
+ return this.config.metric ?? "cosine";
40
+ }
41
+ /**
42
+ * Calculate similarity/distance between vectors
43
+ */
44
+ calculateScore(a, b) {
45
+ switch (this.metric) {
46
+ case "cosine":
47
+ return EmbeddingModel.cosineSimilarity(a, b);
48
+ case "euclidean": {
49
+ const dist = EmbeddingModel.euclideanDistance(a, b);
50
+ return 1 / (1 + dist);
51
+ }
52
+ case "dot_product":
53
+ return EmbeddingModel.dotProduct(a, b);
54
+ default:
55
+ return EmbeddingModel.cosineSimilarity(a, b);
56
+ }
57
+ }
58
+ /**
59
+ * Filter records by metadata
60
+ */
61
+ filterByMetadata(records, filter) {
62
+ if (!filter) return records;
63
+ return records.filter((record) => {
64
+ if (!record.metadata) return false;
65
+ for (const [key, value] of Object.entries(filter)) {
66
+ if (record.metadata[key] !== value) {
67
+ return false;
68
+ }
69
+ }
70
+ return true;
71
+ });
72
+ }
73
+ /**
74
+ * Convert records to search results
75
+ */
76
+ toSearchResults(records, options) {
77
+ return records.map((record) => ({
78
+ id: record.id,
79
+ text: options?.includeText !== false ? record.text ?? "" : "",
80
+ score: record.score,
81
+ metadata: options?.includeMetadata !== false ? record.metadata ?? {} : {},
82
+ distance: this.metric !== "cosine" ? 1 - record.score : void 0
83
+ }));
84
+ }
85
+ };
86
+
87
+ // src/stores/MemoryStore.ts
88
+ import * as fs from "fs/promises";
89
+ var MemoryStore = class extends BaseStore {
90
+ storeType = "memory";
91
+ vectors = /* @__PURE__ */ new Map();
92
+ namespaces = /* @__PURE__ */ new Map();
93
+ persistPath;
94
+ persistInterval;
95
+ maxVectors;
96
+ constructor(config = { type: "memory" }) {
97
+ super(config);
98
+ this.maxVectors = config.maxVectors ?? 1e5;
99
+ this.persistPath = config.persistPath;
100
+ if (config.persistInterval && config.persistPath) {
101
+ this.persistInterval = setInterval(
102
+ () => void this.persist().catch(() => {
103
+ }),
104
+ config.persistInterval
105
+ );
106
+ }
107
+ }
108
+ async upsert(records, options) {
109
+ const startTime = performance.now();
110
+ const namespace = options?.namespace ?? this.namespace;
111
+ const upsertedIds = [];
112
+ const errors = [];
113
+ if (!this.namespaces.has(namespace)) {
114
+ this.namespaces.set(namespace, /* @__PURE__ */ new Set());
115
+ }
116
+ const nsIds = this.namespaces.get(namespace);
117
+ for (const record of records) {
118
+ try {
119
+ if (this.vectors.size >= this.maxVectors && !this.vectors.has(record.id)) {
120
+ const oldestId = this.vectors.keys().next().value;
121
+ if (oldestId) {
122
+ this.vectors.delete(oldestId);
123
+ for (const ns of this.namespaces.values()) {
124
+ ns.delete(oldestId);
125
+ }
126
+ }
127
+ }
128
+ this.vectors.set(record.id, record);
129
+ nsIds.add(record.id);
130
+ upsertedIds.push(record.id);
131
+ } catch (error) {
132
+ errors.push({ id: record.id, error: error.message });
133
+ }
134
+ }
135
+ return Promise.resolve({
136
+ upsertedIds,
137
+ upsertedCount: upsertedIds.length,
138
+ errors,
139
+ durationMs: performance.now() - startTime
140
+ });
141
+ }
142
+ async query(vector, options) {
143
+ const startTime = performance.now();
144
+ const namespace = options?.namespace ?? this.namespace;
145
+ const topK = options?.topK ?? 10;
146
+ const minScore = options?.minScore ?? 0;
147
+ const nsIds = this.namespaces.get(namespace);
148
+ if (!nsIds || nsIds.size === 0) {
149
+ return {
150
+ matches: [],
151
+ namespace,
152
+ durationMs: performance.now() - startTime
153
+ };
154
+ }
155
+ let scoredRecords = [];
156
+ for (const id of nsIds) {
157
+ const record = this.vectors.get(id);
158
+ if (!record) continue;
159
+ const score = this.calculateScore(vector, record.vector);
160
+ if (score >= minScore) {
161
+ scoredRecords.push({ ...record, score });
162
+ }
163
+ }
164
+ if (options?.filter) {
165
+ scoredRecords = this.filterByMetadata(
166
+ scoredRecords,
167
+ options.filter
168
+ );
169
+ }
170
+ scoredRecords.sort((a, b) => b.score - a.score);
171
+ const topResults = scoredRecords.slice(0, topK);
172
+ return Promise.resolve({
173
+ matches: this.toSearchResults(topResults, options),
174
+ namespace,
175
+ durationMs: performance.now() - startTime
176
+ });
177
+ }
178
+ async delete(ids, options) {
179
+ const startTime = performance.now();
180
+ const namespace = options?.namespace ?? this.namespace;
181
+ let deletedCount = 0;
182
+ const nsIds = this.namespaces.get(namespace);
183
+ for (const id of ids) {
184
+ if (this.vectors.has(id)) {
185
+ this.vectors.delete(id);
186
+ nsIds?.delete(id);
187
+ deletedCount++;
188
+ }
189
+ }
190
+ return Promise.resolve({
191
+ deletedCount,
192
+ requestedCount: ids.length,
193
+ countExact: true,
194
+ durationMs: performance.now() - startTime
195
+ });
196
+ }
197
+ async deleteAll(options) {
198
+ const startTime = performance.now();
199
+ const namespace = options?.namespace ?? this.namespace;
200
+ if (options?.deleteAll) {
201
+ const count2 = this.vectors.size;
202
+ this.vectors.clear();
203
+ this.namespaces.clear();
204
+ return Promise.resolve({
205
+ deletedCount: count2,
206
+ requestedCount: count2,
207
+ countExact: true,
208
+ durationMs: performance.now() - startTime
209
+ });
210
+ }
211
+ const nsIds = this.namespaces.get(namespace);
212
+ if (!nsIds) {
213
+ return Promise.resolve({
214
+ deletedCount: 0,
215
+ requestedCount: 0,
216
+ countExact: true,
217
+ durationMs: performance.now() - startTime
218
+ });
219
+ }
220
+ const count = nsIds.size;
221
+ for (const id of nsIds) {
222
+ this.vectors.delete(id);
223
+ }
224
+ this.namespaces.delete(namespace);
225
+ return Promise.resolve({
226
+ deletedCount: count,
227
+ requestedCount: count,
228
+ countExact: true,
229
+ durationMs: performance.now() - startTime
230
+ });
231
+ }
232
+ getStats() {
233
+ return Promise.resolve({
234
+ type: this.storeType,
235
+ vectorCount: this.vectors.size,
236
+ namespaceCount: this.namespaces.size,
237
+ dimensions: this.dimensions ?? 0,
238
+ metric: this.metric,
239
+ lastUpdated: Date.now()
240
+ });
241
+ }
242
+ checkHealth() {
243
+ return Promise.resolve({
244
+ healthy: true,
245
+ latencyMs: 0,
246
+ lastCheck: Date.now()
247
+ });
248
+ }
249
+ async close() {
250
+ if (this.persistInterval) {
251
+ clearInterval(this.persistInterval);
252
+ }
253
+ if (this.persistPath) {
254
+ await this.persist();
255
+ }
256
+ }
257
+ /**
258
+ * Persist store to file
259
+ */
260
+ async persist() {
261
+ if (!this.persistPath) return;
262
+ const data = {
263
+ vectors: Array.from(this.vectors.entries()),
264
+ namespaces: Array.from(this.namespaces.entries()).map(([k, v]) => [
265
+ k,
266
+ Array.from(v)
267
+ ])
268
+ };
269
+ await fs.writeFile(this.persistPath, JSON.stringify(data), "utf-8");
270
+ }
271
+ /**
272
+ * Load store from file
273
+ */
274
+ async load() {
275
+ if (!this.persistPath) return;
276
+ try {
277
+ const data = JSON.parse(await fs.readFile(this.persistPath, "utf-8"));
278
+ this.vectors = new Map(data.vectors);
279
+ this.namespaces = new Map(
280
+ data.namespaces.map(([k, v]) => [k, new Set(v)])
281
+ );
282
+ } catch {
283
+ }
284
+ }
285
+ /**
286
+ * Get all vectors
287
+ */
288
+ getAll() {
289
+ return Array.from(this.vectors.values());
290
+ }
291
+ /**
292
+ * Get vector by ID
293
+ */
294
+ getById(id) {
295
+ return this.vectors.get(id);
296
+ }
297
+ };
298
+ function createMemoryStore(config) {
299
+ return new MemoryStore(config);
300
+ }
301
+
302
+ // src/stores/PineconeStore.ts
303
+ var PineconeStore = class extends BaseStore {
304
+ storeType = "pinecone";
305
+ client;
306
+ index;
307
+ apiKey;
308
+ indexName;
309
+ initialized = false;
310
+ constructor(config) {
311
+ super(config);
312
+ if (!config.apiKey) {
313
+ throw new Error("Pinecone API key is required");
314
+ }
315
+ if (!config.indexName) {
316
+ throw new Error("Pinecone index name is required");
317
+ }
318
+ this.apiKey = config.apiKey;
319
+ this.indexName = config.indexName;
320
+ }
321
+ /**
322
+ * Initialize Pinecone client
323
+ */
324
+ async init() {
325
+ if (this.initialized) return;
326
+ try {
327
+ const { Pinecone } = await import("@pinecone-database/pinecone");
328
+ this.client = new Pinecone({ apiKey: this.apiKey });
329
+ this.index = this.client.index(
330
+ this.indexName
331
+ );
332
+ this.initialized = true;
333
+ } catch (error) {
334
+ throw new Error(
335
+ `Failed to initialize Pinecone: ${error.message}`
336
+ );
337
+ }
338
+ }
339
+ async ensureInitialized() {
340
+ if (!this.initialized) {
341
+ await this.init();
342
+ }
343
+ }
344
+ async upsert(records, options) {
345
+ await this.ensureInitialized();
346
+ const startTime = performance.now();
347
+ const namespace = options?.namespace ?? this.namespace;
348
+ const batchSize = options?.batchSize ?? 100;
349
+ const upsertedIds = [];
350
+ const errors = [];
351
+ const vectors = records.map((record) => ({
352
+ id: record.id,
353
+ values: record.vector,
354
+ metadata: {
355
+ ...record.metadata,
356
+ text: record.text
357
+ }
358
+ }));
359
+ const batches = batch(vectors, batchSize);
360
+ let completed = 0;
361
+ for (const batchVectors of batches) {
362
+ try {
363
+ const ns = this.index.namespace(namespace);
364
+ await ns.upsert(
365
+ batchVectors
366
+ );
367
+ upsertedIds.push(...batchVectors.map((v) => v.id));
368
+ } catch (error) {
369
+ for (const v of batchVectors) {
370
+ errors.push({ id: v.id, error: error.message });
371
+ }
372
+ }
373
+ completed += batchVectors.length;
374
+ options?.onProgress?.({ completed, total: records.length });
375
+ }
376
+ return {
377
+ upsertedIds,
378
+ upsertedCount: upsertedIds.length,
379
+ errors,
380
+ durationMs: performance.now() - startTime
381
+ };
382
+ }
383
+ async query(vector, options) {
384
+ await this.ensureInitialized();
385
+ const startTime = performance.now();
386
+ const namespace = options?.namespace ?? this.namespace;
387
+ const topK = options?.topK ?? 10;
388
+ const ns = this.index.namespace(
389
+ namespace
390
+ );
391
+ const result = await ns.query({
392
+ vector,
393
+ topK,
394
+ filter: options?.filter,
395
+ includeValues: options?.includeVectors ?? false,
396
+ includeMetadata: options?.includeMetadata ?? true
397
+ });
398
+ const matches = result.matches.map((match) => ({
399
+ id: match.id,
400
+ text: match.metadata?.text ?? "",
401
+ score: match.score,
402
+ metadata: match.metadata ?? {}
403
+ }));
404
+ const filtered = options?.minScore ? matches.filter((m) => m.score >= options.minScore) : matches;
405
+ return {
406
+ matches: filtered,
407
+ namespace,
408
+ durationMs: performance.now() - startTime
409
+ };
410
+ }
411
+ async delete(ids, options) {
412
+ await this.ensureInitialized();
413
+ const startTime = performance.now();
414
+ const namespace = options?.namespace ?? this.namespace;
415
+ const ns = this.index.namespace(
416
+ namespace
417
+ );
418
+ await ns.deleteMany(
419
+ ids
420
+ );
421
+ return {
422
+ deletedCount: ids.length,
423
+ requestedCount: ids.length,
424
+ countExact: false,
425
+ durationMs: performance.now() - startTime
426
+ };
427
+ }
428
+ async deleteAll(options) {
429
+ await this.ensureInitialized();
430
+ const startTime = performance.now();
431
+ const namespace = options?.namespace ?? this.namespace;
432
+ const before = await this.getStats().catch(() => void 0);
433
+ const ns = this.index.namespace(
434
+ namespace
435
+ );
436
+ await ns.deleteAll();
437
+ return {
438
+ deletedCount: before?.vectorCount ?? 0,
439
+ requestedCount: before?.vectorCount,
440
+ countExact: before !== void 0,
441
+ durationMs: performance.now() - startTime
442
+ };
443
+ }
444
+ async getStats() {
445
+ await this.ensureInitialized();
446
+ const stats = await this.index.describeIndexStats();
447
+ return {
448
+ type: this.storeType,
449
+ vectorCount: stats.totalVectorCount ?? 0,
450
+ namespaceCount: Object.keys(stats.namespaces ?? {}).length,
451
+ dimensions: stats.dimension,
452
+ metric: this.metric,
453
+ lastUpdated: Date.now()
454
+ };
455
+ }
456
+ async checkHealth() {
457
+ const startTime = performance.now();
458
+ try {
459
+ await this.ensureInitialized();
460
+ await this.index.describeIndexStats();
461
+ return {
462
+ healthy: true,
463
+ latencyMs: performance.now() - startTime,
464
+ lastCheck: Date.now()
465
+ };
466
+ } catch (error) {
467
+ return {
468
+ healthy: false,
469
+ latencyMs: performance.now() - startTime,
470
+ lastCheck: Date.now(),
471
+ error: error.message
472
+ };
473
+ }
474
+ }
475
+ async close() {
476
+ this.initialized = false;
477
+ return Promise.resolve();
478
+ }
479
+ };
480
+ function createPineconeStore(config) {
481
+ return new PineconeStore(config);
482
+ }
483
+
484
+ // src/stores/ChromaStore.ts
485
+ var ChromaStore = class extends BaseStore {
486
+ storeType = "chroma";
487
+ client;
488
+ collection;
489
+ collectionName;
490
+ url;
491
+ initialized = false;
492
+ constructor(config) {
493
+ super(config);
494
+ if (!config.collectionName) {
495
+ throw new Error("Chroma collection name is required");
496
+ }
497
+ this.collectionName = config.collectionName;
498
+ this.url = config.url;
499
+ }
500
+ /**
501
+ * Initialize ChromaDB client
502
+ */
503
+ async init() {
504
+ if (this.initialized) return;
505
+ try {
506
+ const { ChromaClient } = await import("chromadb");
507
+ this.client = this.url ? new ChromaClient({ path: this.url }) : new ChromaClient();
508
+ this.collection = await this.client.getOrCreateCollection({
509
+ name: this.collectionName,
510
+ metadata: {
511
+ "hnsw:space": this.metricToChroma(this.metric)
512
+ }
513
+ });
514
+ this.initialized = true;
515
+ } catch (error) {
516
+ throw new Error(
517
+ `Failed to initialize ChromaDB: ${error.message}`
518
+ );
519
+ }
520
+ }
521
+ metricToChroma(metric) {
522
+ switch (metric) {
523
+ case "cosine":
524
+ return "cosine";
525
+ case "euclidean":
526
+ return "l2";
527
+ case "dot_product":
528
+ return "ip";
529
+ default:
530
+ return "cosine";
531
+ }
532
+ }
533
+ async ensureInitialized() {
534
+ if (!this.initialized) {
535
+ await this.init();
536
+ }
537
+ }
538
+ async upsert(records, _options) {
539
+ await this.ensureInitialized();
540
+ const startTime = performance.now();
541
+ const ids = records.map((r) => r.id);
542
+ const embeddings = records.map((r) => r.vector);
543
+ const documents = records.map((r) => r.text ?? "");
544
+ const metadatas = records.map((r) => r.metadata ?? {});
545
+ const upsertedIds = [];
546
+ const errors = [];
547
+ try {
548
+ await this.collection.upsert({
549
+ ids,
550
+ embeddings,
551
+ documents,
552
+ metadatas
553
+ });
554
+ upsertedIds.push(...ids);
555
+ } catch (error) {
556
+ for (const id of ids) {
557
+ errors.push({ id, error: error.message });
558
+ }
559
+ }
560
+ return {
561
+ upsertedIds,
562
+ upsertedCount: upsertedIds.length,
563
+ errors,
564
+ durationMs: performance.now() - startTime
565
+ };
566
+ }
567
+ async query(vector, options) {
568
+ await this.ensureInitialized();
569
+ const startTime = performance.now();
570
+ const topK = options?.topK ?? 10;
571
+ const result = await this.collection.query({
572
+ queryEmbeddings: [vector],
573
+ nResults: topK,
574
+ where: options?.filter,
575
+ include: ["documents", "metadatas", "distances"]
576
+ });
577
+ const matches = (result.ids[0] ?? []).map((id, i) => {
578
+ const distance = result.distances?.[0]?.[i] ?? 0;
579
+ const score = 1 / (1 + distance);
580
+ return {
581
+ id,
582
+ text: result.documents?.[0]?.[i] ?? "",
583
+ score,
584
+ metadata: result.metadatas?.[0]?.[i] ?? {},
585
+ distance
586
+ };
587
+ });
588
+ const filtered = options?.minScore ? matches.filter((m) => m.score >= options.minScore) : matches;
589
+ return {
590
+ matches: filtered,
591
+ namespace: this.collectionName,
592
+ durationMs: performance.now() - startTime
593
+ };
594
+ }
595
+ async delete(ids, _options) {
596
+ await this.ensureInitialized();
597
+ const startTime = performance.now();
598
+ await this.collection.delete({ ids });
599
+ return {
600
+ deletedCount: ids.length,
601
+ requestedCount: ids.length,
602
+ countExact: false,
603
+ durationMs: performance.now() - startTime
604
+ };
605
+ }
606
+ async deleteAll(_options) {
607
+ await this.ensureInitialized();
608
+ const startTime = performance.now();
609
+ const before = await this.getStats().catch(() => void 0);
610
+ await this.client.deleteCollection({ name: this.collectionName });
611
+ this.collection = await this.client.createCollection({
612
+ name: this.collectionName,
613
+ metadata: {
614
+ "hnsw:space": this.metricToChroma(this.metric)
615
+ }
616
+ });
617
+ return {
618
+ deletedCount: before?.vectorCount ?? 0,
619
+ requestedCount: before?.vectorCount,
620
+ countExact: before !== void 0,
621
+ durationMs: performance.now() - startTime
622
+ };
623
+ }
624
+ async getStats() {
625
+ await this.ensureInitialized();
626
+ const count = await this.collection.count();
627
+ return {
628
+ type: this.storeType,
629
+ vectorCount: count,
630
+ namespaceCount: 1,
631
+ dimensions: this.dimensions ?? 0,
632
+ metric: this.metric,
633
+ lastUpdated: Date.now()
634
+ };
635
+ }
636
+ async checkHealth() {
637
+ const startTime = performance.now();
638
+ try {
639
+ await this.ensureInitialized();
640
+ await this.collection.count();
641
+ return {
642
+ healthy: true,
643
+ latencyMs: performance.now() - startTime,
644
+ lastCheck: Date.now()
645
+ };
646
+ } catch (error) {
647
+ return {
648
+ healthy: false,
649
+ latencyMs: performance.now() - startTime,
650
+ lastCheck: Date.now(),
651
+ error: error.message
652
+ };
653
+ }
654
+ }
655
+ async close() {
656
+ this.initialized = false;
657
+ return Promise.resolve();
658
+ }
659
+ };
660
+ function createChromaStore(config) {
661
+ return new ChromaStore(config);
662
+ }
663
+
664
+ // src/stores/QdrantStore.ts
665
+ var QdrantStore = class extends BaseStore {
666
+ storeType = "qdrant";
667
+ client;
668
+ collectionName;
669
+ url;
670
+ apiKey;
671
+ initialized = false;
672
+ constructor(config) {
673
+ super(config);
674
+ if (!config.url) {
675
+ throw new Error("Qdrant URL is required");
676
+ }
677
+ if (!config.collectionName) {
678
+ throw new Error("Qdrant collection name is required");
679
+ }
680
+ this.url = config.url;
681
+ this.collectionName = config.collectionName;
682
+ this.apiKey = config.apiKey;
683
+ }
684
+ /**
685
+ * Initialize Qdrant client
686
+ */
687
+ async init() {
688
+ if (this.initialized) return;
689
+ try {
690
+ const { QdrantClient } = await import("@qdrant/js-client-rest");
691
+ this.client = new QdrantClient({
692
+ url: this.url,
693
+ apiKey: this.apiKey
694
+ });
695
+ const collections = await this.client.getCollections();
696
+ const exists = collections.collections.some(
697
+ (c) => c.name === this.collectionName
698
+ );
699
+ if (!exists && this.dimensions) {
700
+ await this.client.createCollection(this.collectionName, {
701
+ vectors: {
702
+ size: this.dimensions,
703
+ distance: this.metricToQdrant(this.metric)
704
+ }
705
+ });
706
+ }
707
+ this.initialized = true;
708
+ } catch (error) {
709
+ throw new Error(
710
+ `Failed to initialize Qdrant: ${error.message}`
711
+ );
712
+ }
713
+ }
714
+ metricToQdrant(metric) {
715
+ switch (metric) {
716
+ case "cosine":
717
+ return "Cosine";
718
+ case "euclidean":
719
+ return "Euclid";
720
+ case "dot_product":
721
+ return "Dot";
722
+ default:
723
+ return "Cosine";
724
+ }
725
+ }
726
+ async ensureInitialized() {
727
+ if (!this.initialized) {
728
+ await this.init();
729
+ }
730
+ }
731
+ async upsert(records, options) {
732
+ await this.ensureInitialized();
733
+ const startTime = performance.now();
734
+ const batchSize = options?.batchSize ?? 100;
735
+ const upsertedIds = [];
736
+ const errors = [];
737
+ const points = records.map((record) => ({
738
+ id: record.id,
739
+ vector: record.vector,
740
+ payload: {
741
+ ...record.metadata,
742
+ text: record.text
743
+ }
744
+ }));
745
+ const batches = batch(points, batchSize);
746
+ let completed = 0;
747
+ for (const batchPoints of batches) {
748
+ try {
749
+ await this.client.upsert(this.collectionName, {
750
+ points: batchPoints
751
+ });
752
+ upsertedIds.push(...batchPoints.map((p) => p.id));
753
+ } catch (error) {
754
+ for (const p of batchPoints) {
755
+ errors.push({ id: p.id, error: error.message });
756
+ }
757
+ }
758
+ completed += batchPoints.length;
759
+ options?.onProgress?.({ completed, total: records.length });
760
+ }
761
+ return {
762
+ upsertedIds,
763
+ upsertedCount: upsertedIds.length,
764
+ errors,
765
+ durationMs: performance.now() - startTime
766
+ };
767
+ }
768
+ async query(vector, options) {
769
+ await this.ensureInitialized();
770
+ const startTime = performance.now();
771
+ const topK = options?.topK ?? 10;
772
+ const result = await this.client.search(this.collectionName, {
773
+ vector,
774
+ limit: topK,
775
+ filter: options?.filter ? this.buildQdrantFilter(options.filter) : void 0,
776
+ with_payload: options?.includeMetadata ?? true,
777
+ with_vector: options?.includeVectors ?? false,
778
+ score_threshold: options?.minScore
779
+ });
780
+ const matches = result.map((point) => ({
781
+ id: point.id.toString(),
782
+ text: point.payload?.text ?? "",
783
+ score: point.score,
784
+ metadata: point.payload ?? {}
785
+ }));
786
+ return {
787
+ matches,
788
+ namespace: this.collectionName,
789
+ durationMs: performance.now() - startTime
790
+ };
791
+ }
792
+ buildQdrantFilter(filter) {
793
+ const must = [];
794
+ for (const [key, value] of Object.entries(filter)) {
795
+ must.push({
796
+ key,
797
+ match: { value }
798
+ });
799
+ }
800
+ return { must };
801
+ }
802
+ async delete(ids, _options) {
803
+ await this.ensureInitialized();
804
+ const startTime = performance.now();
805
+ await this.client.delete(this.collectionName, {
806
+ points: ids
807
+ });
808
+ return {
809
+ deletedCount: ids.length,
810
+ requestedCount: ids.length,
811
+ countExact: false,
812
+ durationMs: performance.now() - startTime
813
+ };
814
+ }
815
+ async deleteAll(_options) {
816
+ await this.ensureInitialized();
817
+ const startTime = performance.now();
818
+ const before = await this.getStats().catch(() => void 0);
819
+ await this.client.deleteCollection(this.collectionName);
820
+ if (this.dimensions) {
821
+ await this.client.createCollection(this.collectionName, {
822
+ vectors: {
823
+ size: this.dimensions,
824
+ distance: this.metricToQdrant(this.metric)
825
+ }
826
+ });
827
+ }
828
+ return {
829
+ deletedCount: before?.vectorCount ?? 0,
830
+ requestedCount: before?.vectorCount,
831
+ countExact: before !== void 0,
832
+ durationMs: performance.now() - startTime
833
+ };
834
+ }
835
+ async getStats() {
836
+ await this.ensureInitialized();
837
+ const info = await this.client.getCollection(this.collectionName);
838
+ return {
839
+ type: this.storeType,
840
+ vectorCount: info.vectors_count,
841
+ namespaceCount: 1,
842
+ dimensions: info.config?.params?.vectors?.size,
843
+ metric: this.metric,
844
+ lastUpdated: Date.now()
845
+ };
846
+ }
847
+ async checkHealth() {
848
+ const startTime = performance.now();
849
+ try {
850
+ await this.ensureInitialized();
851
+ await this.client.getCollection(this.collectionName);
852
+ return {
853
+ healthy: true,
854
+ latencyMs: performance.now() - startTime,
855
+ lastCheck: Date.now()
856
+ };
857
+ } catch (error) {
858
+ return {
859
+ healthy: false,
860
+ latencyMs: performance.now() - startTime,
861
+ lastCheck: Date.now(),
862
+ error: error.message
863
+ };
864
+ }
865
+ }
866
+ async close() {
867
+ this.initialized = false;
868
+ return Promise.resolve();
869
+ }
870
+ };
871
+ function createQdrantStore(config) {
872
+ return new QdrantStore(config);
873
+ }
874
+
875
+ // src/stores/PgVectorStore.ts
876
+ var PgVectorStore = class extends BaseStore {
877
+ storeType = "pgvector";
878
+ pool;
879
+ injectedPool;
880
+ table;
881
+ vectorColumn;
882
+ contentColumn;
883
+ metadataColumn;
884
+ pgConfig;
885
+ initialized = false;
886
+ constructor(config) {
887
+ super(config);
888
+ if (!config.tableName) {
889
+ throw new Error("pgvector store requires a `tableName`");
890
+ }
891
+ this.pgConfig = config;
892
+ this.injectedPool = config.pool;
893
+ this.table = config.tableName;
894
+ this.vectorColumn = config.vectorColumn ?? "embedding";
895
+ this.contentColumn = config.contentColumn ?? "content";
896
+ this.metadataColumn = config.metadataColumn ?? "metadata";
897
+ }
898
+ /** The pgvector distance operator for the configured metric. */
899
+ get distanceOperator() {
900
+ switch (this.metric) {
901
+ case "euclidean":
902
+ return "<->";
903
+ case "dot_product":
904
+ return "<#>";
905
+ case "cosine":
906
+ default:
907
+ return "<=>";
908
+ }
909
+ }
910
+ async init() {
911
+ if (this.initialized) return;
912
+ if (this.injectedPool) {
913
+ this.pool = this.injectedPool;
914
+ } else {
915
+ let mod;
916
+ try {
917
+ mod = await importOptional("pg");
918
+ } catch {
919
+ throw new Error(
920
+ 'pgvector store requires the "pg" package. Install it, or pass a pre-built `pool` to the store.'
921
+ );
922
+ }
923
+ const pg = mod.default ?? mod;
924
+ const Pool = pg.Pool;
925
+ this.pool = new Pool(
926
+ this.pgConfig.connectionString ? { connectionString: this.pgConfig.connectionString } : {
927
+ host: this.pgConfig.host,
928
+ port: this.pgConfig.port,
929
+ database: this.pgConfig.database,
930
+ user: this.pgConfig.user,
931
+ password: this.pgConfig.password
932
+ }
933
+ );
934
+ }
935
+ await this.pool.query("CREATE EXTENSION IF NOT EXISTS vector");
936
+ const dims = this.dimensions;
937
+ const vectorType = dims ? `vector(${dims})` : "vector";
938
+ await this.pool.query(
939
+ `CREATE TABLE IF NOT EXISTS ${this.ident(this.table)} (
940
+ id text PRIMARY KEY,
941
+ ${this.ident(this.contentColumn)} text,
942
+ ${this.ident(this.metadataColumn)} jsonb,
943
+ ${this.ident(this.vectorColumn)} ${vectorType}
944
+ )`
945
+ );
946
+ this.initialized = true;
947
+ }
948
+ async ensureInitialized() {
949
+ if (!this.initialized) await this.init();
950
+ }
951
+ /** Quote an SQL identifier to guard against injection via config names. */
952
+ ident(name) {
953
+ return `"${name.replace(/"/g, '""')}"`;
954
+ }
955
+ toVectorLiteral(vector) {
956
+ return `[${Array.from(vector).join(",")}]`;
957
+ }
958
+ async upsert(records, options) {
959
+ await this.ensureInitialized();
960
+ const start = performance.now();
961
+ const batchSize = options?.batchSize ?? 100;
962
+ const upsertedIds = [];
963
+ const errors = [];
964
+ let completed = 0;
965
+ for (const group of batch(records, batchSize)) {
966
+ for (const record of group) {
967
+ try {
968
+ await this.pool.query(
969
+ `INSERT INTO ${this.ident(this.table)} (id, ${this.ident(this.contentColumn)}, ${this.ident(this.metadataColumn)}, ${this.ident(this.vectorColumn)})
970
+ VALUES ($1, $2, $3, $4)
971
+ ON CONFLICT (id) DO UPDATE SET
972
+ ${this.ident(this.contentColumn)} = EXCLUDED.${this.ident(this.contentColumn)},
973
+ ${this.ident(this.metadataColumn)} = EXCLUDED.${this.ident(this.metadataColumn)},
974
+ ${this.ident(this.vectorColumn)} = EXCLUDED.${this.ident(this.vectorColumn)}`,
975
+ [
976
+ record.id,
977
+ record.text ?? null,
978
+ JSON.stringify(record.metadata ?? {}),
979
+ this.toVectorLiteral(record.vector)
980
+ ]
981
+ );
982
+ upsertedIds.push(record.id);
983
+ } catch (e) {
984
+ errors.push({ id: record.id, error: e.message });
985
+ }
986
+ }
987
+ completed += group.length;
988
+ options?.onProgress?.({ completed, total: records.length });
989
+ }
990
+ return {
991
+ upsertedIds,
992
+ upsertedCount: upsertedIds.length,
993
+ errors,
994
+ durationMs: performance.now() - start
995
+ };
996
+ }
997
+ async query(vector, options) {
998
+ await this.ensureInitialized();
999
+ const start = performance.now();
1000
+ const topK = options?.topK ?? 10;
1001
+ const params = [this.toVectorLiteral(vector)];
1002
+ let where = "";
1003
+ if (options?.filter && Object.keys(options.filter).length > 0) {
1004
+ params.push(JSON.stringify(options.filter));
1005
+ where = `WHERE ${this.ident(this.metadataColumn)} @> $${params.length}::jsonb`;
1006
+ }
1007
+ params.push(topK);
1008
+ const op = this.distanceOperator;
1009
+ const sql = `SELECT id, ${this.ident(this.contentColumn)} AS content, ${this.ident(this.metadataColumn)} AS metadata, ${this.ident(this.vectorColumn)} ${op} $1 AS distance FROM ${this.ident(this.table)} ${where} ORDER BY ${this.ident(this.vectorColumn)} ${op} $1 ASC LIMIT $${params.length}`;
1010
+ const result = await this.pool.query(sql, params);
1011
+ const matches = result.rows.map((row) => {
1012
+ const distance = Number(row.distance);
1013
+ const score = this.metric === "cosine" ? 1 - distance : 1 / (1 + distance);
1014
+ return {
1015
+ id: String(row.id),
1016
+ text: row.content ?? "",
1017
+ score,
1018
+ distance,
1019
+ metadata: row.metadata ?? {}
1020
+ };
1021
+ }).filter(
1022
+ (m) => options?.minScore === void 0 || m.score >= options.minScore
1023
+ );
1024
+ return {
1025
+ matches,
1026
+ namespace: this.namespace,
1027
+ durationMs: performance.now() - start
1028
+ };
1029
+ }
1030
+ async delete(ids, _options) {
1031
+ await this.ensureInitialized();
1032
+ const start = performance.now();
1033
+ const result = await this.pool.query(
1034
+ `DELETE FROM ${this.ident(this.table)} WHERE id = ANY($1)`,
1035
+ [ids]
1036
+ );
1037
+ const deleted = result.rowCount ?? ids.length;
1038
+ return {
1039
+ deletedCount: deleted,
1040
+ requestedCount: ids.length,
1041
+ countExact: result.rowCount !== null,
1042
+ durationMs: performance.now() - start
1043
+ };
1044
+ }
1045
+ async deleteAll(_options) {
1046
+ await this.ensureInitialized();
1047
+ const start = performance.now();
1048
+ const result = await this.pool.query(
1049
+ `DELETE FROM ${this.ident(this.table)}`
1050
+ );
1051
+ return {
1052
+ deletedCount: result.rowCount ?? 0,
1053
+ requestedCount: result.rowCount ?? void 0,
1054
+ countExact: result.rowCount !== null,
1055
+ durationMs: performance.now() - start
1056
+ };
1057
+ }
1058
+ async getStats() {
1059
+ await this.ensureInitialized();
1060
+ const result = await this.pool.query(
1061
+ `SELECT COUNT(*)::int AS count FROM ${this.ident(this.table)}`
1062
+ );
1063
+ const vectorCount = Number(result.rows[0]?.count ?? 0);
1064
+ return {
1065
+ type: this.storeType,
1066
+ vectorCount,
1067
+ namespaceCount: 1,
1068
+ dimensions: this.dimensions ?? 0,
1069
+ metric: this.metric,
1070
+ lastUpdated: Date.now()
1071
+ };
1072
+ }
1073
+ async checkHealth() {
1074
+ const start = performance.now();
1075
+ try {
1076
+ await this.ensureInitialized();
1077
+ await this.pool.query("SELECT 1");
1078
+ return {
1079
+ healthy: true,
1080
+ latencyMs: performance.now() - start,
1081
+ lastCheck: Date.now()
1082
+ };
1083
+ } catch (e) {
1084
+ return {
1085
+ healthy: false,
1086
+ latencyMs: performance.now() - start,
1087
+ lastCheck: Date.now(),
1088
+ error: e.message
1089
+ };
1090
+ }
1091
+ }
1092
+ async close() {
1093
+ if (this.pool && !this.injectedPool) {
1094
+ await this.pool.end();
1095
+ }
1096
+ this.initialized = false;
1097
+ }
1098
+ };
1099
+ function createPgVectorStore(config) {
1100
+ return new PgVectorStore(config);
1101
+ }
1102
+
1103
+ // src/stores/WeaviateStore.ts
1104
+ var WeaviateStore = class extends BaseStore {
1105
+ storeType = "weaviate";
1106
+ backend;
1107
+ injectedBackend;
1108
+ className;
1109
+ url;
1110
+ apiKey;
1111
+ initialized = false;
1112
+ constructor(config) {
1113
+ super(config);
1114
+ if (!config.url) throw new Error("Weaviate store requires a `url`");
1115
+ if (!config.className) {
1116
+ throw new Error("Weaviate store requires a `className`");
1117
+ }
1118
+ this.url = config.url;
1119
+ this.className = config.className;
1120
+ this.apiKey = config.apiKey;
1121
+ this.injectedBackend = config.backend;
1122
+ }
1123
+ async init() {
1124
+ if (this.initialized) return;
1125
+ this.backend = this.injectedBackend ?? await this.buildSdkBackend();
1126
+ await this.backend.ensureClass(this.className, this.dimensions);
1127
+ this.initialized = true;
1128
+ }
1129
+ async ensureInitialized() {
1130
+ if (!this.initialized) await this.init();
1131
+ }
1132
+ /** Build a {@link WeaviateBackend} backed by the real `weaviate-ts-client`. */
1133
+ async buildSdkBackend() {
1134
+ let mod;
1135
+ try {
1136
+ mod = await importOptional("weaviate-ts-client");
1137
+ } catch {
1138
+ throw new Error(
1139
+ 'Weaviate store requires the "weaviate-ts-client" package. Install it, or pass a custom `backend`.'
1140
+ );
1141
+ }
1142
+ const weaviate = mod.default ?? mod;
1143
+ const u = new URL(this.url);
1144
+ const client = weaviate.client({
1145
+ scheme: u.protocol.replace(":", ""),
1146
+ host: u.host,
1147
+ apiKey: this.apiKey && weaviate.ApiKey ? new weaviate.ApiKey(this.apiKey) : void 0
1148
+ });
1149
+ return new WeaviateSdkBackend(client);
1150
+ }
1151
+ async upsert(records, options) {
1152
+ await this.ensureInitialized();
1153
+ const start = performance.now();
1154
+ const batchSize = options?.batchSize ?? 100;
1155
+ const upsertedIds = [];
1156
+ const errors = [];
1157
+ let completed = 0;
1158
+ for (const group of batch(records, batchSize)) {
1159
+ try {
1160
+ await this.backend.upsert(
1161
+ this.className,
1162
+ group.map((r) => ({
1163
+ id: r.id,
1164
+ vector: Array.from(r.vector),
1165
+ properties: { ...r.metadata ?? {}, text: r.text ?? "" }
1166
+ }))
1167
+ );
1168
+ upsertedIds.push(...group.map((r) => r.id));
1169
+ } catch (e) {
1170
+ for (const r of group)
1171
+ errors.push({ id: r.id, error: e.message });
1172
+ }
1173
+ completed += group.length;
1174
+ options?.onProgress?.({ completed, total: records.length });
1175
+ }
1176
+ return {
1177
+ upsertedIds,
1178
+ upsertedCount: upsertedIds.length,
1179
+ errors,
1180
+ durationMs: performance.now() - start
1181
+ };
1182
+ }
1183
+ async query(vector, options) {
1184
+ await this.ensureInitialized();
1185
+ const start = performance.now();
1186
+ const topK = options?.topK ?? 10;
1187
+ const hits = await this.backend.nearVector(
1188
+ this.className,
1189
+ Array.from(vector),
1190
+ topK,
1191
+ options?.filter
1192
+ );
1193
+ const matches = hits.map((h) => ({
1194
+ id: h.id,
1195
+ text: h.properties.text ?? "",
1196
+ score: h.score,
1197
+ metadata: h.properties
1198
+ })).filter(
1199
+ (m) => options?.minScore === void 0 || m.score >= options.minScore
1200
+ );
1201
+ return {
1202
+ matches,
1203
+ namespace: this.className,
1204
+ durationMs: performance.now() - start
1205
+ };
1206
+ }
1207
+ async delete(ids, _options) {
1208
+ await this.ensureInitialized();
1209
+ const start = performance.now();
1210
+ const deleted = await this.backend.deleteByIds(this.className, ids);
1211
+ return {
1212
+ deletedCount: deleted,
1213
+ requestedCount: ids.length,
1214
+ countExact: true,
1215
+ durationMs: performance.now() - start
1216
+ };
1217
+ }
1218
+ async deleteAll(_options) {
1219
+ await this.ensureInitialized();
1220
+ const start = performance.now();
1221
+ const deleted = await this.backend.deleteAll(this.className);
1222
+ return {
1223
+ deletedCount: deleted,
1224
+ requestedCount: deleted,
1225
+ countExact: true,
1226
+ durationMs: performance.now() - start
1227
+ };
1228
+ }
1229
+ async getStats() {
1230
+ await this.ensureInitialized();
1231
+ return {
1232
+ type: this.storeType,
1233
+ vectorCount: await this.backend.count(this.className),
1234
+ namespaceCount: 1,
1235
+ dimensions: this.dimensions ?? 0,
1236
+ metric: this.metric,
1237
+ lastUpdated: Date.now()
1238
+ };
1239
+ }
1240
+ async checkHealth() {
1241
+ const start = performance.now();
1242
+ try {
1243
+ await this.ensureInitialized();
1244
+ await this.backend.ping();
1245
+ return {
1246
+ healthy: true,
1247
+ latencyMs: performance.now() - start,
1248
+ lastCheck: Date.now()
1249
+ };
1250
+ } catch (e) {
1251
+ return {
1252
+ healthy: false,
1253
+ latencyMs: performance.now() - start,
1254
+ lastCheck: Date.now(),
1255
+ error: e.message
1256
+ };
1257
+ }
1258
+ }
1259
+ close() {
1260
+ this.initialized = false;
1261
+ return Promise.resolve();
1262
+ }
1263
+ };
1264
+ var WeaviateSdkBackend = class {
1265
+ constructor(client) {
1266
+ this.client = client;
1267
+ }
1268
+ async ensureClass(className, _dimensions) {
1269
+ const schema = await this.client.schema.getter().do();
1270
+ if (schema.classes?.some((c) => c.class === className)) return;
1271
+ await this.client.schema.classCreator().withClass({ class: className, vectorizer: "none" }).do();
1272
+ }
1273
+ async upsert(className, objects) {
1274
+ let batcher = this.client.batch.objectsBatcher();
1275
+ for (const o of objects) {
1276
+ batcher = batcher.withObject({
1277
+ class: className,
1278
+ id: o.id,
1279
+ vector: o.vector,
1280
+ properties: o.properties
1281
+ });
1282
+ }
1283
+ await batcher.do();
1284
+ }
1285
+ async nearVector(className, vector, limit, filter) {
1286
+ let q = this.client.graphql.get().withClassName(className).withFields("_additional { id certainty } text").withNearVector({ vector }).withLimit(limit);
1287
+ if (filter) q = q.withWhere(this.toWhere(filter));
1288
+ const res = await q.do();
1289
+ const rows = res.data?.Get?.[className] ?? [];
1290
+ return rows.map((row) => {
1291
+ const additional = row._additional;
1292
+ const { _additional, ...properties } = row;
1293
+ void _additional;
1294
+ return {
1295
+ id: additional.id,
1296
+ score: additional.certainty ?? 0,
1297
+ properties
1298
+ };
1299
+ });
1300
+ }
1301
+ async deleteByIds(className, ids) {
1302
+ let n = 0;
1303
+ for (const id of ids) {
1304
+ await this.client.data.deleter().withClassName(className).withId(id).do();
1305
+ n++;
1306
+ }
1307
+ return n;
1308
+ }
1309
+ async deleteAll(className) {
1310
+ const count = await this.count(className);
1311
+ await this.client.schema.classCreator().withClass({ class: className, vectorizer: "none" }).do().catch(() => void 0);
1312
+ return count;
1313
+ }
1314
+ async count(className) {
1315
+ const res = await this.client.graphql.aggregate().withClassName(className).withFields("meta { count }").do();
1316
+ const agg = res.data?.Aggregate?.[className] ?? [];
1317
+ return agg[0]?.meta?.count ?? 0;
1318
+ }
1319
+ async ping() {
1320
+ await this.client.misc.liveChecker().do();
1321
+ }
1322
+ toWhere(filter) {
1323
+ const operands = Object.entries(filter).map(([path, value]) => ({
1324
+ path: [path],
1325
+ operator: "Equal",
1326
+ ...typeof value === "number" ? { valueNumber: value } : typeof value === "boolean" ? { valueBoolean: value } : { valueText: String(value) }
1327
+ }));
1328
+ return operands.length === 1 ? operands[0] : { operator: "And", operands };
1329
+ }
1330
+ };
1331
+ function createWeaviateStore(config) {
1332
+ return new WeaviateStore(config);
1333
+ }
1334
+
1335
+ // src/stores/MilvusStore.ts
1336
+ var MilvusStore = class extends BaseStore {
1337
+ storeType = "milvus";
1338
+ backend;
1339
+ injectedBackend;
1340
+ collection;
1341
+ milvusConfig;
1342
+ initialized = false;
1343
+ constructor(config) {
1344
+ super(config);
1345
+ if (!config.url) throw new Error("Milvus store requires a `url`");
1346
+ if (!config.collectionName) {
1347
+ throw new Error("Milvus store requires a `collectionName`");
1348
+ }
1349
+ if (!config.dimensions) {
1350
+ throw new Error("Milvus store requires `dimensions`");
1351
+ }
1352
+ this.milvusConfig = config;
1353
+ this.collection = config.collectionName;
1354
+ this.injectedBackend = config.backend;
1355
+ }
1356
+ /** Milvus metric type string for the configured distance metric. */
1357
+ get metricType() {
1358
+ switch (this.metric) {
1359
+ case "euclidean":
1360
+ return "L2";
1361
+ case "dot_product":
1362
+ return "IP";
1363
+ case "cosine":
1364
+ default:
1365
+ return "COSINE";
1366
+ }
1367
+ }
1368
+ async init() {
1369
+ if (this.initialized) return;
1370
+ this.backend = this.injectedBackend ?? await this.buildSdkBackend();
1371
+ await this.backend.ensureCollection(
1372
+ this.collection,
1373
+ this.dimensions,
1374
+ this.metricType
1375
+ );
1376
+ this.initialized = true;
1377
+ }
1378
+ async ensureInitialized() {
1379
+ if (!this.initialized) await this.init();
1380
+ }
1381
+ async buildSdkBackend() {
1382
+ let mod;
1383
+ try {
1384
+ mod = await importOptional("@zilliz/milvus2-sdk-node");
1385
+ } catch {
1386
+ throw new Error(
1387
+ 'Milvus store requires the "@zilliz/milvus2-sdk-node" package. Install it, or pass a custom `backend`.'
1388
+ );
1389
+ }
1390
+ const sdk = mod;
1391
+ const client = new sdk.MilvusClient({
1392
+ address: this.milvusConfig.url,
1393
+ username: this.milvusConfig.username,
1394
+ password: this.milvusConfig.password
1395
+ });
1396
+ return new MilvusSdkBackend(client);
1397
+ }
1398
+ async upsert(records, options) {
1399
+ await this.ensureInitialized();
1400
+ const start = performance.now();
1401
+ const batchSize = options?.batchSize ?? 100;
1402
+ const upsertedIds = [];
1403
+ const errors = [];
1404
+ let completed = 0;
1405
+ for (const group of batch(records, batchSize)) {
1406
+ try {
1407
+ await this.backend.upsert(
1408
+ this.collection,
1409
+ group.map((r) => ({
1410
+ id: r.id,
1411
+ vector: Array.from(r.vector),
1412
+ text: r.text ?? "",
1413
+ metadata: r.metadata ?? {}
1414
+ }))
1415
+ );
1416
+ upsertedIds.push(...group.map((r) => r.id));
1417
+ } catch (e) {
1418
+ for (const r of group)
1419
+ errors.push({ id: r.id, error: e.message });
1420
+ }
1421
+ completed += group.length;
1422
+ options?.onProgress?.({ completed, total: records.length });
1423
+ }
1424
+ return {
1425
+ upsertedIds,
1426
+ upsertedCount: upsertedIds.length,
1427
+ errors,
1428
+ durationMs: performance.now() - start
1429
+ };
1430
+ }
1431
+ async query(vector, options) {
1432
+ await this.ensureInitialized();
1433
+ const start = performance.now();
1434
+ const topK = options?.topK ?? 10;
1435
+ const hits = await this.backend.search(
1436
+ this.collection,
1437
+ Array.from(vector),
1438
+ topK,
1439
+ options?.filter ? this.toExpr(options.filter) : void 0
1440
+ );
1441
+ const matches = hits.map((h) => ({
1442
+ id: h.id,
1443
+ text: h.text,
1444
+ score: h.score,
1445
+ metadata: h.metadata
1446
+ })).filter(
1447
+ (m) => options?.minScore === void 0 || m.score >= options.minScore
1448
+ );
1449
+ return {
1450
+ matches,
1451
+ namespace: this.collection,
1452
+ durationMs: performance.now() - start
1453
+ };
1454
+ }
1455
+ async delete(ids, _options) {
1456
+ await this.ensureInitialized();
1457
+ const start = performance.now();
1458
+ const deleted = await this.backend.deleteByIds(this.collection, ids);
1459
+ return {
1460
+ deletedCount: deleted,
1461
+ requestedCount: ids.length,
1462
+ countExact: true,
1463
+ durationMs: performance.now() - start
1464
+ };
1465
+ }
1466
+ async deleteAll(_options) {
1467
+ await this.ensureInitialized();
1468
+ const start = performance.now();
1469
+ const deleted = await this.backend.deleteAll(this.collection);
1470
+ return {
1471
+ deletedCount: deleted,
1472
+ requestedCount: deleted,
1473
+ countExact: true,
1474
+ durationMs: performance.now() - start
1475
+ };
1476
+ }
1477
+ async getStats() {
1478
+ await this.ensureInitialized();
1479
+ return {
1480
+ type: this.storeType,
1481
+ vectorCount: await this.backend.count(this.collection),
1482
+ namespaceCount: 1,
1483
+ dimensions: this.dimensions ?? 0,
1484
+ metric: this.metric,
1485
+ lastUpdated: Date.now()
1486
+ };
1487
+ }
1488
+ async checkHealth() {
1489
+ const start = performance.now();
1490
+ try {
1491
+ await this.ensureInitialized();
1492
+ await this.backend.ping();
1493
+ return {
1494
+ healthy: true,
1495
+ latencyMs: performance.now() - start,
1496
+ lastCheck: Date.now()
1497
+ };
1498
+ } catch (e) {
1499
+ return {
1500
+ healthy: false,
1501
+ latencyMs: performance.now() - start,
1502
+ lastCheck: Date.now(),
1503
+ error: e.message
1504
+ };
1505
+ }
1506
+ }
1507
+ close() {
1508
+ this.initialized = false;
1509
+ return Promise.resolve();
1510
+ }
1511
+ /** Translate a flat equality filter to a Milvus boolean expression. */
1512
+ toExpr(filter) {
1513
+ return Object.entries(filter).map(
1514
+ ([k, v]) => typeof v === "number" ? `metadata["${k}"] == ${v}` : `metadata["${k}"] == "${String(v)}"`
1515
+ ).join(" && ");
1516
+ }
1517
+ };
1518
+ var MilvusSdkBackend = class {
1519
+ constructor(client) {
1520
+ this.client = client;
1521
+ }
1522
+ async ensureCollection(collection, dimensions, metric) {
1523
+ const has = await this.client.hasCollection({
1524
+ collection_name: collection
1525
+ });
1526
+ if (!has.value) {
1527
+ await this.client.createCollection({
1528
+ collection_name: collection,
1529
+ fields: [
1530
+ {
1531
+ name: "id",
1532
+ data_type: "VarChar",
1533
+ is_primary_key: true,
1534
+ max_length: 512
1535
+ },
1536
+ { name: "vector", data_type: "FloatVector", dim: dimensions },
1537
+ { name: "text", data_type: "VarChar", max_length: 65535 },
1538
+ { name: "metadata", data_type: "JSON" }
1539
+ ]
1540
+ });
1541
+ await this.client.createIndex({
1542
+ collection_name: collection,
1543
+ field_name: "vector",
1544
+ index_type: "HNSW",
1545
+ metric_type: metric,
1546
+ params: { M: 16, efConstruction: 200 }
1547
+ });
1548
+ }
1549
+ await this.client.loadCollectionSync({ collection_name: collection });
1550
+ }
1551
+ async upsert(collection, rows) {
1552
+ await this.client.insert({
1553
+ collection_name: collection,
1554
+ data: rows.map((r) => ({
1555
+ id: r.id,
1556
+ vector: r.vector,
1557
+ text: r.text,
1558
+ metadata: r.metadata
1559
+ }))
1560
+ });
1561
+ }
1562
+ async search(collection, vector, limit, filter) {
1563
+ const res = await this.client.search({
1564
+ collection_name: collection,
1565
+ data: [vector],
1566
+ limit,
1567
+ filter,
1568
+ output_fields: ["text", "metadata"]
1569
+ });
1570
+ return res.results.map((r) => ({
1571
+ id: String(r.id),
1572
+ score: Number(r.score),
1573
+ text: r.text ?? "",
1574
+ metadata: r.metadata ?? {}
1575
+ }));
1576
+ }
1577
+ async deleteByIds(collection, ids) {
1578
+ const list = ids.map((id) => `"${id}"`).join(", ");
1579
+ await this.client.deleteEntities({
1580
+ collection_name: collection,
1581
+ expr: `id in [${list}]`
1582
+ });
1583
+ return ids.length;
1584
+ }
1585
+ async deleteAll(collection) {
1586
+ const count = await this.count(collection);
1587
+ await this.client.deleteEntities({
1588
+ collection_name: collection,
1589
+ expr: 'id != ""'
1590
+ });
1591
+ return count;
1592
+ }
1593
+ async count(collection) {
1594
+ const stats = await this.client.getCollectionStatistics({
1595
+ collection_name: collection
1596
+ });
1597
+ return Number(stats.data?.row_count ?? 0);
1598
+ }
1599
+ async ping() {
1600
+ await this.client.hasCollection({ collection_name: "__ping__" });
1601
+ }
1602
+ };
1603
+ function createMilvusStore(config) {
1604
+ return new MilvusStore(config);
1605
+ }
1606
+
1607
+ // src/stores/index.ts
1608
+ function createStore(type, config) {
1609
+ switch (type) {
1610
+ case "memory":
1611
+ return new MemoryStore(config);
1612
+ case "pinecone":
1613
+ return new PineconeStore(config);
1614
+ case "chroma":
1615
+ return new ChromaStore(config);
1616
+ case "qdrant":
1617
+ return new QdrantStore(config);
1618
+ case "weaviate":
1619
+ return new WeaviateStore(config);
1620
+ case "milvus":
1621
+ return new MilvusStore(config);
1622
+ case "pgvector":
1623
+ return new PgVectorStore(config);
1624
+ default:
1625
+ throw new Error(
1626
+ `Unknown vector store type "${String(type)}". Supported stores: memory, pinecone, chroma, qdrant, weaviate, milvus, pgvector.`
1627
+ );
1628
+ }
1629
+ }
1630
+
1631
+ export {
1632
+ BaseStore,
1633
+ MemoryStore,
1634
+ createMemoryStore,
1635
+ PineconeStore,
1636
+ createPineconeStore,
1637
+ ChromaStore,
1638
+ createChromaStore,
1639
+ QdrantStore,
1640
+ createQdrantStore,
1641
+ PgVectorStore,
1642
+ createPgVectorStore,
1643
+ WeaviateStore,
1644
+ WeaviateSdkBackend,
1645
+ createWeaviateStore,
1646
+ MilvusStore,
1647
+ MilvusSdkBackend,
1648
+ createMilvusStore,
1649
+ createStore
1650
+ };