@wiscale/velesdb-sdk 1.4.1 → 1.6.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.mjs CHANGED
@@ -25,6 +25,44 @@ var NotFoundError = class extends VelesDBError {
25
25
  this.name = "NotFoundError";
26
26
  }
27
27
  };
28
+ var BackpressureError = class extends VelesDBError {
29
+ constructor(message = "Server backpressure: too many requests") {
30
+ super(message, "BACKPRESSURE");
31
+ this.name = "BackpressureError";
32
+ }
33
+ };
34
+
35
+ // src/backends/shared.ts
36
+ function throwOnError(response, resourceLabel) {
37
+ if (!response.error) {
38
+ return;
39
+ }
40
+ if (response.error.code === "NOT_FOUND" && resourceLabel !== void 0) {
41
+ throw new NotFoundError(resourceLabel);
42
+ }
43
+ throw new VelesDBError(response.error.message, response.error.code);
44
+ }
45
+ function returnNullOnNotFound(response) {
46
+ if (!response.error) {
47
+ return void 0;
48
+ }
49
+ if (response.error.code === "NOT_FOUND") {
50
+ return true;
51
+ }
52
+ throw new VelesDBError(response.error.message, response.error.code);
53
+ }
54
+ function collectionPath(collection) {
55
+ return `/collections/${encodeURIComponent(collection)}`;
56
+ }
57
+ function toNumberArray(v) {
58
+ return v instanceof Float32Array ? Array.from(v) : v;
59
+ }
60
+ function wasmNotSupported(feature) {
61
+ throw new VelesDBError(
62
+ `${feature}: not supported in WASM backend. Use REST backend.`,
63
+ "NOT_SUPPORTED"
64
+ );
65
+ }
28
66
 
29
67
  // src/backends/wasm.ts
30
68
  var WasmBackend = class {
@@ -56,6 +94,33 @@ var WasmBackend = class {
56
94
  throw new ConnectionError("WASM backend not initialized");
57
95
  }
58
96
  }
97
+ normalizeIdString(id) {
98
+ const trimmed = id.trim();
99
+ return /^\d+$/.test(trimmed) ? trimmed : null;
100
+ }
101
+ canonicalPayloadKeyFromResultId(id) {
102
+ if (typeof id === "bigint") {
103
+ return id.toString();
104
+ }
105
+ if (typeof id === "number") {
106
+ return String(Math.trunc(id));
107
+ }
108
+ const normalized = this.normalizeIdString(id);
109
+ if (normalized !== null) {
110
+ return normalized.replace(/^0+(?=\d)/, "");
111
+ }
112
+ return String(this.toNumericId(id));
113
+ }
114
+ canonicalPayloadKey(id) {
115
+ if (typeof id === "number") {
116
+ return String(Math.trunc(id));
117
+ }
118
+ const normalized = this.normalizeIdString(id);
119
+ if (normalized !== null) {
120
+ return normalized.replace(/^0+(?=\d)/, "");
121
+ }
122
+ return String(this.toNumericId(id));
123
+ }
59
124
  async createCollection(name, config) {
60
125
  this.ensureInitialized();
61
126
  if (this.collections.has(name)) {
@@ -121,9 +186,13 @@ var WasmBackend = class {
121
186
  "DIMENSION_MISMATCH"
122
187
  );
123
188
  }
124
- collection.store.insert(BigInt(id), vector);
125
189
  if (doc.payload) {
126
- collection.payloads.set(String(doc.id), doc.payload);
190
+ collection.store.insert_with_payload(BigInt(id), vector, doc.payload);
191
+ } else {
192
+ collection.store.insert(BigInt(id), vector);
193
+ }
194
+ if (doc.payload) {
195
+ collection.payloads.set(this.canonicalPayloadKey(doc.id), doc.payload);
127
196
  }
128
197
  }
129
198
  async insertBatch(collectionName, docs) {
@@ -142,24 +211,32 @@ var WasmBackend = class {
142
211
  }
143
212
  }
144
213
  collection.store.reserve(docs.length);
145
- const batch = docs.map((doc) => [
146
- BigInt(this.toNumericId(doc.id)),
147
- Array.isArray(doc.vector) ? doc.vector : Array.from(doc.vector)
148
- ]);
149
- collection.store.insert_batch(batch);
214
+ const batch = [];
150
215
  for (const doc of docs) {
216
+ const id = BigInt(this.toNumericId(doc.id));
217
+ const vector = doc.vector instanceof Float32Array ? doc.vector : new Float32Array(doc.vector);
151
218
  if (doc.payload) {
152
- collection.payloads.set(String(doc.id), doc.payload);
219
+ collection.store.insert_with_payload(id, vector, doc.payload);
220
+ } else {
221
+ batch.push([id, Array.from(vector)]);
222
+ }
223
+ }
224
+ if (batch.length > 0) {
225
+ collection.store.insert_batch(batch);
226
+ }
227
+ for (const doc of docs) {
228
+ if (doc.payload) {
229
+ collection.payloads.set(this.canonicalPayloadKey(doc.id), doc.payload);
153
230
  }
154
231
  }
155
232
  }
156
- async search(collectionName, query, options) {
233
+ async search(collectionName, query2, options) {
157
234
  this.ensureInitialized();
158
235
  const collection = this.collections.get(collectionName);
159
236
  if (!collection) {
160
237
  throw new NotFoundError(`Collection '${collectionName}'`);
161
238
  }
162
- const queryVector = query instanceof Float32Array ? query : new Float32Array(query);
239
+ const queryVector = query2 instanceof Float32Array ? query2 : new Float32Array(query2);
163
240
  if (queryVector.length !== collection.config.dimension) {
164
241
  throw new VelesDBError(
165
242
  `Query dimension mismatch: expected ${collection.config.dimension}, got ${queryVector.length}`,
@@ -167,12 +244,43 @@ var WasmBackend = class {
167
244
  );
168
245
  }
169
246
  const k = options?.k ?? 10;
247
+ if (options?.sparseVector) {
248
+ const { indices, values } = this.sparseVectorToArrays(options.sparseVector);
249
+ if (queryVector.length > 0 && collection.config.dimension && collection.config.dimension > 0) {
250
+ const denseResults = collection.store.search(queryVector, k);
251
+ const sparseResults = collection.store.sparse_search(
252
+ new Uint32Array(indices),
253
+ new Float32Array(values),
254
+ k
255
+ );
256
+ const sparseArray = sparseResults;
257
+ const denseForFuse = denseResults.map(([id, score]) => [Number(id), score]);
258
+ const sparseForFuse = sparseArray.map((r) => [Number(r.doc_id), r.score]);
259
+ const fused = this.wasmModule.hybrid_search_fuse(denseForFuse, sparseForFuse, 60);
260
+ return fused.slice(0, k).map((r) => ({
261
+ id: String(r.doc_id),
262
+ score: r.score,
263
+ payload: collection.payloads.get(this.canonicalPayloadKeyFromResultId(r.doc_id))
264
+ }));
265
+ } else {
266
+ const sparseResults = collection.store.sparse_search(
267
+ new Uint32Array(indices),
268
+ new Float32Array(values),
269
+ k
270
+ );
271
+ return sparseResults.map((r) => ({
272
+ id: String(r.doc_id),
273
+ score: r.score,
274
+ payload: collection.payloads.get(this.canonicalPayloadKeyFromResultId(r.doc_id))
275
+ }));
276
+ }
277
+ }
170
278
  if (options?.filter) {
171
279
  const results = collection.store.search_with_filter(queryVector, k, options.filter);
172
280
  return results.map((r) => ({
173
281
  id: String(r.id),
174
282
  score: r.score,
175
- payload: r.payload || collection.payloads.get(String(r.id))
283
+ payload: r.payload || collection.payloads.get(this.canonicalPayloadKeyFromResultId(r.id))
176
284
  }));
177
285
  }
178
286
  const rawResults = collection.store.search(queryVector, k);
@@ -182,7 +290,7 @@ var WasmBackend = class {
182
290
  id: stringId,
183
291
  score
184
292
  };
185
- const payload = collection.payloads.get(stringId);
293
+ const payload = collection.payloads.get(this.canonicalPayloadKeyFromResultId(id));
186
294
  if (payload) {
187
295
  result.payload = payload;
188
296
  }
@@ -206,7 +314,7 @@ var WasmBackend = class {
206
314
  const numericId = this.toNumericId(id);
207
315
  const removed = collection.store.remove(BigInt(numericId));
208
316
  if (removed) {
209
- collection.payloads.delete(String(id));
317
+ collection.payloads.delete(this.canonicalPayloadKey(id));
210
318
  }
211
319
  return removed;
212
320
  }
@@ -216,40 +324,129 @@ var WasmBackend = class {
216
324
  if (!collection) {
217
325
  throw new NotFoundError(`Collection '${collectionName}'`);
218
326
  }
219
- const payload = collection.payloads.get(String(id));
220
- if (!payload) {
327
+ const numericId = this.toNumericId(id);
328
+ const point = collection.store.get(BigInt(numericId));
329
+ if (!point) {
221
330
  return null;
222
331
  }
332
+ const payload = point.payload ?? collection.payloads.get(this.canonicalPayloadKey(numericId));
223
333
  return {
224
- id,
225
- vector: [],
226
- // Not available in current WASM impl
334
+ id: String(point.id),
335
+ vector: Array.isArray(point.vector) ? point.vector : Array.from(point.vector),
227
336
  payload
228
337
  };
229
338
  }
230
339
  async textSearch(_collection, _query, _options) {
231
- throw new VelesDBError(
232
- "Text search is not supported in WASM backend. Use REST backend for BM25 search.",
233
- "NOT_SUPPORTED"
234
- );
340
+ this.ensureInitialized();
341
+ const collection = this.collections.get(_collection);
342
+ if (!collection) {
343
+ throw new NotFoundError(`Collection '${_collection}'`);
344
+ }
345
+ const k = _options?.k ?? 10;
346
+ const field = void 0;
347
+ const raw = collection.store.text_search(_query, k, field);
348
+ return raw.map((r) => {
349
+ if (Array.isArray(r)) {
350
+ const key2 = this.canonicalPayloadKeyFromResultId(r[0]);
351
+ return { id: String(r[0]), score: r[1], payload: collection.payloads.get(key2) };
352
+ }
353
+ const key = this.canonicalPayloadKeyFromResultId(r.id);
354
+ return { id: String(r.id), score: r.score, payload: r.payload ?? collection.payloads.get(key) };
355
+ });
235
356
  }
236
357
  async hybridSearch(_collection, _vector, _textQuery, _options) {
237
- throw new VelesDBError(
238
- "Hybrid search is not supported in WASM backend. Use REST backend for hybrid search.",
239
- "NOT_SUPPORTED"
240
- );
358
+ this.ensureInitialized();
359
+ const collection = this.collections.get(_collection);
360
+ if (!collection) {
361
+ throw new NotFoundError(`Collection '${_collection}'`);
362
+ }
363
+ const queryVector = _vector instanceof Float32Array ? _vector : new Float32Array(_vector);
364
+ const k = _options?.k ?? 10;
365
+ const vectorWeight = _options?.vectorWeight ?? 0.5;
366
+ const raw = collection.store.hybrid_search(queryVector, _textQuery, k, vectorWeight);
367
+ return raw.map((r) => {
368
+ const key = this.canonicalPayloadKeyFromResultId(r.id);
369
+ return {
370
+ id: String(r.id),
371
+ score: r.score,
372
+ payload: r.payload ?? collection.payloads.get(key)
373
+ };
374
+ });
241
375
  }
242
376
  async query(_collection, _queryString, _params, _options) {
243
- throw new VelesDBError(
244
- "VelesQL queries are not supported in WASM backend. Use REST backend for query support.",
245
- "NOT_SUPPORTED"
377
+ this.ensureInitialized();
378
+ const collection = this.collections.get(_collection);
379
+ if (!collection) {
380
+ throw new NotFoundError(`Collection '${_collection}'`);
381
+ }
382
+ const paramsVector = _params?.q;
383
+ if (!Array.isArray(paramsVector) && !(paramsVector instanceof Float32Array)) {
384
+ throw new VelesDBError(
385
+ "WASM query() expects params.q to contain the query embedding vector.",
386
+ "BAD_REQUEST"
387
+ );
388
+ }
389
+ const requestedK = _params?.k;
390
+ const k = typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
391
+ const raw = collection.store.query(
392
+ paramsVector instanceof Float32Array ? paramsVector : new Float32Array(paramsVector),
393
+ k
246
394
  );
395
+ return {
396
+ results: raw,
397
+ stats: {
398
+ executionTimeMs: 0,
399
+ strategy: "wasm-query",
400
+ scannedNodes: raw.length
401
+ }
402
+ };
247
403
  }
248
404
  async multiQuerySearch(_collection, _vectors, _options) {
249
- throw new VelesDBError(
250
- "Multi-query fusion is not supported in WASM backend. Use REST backend for MQF search.",
251
- "NOT_SUPPORTED"
405
+ this.ensureInitialized();
406
+ const collection = this.collections.get(_collection);
407
+ if (!collection) {
408
+ throw new NotFoundError(`Collection '${_collection}'`);
409
+ }
410
+ if (_vectors.length === 0) {
411
+ return [];
412
+ }
413
+ const numVectors = _vectors.length;
414
+ const dimension = collection.config.dimension ?? 0;
415
+ const flat = new Float32Array(numVectors * dimension);
416
+ _vectors.forEach((vector, idx) => {
417
+ const src = vector instanceof Float32Array ? vector : new Float32Array(vector);
418
+ flat.set(src, idx * dimension);
419
+ });
420
+ const strategy = _options?.fusion ?? "rrf";
421
+ if (strategy === "weighted") {
422
+ throw new VelesDBError(
423
+ "Fusion strategy 'weighted' is not supported in WASM backend.",
424
+ "NOT_SUPPORTED"
425
+ );
426
+ }
427
+ const raw = collection.store.multi_query_search(
428
+ flat,
429
+ numVectors,
430
+ _options?.k ?? 10,
431
+ strategy,
432
+ _options?.fusionParams?.k ?? 60
252
433
  );
434
+ return raw.map((r) => {
435
+ if (Array.isArray(r)) {
436
+ const key2 = this.canonicalPayloadKeyFromResultId(r[0]);
437
+ return { id: String(r[0]), score: r[1], payload: collection.payloads.get(key2) };
438
+ }
439
+ const key = this.canonicalPayloadKeyFromResultId(r.id);
440
+ return { id: String(r.id), score: r.score, payload: r.payload ?? collection.payloads.get(key) };
441
+ });
442
+ }
443
+ async queryExplain(_queryString, _params) {
444
+ this.ensureInitialized();
445
+ wasmNotSupported("Query explain");
446
+ }
447
+ async collectionSanity(_collection) {
448
+ this.ensureInitialized();
449
+ wasmNotSupported("Collection sanity endpoint");
253
450
  }
254
451
  async isEmpty(collectionName) {
255
452
  this.ensureInitialized();
@@ -273,13 +470,25 @@ var WasmBackend = class {
273
470
  this.collections.clear();
274
471
  this._initialized = false;
275
472
  }
473
+ sparseVectorToArrays(sv) {
474
+ const indices = [];
475
+ const values = [];
476
+ for (const [k, v] of Object.entries(sv)) {
477
+ indices.push(Number(k));
478
+ values.push(v);
479
+ }
480
+ return { indices, values };
481
+ }
276
482
  toNumericId(id) {
277
483
  if (typeof id === "number") {
278
484
  return id;
279
485
  }
280
- const parsed = parseInt(id, 10);
281
- if (!isNaN(parsed)) {
282
- return parsed;
486
+ const normalized = this.normalizeIdString(id);
487
+ if (normalized !== null) {
488
+ const parsed = Number(normalized);
489
+ if (Number.isSafeInteger(parsed)) {
490
+ return parsed;
491
+ }
283
492
  }
284
493
  let hash = 0;
285
494
  for (let i = 0; i < id.length; i++) {
@@ -317,34 +526,715 @@ var WasmBackend = class {
317
526
  // ========================================================================
318
527
  async addEdge(_collection, _edge) {
319
528
  this.ensureInitialized();
320
- throw new VelesDBError(
321
- "Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
322
- "NOT_SUPPORTED"
323
- );
529
+ wasmNotSupported("Knowledge Graph operations");
324
530
  }
325
531
  async getEdges(_collection, _options) {
326
532
  this.ensureInitialized();
327
- throw new VelesDBError(
328
- "Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
329
- "NOT_SUPPORTED"
330
- );
533
+ wasmNotSupported("Knowledge Graph operations");
331
534
  }
332
535
  async traverseGraph(_collection, _request) {
333
536
  this.ensureInitialized();
334
- throw new VelesDBError(
335
- "Graph traversal is not supported in WASM backend. Use REST backend for graph features.",
336
- "NOT_SUPPORTED"
337
- );
537
+ wasmNotSupported("Graph traversal");
338
538
  }
339
539
  async getNodeDegree(_collection, _nodeId) {
340
540
  this.ensureInitialized();
341
- throw new VelesDBError(
342
- "Graph degree query is not supported in WASM backend. Use REST backend for graph features.",
343
- "NOT_SUPPORTED"
344
- );
541
+ wasmNotSupported("Graph degree query");
542
+ }
543
+ // ========================================================================
544
+ // Sparse / PQ / Streaming (v1.5)
545
+ // ========================================================================
546
+ async trainPq(_collection, _options) {
547
+ this.ensureInitialized();
548
+ wasmNotSupported("PQ training");
549
+ }
550
+ async streamInsert(_collection, _docs) {
551
+ this.ensureInitialized();
552
+ wasmNotSupported("Streaming insert");
553
+ }
554
+ // ========================================================================
555
+ // Graph Collection / Stats / Agent Memory (Phase 8) - WASM stubs
556
+ // ========================================================================
557
+ async createGraphCollection(_name, _config) {
558
+ this.ensureInitialized();
559
+ wasmNotSupported("Graph collections");
560
+ }
561
+ async getCollectionStats(_collection) {
562
+ this.ensureInitialized();
563
+ wasmNotSupported("Collection stats");
564
+ }
565
+ async analyzeCollection(_collection) {
566
+ this.ensureInitialized();
567
+ wasmNotSupported("Collection analyze");
568
+ }
569
+ async getCollectionConfig(_collection) {
570
+ this.ensureInitialized();
571
+ wasmNotSupported("Collection config");
572
+ }
573
+ async searchIds(_collection, _query, _options) {
574
+ this.ensureInitialized();
575
+ wasmNotSupported("searchIds");
576
+ }
577
+ async storeSemanticFact(_collection, _entry) {
578
+ this.ensureInitialized();
579
+ wasmNotSupported("Agent memory");
580
+ }
581
+ async searchSemanticMemory(_collection, _embedding, _k) {
582
+ this.ensureInitialized();
583
+ wasmNotSupported("Agent memory");
584
+ }
585
+ async recordEpisodicEvent(_collection, _event) {
586
+ this.ensureInitialized();
587
+ wasmNotSupported("Agent memory");
588
+ }
589
+ async recallEpisodicEvents(_collection, _embedding, _k) {
590
+ this.ensureInitialized();
591
+ wasmNotSupported("Agent memory");
592
+ }
593
+ async storeProceduralPattern(_collection, _pattern) {
594
+ this.ensureInitialized();
595
+ wasmNotSupported("Agent memory");
596
+ }
597
+ async matchProceduralPatterns(_collection, _embedding, _k) {
598
+ this.ensureInitialized();
599
+ wasmNotSupported("Agent memory");
345
600
  }
346
601
  };
347
602
 
603
+ // src/backends/agent-memory-backend.ts
604
+ var _idCounter = 0;
605
+ var _lastTimestamp = 0;
606
+ function generateUniqueId() {
607
+ let now = Date.now();
608
+ if (now <= _lastTimestamp) {
609
+ _idCounter++;
610
+ if (_idCounter >= 1e3) {
611
+ _lastTimestamp++;
612
+ _idCounter = 0;
613
+ }
614
+ } else {
615
+ _lastTimestamp = now;
616
+ _idCounter = 0;
617
+ }
618
+ return _lastTimestamp * 1e3 + _idCounter;
619
+ }
620
+ async function storeSemanticFact(transport, collection, entry) {
621
+ const response = await transport.requestJson(
622
+ "POST",
623
+ `${collectionPath(collection)}/points`,
624
+ {
625
+ points: [{
626
+ id: entry.id,
627
+ vector: entry.embedding,
628
+ payload: {
629
+ _memory_type: "semantic",
630
+ text: entry.text,
631
+ ...entry.metadata
632
+ }
633
+ }]
634
+ }
635
+ );
636
+ throwOnError(response);
637
+ }
638
+ async function searchSemanticMemory(transport, collection, embedding, k = 5) {
639
+ return transport.searchVectors(collection, embedding, k, { _memory_type: "semantic" });
640
+ }
641
+ async function recordEpisodicEvent(transport, collection, event) {
642
+ const id = generateUniqueId();
643
+ const response = await transport.requestJson(
644
+ "POST",
645
+ `${collectionPath(collection)}/points`,
646
+ {
647
+ points: [{
648
+ id,
649
+ vector: event.embedding,
650
+ payload: {
651
+ _memory_type: "episodic",
652
+ event_type: event.eventType,
653
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
654
+ ...event.data,
655
+ ...event.metadata
656
+ }
657
+ }]
658
+ }
659
+ );
660
+ throwOnError(response);
661
+ }
662
+ async function recallEpisodicEvents(transport, collection, embedding, k = 5) {
663
+ return transport.searchVectors(collection, embedding, k, { _memory_type: "episodic" });
664
+ }
665
+ async function storeProceduralPattern(transport, collection, pattern) {
666
+ const id = generateUniqueId();
667
+ const response = await transport.requestJson(
668
+ "POST",
669
+ `${collectionPath(collection)}/points`,
670
+ {
671
+ points: [{
672
+ id,
673
+ payload: {
674
+ _memory_type: "procedural",
675
+ name: pattern.name,
676
+ steps: pattern.steps,
677
+ ...pattern.metadata
678
+ }
679
+ }]
680
+ }
681
+ );
682
+ throwOnError(response);
683
+ }
684
+ async function matchProceduralPatterns(transport, collection, embedding, k = 5) {
685
+ return transport.searchVectors(collection, embedding, k, { _memory_type: "procedural" });
686
+ }
687
+
688
+ // src/backends/search-backend.ts
689
+ async function search(transport, collection, query2, options) {
690
+ const queryVector = toNumberArray(query2);
691
+ const body = {
692
+ vector: queryVector,
693
+ top_k: options?.k ?? 10,
694
+ filter: options?.filter,
695
+ include_vectors: options?.includeVectors ?? false
696
+ };
697
+ if (options?.sparseVector) {
698
+ body.sparse_vector = transport.sparseToRest(options.sparseVector);
699
+ }
700
+ const response = await transport.requestJson(
701
+ "POST",
702
+ `${collectionPath(collection)}/search`,
703
+ body
704
+ );
705
+ throwOnError(response, `Collection '${collection}'`);
706
+ return response.data?.results ?? [];
707
+ }
708
+ async function searchBatch(transport, collection, searches) {
709
+ const formattedSearches = searches.map((s) => ({
710
+ vector: toNumberArray(s.vector),
711
+ top_k: s.k ?? 10,
712
+ filter: s.filter
713
+ }));
714
+ const response = await transport.requestJson(
715
+ "POST",
716
+ `${collectionPath(collection)}/search/batch`,
717
+ { searches: formattedSearches }
718
+ );
719
+ throwOnError(response, `Collection '${collection}'`);
720
+ return response.data?.results.map((r) => r.results) ?? [];
721
+ }
722
+ async function textSearch(transport, collection, query2, options) {
723
+ const response = await transport.requestJson(
724
+ "POST",
725
+ `${collectionPath(collection)}/search/text`,
726
+ {
727
+ query: query2,
728
+ top_k: options?.k ?? 10,
729
+ filter: options?.filter
730
+ }
731
+ );
732
+ throwOnError(response, `Collection '${collection}'`);
733
+ return response.data?.results ?? [];
734
+ }
735
+ async function hybridSearch(transport, collection, vector, textQuery, options) {
736
+ const queryVector = toNumberArray(vector);
737
+ const response = await transport.requestJson(
738
+ "POST",
739
+ `${collectionPath(collection)}/search/hybrid`,
740
+ {
741
+ vector: queryVector,
742
+ query: textQuery,
743
+ top_k: options?.k ?? 10,
744
+ vector_weight: options?.vectorWeight ?? 0.5,
745
+ filter: options?.filter
746
+ }
747
+ );
748
+ throwOnError(response, `Collection '${collection}'`);
749
+ return response.data?.results ?? [];
750
+ }
751
+ async function multiQuerySearch(transport, collection, vectors, options) {
752
+ const formattedVectors = vectors.map(toNumberArray);
753
+ const response = await transport.requestJson(
754
+ "POST",
755
+ `${collectionPath(collection)}/search/multi`,
756
+ {
757
+ vectors: formattedVectors,
758
+ top_k: options?.k ?? 10,
759
+ strategy: options?.fusion ?? "rrf",
760
+ rrf_k: options?.fusionParams?.k ?? 60,
761
+ avg_weight: options?.fusionParams?.avgWeight,
762
+ max_weight: options?.fusionParams?.maxWeight,
763
+ hit_weight: options?.fusionParams?.hitWeight,
764
+ filter: options?.filter
765
+ }
766
+ );
767
+ throwOnError(response, `Collection '${collection}'`);
768
+ return response.data?.results ?? [];
769
+ }
770
+ async function searchIds(transport, collection, query2, options) {
771
+ const queryVector = toNumberArray(query2);
772
+ const response = await transport.requestJson(
773
+ "POST",
774
+ `${collectionPath(collection)}/search/ids`,
775
+ {
776
+ vector: queryVector,
777
+ top_k: options?.k ?? 10,
778
+ filter: options?.filter
779
+ }
780
+ );
781
+ throwOnError(response, `Collection '${collection}'`);
782
+ return response.data?.results ?? [];
783
+ }
784
+
785
+ // src/backends/graph-backend.ts
786
+ async function addEdge(transport, collection, edge) {
787
+ const response = await transport.requestJson(
788
+ "POST",
789
+ `${collectionPath(collection)}/graph/edges`,
790
+ {
791
+ id: edge.id,
792
+ source: edge.source,
793
+ target: edge.target,
794
+ label: edge.label,
795
+ properties: edge.properties ?? {}
796
+ }
797
+ );
798
+ throwOnError(response, `Collection '${collection}'`);
799
+ }
800
+ async function getEdges(transport, collection, options) {
801
+ const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
802
+ const response = await transport.requestJson(
803
+ "GET",
804
+ `${collectionPath(collection)}/graph/edges${queryParams}`
805
+ );
806
+ throwOnError(response, `Collection '${collection}'`);
807
+ return response.data?.edges ?? [];
808
+ }
809
+ async function traverseGraph(transport, collection, request) {
810
+ const response = await transport.requestJson(
811
+ "POST",
812
+ `${collectionPath(collection)}/graph/traverse`,
813
+ {
814
+ source: request.source,
815
+ strategy: request.strategy ?? "bfs",
816
+ max_depth: request.maxDepth ?? 3,
817
+ limit: request.limit ?? 100,
818
+ cursor: request.cursor,
819
+ rel_types: request.relTypes ?? []
820
+ }
821
+ );
822
+ throwOnError(response, `Collection '${collection}'`);
823
+ const data = response.data;
824
+ return {
825
+ results: data.results.map((r) => ({
826
+ targetId: r.target_id,
827
+ depth: r.depth,
828
+ path: r.path
829
+ })),
830
+ nextCursor: data.next_cursor ?? void 0,
831
+ hasMore: data.has_more,
832
+ stats: {
833
+ visited: data.stats.visited,
834
+ depthReached: data.stats.depth_reached
835
+ }
836
+ };
837
+ }
838
+ async function getNodeDegree(transport, collection, nodeId) {
839
+ const response = await transport.requestJson(
840
+ "GET",
841
+ `${collectionPath(collection)}/graph/nodes/${nodeId}/degree`
842
+ );
843
+ throwOnError(response, `Collection '${collection}'`);
844
+ return {
845
+ inDegree: response.data?.in_degree ?? 0,
846
+ outDegree: response.data?.out_degree ?? 0
847
+ };
848
+ }
849
+ async function createGraphCollection(transport, name, config) {
850
+ const response = await transport.requestJson("POST", "/collections", {
851
+ name,
852
+ collection_type: "graph",
853
+ dimension: config?.dimension,
854
+ metric: config?.metric ?? "cosine",
855
+ schema_mode: config?.schemaMode ?? "schemaless"
856
+ });
857
+ throwOnError(response);
858
+ }
859
+
860
+ // src/backends/query-backend.ts
861
+ function isLikelyAggregationQuery(queryString) {
862
+ return /\bGROUP\s+BY\b|\bHAVING\b|\bCOUNT\s*\(|\bSUM\s*\(|\bAVG\s*\(|\bMIN\s*\(|\bMAX\s*\(/i.test(
863
+ queryString
864
+ );
865
+ }
866
+ async function query(transport, collection, queryString, params, options) {
867
+ const endpoint = isLikelyAggregationQuery(queryString) ? "/aggregate" : "/query";
868
+ const response = await transport.requestJson(
869
+ "POST",
870
+ endpoint,
871
+ {
872
+ query: queryString,
873
+ params: params ?? {},
874
+ collection,
875
+ timeout_ms: options?.timeoutMs,
876
+ stream: options?.stream ?? false
877
+ }
878
+ );
879
+ throwOnError(response, `Collection '${collection}'`);
880
+ const rawData = response.data;
881
+ if (rawData && Object.prototype.hasOwnProperty.call(rawData, "result")) {
882
+ return {
883
+ result: rawData.result,
884
+ stats: {
885
+ executionTimeMs: rawData.timing_ms ?? 0,
886
+ strategy: "aggregation",
887
+ scannedNodes: 0
888
+ }
889
+ };
890
+ }
891
+ return {
892
+ results: rawData?.results ?? [],
893
+ stats: {
894
+ executionTimeMs: rawData?.timing_ms ?? 0,
895
+ strategy: "select",
896
+ scannedNodes: rawData?.rows_returned ?? 0
897
+ }
898
+ };
899
+ }
900
+ async function queryExplain(transport, queryString, params) {
901
+ const response = await transport.requestJson(
902
+ "POST",
903
+ "/query/explain",
904
+ {
905
+ query: queryString,
906
+ params: params ?? {}
907
+ }
908
+ );
909
+ throwOnError(response);
910
+ const data = response.data;
911
+ return {
912
+ query: data.query,
913
+ queryType: data.query_type,
914
+ collection: data.collection,
915
+ plan: data.plan.map((step) => ({
916
+ step: step.step,
917
+ operation: step.operation,
918
+ description: step.description,
919
+ estimatedRows: step.estimated_rows
920
+ })),
921
+ estimatedCost: {
922
+ usesIndex: data.estimated_cost.uses_index,
923
+ indexName: data.estimated_cost.index_name,
924
+ selectivity: data.estimated_cost.selectivity,
925
+ complexity: data.estimated_cost.complexity
926
+ },
927
+ features: {
928
+ hasVectorSearch: data.features.has_vector_search,
929
+ hasFilter: data.features.has_filter,
930
+ hasOrderBy: data.features.has_order_by,
931
+ hasGroupBy: data.features.has_group_by,
932
+ hasAggregation: data.features.has_aggregation,
933
+ hasJoin: data.features.has_join,
934
+ hasFusion: data.features.has_fusion,
935
+ limit: data.features.limit,
936
+ offset: data.features.offset
937
+ }
938
+ };
939
+ }
940
+ async function collectionSanity(transport, collection) {
941
+ const response = await transport.requestJson(
942
+ "GET",
943
+ `${collectionPath(collection)}/sanity`
944
+ );
945
+ throwOnError(response, `Collection '${collection}'`);
946
+ const data = response.data;
947
+ return {
948
+ collection: data.collection,
949
+ dimension: data.dimension,
950
+ metric: data.metric,
951
+ pointCount: data.point_count,
952
+ isEmpty: data.is_empty,
953
+ checks: {
954
+ hasVectors: data.checks.has_vectors,
955
+ searchReady: data.checks.search_ready,
956
+ dimensionConfigured: data.checks.dimension_configured
957
+ },
958
+ diagnostics: {
959
+ searchRequestsTotal: data.diagnostics.search_requests_total,
960
+ dimensionMismatchTotal: data.diagnostics.dimension_mismatch_total,
961
+ emptySearchResultsTotal: data.diagnostics.empty_search_results_total,
962
+ filterParseErrorsTotal: data.diagnostics.filter_parse_errors_total
963
+ },
964
+ hints: data.hints ?? []
965
+ };
966
+ }
967
+
968
+ // src/backends/admin-backend.ts
969
+ function mapStatsResponse(data) {
970
+ return {
971
+ totalPoints: data.total_points,
972
+ totalSizeBytes: data.total_size_bytes,
973
+ rowCount: data.row_count,
974
+ deletedCount: data.deleted_count,
975
+ avgRowSizeBytes: data.avg_row_size_bytes,
976
+ payloadSizeBytes: data.payload_size_bytes,
977
+ lastAnalyzedEpochMs: data.last_analyzed_epoch_ms
978
+ };
979
+ }
980
+ async function getCollectionStats(transport, collection) {
981
+ const response = await transport.requestJson(
982
+ "GET",
983
+ `${collectionPath(collection)}/stats`
984
+ );
985
+ if (returnNullOnNotFound(response)) {
986
+ return null;
987
+ }
988
+ return mapStatsResponse(response.data);
989
+ }
990
+ async function analyzeCollection(transport, collection) {
991
+ const response = await transport.requestJson(
992
+ "POST",
993
+ `${collectionPath(collection)}/analyze`
994
+ );
995
+ throwOnError(response, `Collection '${collection}'`);
996
+ return mapStatsResponse(response.data);
997
+ }
998
+ async function getCollectionConfig(transport, collection) {
999
+ const response = await transport.requestJson("GET", `${collectionPath(collection)}/config`);
1000
+ throwOnError(response, `Collection '${collection}'`);
1001
+ const data = response.data;
1002
+ return {
1003
+ name: data.name,
1004
+ dimension: data.dimension,
1005
+ metric: data.metric,
1006
+ storageMode: data.storage_mode,
1007
+ pointCount: data.point_count,
1008
+ metadataOnly: data.metadata_only,
1009
+ graphSchema: data.graph_schema,
1010
+ embeddingDimension: data.embedding_dimension
1011
+ };
1012
+ }
1013
+
1014
+ // src/backends/index-backend.ts
1015
+ async function createIndex(transport, collection, options) {
1016
+ const response = await transport.requestJson(
1017
+ "POST",
1018
+ `${collectionPath(collection)}/indexes`,
1019
+ {
1020
+ label: options.label,
1021
+ property: options.property,
1022
+ index_type: options.indexType ?? "hash"
1023
+ }
1024
+ );
1025
+ throwOnError(response, `Collection '${collection}'`);
1026
+ }
1027
+ async function listIndexes(transport, collection) {
1028
+ const response = await transport.requestJson(
1029
+ "GET",
1030
+ `${collectionPath(collection)}/indexes`
1031
+ );
1032
+ throwOnError(response, `Collection '${collection}'`);
1033
+ return (response.data?.indexes ?? []).map((idx) => ({
1034
+ label: idx.label,
1035
+ property: idx.property,
1036
+ indexType: idx.index_type,
1037
+ cardinality: idx.cardinality,
1038
+ memoryBytes: idx.memory_bytes
1039
+ }));
1040
+ }
1041
+ async function hasIndex(transport, collection, label, property) {
1042
+ const indexes = await listIndexes(transport, collection);
1043
+ return indexes.some((idx) => idx.label === label && idx.property === property);
1044
+ }
1045
+ async function dropIndex(transport, collection, label, property) {
1046
+ const response = await transport.requestJson(
1047
+ "DELETE",
1048
+ `${collectionPath(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
1049
+ );
1050
+ if (returnNullOnNotFound(response)) {
1051
+ return false;
1052
+ }
1053
+ return response.data?.dropped ?? true;
1054
+ }
1055
+
1056
+ // src/backends/streaming-backend.ts
1057
+ async function trainPq(transport, collection, options) {
1058
+ const m = options?.m ?? 8;
1059
+ const k = options?.k ?? 256;
1060
+ const withClause = options?.opq ? `WITH (m=${m}, k=${k}, opq=true)` : `WITH (m=${m}, k=${k})`;
1061
+ const queryString = `TRAIN QUANTIZER ON ${collection} ${withClause}`;
1062
+ const response = await transport.requestJson(
1063
+ "POST",
1064
+ "/query",
1065
+ { query: queryString }
1066
+ );
1067
+ throwOnError(response);
1068
+ return response.data?.message ?? "PQ training initiated";
1069
+ }
1070
+ async function streamInsert(transport, collection, docs) {
1071
+ for (const doc of docs) {
1072
+ const restId = transport.parseRestPointId(doc.id);
1073
+ const vector = toNumberArray(doc.vector);
1074
+ const body = {
1075
+ id: restId,
1076
+ vector,
1077
+ payload: doc.payload
1078
+ };
1079
+ if (doc.sparseVector) {
1080
+ body.sparse_vector = transport.sparseVectorToRestFormat(doc.sparseVector);
1081
+ }
1082
+ const url = `${transport.baseUrl}${collectionPath(collection)}/stream/insert`;
1083
+ const headers = {
1084
+ "Content-Type": "application/json"
1085
+ };
1086
+ if (transport.apiKey) {
1087
+ headers["Authorization"] = `Bearer ${transport.apiKey}`;
1088
+ }
1089
+ const controller = new AbortController();
1090
+ const timeoutId = setTimeout(() => controller.abort(), transport.timeout);
1091
+ try {
1092
+ const response = await fetch(url, {
1093
+ method: "POST",
1094
+ headers,
1095
+ body: JSON.stringify(body),
1096
+ signal: controller.signal
1097
+ });
1098
+ clearTimeout(timeoutId);
1099
+ if (response.status === 429) {
1100
+ throw new BackpressureError();
1101
+ }
1102
+ if (!response.ok && response.status !== 202) {
1103
+ const data = await response.json().catch(() => ({}));
1104
+ const errorPayload = transport.extractErrorPayload(data);
1105
+ throw new VelesDBError(
1106
+ errorPayload.message ?? `HTTP ${response.status}`,
1107
+ errorPayload.code ?? transport.mapStatusToErrorCode(response.status)
1108
+ );
1109
+ }
1110
+ } catch (error) {
1111
+ clearTimeout(timeoutId);
1112
+ if (error instanceof BackpressureError || error instanceof VelesDBError) {
1113
+ throw error;
1114
+ }
1115
+ if (error instanceof Error && error.name === "AbortError") {
1116
+ throw new ConnectionError("Request timeout");
1117
+ }
1118
+ throw new ConnectionError(
1119
+ `Stream insert failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1120
+ error instanceof Error ? error : void 0
1121
+ );
1122
+ }
1123
+ }
1124
+ }
1125
+
1126
+ // src/backends/crud-backend.ts
1127
+ function parseRestPointId(id) {
1128
+ if (typeof id !== "number" || !Number.isFinite(id) || id < 0 || !Number.isInteger(id) || id > Number.MAX_SAFE_INTEGER) {
1129
+ throw new ValidationError(
1130
+ `REST backend requires numeric u64-compatible IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER}). Received: ${String(id)}`
1131
+ );
1132
+ }
1133
+ return id;
1134
+ }
1135
+ function sparseVectorToRestFormat(sv) {
1136
+ const result = {};
1137
+ for (const [k, v] of Object.entries(sv)) {
1138
+ result[String(k)] = v;
1139
+ }
1140
+ return result;
1141
+ }
1142
+ async function createCollection(transport, name, config) {
1143
+ const response = await transport.requestJson("POST", "/collections", {
1144
+ name,
1145
+ dimension: config.dimension,
1146
+ metric: config.metric ?? "cosine",
1147
+ storage_mode: config.storageMode ?? "full",
1148
+ collection_type: config.collectionType ?? "vector",
1149
+ description: config.description,
1150
+ hnsw_m: config.hnsw?.m,
1151
+ hnsw_ef_construction: config.hnsw?.efConstruction
1152
+ });
1153
+ throwOnError(response);
1154
+ }
1155
+ async function deleteCollection(transport, name) {
1156
+ const response = await transport.requestJson(
1157
+ "DELETE",
1158
+ collectionPath(name)
1159
+ );
1160
+ throwOnError(response, `Collection '${name}'`);
1161
+ }
1162
+ async function getCollection(transport, name) {
1163
+ const response = await transport.requestJson(
1164
+ "GET",
1165
+ collectionPath(name)
1166
+ );
1167
+ if (returnNullOnNotFound(response)) {
1168
+ return null;
1169
+ }
1170
+ return response.data ?? null;
1171
+ }
1172
+ async function listCollections(transport) {
1173
+ const response = await transport.requestJson("GET", "/collections");
1174
+ throwOnError(response);
1175
+ return response.data ?? [];
1176
+ }
1177
+ async function insert(transport, collection, doc) {
1178
+ const restId = parseRestPointId(doc.id);
1179
+ const vector = toNumberArray(doc.vector);
1180
+ const response = await transport.requestJson(
1181
+ "POST",
1182
+ `${collectionPath(collection)}/points`,
1183
+ { points: [{ id: restId, vector, payload: doc.payload }] }
1184
+ );
1185
+ throwOnError(response, `Collection '${collection}'`);
1186
+ }
1187
+ async function insertBatch(transport, collection, docs) {
1188
+ const vectors = docs.map((doc) => ({
1189
+ id: parseRestPointId(doc.id),
1190
+ vector: toNumberArray(doc.vector),
1191
+ payload: doc.payload
1192
+ }));
1193
+ const response = await transport.requestJson(
1194
+ "POST",
1195
+ `${collectionPath(collection)}/points`,
1196
+ { points: vectors }
1197
+ );
1198
+ throwOnError(response, `Collection '${collection}'`);
1199
+ }
1200
+ async function deletePoint(transport, collection, id) {
1201
+ const restId = parseRestPointId(id);
1202
+ const response = await transport.requestJson(
1203
+ "DELETE",
1204
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(restId))}`
1205
+ );
1206
+ if (returnNullOnNotFound(response)) {
1207
+ return false;
1208
+ }
1209
+ return response.data?.deleted ?? false;
1210
+ }
1211
+ async function get(transport, collection, id) {
1212
+ const restId = parseRestPointId(id);
1213
+ const response = await transport.requestJson(
1214
+ "GET",
1215
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(restId))}`
1216
+ );
1217
+ if (returnNullOnNotFound(response)) {
1218
+ return null;
1219
+ }
1220
+ return response.data ?? null;
1221
+ }
1222
+ async function isEmpty(transport, collection) {
1223
+ const response = await transport.requestJson(
1224
+ "GET",
1225
+ `${collectionPath(collection)}/empty`
1226
+ );
1227
+ throwOnError(response, `Collection '${collection}'`);
1228
+ return response.data?.is_empty ?? true;
1229
+ }
1230
+ async function flush(transport, collection) {
1231
+ const response = await transport.requestJson(
1232
+ "POST",
1233
+ `${collectionPath(collection)}/flush`
1234
+ );
1235
+ throwOnError(response, `Collection '${collection}'`);
1236
+ }
1237
+
348
1238
  // src/backends/rest.ts
349
1239
  var RestBackend = class {
350
1240
  constructor(url, apiKey, timeout = 3e4) {
@@ -405,15 +1295,13 @@ var RestBackend = class {
405
1295
  return {};
406
1296
  }
407
1297
  const payload = data;
408
- const code = typeof payload.code === "string" ? payload.code : void 0;
409
- const messageField = payload.message ?? payload.error;
1298
+ const nestedError = payload.error && typeof payload.error === "object" ? payload.error : void 0;
1299
+ const codeField = nestedError?.code ?? payload.code;
1300
+ const code = typeof codeField === "string" ? codeField : void 0;
1301
+ const messageField = nestedError?.message ?? payload.message ?? payload.error;
410
1302
  const message = typeof messageField === "string" ? messageField : void 0;
411
1303
  return { code, message };
412
1304
  }
413
- /**
414
- * Parse node ID safely to handle u64 values above Number.MAX_SAFE_INTEGER.
415
- * Returns bigint for large values, number for safe values.
416
- */
417
1305
  parseNodeId(value) {
418
1306
  if (value === null || value === void 0) {
419
1307
  return 0;
@@ -423,24 +1311,16 @@ var RestBackend = class {
423
1311
  }
424
1312
  if (typeof value === "string") {
425
1313
  const num = Number(value);
426
- if (num > Number.MAX_SAFE_INTEGER) {
427
- return BigInt(value);
428
- }
429
- return num;
1314
+ return num > Number.MAX_SAFE_INTEGER ? BigInt(value) : num;
430
1315
  }
431
1316
  if (typeof value === "number") {
432
- if (value > Number.MAX_SAFE_INTEGER) {
433
- return value;
434
- }
435
1317
  return value;
436
1318
  }
437
1319
  return 0;
438
1320
  }
439
1321
  async request(method, path, body) {
440
1322
  const url = `${this.baseUrl}${path}`;
441
- const headers = {
442
- "Content-Type": "application/json"
443
- };
1323
+ const headers = { "Content-Type": "application/json" };
444
1324
  if (this.apiKey) {
445
1325
  headers["Authorization"] = `Bearer ${this.apiKey}`;
446
1326
  }
@@ -476,445 +1356,259 @@ var RestBackend = class {
476
1356
  );
477
1357
  }
478
1358
  }
1359
+ // ==========================================================================
1360
+ // Transport adapters
1361
+ // ==========================================================================
1362
+ asCrudTransport() {
1363
+ return {
1364
+ requestJson: (m, p, b) => this.request(m, p, b)
1365
+ };
1366
+ }
1367
+ asSearchTransport() {
1368
+ return {
1369
+ requestJson: (m, p, b) => this.request(m, p, b),
1370
+ sparseToRest: (sv) => sparseVectorToRestFormat(sv)
1371
+ };
1372
+ }
1373
+ asAgentMemoryTransport() {
1374
+ return {
1375
+ requestJson: (m, p, b) => this.request(m, p, b),
1376
+ searchVectors: (c, e, k, f) => this.search(c, e, { k, filter: f })
1377
+ };
1378
+ }
1379
+ asQueryTransport() {
1380
+ return {
1381
+ requestJson: (m, p, b) => this.request(m, p, b),
1382
+ parseNodeId: (v) => this.parseNodeId(v)
1383
+ };
1384
+ }
1385
+ asStreamingTransport() {
1386
+ return {
1387
+ requestJson: (m, p, b) => this.request(m, p, b),
1388
+ baseUrl: this.baseUrl,
1389
+ apiKey: this.apiKey,
1390
+ timeout: this.timeout,
1391
+ parseRestPointId,
1392
+ sparseVectorToRestFormat,
1393
+ mapStatusToErrorCode: (s) => this.mapStatusToErrorCode(s),
1394
+ extractErrorPayload: (d) => this.extractErrorPayload(d)
1395
+ };
1396
+ }
1397
+ // ==========================================================================
1398
+ // Collection CRUD — delegates to crud-backend.ts
1399
+ // ==========================================================================
479
1400
  async createCollection(name, config) {
480
1401
  this.ensureInitialized();
481
- const response = await this.request("POST", "/collections", {
482
- name,
483
- dimension: config.dimension,
484
- metric: config.metric ?? "cosine",
485
- storage_mode: config.storageMode ?? "full",
486
- collection_type: config.collectionType ?? "vector",
487
- description: config.description
488
- });
489
- if (response.error) {
490
- throw new VelesDBError(response.error.message, response.error.code);
491
- }
1402
+ return createCollection(this.asCrudTransport(), name, config);
492
1403
  }
493
1404
  async deleteCollection(name) {
494
1405
  this.ensureInitialized();
495
- const response = await this.request("DELETE", `/collections/${encodeURIComponent(name)}`);
496
- if (response.error) {
497
- if (response.error.code === "NOT_FOUND") {
498
- throw new NotFoundError(`Collection '${name}'`);
499
- }
500
- throw new VelesDBError(response.error.message, response.error.code);
501
- }
1406
+ return deleteCollection(this.asCrudTransport(), name);
502
1407
  }
503
1408
  async getCollection(name) {
504
1409
  this.ensureInitialized();
505
- const response = await this.request(
506
- "GET",
507
- `/collections/${encodeURIComponent(name)}`
508
- );
509
- if (response.error) {
510
- if (response.error.code === "NOT_FOUND") {
511
- return null;
512
- }
513
- throw new VelesDBError(response.error.message, response.error.code);
514
- }
515
- return response.data ?? null;
1410
+ return getCollection(this.asCrudTransport(), name);
516
1411
  }
517
1412
  async listCollections() {
518
1413
  this.ensureInitialized();
519
- const response = await this.request("GET", "/collections");
520
- if (response.error) {
521
- throw new VelesDBError(response.error.message, response.error.code);
522
- }
523
- return response.data ?? [];
1414
+ return listCollections(this.asCrudTransport());
524
1415
  }
525
1416
  async insert(collection, doc) {
526
1417
  this.ensureInitialized();
527
- const vector = doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector;
528
- const response = await this.request(
529
- "POST",
530
- `/collections/${encodeURIComponent(collection)}/points`,
531
- {
532
- points: [{
533
- id: doc.id,
534
- vector,
535
- payload: doc.payload
536
- }]
537
- }
538
- );
539
- if (response.error) {
540
- if (response.error.code === "NOT_FOUND") {
541
- throw new NotFoundError(`Collection '${collection}'`);
542
- }
543
- throw new VelesDBError(response.error.message, response.error.code);
544
- }
1418
+ return insert(this.asCrudTransport(), collection, doc);
545
1419
  }
546
1420
  async insertBatch(collection, docs) {
547
1421
  this.ensureInitialized();
548
- const vectors = docs.map((doc) => ({
549
- id: doc.id,
550
- vector: doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector,
551
- payload: doc.payload
552
- }));
553
- const response = await this.request(
554
- "POST",
555
- `/collections/${encodeURIComponent(collection)}/points`,
556
- { points: vectors }
557
- );
558
- if (response.error) {
559
- if (response.error.code === "NOT_FOUND") {
560
- throw new NotFoundError(`Collection '${collection}'`);
561
- }
562
- throw new VelesDBError(response.error.message, response.error.code);
563
- }
1422
+ return insertBatch(this.asCrudTransport(), collection, docs);
564
1423
  }
565
- async search(collection, query, options) {
1424
+ async delete(collection, id) {
566
1425
  this.ensureInitialized();
567
- const queryVector = query instanceof Float32Array ? Array.from(query) : query;
568
- const response = await this.request(
569
- "POST",
570
- `/collections/${encodeURIComponent(collection)}/search`,
571
- {
572
- vector: queryVector,
573
- k: options?.k ?? 10,
574
- filter: options?.filter,
575
- include_vectors: options?.includeVectors ?? false
576
- }
577
- );
578
- if (response.error) {
579
- if (response.error.code === "NOT_FOUND") {
580
- throw new NotFoundError(`Collection '${collection}'`);
581
- }
582
- throw new VelesDBError(response.error.message, response.error.code);
583
- }
584
- return response.data ?? [];
1426
+ return deletePoint(this.asCrudTransport(), collection, id);
1427
+ }
1428
+ async get(collection, id) {
1429
+ this.ensureInitialized();
1430
+ return get(this.asCrudTransport(), collection, id);
1431
+ }
1432
+ async isEmpty(collection) {
1433
+ this.ensureInitialized();
1434
+ return isEmpty(this.asCrudTransport(), collection);
1435
+ }
1436
+ async flush(collection) {
1437
+ this.ensureInitialized();
1438
+ return flush(this.asCrudTransport(), collection);
1439
+ }
1440
+ async close() {
1441
+ this._initialized = false;
1442
+ }
1443
+ // ==========================================================================
1444
+ // Search — delegates to search-backend.ts
1445
+ // ==========================================================================
1446
+ async search(c, q, o) {
1447
+ this.ensureInitialized();
1448
+ return search(this.asSearchTransport(), c, q, o);
585
1449
  }
586
1450
  async searchBatch(collection, searches) {
587
1451
  this.ensureInitialized();
588
- const formattedSearches = searches.map((s) => ({
589
- vector: s.vector instanceof Float32Array ? Array.from(s.vector) : s.vector,
590
- top_k: s.k ?? 10,
591
- filter: s.filter
592
- }));
593
- const response = await this.request(
594
- "POST",
595
- `/collections/${encodeURIComponent(collection)}/search/batch`,
596
- { searches: formattedSearches }
597
- );
598
- if (response.error) {
599
- if (response.error.code === "NOT_FOUND") {
600
- throw new NotFoundError(`Collection '${collection}'`);
601
- }
602
- throw new VelesDBError(response.error.message, response.error.code);
603
- }
604
- return response.data?.results.map((r) => r.results) ?? [];
1452
+ return searchBatch(this.asSearchTransport(), collection, searches);
605
1453
  }
606
- async delete(collection, id) {
1454
+ async textSearch(c, q, o) {
607
1455
  this.ensureInitialized();
608
- const response = await this.request(
609
- "DELETE",
610
- `/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(id))}`
611
- );
612
- if (response.error) {
613
- if (response.error.code === "NOT_FOUND") {
614
- return false;
615
- }
616
- throw new VelesDBError(response.error.message, response.error.code);
617
- }
618
- return response.data?.deleted ?? false;
1456
+ return textSearch(this.asSearchTransport(), c, q, o);
619
1457
  }
620
- async get(collection, id) {
1458
+ async hybridSearch(c, v, t, o) {
621
1459
  this.ensureInitialized();
622
- const response = await this.request(
623
- "GET",
624
- `/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(id))}`
625
- );
626
- if (response.error) {
627
- if (response.error.code === "NOT_FOUND") {
628
- return null;
629
- }
630
- throw new VelesDBError(response.error.message, response.error.code);
631
- }
632
- return response.data ?? null;
1460
+ return hybridSearch(this.asSearchTransport(), c, v, t, o);
633
1461
  }
634
- async textSearch(collection, query, options) {
1462
+ async multiQuerySearch(c, v, o) {
635
1463
  this.ensureInitialized();
636
- const response = await this.request(
637
- "POST",
638
- `/collections/${encodeURIComponent(collection)}/search/text`,
639
- {
640
- query,
641
- top_k: options?.k ?? 10,
642
- filter: options?.filter
643
- }
644
- );
645
- if (response.error) {
646
- if (response.error.code === "NOT_FOUND") {
647
- throw new NotFoundError(`Collection '${collection}'`);
648
- }
649
- throw new VelesDBError(response.error.message, response.error.code);
650
- }
651
- return response.data?.results ?? [];
1464
+ return multiQuerySearch(this.asSearchTransport(), c, v, o);
652
1465
  }
653
- async hybridSearch(collection, vector, textQuery, options) {
1466
+ async searchIds(c, q, o) {
654
1467
  this.ensureInitialized();
655
- const queryVector = vector instanceof Float32Array ? Array.from(vector) : vector;
656
- const response = await this.request(
657
- "POST",
658
- `/collections/${encodeURIComponent(collection)}/search/hybrid`,
659
- {
660
- vector: queryVector,
661
- query: textQuery,
662
- top_k: options?.k ?? 10,
663
- vector_weight: options?.vectorWeight ?? 0.5,
664
- filter: options?.filter
665
- }
666
- );
667
- if (response.error) {
668
- if (response.error.code === "NOT_FOUND") {
669
- throw new NotFoundError(`Collection '${collection}'`);
670
- }
671
- throw new VelesDBError(response.error.message, response.error.code);
672
- }
673
- return response.data?.results ?? [];
1468
+ return searchIds(this.asSearchTransport(), c, q, o);
674
1469
  }
675
- async query(collection, queryString, params, _options) {
1470
+ // ==========================================================================
1471
+ // Query — delegates to query-backend.ts
1472
+ // ==========================================================================
1473
+ async query(c, q, p, o) {
676
1474
  this.ensureInitialized();
677
- const response = await this.request(
678
- "POST",
679
- "/query",
680
- {
681
- query: queryString,
682
- params: params ?? {}
683
- }
684
- );
685
- if (response.error) {
686
- if (response.error.code === "NOT_FOUND") {
687
- throw new NotFoundError(`Collection '${collection}'`);
688
- }
689
- throw new VelesDBError(response.error.message, response.error.code);
690
- }
691
- const rawData = response.data;
692
- return {
693
- results: (rawData?.results ?? []).map((r) => ({
694
- // Server returns `id` (u64), map to nodeId with precision handling
695
- nodeId: this.parseNodeId(r.id ?? r.node_id ?? r.nodeId),
696
- // Server returns `score`, map to vectorScore (primary score for SELECT queries)
697
- vectorScore: r.score ?? r.vector_score ?? r.vectorScore,
698
- // graph_score not returned by SELECT queries, only by future MATCH queries
699
- graphScore: r.graph_score ?? r.graphScore,
700
- // Use score as fusedScore for compatibility
701
- fusedScore: r.score ?? r.fused_score ?? r.fusedScore ?? 0,
702
- // payload maps to bindings for compatibility
703
- bindings: r.payload ?? r.bindings ?? {},
704
- columnData: r.column_data ?? r.columnData
705
- })),
706
- stats: {
707
- executionTimeMs: rawData?.timing_ms ?? 0,
708
- strategy: "select",
709
- scannedNodes: rawData?.rows_returned ?? 0
710
- }
711
- };
1475
+ return query(this.asQueryTransport(), c, q, p, o);
712
1476
  }
713
- async multiQuerySearch(collection, vectors, options) {
1477
+ async queryExplain(q, p) {
714
1478
  this.ensureInitialized();
715
- const formattedVectors = vectors.map(
716
- (v) => v instanceof Float32Array ? Array.from(v) : v
717
- );
718
- const response = await this.request(
719
- "POST",
720
- `/collections/${encodeURIComponent(collection)}/search/multi`,
721
- {
722
- vectors: formattedVectors,
723
- top_k: options?.k ?? 10,
724
- strategy: options?.fusion ?? "rrf",
725
- rrf_k: options?.fusionParams?.k ?? 60,
726
- filter: options?.filter
727
- }
728
- );
729
- if (response.error) {
730
- if (response.error.code === "NOT_FOUND") {
731
- throw new NotFoundError(`Collection '${collection}'`);
732
- }
733
- throw new VelesDBError(response.error.message, response.error.code);
734
- }
735
- return response.data?.results ?? [];
1479
+ return queryExplain(this.asQueryTransport(), q, p);
736
1480
  }
737
- async isEmpty(collection) {
1481
+ async collectionSanity(collection) {
738
1482
  this.ensureInitialized();
739
- const response = await this.request(
740
- "GET",
741
- `/collections/${encodeURIComponent(collection)}/empty`
742
- );
743
- if (response.error) {
744
- if (response.error.code === "NOT_FOUND") {
745
- throw new NotFoundError(`Collection '${collection}'`);
746
- }
747
- throw new VelesDBError(response.error.message, response.error.code);
748
- }
749
- return response.data?.is_empty ?? true;
1483
+ return collectionSanity(this.asQueryTransport(), collection);
750
1484
  }
751
- async flush(collection) {
1485
+ // ==========================================================================
1486
+ // Graph — delegates to graph-backend.ts
1487
+ // ==========================================================================
1488
+ async addEdge(collection, edge) {
752
1489
  this.ensureInitialized();
753
- const response = await this.request(
754
- "POST",
755
- `/collections/${encodeURIComponent(collection)}/flush`
756
- );
757
- if (response.error) {
758
- if (response.error.code === "NOT_FOUND") {
759
- throw new NotFoundError(`Collection '${collection}'`);
760
- }
761
- throw new VelesDBError(response.error.message, response.error.code);
762
- }
1490
+ return addEdge(this.asCrudTransport(), collection, edge);
763
1491
  }
764
- async close() {
765
- this._initialized = false;
1492
+ async getEdges(collection, options) {
1493
+ this.ensureInitialized();
1494
+ return getEdges(this.asCrudTransport(), collection, options);
766
1495
  }
767
- // ========================================================================
768
- // Index Management (EPIC-009)
769
- // ========================================================================
1496
+ async traverseGraph(collection, req) {
1497
+ this.ensureInitialized();
1498
+ return traverseGraph(this.asCrudTransport(), collection, req);
1499
+ }
1500
+ async getNodeDegree(collection, nodeId) {
1501
+ this.ensureInitialized();
1502
+ return getNodeDegree(this.asCrudTransport(), collection, nodeId);
1503
+ }
1504
+ async createGraphCollection(name, config) {
1505
+ this.ensureInitialized();
1506
+ return createGraphCollection(this.asCrudTransport(), name, config);
1507
+ }
1508
+ // ==========================================================================
1509
+ // Index — delegates to index-backend.ts
1510
+ // ==========================================================================
770
1511
  async createIndex(collection, options) {
771
1512
  this.ensureInitialized();
772
- const response = await this.request(
773
- "POST",
774
- `/collections/${encodeURIComponent(collection)}/indexes`,
775
- {
776
- label: options.label,
777
- property: options.property,
778
- index_type: options.indexType ?? "hash"
779
- }
780
- );
781
- if (response.error) {
782
- if (response.error.code === "NOT_FOUND") {
783
- throw new NotFoundError(`Collection '${collection}'`);
784
- }
785
- throw new VelesDBError(response.error.message, response.error.code);
786
- }
1513
+ return createIndex(this.asCrudTransport(), collection, options);
787
1514
  }
788
1515
  async listIndexes(collection) {
789
1516
  this.ensureInitialized();
790
- const response = await this.request(
791
- "GET",
792
- `/collections/${encodeURIComponent(collection)}/indexes`
793
- );
794
- if (response.error) {
795
- if (response.error.code === "NOT_FOUND") {
796
- throw new NotFoundError(`Collection '${collection}'`);
797
- }
798
- throw new VelesDBError(response.error.message, response.error.code);
799
- }
800
- return (response.data?.indexes ?? []).map((idx) => ({
801
- label: idx.label,
802
- property: idx.property,
803
- indexType: idx.index_type,
804
- cardinality: idx.cardinality,
805
- memoryBytes: idx.memory_bytes
806
- }));
1517
+ return listIndexes(this.asCrudTransport(), collection);
807
1518
  }
808
1519
  async hasIndex(collection, label, property) {
809
- const indexes = await this.listIndexes(collection);
810
- return indexes.some((idx) => idx.label === label && idx.property === property);
1520
+ this.ensureInitialized();
1521
+ return hasIndex(this.asCrudTransport(), collection, label, property);
811
1522
  }
812
1523
  async dropIndex(collection, label, property) {
813
1524
  this.ensureInitialized();
814
- const response = await this.request(
815
- "DELETE",
816
- `/collections/${encodeURIComponent(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
817
- );
818
- if (response.error) {
819
- if (response.error.code === "NOT_FOUND") {
820
- return false;
821
- }
822
- throw new VelesDBError(response.error.message, response.error.code);
823
- }
824
- return response.data?.dropped ?? true;
1525
+ return dropIndex(this.asCrudTransport(), collection, label, property);
825
1526
  }
826
- // ========================================================================
827
- // Knowledge Graph (EPIC-016 US-041)
828
- // ========================================================================
829
- async addEdge(collection, edge) {
1527
+ // ==========================================================================
1528
+ // Admin delegates to admin-backend.ts
1529
+ // ==========================================================================
1530
+ async getCollectionStats(collection) {
830
1531
  this.ensureInitialized();
831
- const response = await this.request(
832
- "POST",
833
- `/collections/${encodeURIComponent(collection)}/graph/edges`,
834
- {
835
- id: edge.id,
836
- source: edge.source,
837
- target: edge.target,
838
- label: edge.label,
839
- properties: edge.properties ?? {}
840
- }
841
- );
842
- if (response.error) {
843
- if (response.error.code === "NOT_FOUND") {
844
- throw new NotFoundError(`Collection '${collection}'`);
845
- }
846
- throw new VelesDBError(response.error.message, response.error.code);
847
- }
1532
+ return getCollectionStats(this.asCrudTransport(), collection);
848
1533
  }
849
- async getEdges(collection, options) {
1534
+ async analyzeCollection(collection) {
850
1535
  this.ensureInitialized();
851
- const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
852
- const response = await this.request(
853
- "GET",
854
- `/collections/${encodeURIComponent(collection)}/graph/edges${queryParams}`
855
- );
856
- if (response.error) {
857
- if (response.error.code === "NOT_FOUND") {
858
- throw new NotFoundError(`Collection '${collection}'`);
859
- }
860
- throw new VelesDBError(response.error.message, response.error.code);
861
- }
862
- return response.data?.edges ?? [];
1536
+ return analyzeCollection(this.asCrudTransport(), collection);
863
1537
  }
864
- // ========================================================================
865
- // Graph Traversal (EPIC-016 US-050)
866
- // ========================================================================
867
- async traverseGraph(collection, request) {
1538
+ async getCollectionConfig(collection) {
868
1539
  this.ensureInitialized();
869
- const response = await this.request(
870
- "POST",
871
- `/collections/${encodeURIComponent(collection)}/graph/traverse`,
872
- {
873
- source: request.source,
874
- strategy: request.strategy ?? "bfs",
875
- max_depth: request.maxDepth ?? 3,
876
- limit: request.limit ?? 100,
877
- cursor: request.cursor,
878
- rel_types: request.relTypes ?? []
879
- }
880
- );
881
- if (response.error) {
882
- if (response.error.code === "NOT_FOUND") {
883
- throw new NotFoundError(`Collection '${collection}'`);
884
- }
885
- throw new VelesDBError(response.error.message, response.error.code);
886
- }
887
- const data = response.data;
888
- return {
889
- results: data.results.map((r) => ({
890
- targetId: r.target_id,
891
- depth: r.depth,
892
- path: r.path
893
- })),
894
- nextCursor: data.next_cursor ?? void 0,
895
- hasMore: data.has_more,
896
- stats: {
897
- visited: data.stats.visited,
898
- depthReached: data.stats.depth_reached
899
- }
900
- };
1540
+ return getCollectionConfig(this.asCrudTransport(), collection);
901
1541
  }
902
- async getNodeDegree(collection, nodeId) {
1542
+ // ==========================================================================
1543
+ // Streaming / PQ — delegates to streaming-backend.ts
1544
+ // ==========================================================================
1545
+ async trainPq(collection, options) {
903
1546
  this.ensureInitialized();
904
- const response = await this.request(
905
- "GET",
906
- `/collections/${encodeURIComponent(collection)}/graph/nodes/${nodeId}/degree`
907
- );
908
- if (response.error) {
909
- if (response.error.code === "NOT_FOUND") {
910
- throw new NotFoundError(`Collection '${collection}'`);
911
- }
912
- throw new VelesDBError(response.error.message, response.error.code);
913
- }
914
- return {
915
- inDegree: response.data?.in_degree ?? 0,
916
- outDegree: response.data?.out_degree ?? 0
917
- };
1547
+ return trainPq(this.asStreamingTransport(), collection, options);
1548
+ }
1549
+ async streamInsert(collection, docs) {
1550
+ this.ensureInitialized();
1551
+ return streamInsert(this.asStreamingTransport(), collection, docs);
1552
+ }
1553
+ // ==========================================================================
1554
+ // Agent Memory — delegates to agent-memory-backend.ts
1555
+ // ==========================================================================
1556
+ async storeSemanticFact(collection, entry) {
1557
+ this.ensureInitialized();
1558
+ return storeSemanticFact(this.asAgentMemoryTransport(), collection, entry);
1559
+ }
1560
+ async searchSemanticMemory(collection, embedding, k = 5) {
1561
+ return searchSemanticMemory(this.asAgentMemoryTransport(), collection, embedding, k);
1562
+ }
1563
+ async recordEpisodicEvent(collection, event) {
1564
+ this.ensureInitialized();
1565
+ return recordEpisodicEvent(this.asAgentMemoryTransport(), collection, event);
1566
+ }
1567
+ async recallEpisodicEvents(collection, embedding, k = 5) {
1568
+ return recallEpisodicEvents(this.asAgentMemoryTransport(), collection, embedding, k);
1569
+ }
1570
+ async storeProceduralPattern(collection, pattern) {
1571
+ this.ensureInitialized();
1572
+ return storeProceduralPattern(this.asAgentMemoryTransport(), collection, pattern);
1573
+ }
1574
+ async matchProceduralPatterns(collection, embedding, k = 5) {
1575
+ return matchProceduralPatterns(this.asAgentMemoryTransport(), collection, embedding, k);
1576
+ }
1577
+ };
1578
+
1579
+ // src/agent-memory.ts
1580
+ var AgentMemoryClient = class {
1581
+ constructor(backend, config) {
1582
+ this.backend = backend;
1583
+ this.config = config;
1584
+ }
1585
+ /** Configured embedding dimension (default: 384) */
1586
+ get dimension() {
1587
+ return this.config?.dimension ?? 384;
1588
+ }
1589
+ /** Store a semantic fact */
1590
+ async storeFact(collection, entry) {
1591
+ return this.backend.storeSemanticFact(collection, entry);
1592
+ }
1593
+ /** Search semantic memory */
1594
+ async searchFacts(collection, embedding, k = 5) {
1595
+ return this.backend.searchSemanticMemory(collection, embedding, k);
1596
+ }
1597
+ /** Record an episodic event */
1598
+ async recordEvent(collection, event) {
1599
+ return this.backend.recordEpisodicEvent(collection, event);
1600
+ }
1601
+ /** Recall episodic events */
1602
+ async recallEvents(collection, embedding, k = 5) {
1603
+ return this.backend.recallEpisodicEvents(collection, embedding, k);
1604
+ }
1605
+ /** Store a procedural pattern */
1606
+ async learnProcedure(collection, pattern) {
1607
+ return this.backend.storeProceduralPattern(collection, pattern);
1608
+ }
1609
+ /** Match procedural patterns */
1610
+ async recallProcedures(collection, embedding, k = 5) {
1611
+ return this.backend.matchProceduralPatterns(collection, embedding, k);
918
1612
  }
919
1613
  };
920
1614
 
@@ -1077,6 +1771,18 @@ var VelesDB = class {
1077
1771
  if (!Array.isArray(doc.vector) && !(doc.vector instanceof Float32Array)) {
1078
1772
  throw new ValidationError("Vector must be an array or Float32Array");
1079
1773
  }
1774
+ if (this.config.backend === "rest" && (typeof doc.id !== "number" || !Number.isInteger(doc.id) || doc.id < 0 || doc.id > Number.MAX_SAFE_INTEGER)) {
1775
+ throw new ValidationError(
1776
+ `REST backend requires numeric u64-compatible document IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER})`
1777
+ );
1778
+ }
1779
+ }
1780
+ validateRestPointId(id) {
1781
+ if (this.config.backend === "rest" && (typeof id !== "number" || !Number.isInteger(id) || id < 0 || id > Number.MAX_SAFE_INTEGER)) {
1782
+ throw new ValidationError(
1783
+ `REST backend requires numeric u64-compatible document IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER})`
1784
+ );
1785
+ }
1080
1786
  }
1081
1787
  /**
1082
1788
  * Search for similar vectors
@@ -1086,12 +1792,12 @@ var VelesDB = class {
1086
1792
  * @param options - Search options
1087
1793
  * @returns Search results sorted by relevance
1088
1794
  */
1089
- async search(collection, query, options) {
1795
+ async search(collection, query2, options) {
1090
1796
  this.ensureInitialized();
1091
- if (!query || !Array.isArray(query) && !(query instanceof Float32Array)) {
1797
+ if (!query2 || !Array.isArray(query2) && !(query2 instanceof Float32Array)) {
1092
1798
  throw new ValidationError("Query must be an array or Float32Array");
1093
1799
  }
1094
- return this.backend.search(collection, query, options);
1800
+ return this.backend.search(collection, query2, options);
1095
1801
  }
1096
1802
  /**
1097
1803
  * Search for multiple vectors in parallel
@@ -1121,6 +1827,7 @@ var VelesDB = class {
1121
1827
  */
1122
1828
  async delete(collection, id) {
1123
1829
  this.ensureInitialized();
1830
+ this.validateRestPointId(id);
1124
1831
  return this.backend.delete(collection, id);
1125
1832
  }
1126
1833
  /**
@@ -1132,6 +1839,7 @@ var VelesDB = class {
1132
1839
  */
1133
1840
  async get(collection, id) {
1134
1841
  this.ensureInitialized();
1842
+ this.validateRestPointId(id);
1135
1843
  return this.backend.get(collection, id);
1136
1844
  }
1137
1845
  /**
@@ -1142,12 +1850,12 @@ var VelesDB = class {
1142
1850
  * @param options - Search options (k, filter)
1143
1851
  * @returns Search results sorted by BM25 score
1144
1852
  */
1145
- async textSearch(collection, query, options) {
1853
+ async textSearch(collection, query2, options) {
1146
1854
  this.ensureInitialized();
1147
- if (!query || typeof query !== "string") {
1855
+ if (!query2 || typeof query2 !== "string") {
1148
1856
  throw new ValidationError("Query must be a non-empty string");
1149
1857
  }
1150
- return this.backend.textSearch(collection, query, options);
1858
+ return this.backend.textSearch(collection, query2, options);
1151
1859
  }
1152
1860
  /**
1153
1861
  * Perform hybrid search combining vector similarity and BM25 text search
@@ -1186,7 +1894,7 @@ var VelesDB = class {
1186
1894
  * `, { q: queryVector });
1187
1895
  *
1188
1896
  * for (const r of response.results) {
1189
- * console.log(`Node ${r.nodeId}: ${r.fusedScore}`);
1897
+ * console.log(`ID ${r.id}, title: ${r.title}`);
1190
1898
  * }
1191
1899
  * ```
1192
1900
  */
@@ -1227,6 +1935,20 @@ var VelesDB = class {
1227
1935
  * });
1228
1936
  * ```
1229
1937
  */
1938
+ async queryExplain(queryString, params) {
1939
+ this.ensureInitialized();
1940
+ if (!queryString || typeof queryString !== "string") {
1941
+ throw new ValidationError("Query string must be a non-empty string");
1942
+ }
1943
+ return this.backend.queryExplain(queryString, params);
1944
+ }
1945
+ async collectionSanity(collection) {
1946
+ this.ensureInitialized();
1947
+ if (!collection || typeof collection !== "string") {
1948
+ throw new ValidationError("Collection name must be a non-empty string");
1949
+ }
1950
+ return this.backend.collectionSanity(collection);
1951
+ }
1230
1952
  async multiQuerySearch(collection, vectors, options) {
1231
1953
  this.ensureInitialized();
1232
1954
  if (!Array.isArray(vectors) || vectors.length === 0) {
@@ -1239,9 +1961,39 @@ var VelesDB = class {
1239
1961
  }
1240
1962
  return this.backend.multiQuerySearch(collection, vectors, options);
1241
1963
  }
1964
+ /**
1965
+ * Train Product Quantization on a collection
1966
+ *
1967
+ * @param collection - Collection name
1968
+ * @param options - PQ training options (m, k, opq)
1969
+ * @returns Server response message
1970
+ */
1971
+ async trainPq(collection, options) {
1972
+ this.ensureInitialized();
1973
+ return this.backend.trainPq(collection, options);
1974
+ }
1975
+ /**
1976
+ * Stream-insert documents with backpressure support
1977
+ *
1978
+ * Sends documents sequentially to respect server backpressure.
1979
+ * Throws BackpressureError on 429 responses.
1980
+ *
1981
+ * @param collection - Collection name
1982
+ * @param docs - Documents to insert
1983
+ */
1984
+ async streamInsert(collection, docs) {
1985
+ this.ensureInitialized();
1986
+ if (!Array.isArray(docs)) {
1987
+ throw new ValidationError("Documents must be an array");
1988
+ }
1989
+ for (const doc of docs) {
1990
+ this.validateDocument(doc);
1991
+ }
1992
+ await this.backend.streamInsert(collection, docs);
1993
+ }
1242
1994
  /**
1243
1995
  * Check if a collection is empty
1244
- *
1996
+ *
1245
1997
  * @param collection - Collection name
1246
1998
  * @returns true if empty, false otherwise
1247
1999
  */
@@ -1435,6 +2187,77 @@ var VelesDB = class {
1435
2187
  }
1436
2188
  return this.backend.getNodeDegree(collection, nodeId);
1437
2189
  }
2190
+ // ========================================================================
2191
+ // Graph Collection Management (Phase 8)
2192
+ // ========================================================================
2193
+ /**
2194
+ * Create a graph collection
2195
+ *
2196
+ * @param name - Collection name
2197
+ * @param config - Optional graph collection configuration
2198
+ */
2199
+ async createGraphCollection(name, config) {
2200
+ this.ensureInitialized();
2201
+ if (!name || typeof name !== "string") {
2202
+ throw new ValidationError("Collection name must be a non-empty string");
2203
+ }
2204
+ await this.backend.createGraphCollection(name, config);
2205
+ }
2206
+ /**
2207
+ * Get collection statistics (requires prior analyze)
2208
+ *
2209
+ * @param collection - Collection name
2210
+ * @returns Statistics or null if not yet analyzed
2211
+ */
2212
+ async getCollectionStats(collection) {
2213
+ this.ensureInitialized();
2214
+ return this.backend.getCollectionStats(collection);
2215
+ }
2216
+ /**
2217
+ * Analyze a collection to compute statistics
2218
+ *
2219
+ * @param collection - Collection name
2220
+ * @returns Computed statistics
2221
+ */
2222
+ async analyzeCollection(collection) {
2223
+ this.ensureInitialized();
2224
+ return this.backend.analyzeCollection(collection);
2225
+ }
2226
+ /**
2227
+ * Get collection configuration
2228
+ *
2229
+ * @param collection - Collection name
2230
+ * @returns Collection configuration details
2231
+ */
2232
+ async getCollectionConfig(collection) {
2233
+ this.ensureInitialized();
2234
+ return this.backend.getCollectionConfig(collection);
2235
+ }
2236
+ /**
2237
+ * Search returning only IDs and scores (lightweight)
2238
+ *
2239
+ * @param collection - Collection name
2240
+ * @param query - Query vector
2241
+ * @param options - Search options
2242
+ * @returns Array of id/score pairs
2243
+ */
2244
+ async searchIds(collection, query2, options) {
2245
+ this.ensureInitialized();
2246
+ return this.backend.searchIds(collection, query2, options);
2247
+ }
2248
+ // ========================================================================
2249
+ // Agent Memory (Phase 8)
2250
+ // ========================================================================
2251
+ /**
2252
+ * Create an agent memory interface
2253
+ *
2254
+ * @param config - Optional agent memory configuration
2255
+ * @returns AgentMemoryClient instance
2256
+ */
2257
+ agentMemory(config) {
2258
+ this.ensureInitialized();
2259
+ return new AgentMemoryClient(this.backend, config);
2260
+ }
1438
2261
  };
1439
2262
 
1440
2263
  // src/query-builder.ts
@@ -1739,6 +2562,8 @@ function velesql() {
1739
2562
  return new VelesQLBuilder();
1740
2563
  }
1741
2564
  export {
2565
+ AgentMemoryClient,
2566
+ BackpressureError,
1742
2567
  ConnectionError,
1743
2568
  NotFoundError,
1744
2569
  RestBackend,