@lov3kaizen/agentsea-embeddings 1.0.0 → 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.
package/dist/index.js CHANGED
@@ -1500,20 +1500,33 @@ function createVoyageProvider(config) {
1500
1500
  return new VoyageProvider(config);
1501
1501
  }
1502
1502
 
1503
+ // src/core/optional-import.ts
1504
+ function importOptional(name) {
1505
+ return import(
1506
+ /* @vite-ignore */
1507
+ /* webpackIgnore: true */
1508
+ name
1509
+ );
1510
+ }
1511
+
1503
1512
  // src/providers/LocalProvider.ts
1504
1513
  var LocalProvider = class extends BaseProvider {
1505
1514
  modelInfo;
1506
1515
  embedFn = null;
1507
1516
  normalize;
1508
1517
  batchSize;
1518
+ modelPath;
1519
+ /** Lazily-built ONNX extractor (only when loading from `modelPath`). */
1520
+ extractorPromise;
1509
1521
  constructor(config) {
1510
1522
  super({ ...config, type: "local" });
1511
1523
  if (!config.embedFn && !config.modelPath) {
1512
1524
  throw new Error(
1513
- "Either embedFn or modelPath is required for local provider"
1525
+ "`embedFn` or `modelPath` (ONNX) is required for the local provider"
1514
1526
  );
1515
1527
  }
1516
1528
  this.embedFn = config.embedFn ?? null;
1529
+ this.modelPath = config.modelPath;
1517
1530
  this.normalize = config.normalize ?? true;
1518
1531
  this.batchSize = config.batchSize ?? 32;
1519
1532
  this.modelInfo = {
@@ -1527,10 +1540,43 @@ var LocalProvider = class extends BaseProvider {
1527
1540
  description: "Local embedding model"
1528
1541
  };
1529
1542
  }
1543
+ /**
1544
+ * Lazily load the ONNX feature-extraction pipeline from `modelPath` via
1545
+ * Transformers.js and adapt it into a {@link LocalEmbeddingFn}.
1546
+ */
1547
+ getOnnxEmbedFn() {
1548
+ if (!this.extractorPromise) {
1549
+ this.extractorPromise = (async () => {
1550
+ let mod;
1551
+ try {
1552
+ mod = await importOptional("@xenova/transformers");
1553
+ } catch {
1554
+ throw new Error(
1555
+ 'Loading local ONNX models from `modelPath` requires the "@xenova/transformers" package. Install it, or pass an `embedFn`.'
1556
+ );
1557
+ }
1558
+ const transformers = mod;
1559
+ return transformers.pipeline("feature-extraction", this.modelPath);
1560
+ })();
1561
+ }
1562
+ return this.extractorPromise;
1563
+ }
1530
1564
  get info() {
1531
1565
  return this.modelInfo;
1532
1566
  }
1533
1567
  async doEmbed(texts, options) {
1568
+ if (!this.embedFn && this.modelPath) {
1569
+ const extractor = await this.getOnnxEmbedFn();
1570
+ this.embedFn = async (input) => {
1571
+ const output = await extractor(input, {
1572
+ pooling: "mean",
1573
+ normalize: false
1574
+ // we normalize below if configured
1575
+ });
1576
+ const list = output.tolist();
1577
+ return Array.isArray(list[0]) ? list : [list];
1578
+ };
1579
+ }
1534
1580
  if (!this.embedFn) {
1535
1581
  throw new Error("No embedding function configured");
1536
1582
  }
@@ -3433,7 +3479,13 @@ var SQLiteCache = class extends BaseCache {
3433
3479
  `);
3434
3480
  updateStmt.run(Date.now(), key);
3435
3481
  const vectorBuffer = row.vector;
3436
- const vector = Array.from(new Float32Array(vectorBuffer.buffer));
3482
+ const vector = Array.from(
3483
+ new Float32Array(
3484
+ vectorBuffer.buffer,
3485
+ vectorBuffer.byteOffset,
3486
+ vectorBuffer.byteLength / Float32Array.BYTES_PER_ELEMENT
3487
+ )
3488
+ );
3437
3489
  return {
3438
3490
  key: row.key,
3439
3491
  vector,
@@ -3984,6 +4036,8 @@ var MemoryStore = class extends BaseStore {
3984
4036
  }
3985
4037
  return Promise.resolve({
3986
4038
  deletedCount,
4039
+ requestedCount: ids.length,
4040
+ countExact: true,
3987
4041
  durationMs: performance.now() - startTime
3988
4042
  });
3989
4043
  }
@@ -3996,6 +4050,8 @@ var MemoryStore = class extends BaseStore {
3996
4050
  this.namespaces.clear();
3997
4051
  return Promise.resolve({
3998
4052
  deletedCount: count2,
4053
+ requestedCount: count2,
4054
+ countExact: true,
3999
4055
  durationMs: performance.now() - startTime
4000
4056
  });
4001
4057
  }
@@ -4003,6 +4059,8 @@ var MemoryStore = class extends BaseStore {
4003
4059
  if (!nsIds) {
4004
4060
  return Promise.resolve({
4005
4061
  deletedCount: 0,
4062
+ requestedCount: 0,
4063
+ countExact: true,
4006
4064
  durationMs: performance.now() - startTime
4007
4065
  });
4008
4066
  }
@@ -4013,6 +4071,8 @@ var MemoryStore = class extends BaseStore {
4013
4071
  this.namespaces.delete(namespace);
4014
4072
  return Promise.resolve({
4015
4073
  deletedCount: count,
4074
+ requestedCount: count,
4075
+ countExact: true,
4016
4076
  durationMs: performance.now() - startTime
4017
4077
  });
4018
4078
  }
@@ -4207,6 +4267,8 @@ var PineconeStore = class extends BaseStore {
4207
4267
  );
4208
4268
  return {
4209
4269
  deletedCount: ids.length,
4270
+ requestedCount: ids.length,
4271
+ countExact: false,
4210
4272
  durationMs: performance.now() - startTime
4211
4273
  };
4212
4274
  }
@@ -4214,13 +4276,15 @@ var PineconeStore = class extends BaseStore {
4214
4276
  await this.ensureInitialized();
4215
4277
  const startTime = performance.now();
4216
4278
  const namespace = options?.namespace ?? this.namespace;
4279
+ const before = await this.getStats().catch(() => void 0);
4217
4280
  const ns = this.index.namespace(
4218
4281
  namespace
4219
4282
  );
4220
4283
  await ns.deleteAll();
4221
4284
  return {
4222
- deletedCount: -1,
4223
- // Unknown count
4285
+ deletedCount: before?.vectorCount ?? 0,
4286
+ requestedCount: before?.vectorCount,
4287
+ countExact: before !== void 0,
4224
4288
  durationMs: performance.now() - startTime
4225
4289
  };
4226
4290
  }
@@ -4381,12 +4445,15 @@ var ChromaStore = class extends BaseStore {
4381
4445
  await this.collection.delete({ ids });
4382
4446
  return {
4383
4447
  deletedCount: ids.length,
4448
+ requestedCount: ids.length,
4449
+ countExact: false,
4384
4450
  durationMs: performance.now() - startTime
4385
4451
  };
4386
4452
  }
4387
4453
  async deleteAll(_options) {
4388
4454
  await this.ensureInitialized();
4389
4455
  const startTime = performance.now();
4456
+ const before = await this.getStats().catch(() => void 0);
4390
4457
  await this.client.deleteCollection({ name: this.collectionName });
4391
4458
  this.collection = await this.client.createCollection({
4392
4459
  name: this.collectionName,
@@ -4395,7 +4462,9 @@ var ChromaStore = class extends BaseStore {
4395
4462
  }
4396
4463
  });
4397
4464
  return {
4398
- deletedCount: -1,
4465
+ deletedCount: before?.vectorCount ?? 0,
4466
+ requestedCount: before?.vectorCount,
4467
+ countExact: before !== void 0,
4399
4468
  durationMs: performance.now() - startTime
4400
4469
  };
4401
4470
  }
@@ -4585,12 +4654,15 @@ var QdrantStore = class extends BaseStore {
4585
4654
  });
4586
4655
  return {
4587
4656
  deletedCount: ids.length,
4657
+ requestedCount: ids.length,
4658
+ countExact: false,
4588
4659
  durationMs: performance.now() - startTime
4589
4660
  };
4590
4661
  }
4591
4662
  async deleteAll(_options) {
4592
4663
  await this.ensureInitialized();
4593
4664
  const startTime = performance.now();
4665
+ const before = await this.getStats().catch(() => void 0);
4594
4666
  await this.client.deleteCollection(this.collectionName);
4595
4667
  if (this.dimensions) {
4596
4668
  await this.client.createCollection(this.collectionName, {
@@ -4601,7 +4673,9 @@ var QdrantStore = class extends BaseStore {
4601
4673
  });
4602
4674
  }
4603
4675
  return {
4604
- deletedCount: -1,
4676
+ deletedCount: before?.vectorCount ?? 0,
4677
+ requestedCount: before?.vectorCount,
4678
+ countExact: before !== void 0,
4605
4679
  durationMs: performance.now() - startTime
4606
4680
  };
4607
4681
  }
@@ -4645,6 +4719,729 @@ function createQdrantStore(config) {
4645
4719
  return new QdrantStore(config);
4646
4720
  }
4647
4721
 
4722
+ // src/stores/PgVectorStore.ts
4723
+ var PgVectorStore = class extends BaseStore {
4724
+ storeType = "pgvector";
4725
+ pool;
4726
+ injectedPool;
4727
+ table;
4728
+ vectorColumn;
4729
+ contentColumn;
4730
+ metadataColumn;
4731
+ pgConfig;
4732
+ initialized = false;
4733
+ constructor(config) {
4734
+ super(config);
4735
+ if (!config.tableName) {
4736
+ throw new Error("pgvector store requires a `tableName`");
4737
+ }
4738
+ this.pgConfig = config;
4739
+ this.injectedPool = config.pool;
4740
+ this.table = config.tableName;
4741
+ this.vectorColumn = config.vectorColumn ?? "embedding";
4742
+ this.contentColumn = config.contentColumn ?? "content";
4743
+ this.metadataColumn = config.metadataColumn ?? "metadata";
4744
+ }
4745
+ /** The pgvector distance operator for the configured metric. */
4746
+ get distanceOperator() {
4747
+ switch (this.metric) {
4748
+ case "euclidean":
4749
+ return "<->";
4750
+ case "dot_product":
4751
+ return "<#>";
4752
+ case "cosine":
4753
+ default:
4754
+ return "<=>";
4755
+ }
4756
+ }
4757
+ async init() {
4758
+ if (this.initialized) return;
4759
+ if (this.injectedPool) {
4760
+ this.pool = this.injectedPool;
4761
+ } else {
4762
+ let mod;
4763
+ try {
4764
+ mod = await importOptional("pg");
4765
+ } catch {
4766
+ throw new Error(
4767
+ 'pgvector store requires the "pg" package. Install it, or pass a pre-built `pool` to the store.'
4768
+ );
4769
+ }
4770
+ const pg = mod.default ?? mod;
4771
+ const Pool = pg.Pool;
4772
+ this.pool = new Pool(
4773
+ this.pgConfig.connectionString ? { connectionString: this.pgConfig.connectionString } : {
4774
+ host: this.pgConfig.host,
4775
+ port: this.pgConfig.port,
4776
+ database: this.pgConfig.database,
4777
+ user: this.pgConfig.user,
4778
+ password: this.pgConfig.password
4779
+ }
4780
+ );
4781
+ }
4782
+ await this.pool.query("CREATE EXTENSION IF NOT EXISTS vector");
4783
+ const dims = this.dimensions;
4784
+ const vectorType = dims ? `vector(${dims})` : "vector";
4785
+ await this.pool.query(
4786
+ `CREATE TABLE IF NOT EXISTS ${this.ident(this.table)} (
4787
+ id text PRIMARY KEY,
4788
+ ${this.ident(this.contentColumn)} text,
4789
+ ${this.ident(this.metadataColumn)} jsonb,
4790
+ ${this.ident(this.vectorColumn)} ${vectorType}
4791
+ )`
4792
+ );
4793
+ this.initialized = true;
4794
+ }
4795
+ async ensureInitialized() {
4796
+ if (!this.initialized) await this.init();
4797
+ }
4798
+ /** Quote an SQL identifier to guard against injection via config names. */
4799
+ ident(name) {
4800
+ return `"${name.replace(/"/g, '""')}"`;
4801
+ }
4802
+ toVectorLiteral(vector) {
4803
+ return `[${Array.from(vector).join(",")}]`;
4804
+ }
4805
+ async upsert(records, options) {
4806
+ await this.ensureInitialized();
4807
+ const start = performance.now();
4808
+ const batchSize = options?.batchSize ?? 100;
4809
+ const upsertedIds = [];
4810
+ const errors = [];
4811
+ let completed = 0;
4812
+ for (const group of batch(records, batchSize)) {
4813
+ for (const record of group) {
4814
+ try {
4815
+ await this.pool.query(
4816
+ `INSERT INTO ${this.ident(this.table)} (id, ${this.ident(this.contentColumn)}, ${this.ident(this.metadataColumn)}, ${this.ident(this.vectorColumn)})
4817
+ VALUES ($1, $2, $3, $4)
4818
+ ON CONFLICT (id) DO UPDATE SET
4819
+ ${this.ident(this.contentColumn)} = EXCLUDED.${this.ident(this.contentColumn)},
4820
+ ${this.ident(this.metadataColumn)} = EXCLUDED.${this.ident(this.metadataColumn)},
4821
+ ${this.ident(this.vectorColumn)} = EXCLUDED.${this.ident(this.vectorColumn)}`,
4822
+ [
4823
+ record.id,
4824
+ record.text ?? null,
4825
+ JSON.stringify(record.metadata ?? {}),
4826
+ this.toVectorLiteral(record.vector)
4827
+ ]
4828
+ );
4829
+ upsertedIds.push(record.id);
4830
+ } catch (e) {
4831
+ errors.push({ id: record.id, error: e.message });
4832
+ }
4833
+ }
4834
+ completed += group.length;
4835
+ options?.onProgress?.({ completed, total: records.length });
4836
+ }
4837
+ return {
4838
+ upsertedIds,
4839
+ upsertedCount: upsertedIds.length,
4840
+ errors,
4841
+ durationMs: performance.now() - start
4842
+ };
4843
+ }
4844
+ async query(vector, options) {
4845
+ await this.ensureInitialized();
4846
+ const start = performance.now();
4847
+ const topK = options?.topK ?? 10;
4848
+ const params = [this.toVectorLiteral(vector)];
4849
+ let where = "";
4850
+ if (options?.filter && Object.keys(options.filter).length > 0) {
4851
+ params.push(JSON.stringify(options.filter));
4852
+ where = `WHERE ${this.ident(this.metadataColumn)} @> $${params.length}::jsonb`;
4853
+ }
4854
+ params.push(topK);
4855
+ const op = this.distanceOperator;
4856
+ 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}`;
4857
+ const result = await this.pool.query(sql, params);
4858
+ const matches = result.rows.map((row) => {
4859
+ const distance = Number(row.distance);
4860
+ const score = this.metric === "cosine" ? 1 - distance : 1 / (1 + distance);
4861
+ return {
4862
+ id: String(row.id),
4863
+ text: row.content ?? "",
4864
+ score,
4865
+ distance,
4866
+ metadata: row.metadata ?? {}
4867
+ };
4868
+ }).filter(
4869
+ (m) => options?.minScore === void 0 || m.score >= options.minScore
4870
+ );
4871
+ return {
4872
+ matches,
4873
+ namespace: this.namespace,
4874
+ durationMs: performance.now() - start
4875
+ };
4876
+ }
4877
+ async delete(ids, _options) {
4878
+ await this.ensureInitialized();
4879
+ const start = performance.now();
4880
+ const result = await this.pool.query(
4881
+ `DELETE FROM ${this.ident(this.table)} WHERE id = ANY($1)`,
4882
+ [ids]
4883
+ );
4884
+ const deleted = result.rowCount ?? ids.length;
4885
+ return {
4886
+ deletedCount: deleted,
4887
+ requestedCount: ids.length,
4888
+ countExact: result.rowCount !== null,
4889
+ durationMs: performance.now() - start
4890
+ };
4891
+ }
4892
+ async deleteAll(_options) {
4893
+ await this.ensureInitialized();
4894
+ const start = performance.now();
4895
+ const result = await this.pool.query(
4896
+ `DELETE FROM ${this.ident(this.table)}`
4897
+ );
4898
+ return {
4899
+ deletedCount: result.rowCount ?? 0,
4900
+ requestedCount: result.rowCount ?? void 0,
4901
+ countExact: result.rowCount !== null,
4902
+ durationMs: performance.now() - start
4903
+ };
4904
+ }
4905
+ async getStats() {
4906
+ await this.ensureInitialized();
4907
+ const result = await this.pool.query(
4908
+ `SELECT COUNT(*)::int AS count FROM ${this.ident(this.table)}`
4909
+ );
4910
+ const vectorCount = Number(result.rows[0]?.count ?? 0);
4911
+ return {
4912
+ type: this.storeType,
4913
+ vectorCount,
4914
+ namespaceCount: 1,
4915
+ dimensions: this.dimensions ?? 0,
4916
+ metric: this.metric,
4917
+ lastUpdated: Date.now()
4918
+ };
4919
+ }
4920
+ async checkHealth() {
4921
+ const start = performance.now();
4922
+ try {
4923
+ await this.ensureInitialized();
4924
+ await this.pool.query("SELECT 1");
4925
+ return {
4926
+ healthy: true,
4927
+ latencyMs: performance.now() - start,
4928
+ lastCheck: Date.now()
4929
+ };
4930
+ } catch (e) {
4931
+ return {
4932
+ healthy: false,
4933
+ latencyMs: performance.now() - start,
4934
+ lastCheck: Date.now(),
4935
+ error: e.message
4936
+ };
4937
+ }
4938
+ }
4939
+ async close() {
4940
+ if (this.pool && !this.injectedPool) {
4941
+ await this.pool.end();
4942
+ }
4943
+ this.initialized = false;
4944
+ }
4945
+ };
4946
+
4947
+ // src/stores/WeaviateStore.ts
4948
+ var WeaviateStore = class extends BaseStore {
4949
+ storeType = "weaviate";
4950
+ backend;
4951
+ injectedBackend;
4952
+ className;
4953
+ url;
4954
+ apiKey;
4955
+ initialized = false;
4956
+ constructor(config) {
4957
+ super(config);
4958
+ if (!config.url) throw new Error("Weaviate store requires a `url`");
4959
+ if (!config.className) {
4960
+ throw new Error("Weaviate store requires a `className`");
4961
+ }
4962
+ this.url = config.url;
4963
+ this.className = config.className;
4964
+ this.apiKey = config.apiKey;
4965
+ this.injectedBackend = config.backend;
4966
+ }
4967
+ async init() {
4968
+ if (this.initialized) return;
4969
+ this.backend = this.injectedBackend ?? await this.buildSdkBackend();
4970
+ await this.backend.ensureClass(this.className, this.dimensions);
4971
+ this.initialized = true;
4972
+ }
4973
+ async ensureInitialized() {
4974
+ if (!this.initialized) await this.init();
4975
+ }
4976
+ /** Build a {@link WeaviateBackend} backed by the real `weaviate-ts-client`. */
4977
+ async buildSdkBackend() {
4978
+ let mod;
4979
+ try {
4980
+ mod = await importOptional("weaviate-ts-client");
4981
+ } catch {
4982
+ throw new Error(
4983
+ 'Weaviate store requires the "weaviate-ts-client" package. Install it, or pass a custom `backend`.'
4984
+ );
4985
+ }
4986
+ const weaviate = mod.default ?? mod;
4987
+ const u = new URL(this.url);
4988
+ const client = weaviate.client({
4989
+ scheme: u.protocol.replace(":", ""),
4990
+ host: u.host,
4991
+ apiKey: this.apiKey && weaviate.ApiKey ? new weaviate.ApiKey(this.apiKey) : void 0
4992
+ });
4993
+ return new WeaviateSdkBackend(client);
4994
+ }
4995
+ async upsert(records, options) {
4996
+ await this.ensureInitialized();
4997
+ const start = performance.now();
4998
+ const batchSize = options?.batchSize ?? 100;
4999
+ const upsertedIds = [];
5000
+ const errors = [];
5001
+ let completed = 0;
5002
+ for (const group of batch(records, batchSize)) {
5003
+ try {
5004
+ await this.backend.upsert(
5005
+ this.className,
5006
+ group.map((r) => ({
5007
+ id: r.id,
5008
+ vector: Array.from(r.vector),
5009
+ properties: { ...r.metadata ?? {}, text: r.text ?? "" }
5010
+ }))
5011
+ );
5012
+ upsertedIds.push(...group.map((r) => r.id));
5013
+ } catch (e) {
5014
+ for (const r of group)
5015
+ errors.push({ id: r.id, error: e.message });
5016
+ }
5017
+ completed += group.length;
5018
+ options?.onProgress?.({ completed, total: records.length });
5019
+ }
5020
+ return {
5021
+ upsertedIds,
5022
+ upsertedCount: upsertedIds.length,
5023
+ errors,
5024
+ durationMs: performance.now() - start
5025
+ };
5026
+ }
5027
+ async query(vector, options) {
5028
+ await this.ensureInitialized();
5029
+ const start = performance.now();
5030
+ const topK = options?.topK ?? 10;
5031
+ const hits = await this.backend.nearVector(
5032
+ this.className,
5033
+ Array.from(vector),
5034
+ topK,
5035
+ options?.filter
5036
+ );
5037
+ const matches = hits.map((h) => ({
5038
+ id: h.id,
5039
+ text: h.properties.text ?? "",
5040
+ score: h.score,
5041
+ metadata: h.properties
5042
+ })).filter(
5043
+ (m) => options?.minScore === void 0 || m.score >= options.minScore
5044
+ );
5045
+ return {
5046
+ matches,
5047
+ namespace: this.className,
5048
+ durationMs: performance.now() - start
5049
+ };
5050
+ }
5051
+ async delete(ids, _options) {
5052
+ await this.ensureInitialized();
5053
+ const start = performance.now();
5054
+ const deleted = await this.backend.deleteByIds(this.className, ids);
5055
+ return {
5056
+ deletedCount: deleted,
5057
+ requestedCount: ids.length,
5058
+ countExact: true,
5059
+ durationMs: performance.now() - start
5060
+ };
5061
+ }
5062
+ async deleteAll(_options) {
5063
+ await this.ensureInitialized();
5064
+ const start = performance.now();
5065
+ const deleted = await this.backend.deleteAll(this.className);
5066
+ return {
5067
+ deletedCount: deleted,
5068
+ requestedCount: deleted,
5069
+ countExact: true,
5070
+ durationMs: performance.now() - start
5071
+ };
5072
+ }
5073
+ async getStats() {
5074
+ await this.ensureInitialized();
5075
+ return {
5076
+ type: this.storeType,
5077
+ vectorCount: await this.backend.count(this.className),
5078
+ namespaceCount: 1,
5079
+ dimensions: this.dimensions ?? 0,
5080
+ metric: this.metric,
5081
+ lastUpdated: Date.now()
5082
+ };
5083
+ }
5084
+ async checkHealth() {
5085
+ const start = performance.now();
5086
+ try {
5087
+ await this.ensureInitialized();
5088
+ await this.backend.ping();
5089
+ return {
5090
+ healthy: true,
5091
+ latencyMs: performance.now() - start,
5092
+ lastCheck: Date.now()
5093
+ };
5094
+ } catch (e) {
5095
+ return {
5096
+ healthy: false,
5097
+ latencyMs: performance.now() - start,
5098
+ lastCheck: Date.now(),
5099
+ error: e.message
5100
+ };
5101
+ }
5102
+ }
5103
+ close() {
5104
+ this.initialized = false;
5105
+ return Promise.resolve();
5106
+ }
5107
+ };
5108
+ var WeaviateSdkBackend = class {
5109
+ constructor(client) {
5110
+ this.client = client;
5111
+ }
5112
+ async ensureClass(className, _dimensions) {
5113
+ const schema = await this.client.schema.getter().do();
5114
+ if (schema.classes?.some((c) => c.class === className)) return;
5115
+ await this.client.schema.classCreator().withClass({ class: className, vectorizer: "none" }).do();
5116
+ }
5117
+ async upsert(className, objects) {
5118
+ let batcher = this.client.batch.objectsBatcher();
5119
+ for (const o of objects) {
5120
+ batcher = batcher.withObject({
5121
+ class: className,
5122
+ id: o.id,
5123
+ vector: o.vector,
5124
+ properties: o.properties
5125
+ });
5126
+ }
5127
+ await batcher.do();
5128
+ }
5129
+ async nearVector(className, vector, limit, filter) {
5130
+ let q = this.client.graphql.get().withClassName(className).withFields("_additional { id certainty } text").withNearVector({ vector }).withLimit(limit);
5131
+ if (filter) q = q.withWhere(this.toWhere(filter));
5132
+ const res = await q.do();
5133
+ const rows = res.data?.Get?.[className] ?? [];
5134
+ return rows.map((row) => {
5135
+ const additional = row._additional;
5136
+ const { _additional, ...properties } = row;
5137
+ void _additional;
5138
+ return {
5139
+ id: additional.id,
5140
+ score: additional.certainty ?? 0,
5141
+ properties
5142
+ };
5143
+ });
5144
+ }
5145
+ async deleteByIds(className, ids) {
5146
+ let n = 0;
5147
+ for (const id of ids) {
5148
+ await this.client.data.deleter().withClassName(className).withId(id).do();
5149
+ n++;
5150
+ }
5151
+ return n;
5152
+ }
5153
+ async deleteAll(className) {
5154
+ const count = await this.count(className);
5155
+ await this.client.schema.classCreator().withClass({ class: className, vectorizer: "none" }).do().catch(() => void 0);
5156
+ return count;
5157
+ }
5158
+ async count(className) {
5159
+ const res = await this.client.graphql.aggregate().withClassName(className).withFields("meta { count }").do();
5160
+ const agg = res.data?.Aggregate?.[className] ?? [];
5161
+ return agg[0]?.meta?.count ?? 0;
5162
+ }
5163
+ async ping() {
5164
+ await this.client.misc.liveChecker().do();
5165
+ }
5166
+ toWhere(filter) {
5167
+ const operands = Object.entries(filter).map(([path, value]) => ({
5168
+ path: [path],
5169
+ operator: "Equal",
5170
+ ...typeof value === "number" ? { valueNumber: value } : typeof value === "boolean" ? { valueBoolean: value } : { valueText: String(value) }
5171
+ }));
5172
+ return operands.length === 1 ? operands[0] : { operator: "And", operands };
5173
+ }
5174
+ };
5175
+
5176
+ // src/stores/MilvusStore.ts
5177
+ var MilvusStore = class extends BaseStore {
5178
+ storeType = "milvus";
5179
+ backend;
5180
+ injectedBackend;
5181
+ collection;
5182
+ milvusConfig;
5183
+ initialized = false;
5184
+ constructor(config) {
5185
+ super(config);
5186
+ if (!config.url) throw new Error("Milvus store requires a `url`");
5187
+ if (!config.collectionName) {
5188
+ throw new Error("Milvus store requires a `collectionName`");
5189
+ }
5190
+ if (!config.dimensions) {
5191
+ throw new Error("Milvus store requires `dimensions`");
5192
+ }
5193
+ this.milvusConfig = config;
5194
+ this.collection = config.collectionName;
5195
+ this.injectedBackend = config.backend;
5196
+ }
5197
+ /** Milvus metric type string for the configured distance metric. */
5198
+ get metricType() {
5199
+ switch (this.metric) {
5200
+ case "euclidean":
5201
+ return "L2";
5202
+ case "dot_product":
5203
+ return "IP";
5204
+ case "cosine":
5205
+ default:
5206
+ return "COSINE";
5207
+ }
5208
+ }
5209
+ async init() {
5210
+ if (this.initialized) return;
5211
+ this.backend = this.injectedBackend ?? await this.buildSdkBackend();
5212
+ await this.backend.ensureCollection(
5213
+ this.collection,
5214
+ this.dimensions,
5215
+ this.metricType
5216
+ );
5217
+ this.initialized = true;
5218
+ }
5219
+ async ensureInitialized() {
5220
+ if (!this.initialized) await this.init();
5221
+ }
5222
+ async buildSdkBackend() {
5223
+ let mod;
5224
+ try {
5225
+ mod = await importOptional("@zilliz/milvus2-sdk-node");
5226
+ } catch {
5227
+ throw new Error(
5228
+ 'Milvus store requires the "@zilliz/milvus2-sdk-node" package. Install it, or pass a custom `backend`.'
5229
+ );
5230
+ }
5231
+ const sdk = mod;
5232
+ const client = new sdk.MilvusClient({
5233
+ address: this.milvusConfig.url,
5234
+ username: this.milvusConfig.username,
5235
+ password: this.milvusConfig.password
5236
+ });
5237
+ return new MilvusSdkBackend(client);
5238
+ }
5239
+ async upsert(records, options) {
5240
+ await this.ensureInitialized();
5241
+ const start = performance.now();
5242
+ const batchSize = options?.batchSize ?? 100;
5243
+ const upsertedIds = [];
5244
+ const errors = [];
5245
+ let completed = 0;
5246
+ for (const group of batch(records, batchSize)) {
5247
+ try {
5248
+ await this.backend.upsert(
5249
+ this.collection,
5250
+ group.map((r) => ({
5251
+ id: r.id,
5252
+ vector: Array.from(r.vector),
5253
+ text: r.text ?? "",
5254
+ metadata: r.metadata ?? {}
5255
+ }))
5256
+ );
5257
+ upsertedIds.push(...group.map((r) => r.id));
5258
+ } catch (e) {
5259
+ for (const r of group)
5260
+ errors.push({ id: r.id, error: e.message });
5261
+ }
5262
+ completed += group.length;
5263
+ options?.onProgress?.({ completed, total: records.length });
5264
+ }
5265
+ return {
5266
+ upsertedIds,
5267
+ upsertedCount: upsertedIds.length,
5268
+ errors,
5269
+ durationMs: performance.now() - start
5270
+ };
5271
+ }
5272
+ async query(vector, options) {
5273
+ await this.ensureInitialized();
5274
+ const start = performance.now();
5275
+ const topK = options?.topK ?? 10;
5276
+ const hits = await this.backend.search(
5277
+ this.collection,
5278
+ Array.from(vector),
5279
+ topK,
5280
+ options?.filter ? this.toExpr(options.filter) : void 0
5281
+ );
5282
+ const matches = hits.map((h) => ({
5283
+ id: h.id,
5284
+ text: h.text,
5285
+ score: h.score,
5286
+ metadata: h.metadata
5287
+ })).filter(
5288
+ (m) => options?.minScore === void 0 || m.score >= options.minScore
5289
+ );
5290
+ return {
5291
+ matches,
5292
+ namespace: this.collection,
5293
+ durationMs: performance.now() - start
5294
+ };
5295
+ }
5296
+ async delete(ids, _options) {
5297
+ await this.ensureInitialized();
5298
+ const start = performance.now();
5299
+ const deleted = await this.backend.deleteByIds(this.collection, ids);
5300
+ return {
5301
+ deletedCount: deleted,
5302
+ requestedCount: ids.length,
5303
+ countExact: true,
5304
+ durationMs: performance.now() - start
5305
+ };
5306
+ }
5307
+ async deleteAll(_options) {
5308
+ await this.ensureInitialized();
5309
+ const start = performance.now();
5310
+ const deleted = await this.backend.deleteAll(this.collection);
5311
+ return {
5312
+ deletedCount: deleted,
5313
+ requestedCount: deleted,
5314
+ countExact: true,
5315
+ durationMs: performance.now() - start
5316
+ };
5317
+ }
5318
+ async getStats() {
5319
+ await this.ensureInitialized();
5320
+ return {
5321
+ type: this.storeType,
5322
+ vectorCount: await this.backend.count(this.collection),
5323
+ namespaceCount: 1,
5324
+ dimensions: this.dimensions ?? 0,
5325
+ metric: this.metric,
5326
+ lastUpdated: Date.now()
5327
+ };
5328
+ }
5329
+ async checkHealth() {
5330
+ const start = performance.now();
5331
+ try {
5332
+ await this.ensureInitialized();
5333
+ await this.backend.ping();
5334
+ return {
5335
+ healthy: true,
5336
+ latencyMs: performance.now() - start,
5337
+ lastCheck: Date.now()
5338
+ };
5339
+ } catch (e) {
5340
+ return {
5341
+ healthy: false,
5342
+ latencyMs: performance.now() - start,
5343
+ lastCheck: Date.now(),
5344
+ error: e.message
5345
+ };
5346
+ }
5347
+ }
5348
+ close() {
5349
+ this.initialized = false;
5350
+ return Promise.resolve();
5351
+ }
5352
+ /** Translate a flat equality filter to a Milvus boolean expression. */
5353
+ toExpr(filter) {
5354
+ return Object.entries(filter).map(
5355
+ ([k, v]) => typeof v === "number" ? `metadata["${k}"] == ${v}` : `metadata["${k}"] == "${String(v)}"`
5356
+ ).join(" && ");
5357
+ }
5358
+ };
5359
+ var MilvusSdkBackend = class {
5360
+ constructor(client) {
5361
+ this.client = client;
5362
+ }
5363
+ async ensureCollection(collection, dimensions, metric) {
5364
+ const has = await this.client.hasCollection({
5365
+ collection_name: collection
5366
+ });
5367
+ if (!has.value) {
5368
+ await this.client.createCollection({
5369
+ collection_name: collection,
5370
+ fields: [
5371
+ {
5372
+ name: "id",
5373
+ data_type: "VarChar",
5374
+ is_primary_key: true,
5375
+ max_length: 512
5376
+ },
5377
+ { name: "vector", data_type: "FloatVector", dim: dimensions },
5378
+ { name: "text", data_type: "VarChar", max_length: 65535 },
5379
+ { name: "metadata", data_type: "JSON" }
5380
+ ]
5381
+ });
5382
+ await this.client.createIndex({
5383
+ collection_name: collection,
5384
+ field_name: "vector",
5385
+ index_type: "HNSW",
5386
+ metric_type: metric,
5387
+ params: { M: 16, efConstruction: 200 }
5388
+ });
5389
+ }
5390
+ await this.client.loadCollectionSync({ collection_name: collection });
5391
+ }
5392
+ async upsert(collection, rows) {
5393
+ await this.client.insert({
5394
+ collection_name: collection,
5395
+ data: rows.map((r) => ({
5396
+ id: r.id,
5397
+ vector: r.vector,
5398
+ text: r.text,
5399
+ metadata: r.metadata
5400
+ }))
5401
+ });
5402
+ }
5403
+ async search(collection, vector, limit, filter) {
5404
+ const res = await this.client.search({
5405
+ collection_name: collection,
5406
+ data: [vector],
5407
+ limit,
5408
+ filter,
5409
+ output_fields: ["text", "metadata"]
5410
+ });
5411
+ return res.results.map((r) => ({
5412
+ id: String(r.id),
5413
+ score: Number(r.score),
5414
+ text: r.text ?? "",
5415
+ metadata: r.metadata ?? {}
5416
+ }));
5417
+ }
5418
+ async deleteByIds(collection, ids) {
5419
+ const list = ids.map((id) => `"${id}"`).join(", ");
5420
+ await this.client.deleteEntities({
5421
+ collection_name: collection,
5422
+ expr: `id in [${list}]`
5423
+ });
5424
+ return ids.length;
5425
+ }
5426
+ async deleteAll(collection) {
5427
+ const count = await this.count(collection);
5428
+ await this.client.deleteEntities({
5429
+ collection_name: collection,
5430
+ expr: 'id != ""'
5431
+ });
5432
+ return count;
5433
+ }
5434
+ async count(collection) {
5435
+ const stats = await this.client.getCollectionStatistics({
5436
+ collection_name: collection
5437
+ });
5438
+ return Number(stats.data?.row_count ?? 0);
5439
+ }
5440
+ async ping() {
5441
+ await this.client.hasCollection({ collection_name: "__ping__" });
5442
+ }
5443
+ };
5444
+
4648
5445
  // src/stores/index.ts
4649
5446
  function createStore(type, config) {
4650
5447
  switch (type) {
@@ -4656,8 +5453,16 @@ function createStore(type, config) {
4656
5453
  return new ChromaStore(config);
4657
5454
  case "qdrant":
4658
5455
  return new QdrantStore(config);
5456
+ case "weaviate":
5457
+ return new WeaviateStore(config);
5458
+ case "milvus":
5459
+ return new MilvusStore(config);
5460
+ case "pgvector":
5461
+ return new PgVectorStore(config);
4659
5462
  default:
4660
- return new MemoryStore(config);
5463
+ throw new Error(
5464
+ `Unknown vector store type "${String(type)}". Supported stores: memory, pinecone, chroma, qdrant, weaviate, milvus, pgvector.`
5465
+ );
4661
5466
  }
4662
5467
  }
4663
5468