@wiscale/velesdb-sdk 1.5.1 → 1.7.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/README.md +479 -194
- package/dist/index.d.mts +261 -62
- package/dist/index.d.ts +261 -62
- package/dist/index.js +1042 -699
- package/dist/index.mjs +1041 -699
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AgentMemoryClient: () => AgentMemoryClient,
|
|
33
34
|
BackpressureError: () => BackpressureError,
|
|
34
35
|
ConnectionError: () => ConnectionError,
|
|
35
36
|
NotFoundError: () => NotFoundError,
|
|
@@ -77,6 +78,38 @@ var BackpressureError = class extends VelesDBError {
|
|
|
77
78
|
}
|
|
78
79
|
};
|
|
79
80
|
|
|
81
|
+
// src/backends/shared.ts
|
|
82
|
+
function throwOnError(response, resourceLabel) {
|
|
83
|
+
if (!response.error) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (response.error.code === "NOT_FOUND" && resourceLabel !== void 0) {
|
|
87
|
+
throw new NotFoundError(resourceLabel);
|
|
88
|
+
}
|
|
89
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
90
|
+
}
|
|
91
|
+
function returnNullOnNotFound(response) {
|
|
92
|
+
if (!response.error) {
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
if (response.error.code === "NOT_FOUND") {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
99
|
+
}
|
|
100
|
+
function collectionPath(collection) {
|
|
101
|
+
return `/collections/${encodeURIComponent(collection)}`;
|
|
102
|
+
}
|
|
103
|
+
function toNumberArray(v) {
|
|
104
|
+
return v instanceof Float32Array ? Array.from(v) : v;
|
|
105
|
+
}
|
|
106
|
+
function wasmNotSupported(feature) {
|
|
107
|
+
throw new VelesDBError(
|
|
108
|
+
`${feature}: not supported in WASM backend. Use REST backend.`,
|
|
109
|
+
"NOT_SUPPORTED"
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
80
113
|
// src/backends/wasm.ts
|
|
81
114
|
var WasmBackend = class {
|
|
82
115
|
constructor() {
|
|
@@ -243,13 +276,13 @@ var WasmBackend = class {
|
|
|
243
276
|
}
|
|
244
277
|
}
|
|
245
278
|
}
|
|
246
|
-
async search(collectionName,
|
|
279
|
+
async search(collectionName, query2, options) {
|
|
247
280
|
this.ensureInitialized();
|
|
248
281
|
const collection = this.collections.get(collectionName);
|
|
249
282
|
if (!collection) {
|
|
250
283
|
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
251
284
|
}
|
|
252
|
-
const queryVector =
|
|
285
|
+
const queryVector = query2 instanceof Float32Array ? query2 : new Float32Array(query2);
|
|
253
286
|
if (queryVector.length !== collection.config.dimension) {
|
|
254
287
|
throw new VelesDBError(
|
|
255
288
|
`Query dimension mismatch: expected ${collection.config.dimension}, got ${queryVector.length}`,
|
|
@@ -406,14 +439,7 @@ var WasmBackend = class {
|
|
|
406
439
|
k
|
|
407
440
|
);
|
|
408
441
|
return {
|
|
409
|
-
results: raw
|
|
410
|
-
nodeId: r.nodeId ?? r.node_id,
|
|
411
|
-
vectorScore: r.vectorScore ?? r.vector_score ?? null,
|
|
412
|
-
graphScore: r.graphScore ?? r.graph_score ?? null,
|
|
413
|
-
fusedScore: r.fusedScore ?? r.fused_score ?? 0,
|
|
414
|
-
bindings: r.bindings ?? {},
|
|
415
|
-
columnData: r.columnData ?? r.column_data ?? null
|
|
416
|
-
})),
|
|
442
|
+
results: raw,
|
|
417
443
|
stats: {
|
|
418
444
|
executionTimeMs: 0,
|
|
419
445
|
strategy: "wasm-query",
|
|
@@ -462,17 +488,11 @@ var WasmBackend = class {
|
|
|
462
488
|
}
|
|
463
489
|
async queryExplain(_queryString, _params) {
|
|
464
490
|
this.ensureInitialized();
|
|
465
|
-
|
|
466
|
-
"Query explain is not supported in WASM backend. Use REST backend for EXPLAIN support.",
|
|
467
|
-
"NOT_SUPPORTED"
|
|
468
|
-
);
|
|
491
|
+
wasmNotSupported("Query explain");
|
|
469
492
|
}
|
|
470
493
|
async collectionSanity(_collection) {
|
|
471
494
|
this.ensureInitialized();
|
|
472
|
-
|
|
473
|
-
"Collection sanity endpoint is not supported in WASM backend. Use REST backend for diagnostics.",
|
|
474
|
-
"NOT_SUPPORTED"
|
|
475
|
-
);
|
|
495
|
+
wasmNotSupported("Collection sanity endpoint");
|
|
476
496
|
}
|
|
477
497
|
async isEmpty(collectionName) {
|
|
478
498
|
this.ensureInitialized();
|
|
@@ -552,51 +572,715 @@ var WasmBackend = class {
|
|
|
552
572
|
// ========================================================================
|
|
553
573
|
async addEdge(_collection, _edge) {
|
|
554
574
|
this.ensureInitialized();
|
|
555
|
-
|
|
556
|
-
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
557
|
-
"NOT_SUPPORTED"
|
|
558
|
-
);
|
|
575
|
+
wasmNotSupported("Knowledge Graph operations");
|
|
559
576
|
}
|
|
560
577
|
async getEdges(_collection, _options) {
|
|
561
578
|
this.ensureInitialized();
|
|
562
|
-
|
|
563
|
-
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
564
|
-
"NOT_SUPPORTED"
|
|
565
|
-
);
|
|
579
|
+
wasmNotSupported("Knowledge Graph operations");
|
|
566
580
|
}
|
|
567
581
|
async traverseGraph(_collection, _request) {
|
|
568
582
|
this.ensureInitialized();
|
|
569
|
-
|
|
570
|
-
"Graph traversal is not supported in WASM backend. Use REST backend for graph features.",
|
|
571
|
-
"NOT_SUPPORTED"
|
|
572
|
-
);
|
|
583
|
+
wasmNotSupported("Graph traversal");
|
|
573
584
|
}
|
|
574
585
|
async getNodeDegree(_collection, _nodeId) {
|
|
575
586
|
this.ensureInitialized();
|
|
576
|
-
|
|
577
|
-
"Graph degree query is not supported in WASM backend. Use REST backend for graph features.",
|
|
578
|
-
"NOT_SUPPORTED"
|
|
579
|
-
);
|
|
587
|
+
wasmNotSupported("Graph degree query");
|
|
580
588
|
}
|
|
581
589
|
// ========================================================================
|
|
582
590
|
// Sparse / PQ / Streaming (v1.5)
|
|
583
591
|
// ========================================================================
|
|
584
592
|
async trainPq(_collection, _options) {
|
|
585
593
|
this.ensureInitialized();
|
|
586
|
-
|
|
587
|
-
"PQ training is not available in WASM mode. Use REST backend for PQ training.",
|
|
588
|
-
"NOT_SUPPORTED"
|
|
589
|
-
);
|
|
594
|
+
wasmNotSupported("PQ training");
|
|
590
595
|
}
|
|
591
596
|
async streamInsert(_collection, _docs) {
|
|
592
597
|
this.ensureInitialized();
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
598
|
+
wasmNotSupported("Streaming insert");
|
|
599
|
+
}
|
|
600
|
+
// ========================================================================
|
|
601
|
+
// Graph Collection / Stats / Agent Memory (Phase 8) - WASM stubs
|
|
602
|
+
// ========================================================================
|
|
603
|
+
async createGraphCollection(_name, _config) {
|
|
604
|
+
this.ensureInitialized();
|
|
605
|
+
wasmNotSupported("Graph collections");
|
|
606
|
+
}
|
|
607
|
+
async getCollectionStats(_collection) {
|
|
608
|
+
this.ensureInitialized();
|
|
609
|
+
wasmNotSupported("Collection stats");
|
|
610
|
+
}
|
|
611
|
+
async analyzeCollection(_collection) {
|
|
612
|
+
this.ensureInitialized();
|
|
613
|
+
wasmNotSupported("Collection analyze");
|
|
614
|
+
}
|
|
615
|
+
async getCollectionConfig(_collection) {
|
|
616
|
+
this.ensureInitialized();
|
|
617
|
+
wasmNotSupported("Collection config");
|
|
618
|
+
}
|
|
619
|
+
async searchIds(_collection, _query, _options) {
|
|
620
|
+
this.ensureInitialized();
|
|
621
|
+
wasmNotSupported("searchIds");
|
|
622
|
+
}
|
|
623
|
+
async storeSemanticFact(_collection, _entry) {
|
|
624
|
+
this.ensureInitialized();
|
|
625
|
+
wasmNotSupported("Agent memory");
|
|
626
|
+
}
|
|
627
|
+
async searchSemanticMemory(_collection, _embedding, _k) {
|
|
628
|
+
this.ensureInitialized();
|
|
629
|
+
wasmNotSupported("Agent memory");
|
|
630
|
+
}
|
|
631
|
+
async recordEpisodicEvent(_collection, _event) {
|
|
632
|
+
this.ensureInitialized();
|
|
633
|
+
wasmNotSupported("Agent memory");
|
|
634
|
+
}
|
|
635
|
+
async recallEpisodicEvents(_collection, _embedding, _k) {
|
|
636
|
+
this.ensureInitialized();
|
|
637
|
+
wasmNotSupported("Agent memory");
|
|
638
|
+
}
|
|
639
|
+
async storeProceduralPattern(_collection, _pattern) {
|
|
640
|
+
this.ensureInitialized();
|
|
641
|
+
wasmNotSupported("Agent memory");
|
|
642
|
+
}
|
|
643
|
+
async matchProceduralPatterns(_collection, _embedding, _k) {
|
|
644
|
+
this.ensureInitialized();
|
|
645
|
+
wasmNotSupported("Agent memory");
|
|
597
646
|
}
|
|
598
647
|
};
|
|
599
648
|
|
|
649
|
+
// src/backends/agent-memory-backend.ts
|
|
650
|
+
var _idCounter = 0;
|
|
651
|
+
var _lastTimestamp = 0;
|
|
652
|
+
function generateUniqueId() {
|
|
653
|
+
let now = Date.now();
|
|
654
|
+
if (now <= _lastTimestamp) {
|
|
655
|
+
_idCounter++;
|
|
656
|
+
if (_idCounter >= 1e3) {
|
|
657
|
+
_lastTimestamp++;
|
|
658
|
+
_idCounter = 0;
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
_lastTimestamp = now;
|
|
662
|
+
_idCounter = 0;
|
|
663
|
+
}
|
|
664
|
+
return _lastTimestamp * 1e3 + _idCounter;
|
|
665
|
+
}
|
|
666
|
+
async function storeSemanticFact(transport, collection, entry) {
|
|
667
|
+
const response = await transport.requestJson(
|
|
668
|
+
"POST",
|
|
669
|
+
`${collectionPath(collection)}/points`,
|
|
670
|
+
{
|
|
671
|
+
points: [{
|
|
672
|
+
id: entry.id,
|
|
673
|
+
vector: entry.embedding,
|
|
674
|
+
payload: {
|
|
675
|
+
_memory_type: "semantic",
|
|
676
|
+
text: entry.text,
|
|
677
|
+
...entry.metadata
|
|
678
|
+
}
|
|
679
|
+
}]
|
|
680
|
+
}
|
|
681
|
+
);
|
|
682
|
+
throwOnError(response);
|
|
683
|
+
}
|
|
684
|
+
async function searchSemanticMemory(transport, collection, embedding, k = 5) {
|
|
685
|
+
return transport.searchVectors(collection, embedding, k, { _memory_type: "semantic" });
|
|
686
|
+
}
|
|
687
|
+
async function recordEpisodicEvent(transport, collection, event) {
|
|
688
|
+
const id = generateUniqueId();
|
|
689
|
+
const response = await transport.requestJson(
|
|
690
|
+
"POST",
|
|
691
|
+
`${collectionPath(collection)}/points`,
|
|
692
|
+
{
|
|
693
|
+
points: [{
|
|
694
|
+
id,
|
|
695
|
+
vector: event.embedding,
|
|
696
|
+
payload: {
|
|
697
|
+
_memory_type: "episodic",
|
|
698
|
+
event_type: event.eventType,
|
|
699
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
700
|
+
...event.data,
|
|
701
|
+
...event.metadata
|
|
702
|
+
}
|
|
703
|
+
}]
|
|
704
|
+
}
|
|
705
|
+
);
|
|
706
|
+
throwOnError(response);
|
|
707
|
+
}
|
|
708
|
+
async function recallEpisodicEvents(transport, collection, embedding, k = 5) {
|
|
709
|
+
return transport.searchVectors(collection, embedding, k, { _memory_type: "episodic" });
|
|
710
|
+
}
|
|
711
|
+
async function storeProceduralPattern(transport, collection, pattern) {
|
|
712
|
+
const id = generateUniqueId();
|
|
713
|
+
const response = await transport.requestJson(
|
|
714
|
+
"POST",
|
|
715
|
+
`${collectionPath(collection)}/points`,
|
|
716
|
+
{
|
|
717
|
+
points: [{
|
|
718
|
+
id,
|
|
719
|
+
payload: {
|
|
720
|
+
_memory_type: "procedural",
|
|
721
|
+
name: pattern.name,
|
|
722
|
+
steps: pattern.steps,
|
|
723
|
+
...pattern.metadata
|
|
724
|
+
}
|
|
725
|
+
}]
|
|
726
|
+
}
|
|
727
|
+
);
|
|
728
|
+
throwOnError(response);
|
|
729
|
+
}
|
|
730
|
+
async function matchProceduralPatterns(transport, collection, embedding, k = 5) {
|
|
731
|
+
return transport.searchVectors(collection, embedding, k, { _memory_type: "procedural" });
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/backends/search-backend.ts
|
|
735
|
+
async function search(transport, collection, query2, options) {
|
|
736
|
+
const queryVector = toNumberArray(query2);
|
|
737
|
+
const body = {
|
|
738
|
+
vector: queryVector,
|
|
739
|
+
top_k: options?.k ?? 10,
|
|
740
|
+
filter: options?.filter,
|
|
741
|
+
include_vectors: options?.includeVectors ?? false
|
|
742
|
+
};
|
|
743
|
+
if (options?.sparseVector) {
|
|
744
|
+
body.sparse_vector = transport.sparseToRest(options.sparseVector);
|
|
745
|
+
}
|
|
746
|
+
const response = await transport.requestJson(
|
|
747
|
+
"POST",
|
|
748
|
+
`${collectionPath(collection)}/search`,
|
|
749
|
+
body
|
|
750
|
+
);
|
|
751
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
752
|
+
return response.data?.results ?? [];
|
|
753
|
+
}
|
|
754
|
+
async function searchBatch(transport, collection, searches) {
|
|
755
|
+
const formattedSearches = searches.map((s) => ({
|
|
756
|
+
vector: toNumberArray(s.vector),
|
|
757
|
+
top_k: s.k ?? 10,
|
|
758
|
+
filter: s.filter
|
|
759
|
+
}));
|
|
760
|
+
const response = await transport.requestJson(
|
|
761
|
+
"POST",
|
|
762
|
+
`${collectionPath(collection)}/search/batch`,
|
|
763
|
+
{ searches: formattedSearches }
|
|
764
|
+
);
|
|
765
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
766
|
+
return response.data?.results.map((r) => r.results) ?? [];
|
|
767
|
+
}
|
|
768
|
+
async function textSearch(transport, collection, query2, options) {
|
|
769
|
+
const response = await transport.requestJson(
|
|
770
|
+
"POST",
|
|
771
|
+
`${collectionPath(collection)}/search/text`,
|
|
772
|
+
{
|
|
773
|
+
query: query2,
|
|
774
|
+
top_k: options?.k ?? 10,
|
|
775
|
+
filter: options?.filter
|
|
776
|
+
}
|
|
777
|
+
);
|
|
778
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
779
|
+
return response.data?.results ?? [];
|
|
780
|
+
}
|
|
781
|
+
async function hybridSearch(transport, collection, vector, textQuery, options) {
|
|
782
|
+
const queryVector = toNumberArray(vector);
|
|
783
|
+
const response = await transport.requestJson(
|
|
784
|
+
"POST",
|
|
785
|
+
`${collectionPath(collection)}/search/hybrid`,
|
|
786
|
+
{
|
|
787
|
+
vector: queryVector,
|
|
788
|
+
query: textQuery,
|
|
789
|
+
top_k: options?.k ?? 10,
|
|
790
|
+
vector_weight: options?.vectorWeight ?? 0.5,
|
|
791
|
+
filter: options?.filter
|
|
792
|
+
}
|
|
793
|
+
);
|
|
794
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
795
|
+
return response.data?.results ?? [];
|
|
796
|
+
}
|
|
797
|
+
async function multiQuerySearch(transport, collection, vectors, options) {
|
|
798
|
+
const formattedVectors = vectors.map(toNumberArray);
|
|
799
|
+
const response = await transport.requestJson(
|
|
800
|
+
"POST",
|
|
801
|
+
`${collectionPath(collection)}/search/multi`,
|
|
802
|
+
{
|
|
803
|
+
vectors: formattedVectors,
|
|
804
|
+
top_k: options?.k ?? 10,
|
|
805
|
+
strategy: options?.fusion ?? "rrf",
|
|
806
|
+
rrf_k: options?.fusionParams?.k ?? 60,
|
|
807
|
+
avg_weight: options?.fusionParams?.avgWeight,
|
|
808
|
+
max_weight: options?.fusionParams?.maxWeight,
|
|
809
|
+
hit_weight: options?.fusionParams?.hitWeight,
|
|
810
|
+
filter: options?.filter
|
|
811
|
+
}
|
|
812
|
+
);
|
|
813
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
814
|
+
return response.data?.results ?? [];
|
|
815
|
+
}
|
|
816
|
+
async function searchIds(transport, collection, query2, options) {
|
|
817
|
+
const queryVector = toNumberArray(query2);
|
|
818
|
+
const response = await transport.requestJson(
|
|
819
|
+
"POST",
|
|
820
|
+
`${collectionPath(collection)}/search/ids`,
|
|
821
|
+
{
|
|
822
|
+
vector: queryVector,
|
|
823
|
+
top_k: options?.k ?? 10,
|
|
824
|
+
filter: options?.filter
|
|
825
|
+
}
|
|
826
|
+
);
|
|
827
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
828
|
+
return response.data?.results ?? [];
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// src/backends/graph-backend.ts
|
|
832
|
+
async function addEdge(transport, collection, edge) {
|
|
833
|
+
const response = await transport.requestJson(
|
|
834
|
+
"POST",
|
|
835
|
+
`${collectionPath(collection)}/graph/edges`,
|
|
836
|
+
{
|
|
837
|
+
id: edge.id,
|
|
838
|
+
source: edge.source,
|
|
839
|
+
target: edge.target,
|
|
840
|
+
label: edge.label,
|
|
841
|
+
properties: edge.properties ?? {}
|
|
842
|
+
}
|
|
843
|
+
);
|
|
844
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
845
|
+
}
|
|
846
|
+
async function getEdges(transport, collection, options) {
|
|
847
|
+
const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
|
|
848
|
+
const response = await transport.requestJson(
|
|
849
|
+
"GET",
|
|
850
|
+
`${collectionPath(collection)}/graph/edges${queryParams}`
|
|
851
|
+
);
|
|
852
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
853
|
+
return response.data?.edges ?? [];
|
|
854
|
+
}
|
|
855
|
+
async function traverseGraph(transport, collection, request) {
|
|
856
|
+
const response = await transport.requestJson(
|
|
857
|
+
"POST",
|
|
858
|
+
`${collectionPath(collection)}/graph/traverse`,
|
|
859
|
+
{
|
|
860
|
+
source: request.source,
|
|
861
|
+
strategy: request.strategy ?? "bfs",
|
|
862
|
+
max_depth: request.maxDepth ?? 3,
|
|
863
|
+
limit: request.limit ?? 100,
|
|
864
|
+
cursor: request.cursor,
|
|
865
|
+
rel_types: request.relTypes ?? []
|
|
866
|
+
}
|
|
867
|
+
);
|
|
868
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
869
|
+
const data = response.data;
|
|
870
|
+
return {
|
|
871
|
+
results: data.results.map((r) => ({
|
|
872
|
+
targetId: r.target_id,
|
|
873
|
+
depth: r.depth,
|
|
874
|
+
path: r.path
|
|
875
|
+
})),
|
|
876
|
+
nextCursor: data.next_cursor ?? void 0,
|
|
877
|
+
hasMore: data.has_more,
|
|
878
|
+
stats: {
|
|
879
|
+
visited: data.stats.visited,
|
|
880
|
+
depthReached: data.stats.depth_reached
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
async function getNodeDegree(transport, collection, nodeId) {
|
|
885
|
+
const response = await transport.requestJson(
|
|
886
|
+
"GET",
|
|
887
|
+
`${collectionPath(collection)}/graph/nodes/${nodeId}/degree`
|
|
888
|
+
);
|
|
889
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
890
|
+
return {
|
|
891
|
+
inDegree: response.data?.in_degree ?? 0,
|
|
892
|
+
outDegree: response.data?.out_degree ?? 0
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
async function createGraphCollection(transport, name, config) {
|
|
896
|
+
const response = await transport.requestJson("POST", "/collections", {
|
|
897
|
+
name,
|
|
898
|
+
collection_type: "graph",
|
|
899
|
+
dimension: config?.dimension,
|
|
900
|
+
metric: config?.metric ?? "cosine",
|
|
901
|
+
schema_mode: config?.schemaMode ?? "schemaless"
|
|
902
|
+
});
|
|
903
|
+
throwOnError(response);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/backends/query-backend.ts
|
|
907
|
+
function isLikelyAggregationQuery(queryString) {
|
|
908
|
+
return /\bGROUP\s+BY\b|\bHAVING\b|\bCOUNT\s*\(|\bSUM\s*\(|\bAVG\s*\(|\bMIN\s*\(|\bMAX\s*\(/i.test(
|
|
909
|
+
queryString
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
async function query(transport, collection, queryString, params, options) {
|
|
913
|
+
const endpoint = isLikelyAggregationQuery(queryString) ? "/aggregate" : "/query";
|
|
914
|
+
const response = await transport.requestJson(
|
|
915
|
+
"POST",
|
|
916
|
+
endpoint,
|
|
917
|
+
{
|
|
918
|
+
query: queryString,
|
|
919
|
+
params: params ?? {},
|
|
920
|
+
collection,
|
|
921
|
+
timeout_ms: options?.timeoutMs,
|
|
922
|
+
stream: options?.stream ?? false
|
|
923
|
+
}
|
|
924
|
+
);
|
|
925
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
926
|
+
const rawData = response.data;
|
|
927
|
+
if (rawData && Object.prototype.hasOwnProperty.call(rawData, "result")) {
|
|
928
|
+
return {
|
|
929
|
+
result: rawData.result,
|
|
930
|
+
stats: {
|
|
931
|
+
executionTimeMs: rawData.timing_ms ?? 0,
|
|
932
|
+
strategy: "aggregation",
|
|
933
|
+
scannedNodes: 0
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
results: rawData?.results ?? [],
|
|
939
|
+
stats: {
|
|
940
|
+
executionTimeMs: rawData?.timing_ms ?? 0,
|
|
941
|
+
strategy: "select",
|
|
942
|
+
scannedNodes: rawData?.rows_returned ?? 0
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
async function queryExplain(transport, queryString, params) {
|
|
947
|
+
const response = await transport.requestJson(
|
|
948
|
+
"POST",
|
|
949
|
+
"/query/explain",
|
|
950
|
+
{
|
|
951
|
+
query: queryString,
|
|
952
|
+
params: params ?? {}
|
|
953
|
+
}
|
|
954
|
+
);
|
|
955
|
+
throwOnError(response);
|
|
956
|
+
const data = response.data;
|
|
957
|
+
return {
|
|
958
|
+
query: data.query,
|
|
959
|
+
queryType: data.query_type,
|
|
960
|
+
collection: data.collection,
|
|
961
|
+
plan: data.plan.map((step) => ({
|
|
962
|
+
step: step.step,
|
|
963
|
+
operation: step.operation,
|
|
964
|
+
description: step.description,
|
|
965
|
+
estimatedRows: step.estimated_rows
|
|
966
|
+
})),
|
|
967
|
+
estimatedCost: {
|
|
968
|
+
usesIndex: data.estimated_cost.uses_index,
|
|
969
|
+
indexName: data.estimated_cost.index_name,
|
|
970
|
+
selectivity: data.estimated_cost.selectivity,
|
|
971
|
+
complexity: data.estimated_cost.complexity
|
|
972
|
+
},
|
|
973
|
+
features: {
|
|
974
|
+
hasVectorSearch: data.features.has_vector_search,
|
|
975
|
+
hasFilter: data.features.has_filter,
|
|
976
|
+
hasOrderBy: data.features.has_order_by,
|
|
977
|
+
hasGroupBy: data.features.has_group_by,
|
|
978
|
+
hasAggregation: data.features.has_aggregation,
|
|
979
|
+
hasJoin: data.features.has_join,
|
|
980
|
+
hasFusion: data.features.has_fusion,
|
|
981
|
+
limit: data.features.limit,
|
|
982
|
+
offset: data.features.offset
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
async function collectionSanity(transport, collection) {
|
|
987
|
+
const response = await transport.requestJson(
|
|
988
|
+
"GET",
|
|
989
|
+
`${collectionPath(collection)}/sanity`
|
|
990
|
+
);
|
|
991
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
992
|
+
const data = response.data;
|
|
993
|
+
return {
|
|
994
|
+
collection: data.collection,
|
|
995
|
+
dimension: data.dimension,
|
|
996
|
+
metric: data.metric,
|
|
997
|
+
pointCount: data.point_count,
|
|
998
|
+
isEmpty: data.is_empty,
|
|
999
|
+
checks: {
|
|
1000
|
+
hasVectors: data.checks.has_vectors,
|
|
1001
|
+
searchReady: data.checks.search_ready,
|
|
1002
|
+
dimensionConfigured: data.checks.dimension_configured
|
|
1003
|
+
},
|
|
1004
|
+
diagnostics: {
|
|
1005
|
+
searchRequestsTotal: data.diagnostics.search_requests_total,
|
|
1006
|
+
dimensionMismatchTotal: data.diagnostics.dimension_mismatch_total,
|
|
1007
|
+
emptySearchResultsTotal: data.diagnostics.empty_search_results_total,
|
|
1008
|
+
filterParseErrorsTotal: data.diagnostics.filter_parse_errors_total
|
|
1009
|
+
},
|
|
1010
|
+
hints: data.hints ?? []
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// src/backends/admin-backend.ts
|
|
1015
|
+
function mapStatsResponse(data) {
|
|
1016
|
+
return {
|
|
1017
|
+
totalPoints: data.total_points,
|
|
1018
|
+
totalSizeBytes: data.total_size_bytes,
|
|
1019
|
+
rowCount: data.row_count,
|
|
1020
|
+
deletedCount: data.deleted_count,
|
|
1021
|
+
avgRowSizeBytes: data.avg_row_size_bytes,
|
|
1022
|
+
payloadSizeBytes: data.payload_size_bytes,
|
|
1023
|
+
lastAnalyzedEpochMs: data.last_analyzed_epoch_ms
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
async function getCollectionStats(transport, collection) {
|
|
1027
|
+
const response = await transport.requestJson(
|
|
1028
|
+
"GET",
|
|
1029
|
+
`${collectionPath(collection)}/stats`
|
|
1030
|
+
);
|
|
1031
|
+
if (returnNullOnNotFound(response)) {
|
|
1032
|
+
return null;
|
|
1033
|
+
}
|
|
1034
|
+
return mapStatsResponse(response.data);
|
|
1035
|
+
}
|
|
1036
|
+
async function analyzeCollection(transport, collection) {
|
|
1037
|
+
const response = await transport.requestJson(
|
|
1038
|
+
"POST",
|
|
1039
|
+
`${collectionPath(collection)}/analyze`
|
|
1040
|
+
);
|
|
1041
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1042
|
+
return mapStatsResponse(response.data);
|
|
1043
|
+
}
|
|
1044
|
+
async function getCollectionConfig(transport, collection) {
|
|
1045
|
+
const response = await transport.requestJson("GET", `${collectionPath(collection)}/config`);
|
|
1046
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1047
|
+
const data = response.data;
|
|
1048
|
+
return {
|
|
1049
|
+
name: data.name,
|
|
1050
|
+
dimension: data.dimension,
|
|
1051
|
+
metric: data.metric,
|
|
1052
|
+
storageMode: data.storage_mode,
|
|
1053
|
+
pointCount: data.point_count,
|
|
1054
|
+
metadataOnly: data.metadata_only,
|
|
1055
|
+
graphSchema: data.graph_schema,
|
|
1056
|
+
embeddingDimension: data.embedding_dimension
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// src/backends/index-backend.ts
|
|
1061
|
+
async function createIndex(transport, collection, options) {
|
|
1062
|
+
const response = await transport.requestJson(
|
|
1063
|
+
"POST",
|
|
1064
|
+
`${collectionPath(collection)}/indexes`,
|
|
1065
|
+
{
|
|
1066
|
+
label: options.label,
|
|
1067
|
+
property: options.property,
|
|
1068
|
+
index_type: options.indexType ?? "hash"
|
|
1069
|
+
}
|
|
1070
|
+
);
|
|
1071
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1072
|
+
}
|
|
1073
|
+
async function listIndexes(transport, collection) {
|
|
1074
|
+
const response = await transport.requestJson(
|
|
1075
|
+
"GET",
|
|
1076
|
+
`${collectionPath(collection)}/indexes`
|
|
1077
|
+
);
|
|
1078
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1079
|
+
return (response.data?.indexes ?? []).map((idx) => ({
|
|
1080
|
+
label: idx.label,
|
|
1081
|
+
property: idx.property,
|
|
1082
|
+
indexType: idx.index_type,
|
|
1083
|
+
cardinality: idx.cardinality,
|
|
1084
|
+
memoryBytes: idx.memory_bytes
|
|
1085
|
+
}));
|
|
1086
|
+
}
|
|
1087
|
+
async function hasIndex(transport, collection, label, property) {
|
|
1088
|
+
const indexes = await listIndexes(transport, collection);
|
|
1089
|
+
return indexes.some((idx) => idx.label === label && idx.property === property);
|
|
1090
|
+
}
|
|
1091
|
+
async function dropIndex(transport, collection, label, property) {
|
|
1092
|
+
const response = await transport.requestJson(
|
|
1093
|
+
"DELETE",
|
|
1094
|
+
`${collectionPath(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
|
|
1095
|
+
);
|
|
1096
|
+
if (returnNullOnNotFound(response)) {
|
|
1097
|
+
return false;
|
|
1098
|
+
}
|
|
1099
|
+
return response.data?.dropped ?? true;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/backends/streaming-backend.ts
|
|
1103
|
+
async function trainPq(transport, collection, options) {
|
|
1104
|
+
const m = options?.m ?? 8;
|
|
1105
|
+
const k = options?.k ?? 256;
|
|
1106
|
+
const withClause = options?.opq ? `WITH (m=${m}, k=${k}, opq=true)` : `WITH (m=${m}, k=${k})`;
|
|
1107
|
+
const queryString = `TRAIN QUANTIZER ON ${collection} ${withClause}`;
|
|
1108
|
+
const response = await transport.requestJson(
|
|
1109
|
+
"POST",
|
|
1110
|
+
"/query",
|
|
1111
|
+
{ query: queryString }
|
|
1112
|
+
);
|
|
1113
|
+
throwOnError(response);
|
|
1114
|
+
return response.data?.message ?? "PQ training initiated";
|
|
1115
|
+
}
|
|
1116
|
+
async function streamInsert(transport, collection, docs) {
|
|
1117
|
+
for (const doc of docs) {
|
|
1118
|
+
const restId = transport.parseRestPointId(doc.id);
|
|
1119
|
+
const vector = toNumberArray(doc.vector);
|
|
1120
|
+
const body = {
|
|
1121
|
+
id: restId,
|
|
1122
|
+
vector,
|
|
1123
|
+
payload: doc.payload
|
|
1124
|
+
};
|
|
1125
|
+
if (doc.sparseVector) {
|
|
1126
|
+
body.sparse_vector = transport.sparseVectorToRestFormat(doc.sparseVector);
|
|
1127
|
+
}
|
|
1128
|
+
const url = `${transport.baseUrl}${collectionPath(collection)}/stream/insert`;
|
|
1129
|
+
const headers = {
|
|
1130
|
+
"Content-Type": "application/json"
|
|
1131
|
+
};
|
|
1132
|
+
if (transport.apiKey) {
|
|
1133
|
+
headers["Authorization"] = `Bearer ${transport.apiKey}`;
|
|
1134
|
+
}
|
|
1135
|
+
const controller = new AbortController();
|
|
1136
|
+
const timeoutId = setTimeout(() => controller.abort(), transport.timeout);
|
|
1137
|
+
try {
|
|
1138
|
+
const response = await fetch(url, {
|
|
1139
|
+
method: "POST",
|
|
1140
|
+
headers,
|
|
1141
|
+
body: JSON.stringify(body),
|
|
1142
|
+
signal: controller.signal
|
|
1143
|
+
});
|
|
1144
|
+
clearTimeout(timeoutId);
|
|
1145
|
+
if (response.status === 429) {
|
|
1146
|
+
throw new BackpressureError();
|
|
1147
|
+
}
|
|
1148
|
+
if (!response.ok && response.status !== 202) {
|
|
1149
|
+
const data = await response.json().catch(() => ({}));
|
|
1150
|
+
const errorPayload = transport.extractErrorPayload(data);
|
|
1151
|
+
throw new VelesDBError(
|
|
1152
|
+
errorPayload.message ?? `HTTP ${response.status}`,
|
|
1153
|
+
errorPayload.code ?? transport.mapStatusToErrorCode(response.status)
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
clearTimeout(timeoutId);
|
|
1158
|
+
if (error instanceof BackpressureError || error instanceof VelesDBError) {
|
|
1159
|
+
throw error;
|
|
1160
|
+
}
|
|
1161
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1162
|
+
throw new ConnectionError("Request timeout");
|
|
1163
|
+
}
|
|
1164
|
+
throw new ConnectionError(
|
|
1165
|
+
`Stream insert failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1166
|
+
error instanceof Error ? error : void 0
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// src/backends/crud-backend.ts
|
|
1173
|
+
function parseRestPointId(id) {
|
|
1174
|
+
if (typeof id !== "number" || !Number.isFinite(id) || id < 0 || !Number.isInteger(id) || id > Number.MAX_SAFE_INTEGER) {
|
|
1175
|
+
throw new ValidationError(
|
|
1176
|
+
`REST backend requires numeric u64-compatible IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER}). Received: ${String(id)}`
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
return id;
|
|
1180
|
+
}
|
|
1181
|
+
function sparseVectorToRestFormat(sv) {
|
|
1182
|
+
const result = {};
|
|
1183
|
+
for (const [k, v] of Object.entries(sv)) {
|
|
1184
|
+
result[String(k)] = v;
|
|
1185
|
+
}
|
|
1186
|
+
return result;
|
|
1187
|
+
}
|
|
1188
|
+
async function createCollection(transport, name, config) {
|
|
1189
|
+
const response = await transport.requestJson("POST", "/collections", {
|
|
1190
|
+
name,
|
|
1191
|
+
dimension: config.dimension,
|
|
1192
|
+
metric: config.metric ?? "cosine",
|
|
1193
|
+
storage_mode: config.storageMode ?? "full",
|
|
1194
|
+
collection_type: config.collectionType ?? "vector",
|
|
1195
|
+
description: config.description,
|
|
1196
|
+
hnsw_m: config.hnsw?.m,
|
|
1197
|
+
hnsw_ef_construction: config.hnsw?.efConstruction
|
|
1198
|
+
});
|
|
1199
|
+
throwOnError(response);
|
|
1200
|
+
}
|
|
1201
|
+
async function deleteCollection(transport, name) {
|
|
1202
|
+
const response = await transport.requestJson(
|
|
1203
|
+
"DELETE",
|
|
1204
|
+
collectionPath(name)
|
|
1205
|
+
);
|
|
1206
|
+
throwOnError(response, `Collection '${name}'`);
|
|
1207
|
+
}
|
|
1208
|
+
async function getCollection(transport, name) {
|
|
1209
|
+
const response = await transport.requestJson(
|
|
1210
|
+
"GET",
|
|
1211
|
+
collectionPath(name)
|
|
1212
|
+
);
|
|
1213
|
+
if (returnNullOnNotFound(response)) {
|
|
1214
|
+
return null;
|
|
1215
|
+
}
|
|
1216
|
+
return response.data ?? null;
|
|
1217
|
+
}
|
|
1218
|
+
async function listCollections(transport) {
|
|
1219
|
+
const response = await transport.requestJson("GET", "/collections");
|
|
1220
|
+
throwOnError(response);
|
|
1221
|
+
return response.data ?? [];
|
|
1222
|
+
}
|
|
1223
|
+
async function insert(transport, collection, doc) {
|
|
1224
|
+
const restId = parseRestPointId(doc.id);
|
|
1225
|
+
const vector = toNumberArray(doc.vector);
|
|
1226
|
+
const response = await transport.requestJson(
|
|
1227
|
+
"POST",
|
|
1228
|
+
`${collectionPath(collection)}/points`,
|
|
1229
|
+
{ points: [{ id: restId, vector, payload: doc.payload }] }
|
|
1230
|
+
);
|
|
1231
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1232
|
+
}
|
|
1233
|
+
async function insertBatch(transport, collection, docs) {
|
|
1234
|
+
const vectors = docs.map((doc) => ({
|
|
1235
|
+
id: parseRestPointId(doc.id),
|
|
1236
|
+
vector: toNumberArray(doc.vector),
|
|
1237
|
+
payload: doc.payload
|
|
1238
|
+
}));
|
|
1239
|
+
const response = await transport.requestJson(
|
|
1240
|
+
"POST",
|
|
1241
|
+
`${collectionPath(collection)}/points`,
|
|
1242
|
+
{ points: vectors }
|
|
1243
|
+
);
|
|
1244
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1245
|
+
}
|
|
1246
|
+
async function deletePoint(transport, collection, id) {
|
|
1247
|
+
const restId = parseRestPointId(id);
|
|
1248
|
+
const response = await transport.requestJson(
|
|
1249
|
+
"DELETE",
|
|
1250
|
+
`${collectionPath(collection)}/points/${encodeURIComponent(String(restId))}`
|
|
1251
|
+
);
|
|
1252
|
+
if (returnNullOnNotFound(response)) {
|
|
1253
|
+
return false;
|
|
1254
|
+
}
|
|
1255
|
+
return response.data?.deleted ?? false;
|
|
1256
|
+
}
|
|
1257
|
+
async function get(transport, collection, id) {
|
|
1258
|
+
const restId = parseRestPointId(id);
|
|
1259
|
+
const response = await transport.requestJson(
|
|
1260
|
+
"GET",
|
|
1261
|
+
`${collectionPath(collection)}/points/${encodeURIComponent(String(restId))}`
|
|
1262
|
+
);
|
|
1263
|
+
if (returnNullOnNotFound(response)) {
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
return response.data ?? null;
|
|
1267
|
+
}
|
|
1268
|
+
async function isEmpty(transport, collection) {
|
|
1269
|
+
const response = await transport.requestJson(
|
|
1270
|
+
"GET",
|
|
1271
|
+
`${collectionPath(collection)}/empty`
|
|
1272
|
+
);
|
|
1273
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1274
|
+
return response.data?.is_empty ?? true;
|
|
1275
|
+
}
|
|
1276
|
+
async function flush(transport, collection) {
|
|
1277
|
+
const response = await transport.requestJson(
|
|
1278
|
+
"POST",
|
|
1279
|
+
`${collectionPath(collection)}/flush`
|
|
1280
|
+
);
|
|
1281
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
600
1284
|
// src/backends/rest.ts
|
|
601
1285
|
var RestBackend = class {
|
|
602
1286
|
constructor(url, apiKey, timeout = 3e4) {
|
|
@@ -664,10 +1348,6 @@ var RestBackend = class {
|
|
|
664
1348
|
const message = typeof messageField === "string" ? messageField : void 0;
|
|
665
1349
|
return { code, message };
|
|
666
1350
|
}
|
|
667
|
-
/**
|
|
668
|
-
* Parse node ID safely to handle u64 values above Number.MAX_SAFE_INTEGER.
|
|
669
|
-
* Returns bigint for large values, number for safe values.
|
|
670
|
-
*/
|
|
671
1351
|
parseNodeId(value) {
|
|
672
1352
|
if (value === null || value === void 0) {
|
|
673
1353
|
return 0;
|
|
@@ -677,37 +1357,16 @@ var RestBackend = class {
|
|
|
677
1357
|
}
|
|
678
1358
|
if (typeof value === "string") {
|
|
679
1359
|
const num = Number(value);
|
|
680
|
-
|
|
681
|
-
return BigInt(value);
|
|
682
|
-
}
|
|
683
|
-
return num;
|
|
1360
|
+
return num > Number.MAX_SAFE_INTEGER ? BigInt(value) : num;
|
|
684
1361
|
}
|
|
685
1362
|
if (typeof value === "number") {
|
|
686
|
-
if (value > Number.MAX_SAFE_INTEGER) {
|
|
687
|
-
return value;
|
|
688
|
-
}
|
|
689
1363
|
return value;
|
|
690
1364
|
}
|
|
691
1365
|
return 0;
|
|
692
1366
|
}
|
|
693
|
-
parseRestPointId(id) {
|
|
694
|
-
if (typeof id !== "number" || !Number.isFinite(id) || id < 0 || !Number.isInteger(id) || id > Number.MAX_SAFE_INTEGER) {
|
|
695
|
-
throw new ValidationError(
|
|
696
|
-
`REST backend requires numeric u64-compatible IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER}). Received: ${String(id)}`
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
return id;
|
|
700
|
-
}
|
|
701
|
-
isLikelyAggregationQuery(query) {
|
|
702
|
-
return /\bGROUP\s+BY\b|\bHAVING\b|\bCOUNT\s*\(|\bSUM\s*\(|\bAVG\s*\(|\bMIN\s*\(|\bMAX\s*\(/i.test(
|
|
703
|
-
query
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
1367
|
async request(method, path, body) {
|
|
707
1368
|
const url = `${this.baseUrl}${path}`;
|
|
708
|
-
const headers = {
|
|
709
|
-
"Content-Type": "application/json"
|
|
710
|
-
};
|
|
1369
|
+
const headers = { "Content-Type": "application/json" };
|
|
711
1370
|
if (this.apiKey) {
|
|
712
1371
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
713
1372
|
}
|
|
@@ -743,631 +1402,281 @@ var RestBackend = class {
|
|
|
743
1402
|
);
|
|
744
1403
|
}
|
|
745
1404
|
}
|
|
1405
|
+
// ==========================================================================
|
|
1406
|
+
// Transport adapters
|
|
1407
|
+
// ==========================================================================
|
|
1408
|
+
asCrudTransport() {
|
|
1409
|
+
return {
|
|
1410
|
+
requestJson: (m, p, b) => this.request(m, p, b)
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
asSearchTransport() {
|
|
1414
|
+
return {
|
|
1415
|
+
requestJson: (m, p, b) => this.request(m, p, b),
|
|
1416
|
+
sparseToRest: (sv) => sparseVectorToRestFormat(sv)
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
asAgentMemoryTransport() {
|
|
1420
|
+
return {
|
|
1421
|
+
requestJson: (m, p, b) => this.request(m, p, b),
|
|
1422
|
+
searchVectors: (c, e, k, f) => this.search(c, e, { k, filter: f })
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
asQueryTransport() {
|
|
1426
|
+
return {
|
|
1427
|
+
requestJson: (m, p, b) => this.request(m, p, b),
|
|
1428
|
+
parseNodeId: (v) => this.parseNodeId(v)
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
asStreamingTransport() {
|
|
1432
|
+
return {
|
|
1433
|
+
requestJson: (m, p, b) => this.request(m, p, b),
|
|
1434
|
+
baseUrl: this.baseUrl,
|
|
1435
|
+
apiKey: this.apiKey,
|
|
1436
|
+
timeout: this.timeout,
|
|
1437
|
+
parseRestPointId,
|
|
1438
|
+
sparseVectorToRestFormat,
|
|
1439
|
+
mapStatusToErrorCode: (s) => this.mapStatusToErrorCode(s),
|
|
1440
|
+
extractErrorPayload: (d) => this.extractErrorPayload(d)
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
// ==========================================================================
|
|
1444
|
+
// Collection CRUD — delegates to crud-backend.ts
|
|
1445
|
+
// ==========================================================================
|
|
746
1446
|
async createCollection(name, config) {
|
|
747
1447
|
this.ensureInitialized();
|
|
748
|
-
|
|
749
|
-
name,
|
|
750
|
-
dimension: config.dimension,
|
|
751
|
-
metric: config.metric ?? "cosine",
|
|
752
|
-
storage_mode: config.storageMode ?? "full",
|
|
753
|
-
collection_type: config.collectionType ?? "vector",
|
|
754
|
-
description: config.description
|
|
755
|
-
});
|
|
756
|
-
if (response.error) {
|
|
757
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
758
|
-
}
|
|
1448
|
+
return createCollection(this.asCrudTransport(), name, config);
|
|
759
1449
|
}
|
|
760
1450
|
async deleteCollection(name) {
|
|
761
1451
|
this.ensureInitialized();
|
|
762
|
-
|
|
763
|
-
if (response.error) {
|
|
764
|
-
if (response.error.code === "NOT_FOUND") {
|
|
765
|
-
throw new NotFoundError(`Collection '${name}'`);
|
|
766
|
-
}
|
|
767
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
768
|
-
}
|
|
1452
|
+
return deleteCollection(this.asCrudTransport(), name);
|
|
769
1453
|
}
|
|
770
1454
|
async getCollection(name) {
|
|
771
1455
|
this.ensureInitialized();
|
|
772
|
-
|
|
773
|
-
"GET",
|
|
774
|
-
`/collections/${encodeURIComponent(name)}`
|
|
775
|
-
);
|
|
776
|
-
if (response.error) {
|
|
777
|
-
if (response.error.code === "NOT_FOUND") {
|
|
778
|
-
return null;
|
|
779
|
-
}
|
|
780
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
781
|
-
}
|
|
782
|
-
return response.data ?? null;
|
|
1456
|
+
return getCollection(this.asCrudTransport(), name);
|
|
783
1457
|
}
|
|
784
1458
|
async listCollections() {
|
|
785
1459
|
this.ensureInitialized();
|
|
786
|
-
|
|
787
|
-
if (response.error) {
|
|
788
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
789
|
-
}
|
|
790
|
-
return response.data ?? [];
|
|
1460
|
+
return listCollections(this.asCrudTransport());
|
|
791
1461
|
}
|
|
792
1462
|
async insert(collection, doc) {
|
|
793
1463
|
this.ensureInitialized();
|
|
794
|
-
|
|
795
|
-
const vector = doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector;
|
|
796
|
-
const response = await this.request(
|
|
797
|
-
"POST",
|
|
798
|
-
`/collections/${encodeURIComponent(collection)}/points`,
|
|
799
|
-
{
|
|
800
|
-
points: [{
|
|
801
|
-
id: restId,
|
|
802
|
-
vector,
|
|
803
|
-
payload: doc.payload
|
|
804
|
-
}]
|
|
805
|
-
}
|
|
806
|
-
);
|
|
807
|
-
if (response.error) {
|
|
808
|
-
if (response.error.code === "NOT_FOUND") {
|
|
809
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
810
|
-
}
|
|
811
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
812
|
-
}
|
|
1464
|
+
return insert(this.asCrudTransport(), collection, doc);
|
|
813
1465
|
}
|
|
814
1466
|
async insertBatch(collection, docs) {
|
|
815
1467
|
this.ensureInitialized();
|
|
816
|
-
|
|
817
|
-
id: this.parseRestPointId(doc.id),
|
|
818
|
-
vector: doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector,
|
|
819
|
-
payload: doc.payload
|
|
820
|
-
}));
|
|
821
|
-
const response = await this.request(
|
|
822
|
-
"POST",
|
|
823
|
-
`/collections/${encodeURIComponent(collection)}/points`,
|
|
824
|
-
{ points: vectors }
|
|
825
|
-
);
|
|
826
|
-
if (response.error) {
|
|
827
|
-
if (response.error.code === "NOT_FOUND") {
|
|
828
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
829
|
-
}
|
|
830
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
831
|
-
}
|
|
1468
|
+
return insertBatch(this.asCrudTransport(), collection, docs);
|
|
832
1469
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
result[String(k)] = v;
|
|
837
|
-
}
|
|
838
|
-
return result;
|
|
1470
|
+
async delete(collection, id) {
|
|
1471
|
+
this.ensureInitialized();
|
|
1472
|
+
return deletePoint(this.asCrudTransport(), collection, id);
|
|
839
1473
|
}
|
|
840
|
-
async
|
|
1474
|
+
async get(collection, id) {
|
|
841
1475
|
this.ensureInitialized();
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
862
|
-
}
|
|
863
|
-
return response.data?.results ?? [];
|
|
1476
|
+
return get(this.asCrudTransport(), collection, id);
|
|
1477
|
+
}
|
|
1478
|
+
async isEmpty(collection) {
|
|
1479
|
+
this.ensureInitialized();
|
|
1480
|
+
return isEmpty(this.asCrudTransport(), collection);
|
|
1481
|
+
}
|
|
1482
|
+
async flush(collection) {
|
|
1483
|
+
this.ensureInitialized();
|
|
1484
|
+
return flush(this.asCrudTransport(), collection);
|
|
1485
|
+
}
|
|
1486
|
+
async close() {
|
|
1487
|
+
this._initialized = false;
|
|
1488
|
+
}
|
|
1489
|
+
// ==========================================================================
|
|
1490
|
+
// Search — delegates to search-backend.ts
|
|
1491
|
+
// ==========================================================================
|
|
1492
|
+
async search(c, q, o) {
|
|
1493
|
+
this.ensureInitialized();
|
|
1494
|
+
return search(this.asSearchTransport(), c, q, o);
|
|
864
1495
|
}
|
|
865
1496
|
async searchBatch(collection, searches) {
|
|
866
1497
|
this.ensureInitialized();
|
|
867
|
-
|
|
868
|
-
vector: s.vector instanceof Float32Array ? Array.from(s.vector) : s.vector,
|
|
869
|
-
top_k: s.k ?? 10,
|
|
870
|
-
filter: s.filter
|
|
871
|
-
}));
|
|
872
|
-
const response = await this.request(
|
|
873
|
-
"POST",
|
|
874
|
-
`/collections/${encodeURIComponent(collection)}/search/batch`,
|
|
875
|
-
{ searches: formattedSearches }
|
|
876
|
-
);
|
|
877
|
-
if (response.error) {
|
|
878
|
-
if (response.error.code === "NOT_FOUND") {
|
|
879
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
880
|
-
}
|
|
881
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
882
|
-
}
|
|
883
|
-
return response.data?.results.map((r) => r.results) ?? [];
|
|
1498
|
+
return searchBatch(this.asSearchTransport(), collection, searches);
|
|
884
1499
|
}
|
|
885
|
-
async
|
|
1500
|
+
async textSearch(c, q, o) {
|
|
886
1501
|
this.ensureInitialized();
|
|
887
|
-
|
|
888
|
-
const response = await this.request(
|
|
889
|
-
"DELETE",
|
|
890
|
-
`/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(restId))}`
|
|
891
|
-
);
|
|
892
|
-
if (response.error) {
|
|
893
|
-
if (response.error.code === "NOT_FOUND") {
|
|
894
|
-
return false;
|
|
895
|
-
}
|
|
896
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
897
|
-
}
|
|
898
|
-
return response.data?.deleted ?? false;
|
|
1502
|
+
return textSearch(this.asSearchTransport(), c, q, o);
|
|
899
1503
|
}
|
|
900
|
-
async
|
|
1504
|
+
async hybridSearch(c, v, t, o) {
|
|
901
1505
|
this.ensureInitialized();
|
|
902
|
-
|
|
903
|
-
const response = await this.request(
|
|
904
|
-
"GET",
|
|
905
|
-
`/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(restId))}`
|
|
906
|
-
);
|
|
907
|
-
if (response.error) {
|
|
908
|
-
if (response.error.code === "NOT_FOUND") {
|
|
909
|
-
return null;
|
|
910
|
-
}
|
|
911
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
912
|
-
}
|
|
913
|
-
return response.data ?? null;
|
|
1506
|
+
return hybridSearch(this.asSearchTransport(), c, v, t, o);
|
|
914
1507
|
}
|
|
915
|
-
async
|
|
1508
|
+
async multiQuerySearch(c, v, o) {
|
|
916
1509
|
this.ensureInitialized();
|
|
917
|
-
|
|
918
|
-
"POST",
|
|
919
|
-
`/collections/${encodeURIComponent(collection)}/search/text`,
|
|
920
|
-
{
|
|
921
|
-
query,
|
|
922
|
-
top_k: options?.k ?? 10,
|
|
923
|
-
filter: options?.filter
|
|
924
|
-
}
|
|
925
|
-
);
|
|
926
|
-
if (response.error) {
|
|
927
|
-
if (response.error.code === "NOT_FOUND") {
|
|
928
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
929
|
-
}
|
|
930
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
931
|
-
}
|
|
932
|
-
return response.data?.results ?? [];
|
|
1510
|
+
return multiQuerySearch(this.asSearchTransport(), c, v, o);
|
|
933
1511
|
}
|
|
934
|
-
async
|
|
1512
|
+
async searchIds(c, q, o) {
|
|
935
1513
|
this.ensureInitialized();
|
|
936
|
-
|
|
937
|
-
const response = await this.request(
|
|
938
|
-
"POST",
|
|
939
|
-
`/collections/${encodeURIComponent(collection)}/search/hybrid`,
|
|
940
|
-
{
|
|
941
|
-
vector: queryVector,
|
|
942
|
-
query: textQuery,
|
|
943
|
-
top_k: options?.k ?? 10,
|
|
944
|
-
vector_weight: options?.vectorWeight ?? 0.5,
|
|
945
|
-
filter: options?.filter
|
|
946
|
-
}
|
|
947
|
-
);
|
|
948
|
-
if (response.error) {
|
|
949
|
-
if (response.error.code === "NOT_FOUND") {
|
|
950
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
951
|
-
}
|
|
952
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
953
|
-
}
|
|
954
|
-
return response.data?.results ?? [];
|
|
1514
|
+
return searchIds(this.asSearchTransport(), c, q, o);
|
|
955
1515
|
}
|
|
956
|
-
|
|
1516
|
+
// ==========================================================================
|
|
1517
|
+
// Query — delegates to query-backend.ts
|
|
1518
|
+
// ==========================================================================
|
|
1519
|
+
async query(c, q, p, o) {
|
|
957
1520
|
this.ensureInitialized();
|
|
958
|
-
|
|
959
|
-
const response = await this.request(
|
|
960
|
-
"POST",
|
|
961
|
-
endpoint,
|
|
962
|
-
{
|
|
963
|
-
query: queryString,
|
|
964
|
-
params: params ?? {},
|
|
965
|
-
collection,
|
|
966
|
-
timeout_ms: options?.timeoutMs,
|
|
967
|
-
stream: options?.stream ?? false
|
|
968
|
-
}
|
|
969
|
-
);
|
|
970
|
-
if (response.error) {
|
|
971
|
-
if (response.error.code === "NOT_FOUND") {
|
|
972
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
973
|
-
}
|
|
974
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
975
|
-
}
|
|
976
|
-
const rawData = response.data;
|
|
977
|
-
if (rawData && Object.prototype.hasOwnProperty.call(rawData, "result")) {
|
|
978
|
-
return {
|
|
979
|
-
result: rawData.result,
|
|
980
|
-
stats: {
|
|
981
|
-
executionTimeMs: rawData.timing_ms ?? 0,
|
|
982
|
-
strategy: "aggregation",
|
|
983
|
-
scannedNodes: 0
|
|
984
|
-
}
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
return {
|
|
988
|
-
results: (rawData?.results ?? []).map((r) => ({
|
|
989
|
-
// Server returns `id` (u64), map to nodeId with precision handling
|
|
990
|
-
nodeId: this.parseNodeId(r.id ?? r.node_id ?? r.nodeId),
|
|
991
|
-
// Server returns `score`, map to vectorScore (primary score for SELECT queries)
|
|
992
|
-
vectorScore: r.score ?? r.vector_score ?? r.vectorScore,
|
|
993
|
-
// graph_score not returned by SELECT queries, only by future MATCH queries
|
|
994
|
-
graphScore: r.graph_score ?? r.graphScore,
|
|
995
|
-
// Use score as fusedScore for compatibility
|
|
996
|
-
fusedScore: r.score ?? r.fused_score ?? r.fusedScore ?? 0,
|
|
997
|
-
// payload maps to bindings for compatibility
|
|
998
|
-
bindings: r.payload ?? r.bindings ?? {},
|
|
999
|
-
columnData: r.column_data ?? r.columnData
|
|
1000
|
-
})),
|
|
1001
|
-
stats: {
|
|
1002
|
-
executionTimeMs: rawData?.timing_ms ?? 0,
|
|
1003
|
-
strategy: "select",
|
|
1004
|
-
scannedNodes: rawData?.rows_returned ?? 0
|
|
1005
|
-
}
|
|
1006
|
-
};
|
|
1521
|
+
return query(this.asQueryTransport(), c, q, p, o);
|
|
1007
1522
|
}
|
|
1008
|
-
async queryExplain(
|
|
1523
|
+
async queryExplain(q, p) {
|
|
1009
1524
|
this.ensureInitialized();
|
|
1010
|
-
|
|
1011
|
-
"POST",
|
|
1012
|
-
"/query/explain",
|
|
1013
|
-
{
|
|
1014
|
-
query: queryString,
|
|
1015
|
-
params: params ?? {}
|
|
1016
|
-
}
|
|
1017
|
-
);
|
|
1018
|
-
if (response.error) {
|
|
1019
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1020
|
-
}
|
|
1021
|
-
const data = response.data;
|
|
1022
|
-
return {
|
|
1023
|
-
query: data.query,
|
|
1024
|
-
queryType: data.query_type,
|
|
1025
|
-
collection: data.collection,
|
|
1026
|
-
plan: data.plan.map((step) => ({
|
|
1027
|
-
step: step.step,
|
|
1028
|
-
operation: step.operation,
|
|
1029
|
-
description: step.description,
|
|
1030
|
-
estimatedRows: step.estimated_rows
|
|
1031
|
-
})),
|
|
1032
|
-
estimatedCost: {
|
|
1033
|
-
usesIndex: data.estimated_cost.uses_index,
|
|
1034
|
-
indexName: data.estimated_cost.index_name,
|
|
1035
|
-
selectivity: data.estimated_cost.selectivity,
|
|
1036
|
-
complexity: data.estimated_cost.complexity
|
|
1037
|
-
},
|
|
1038
|
-
features: {
|
|
1039
|
-
hasVectorSearch: data.features.has_vector_search,
|
|
1040
|
-
hasFilter: data.features.has_filter,
|
|
1041
|
-
hasOrderBy: data.features.has_order_by,
|
|
1042
|
-
hasGroupBy: data.features.has_group_by,
|
|
1043
|
-
hasAggregation: data.features.has_aggregation,
|
|
1044
|
-
hasJoin: data.features.has_join,
|
|
1045
|
-
hasFusion: data.features.has_fusion,
|
|
1046
|
-
limit: data.features.limit,
|
|
1047
|
-
offset: data.features.offset
|
|
1048
|
-
}
|
|
1049
|
-
};
|
|
1525
|
+
return queryExplain(this.asQueryTransport(), q, p);
|
|
1050
1526
|
}
|
|
1051
1527
|
async collectionSanity(collection) {
|
|
1052
1528
|
this.ensureInitialized();
|
|
1053
|
-
|
|
1054
|
-
"GET",
|
|
1055
|
-
`/collections/${encodeURIComponent(collection)}/sanity`
|
|
1056
|
-
);
|
|
1057
|
-
if (response.error) {
|
|
1058
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1059
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1060
|
-
}
|
|
1061
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1062
|
-
}
|
|
1063
|
-
const data = response.data;
|
|
1064
|
-
return {
|
|
1065
|
-
collection: data.collection,
|
|
1066
|
-
dimension: data.dimension,
|
|
1067
|
-
metric: data.metric,
|
|
1068
|
-
pointCount: data.point_count,
|
|
1069
|
-
isEmpty: data.is_empty,
|
|
1070
|
-
checks: {
|
|
1071
|
-
hasVectors: data.checks.has_vectors,
|
|
1072
|
-
searchReady: data.checks.search_ready,
|
|
1073
|
-
dimensionConfigured: data.checks.dimension_configured
|
|
1074
|
-
},
|
|
1075
|
-
diagnostics: {
|
|
1076
|
-
searchRequestsTotal: data.diagnostics.search_requests_total,
|
|
1077
|
-
dimensionMismatchTotal: data.diagnostics.dimension_mismatch_total,
|
|
1078
|
-
emptySearchResultsTotal: data.diagnostics.empty_search_results_total,
|
|
1079
|
-
filterParseErrorsTotal: data.diagnostics.filter_parse_errors_total
|
|
1080
|
-
},
|
|
1081
|
-
hints: data.hints ?? []
|
|
1082
|
-
};
|
|
1529
|
+
return collectionSanity(this.asQueryTransport(), collection);
|
|
1083
1530
|
}
|
|
1084
|
-
|
|
1531
|
+
// ==========================================================================
|
|
1532
|
+
// Graph — delegates to graph-backend.ts
|
|
1533
|
+
// ==========================================================================
|
|
1534
|
+
async addEdge(collection, edge) {
|
|
1085
1535
|
this.ensureInitialized();
|
|
1086
|
-
|
|
1087
|
-
(v) => v instanceof Float32Array ? Array.from(v) : v
|
|
1088
|
-
);
|
|
1089
|
-
const response = await this.request(
|
|
1090
|
-
"POST",
|
|
1091
|
-
`/collections/${encodeURIComponent(collection)}/search/multi`,
|
|
1092
|
-
{
|
|
1093
|
-
vectors: formattedVectors,
|
|
1094
|
-
top_k: options?.k ?? 10,
|
|
1095
|
-
strategy: options?.fusion ?? "rrf",
|
|
1096
|
-
rrf_k: options?.fusionParams?.k ?? 60,
|
|
1097
|
-
avg_weight: options?.fusionParams?.avgWeight,
|
|
1098
|
-
max_weight: options?.fusionParams?.maxWeight,
|
|
1099
|
-
hit_weight: options?.fusionParams?.hitWeight,
|
|
1100
|
-
filter: options?.filter
|
|
1101
|
-
}
|
|
1102
|
-
);
|
|
1103
|
-
if (response.error) {
|
|
1104
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1105
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1106
|
-
}
|
|
1107
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1108
|
-
}
|
|
1109
|
-
return response.data?.results ?? [];
|
|
1536
|
+
return addEdge(this.asCrudTransport(), collection, edge);
|
|
1110
1537
|
}
|
|
1111
|
-
async
|
|
1538
|
+
async getEdges(collection, options) {
|
|
1112
1539
|
this.ensureInitialized();
|
|
1113
|
-
|
|
1114
|
-
"GET",
|
|
1115
|
-
`/collections/${encodeURIComponent(collection)}/empty`
|
|
1116
|
-
);
|
|
1117
|
-
if (response.error) {
|
|
1118
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1119
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1120
|
-
}
|
|
1121
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1122
|
-
}
|
|
1123
|
-
return response.data?.is_empty ?? true;
|
|
1540
|
+
return getEdges(this.asCrudTransport(), collection, options);
|
|
1124
1541
|
}
|
|
1125
|
-
async
|
|
1542
|
+
async traverseGraph(collection, req) {
|
|
1126
1543
|
this.ensureInitialized();
|
|
1127
|
-
|
|
1128
|
-
"POST",
|
|
1129
|
-
`/collections/${encodeURIComponent(collection)}/flush`
|
|
1130
|
-
);
|
|
1131
|
-
if (response.error) {
|
|
1132
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1133
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1134
|
-
}
|
|
1135
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1136
|
-
}
|
|
1544
|
+
return traverseGraph(this.asCrudTransport(), collection, req);
|
|
1137
1545
|
}
|
|
1138
|
-
|
|
1139
|
-
// Sparse / PQ / Streaming (v1.5)
|
|
1140
|
-
// ========================================================================
|
|
1141
|
-
async trainPq(collection, options) {
|
|
1546
|
+
async getNodeDegree(collection, nodeId) {
|
|
1142
1547
|
this.ensureInitialized();
|
|
1143
|
-
|
|
1144
|
-
const k = options?.k ?? 256;
|
|
1145
|
-
const withClause = options?.opq ? `WITH (m=${m}, k=${k}, opq=true)` : `WITH (m=${m}, k=${k})`;
|
|
1146
|
-
const queryString = `TRAIN QUANTIZER ON ${collection} ${withClause}`;
|
|
1147
|
-
const response = await this.request(
|
|
1148
|
-
"POST",
|
|
1149
|
-
"/query",
|
|
1150
|
-
{ query: queryString }
|
|
1151
|
-
);
|
|
1152
|
-
if (response.error) {
|
|
1153
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1154
|
-
}
|
|
1155
|
-
return response.data?.message ?? "PQ training initiated";
|
|
1548
|
+
return getNodeDegree(this.asCrudTransport(), collection, nodeId);
|
|
1156
1549
|
}
|
|
1157
|
-
async
|
|
1550
|
+
async createGraphCollection(name, config) {
|
|
1158
1551
|
this.ensureInitialized();
|
|
1159
|
-
|
|
1160
|
-
const restId = this.parseRestPointId(doc.id);
|
|
1161
|
-
const vector = doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector;
|
|
1162
|
-
const body = {
|
|
1163
|
-
id: restId,
|
|
1164
|
-
vector,
|
|
1165
|
-
payload: doc.payload
|
|
1166
|
-
};
|
|
1167
|
-
if (doc.sparseVector) {
|
|
1168
|
-
body.sparse_vector = this.sparseVectorToRestFormat(doc.sparseVector);
|
|
1169
|
-
}
|
|
1170
|
-
const url = `${this.baseUrl}/collections/${encodeURIComponent(collection)}/stream/insert`;
|
|
1171
|
-
const headers = {
|
|
1172
|
-
"Content-Type": "application/json"
|
|
1173
|
-
};
|
|
1174
|
-
if (this.apiKey) {
|
|
1175
|
-
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
1176
|
-
}
|
|
1177
|
-
const controller = new AbortController();
|
|
1178
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1179
|
-
try {
|
|
1180
|
-
const response = await fetch(url, {
|
|
1181
|
-
method: "POST",
|
|
1182
|
-
headers,
|
|
1183
|
-
body: JSON.stringify(body),
|
|
1184
|
-
signal: controller.signal
|
|
1185
|
-
});
|
|
1186
|
-
clearTimeout(timeoutId);
|
|
1187
|
-
if (response.status === 429) {
|
|
1188
|
-
throw new BackpressureError();
|
|
1189
|
-
}
|
|
1190
|
-
if (!response.ok && response.status !== 202) {
|
|
1191
|
-
const data = await response.json().catch(() => ({}));
|
|
1192
|
-
const errorPayload = this.extractErrorPayload(data);
|
|
1193
|
-
throw new VelesDBError(
|
|
1194
|
-
errorPayload.message ?? `HTTP ${response.status}`,
|
|
1195
|
-
errorPayload.code ?? this.mapStatusToErrorCode(response.status)
|
|
1196
|
-
);
|
|
1197
|
-
}
|
|
1198
|
-
} catch (error) {
|
|
1199
|
-
clearTimeout(timeoutId);
|
|
1200
|
-
if (error instanceof BackpressureError || error instanceof VelesDBError) {
|
|
1201
|
-
throw error;
|
|
1202
|
-
}
|
|
1203
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
1204
|
-
throw new ConnectionError("Request timeout");
|
|
1205
|
-
}
|
|
1206
|
-
throw new ConnectionError(
|
|
1207
|
-
`Stream insert failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1208
|
-
error instanceof Error ? error : void 0
|
|
1209
|
-
);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1552
|
+
return createGraphCollection(this.asCrudTransport(), name, config);
|
|
1212
1553
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
// ========================================================================
|
|
1217
|
-
// Index Management (EPIC-009)
|
|
1218
|
-
// ========================================================================
|
|
1554
|
+
// ==========================================================================
|
|
1555
|
+
// Index — delegates to index-backend.ts
|
|
1556
|
+
// ==========================================================================
|
|
1219
1557
|
async createIndex(collection, options) {
|
|
1220
1558
|
this.ensureInitialized();
|
|
1221
|
-
|
|
1222
|
-
"POST",
|
|
1223
|
-
`/collections/${encodeURIComponent(collection)}/indexes`,
|
|
1224
|
-
{
|
|
1225
|
-
label: options.label,
|
|
1226
|
-
property: options.property,
|
|
1227
|
-
index_type: options.indexType ?? "hash"
|
|
1228
|
-
}
|
|
1229
|
-
);
|
|
1230
|
-
if (response.error) {
|
|
1231
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1232
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1233
|
-
}
|
|
1234
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1235
|
-
}
|
|
1559
|
+
return createIndex(this.asCrudTransport(), collection, options);
|
|
1236
1560
|
}
|
|
1237
1561
|
async listIndexes(collection) {
|
|
1238
1562
|
this.ensureInitialized();
|
|
1239
|
-
|
|
1240
|
-
"GET",
|
|
1241
|
-
`/collections/${encodeURIComponent(collection)}/indexes`
|
|
1242
|
-
);
|
|
1243
|
-
if (response.error) {
|
|
1244
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1245
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1246
|
-
}
|
|
1247
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1248
|
-
}
|
|
1249
|
-
return (response.data?.indexes ?? []).map((idx) => ({
|
|
1250
|
-
label: idx.label,
|
|
1251
|
-
property: idx.property,
|
|
1252
|
-
indexType: idx.index_type,
|
|
1253
|
-
cardinality: idx.cardinality,
|
|
1254
|
-
memoryBytes: idx.memory_bytes
|
|
1255
|
-
}));
|
|
1563
|
+
return listIndexes(this.asCrudTransport(), collection);
|
|
1256
1564
|
}
|
|
1257
1565
|
async hasIndex(collection, label, property) {
|
|
1258
|
-
|
|
1259
|
-
return
|
|
1566
|
+
this.ensureInitialized();
|
|
1567
|
+
return hasIndex(this.asCrudTransport(), collection, label, property);
|
|
1260
1568
|
}
|
|
1261
1569
|
async dropIndex(collection, label, property) {
|
|
1262
1570
|
this.ensureInitialized();
|
|
1263
|
-
|
|
1264
|
-
"DELETE",
|
|
1265
|
-
`/collections/${encodeURIComponent(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
|
|
1266
|
-
);
|
|
1267
|
-
if (response.error) {
|
|
1268
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1269
|
-
return false;
|
|
1270
|
-
}
|
|
1271
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1272
|
-
}
|
|
1273
|
-
return response.data?.dropped ?? true;
|
|
1571
|
+
return dropIndex(this.asCrudTransport(), collection, label, property);
|
|
1274
1572
|
}
|
|
1275
|
-
//
|
|
1276
|
-
//
|
|
1277
|
-
//
|
|
1278
|
-
async
|
|
1573
|
+
// ==========================================================================
|
|
1574
|
+
// Admin — delegates to admin-backend.ts
|
|
1575
|
+
// ==========================================================================
|
|
1576
|
+
async getCollectionStats(collection) {
|
|
1279
1577
|
this.ensureInitialized();
|
|
1280
|
-
|
|
1281
|
-
"POST",
|
|
1282
|
-
`/collections/${encodeURIComponent(collection)}/graph/edges`,
|
|
1283
|
-
{
|
|
1284
|
-
id: edge.id,
|
|
1285
|
-
source: edge.source,
|
|
1286
|
-
target: edge.target,
|
|
1287
|
-
label: edge.label,
|
|
1288
|
-
properties: edge.properties ?? {}
|
|
1289
|
-
}
|
|
1290
|
-
);
|
|
1291
|
-
if (response.error) {
|
|
1292
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1293
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1294
|
-
}
|
|
1295
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1296
|
-
}
|
|
1578
|
+
return getCollectionStats(this.asCrudTransport(), collection);
|
|
1297
1579
|
}
|
|
1298
|
-
async
|
|
1580
|
+
async analyzeCollection(collection) {
|
|
1299
1581
|
this.ensureInitialized();
|
|
1300
|
-
|
|
1301
|
-
const response = await this.request(
|
|
1302
|
-
"GET",
|
|
1303
|
-
`/collections/${encodeURIComponent(collection)}/graph/edges${queryParams}`
|
|
1304
|
-
);
|
|
1305
|
-
if (response.error) {
|
|
1306
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1307
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1308
|
-
}
|
|
1309
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1310
|
-
}
|
|
1311
|
-
return response.data?.edges ?? [];
|
|
1582
|
+
return analyzeCollection(this.asCrudTransport(), collection);
|
|
1312
1583
|
}
|
|
1313
|
-
|
|
1314
|
-
// Graph Traversal (EPIC-016 US-050)
|
|
1315
|
-
// ========================================================================
|
|
1316
|
-
async traverseGraph(collection, request) {
|
|
1584
|
+
async getCollectionConfig(collection) {
|
|
1317
1585
|
this.ensureInitialized();
|
|
1318
|
-
|
|
1319
|
-
"POST",
|
|
1320
|
-
`/collections/${encodeURIComponent(collection)}/graph/traverse`,
|
|
1321
|
-
{
|
|
1322
|
-
source: request.source,
|
|
1323
|
-
strategy: request.strategy ?? "bfs",
|
|
1324
|
-
max_depth: request.maxDepth ?? 3,
|
|
1325
|
-
limit: request.limit ?? 100,
|
|
1326
|
-
cursor: request.cursor,
|
|
1327
|
-
rel_types: request.relTypes ?? []
|
|
1328
|
-
}
|
|
1329
|
-
);
|
|
1330
|
-
if (response.error) {
|
|
1331
|
-
if (response.error.code === "NOT_FOUND") {
|
|
1332
|
-
throw new NotFoundError(`Collection '${collection}'`);
|
|
1333
|
-
}
|
|
1334
|
-
throw new VelesDBError(response.error.message, response.error.code);
|
|
1335
|
-
}
|
|
1336
|
-
const data = response.data;
|
|
1337
|
-
return {
|
|
1338
|
-
results: data.results.map((r) => ({
|
|
1339
|
-
targetId: r.target_id,
|
|
1340
|
-
depth: r.depth,
|
|
1341
|
-
path: r.path
|
|
1342
|
-
})),
|
|
1343
|
-
nextCursor: data.next_cursor ?? void 0,
|
|
1344
|
-
hasMore: data.has_more,
|
|
1345
|
-
stats: {
|
|
1346
|
-
visited: data.stats.visited,
|
|
1347
|
-
depthReached: data.stats.depth_reached
|
|
1348
|
-
}
|
|
1349
|
-
};
|
|
1586
|
+
return getCollectionConfig(this.asCrudTransport(), collection);
|
|
1350
1587
|
}
|
|
1351
|
-
|
|
1588
|
+
// ==========================================================================
|
|
1589
|
+
// Streaming / PQ — delegates to streaming-backend.ts
|
|
1590
|
+
// ==========================================================================
|
|
1591
|
+
async trainPq(collection, options) {
|
|
1352
1592
|
this.ensureInitialized();
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
);
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1593
|
+
return trainPq(this.asStreamingTransport(), collection, options);
|
|
1594
|
+
}
|
|
1595
|
+
async streamInsert(collection, docs) {
|
|
1596
|
+
this.ensureInitialized();
|
|
1597
|
+
return streamInsert(this.asStreamingTransport(), collection, docs);
|
|
1598
|
+
}
|
|
1599
|
+
// ==========================================================================
|
|
1600
|
+
// Agent Memory — delegates to agent-memory-backend.ts
|
|
1601
|
+
// ==========================================================================
|
|
1602
|
+
async storeSemanticFact(collection, entry) {
|
|
1603
|
+
this.ensureInitialized();
|
|
1604
|
+
return storeSemanticFact(this.asAgentMemoryTransport(), collection, entry);
|
|
1605
|
+
}
|
|
1606
|
+
async searchSemanticMemory(collection, embedding, k = 5) {
|
|
1607
|
+
return searchSemanticMemory(this.asAgentMemoryTransport(), collection, embedding, k);
|
|
1608
|
+
}
|
|
1609
|
+
async recordEpisodicEvent(collection, event) {
|
|
1610
|
+
this.ensureInitialized();
|
|
1611
|
+
return recordEpisodicEvent(this.asAgentMemoryTransport(), collection, event);
|
|
1612
|
+
}
|
|
1613
|
+
async recallEpisodicEvents(collection, embedding, k = 5) {
|
|
1614
|
+
return recallEpisodicEvents(this.asAgentMemoryTransport(), collection, embedding, k);
|
|
1615
|
+
}
|
|
1616
|
+
async storeProceduralPattern(collection, pattern) {
|
|
1617
|
+
this.ensureInitialized();
|
|
1618
|
+
return storeProceduralPattern(this.asAgentMemoryTransport(), collection, pattern);
|
|
1619
|
+
}
|
|
1620
|
+
async matchProceduralPatterns(collection, embedding, k = 5) {
|
|
1621
|
+
return matchProceduralPatterns(this.asAgentMemoryTransport(), collection, embedding, k);
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
// src/agent-memory.ts
|
|
1626
|
+
var AgentMemoryClient = class {
|
|
1627
|
+
constructor(backend, config) {
|
|
1628
|
+
this.backend = backend;
|
|
1629
|
+
this.config = config;
|
|
1630
|
+
}
|
|
1631
|
+
/** Configured embedding dimension (default: 384) */
|
|
1632
|
+
get dimension() {
|
|
1633
|
+
return this.config?.dimension ?? 384;
|
|
1634
|
+
}
|
|
1635
|
+
/** Store a semantic fact */
|
|
1636
|
+
async storeFact(collection, entry) {
|
|
1637
|
+
return this.backend.storeSemanticFact(collection, entry);
|
|
1638
|
+
}
|
|
1639
|
+
/** Search semantic memory */
|
|
1640
|
+
async searchFacts(collection, embedding, k = 5) {
|
|
1641
|
+
return this.backend.searchSemanticMemory(collection, embedding, k);
|
|
1642
|
+
}
|
|
1643
|
+
/** Record an episodic event */
|
|
1644
|
+
async recordEvent(collection, event) {
|
|
1645
|
+
return this.backend.recordEpisodicEvent(collection, event);
|
|
1646
|
+
}
|
|
1647
|
+
/** Recall episodic events */
|
|
1648
|
+
async recallEvents(collection, embedding, k = 5) {
|
|
1649
|
+
return this.backend.recallEpisodicEvents(collection, embedding, k);
|
|
1650
|
+
}
|
|
1651
|
+
/** Store a procedural pattern */
|
|
1652
|
+
async learnProcedure(collection, pattern) {
|
|
1653
|
+
return this.backend.storeProceduralPattern(collection, pattern);
|
|
1654
|
+
}
|
|
1655
|
+
/** Match procedural patterns */
|
|
1656
|
+
async recallProcedures(collection, embedding, k = 5) {
|
|
1657
|
+
return this.backend.matchProceduralPatterns(collection, embedding, k);
|
|
1367
1658
|
}
|
|
1368
1659
|
};
|
|
1369
1660
|
|
|
1370
1661
|
// src/client.ts
|
|
1662
|
+
function requireNonEmptyString(value, label) {
|
|
1663
|
+
if (!value || typeof value !== "string") {
|
|
1664
|
+
throw new ValidationError(`${label} must be a non-empty string`);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
function requireVector(value, label) {
|
|
1668
|
+
if (!value || !Array.isArray(value) && !(value instanceof Float32Array)) {
|
|
1669
|
+
throw new ValidationError(`${label} must be an array or Float32Array`);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
function validateDocsBatch(docs, validateDoc) {
|
|
1673
|
+
if (!Array.isArray(docs)) {
|
|
1674
|
+
throw new ValidationError("Documents must be an array");
|
|
1675
|
+
}
|
|
1676
|
+
for (const doc of docs) {
|
|
1677
|
+
validateDoc(doc);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1371
1680
|
var VelesDB = class {
|
|
1372
1681
|
/**
|
|
1373
1682
|
* Create a new VelesDB client
|
|
@@ -1432,9 +1741,7 @@ var VelesDB = class {
|
|
|
1432
1741
|
*/
|
|
1433
1742
|
async createCollection(name, config) {
|
|
1434
1743
|
this.ensureInitialized();
|
|
1435
|
-
|
|
1436
|
-
throw new ValidationError("Collection name must be a non-empty string");
|
|
1437
|
-
}
|
|
1744
|
+
requireNonEmptyString(name, "Collection name");
|
|
1438
1745
|
const isMetadataOnly = config.collectionType === "metadata_only";
|
|
1439
1746
|
if (!isMetadataOnly && (!config.dimension || config.dimension <= 0)) {
|
|
1440
1747
|
throw new ValidationError("Dimension must be a positive integer for vector collections");
|
|
@@ -1456,9 +1763,7 @@ var VelesDB = class {
|
|
|
1456
1763
|
*/
|
|
1457
1764
|
async createMetadataCollection(name) {
|
|
1458
1765
|
this.ensureInitialized();
|
|
1459
|
-
|
|
1460
|
-
throw new ValidationError("Collection name must be a non-empty string");
|
|
1461
|
-
}
|
|
1766
|
+
requireNonEmptyString(name, "Collection name");
|
|
1462
1767
|
await this.backend.createCollection(name, { collectionType: "metadata_only" });
|
|
1463
1768
|
}
|
|
1464
1769
|
/**
|
|
@@ -1508,29 +1813,15 @@ var VelesDB = class {
|
|
|
1508
1813
|
*/
|
|
1509
1814
|
async insertBatch(collection, docs) {
|
|
1510
1815
|
this.ensureInitialized();
|
|
1511
|
-
|
|
1512
|
-
throw new ValidationError("Documents must be an array");
|
|
1513
|
-
}
|
|
1514
|
-
for (const doc of docs) {
|
|
1515
|
-
this.validateDocument(doc);
|
|
1516
|
-
}
|
|
1816
|
+
validateDocsBatch(docs, (doc) => this.validateDocument(doc));
|
|
1517
1817
|
await this.backend.insertBatch(collection, docs);
|
|
1518
1818
|
}
|
|
1519
1819
|
validateDocument(doc) {
|
|
1520
1820
|
if (doc.id === void 0 || doc.id === null) {
|
|
1521
1821
|
throw new ValidationError("Document ID is required");
|
|
1522
1822
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
}
|
|
1526
|
-
if (!Array.isArray(doc.vector) && !(doc.vector instanceof Float32Array)) {
|
|
1527
|
-
throw new ValidationError("Vector must be an array or Float32Array");
|
|
1528
|
-
}
|
|
1529
|
-
if (this.config.backend === "rest" && (typeof doc.id !== "number" || !Number.isInteger(doc.id) || doc.id < 0 || doc.id > Number.MAX_SAFE_INTEGER)) {
|
|
1530
|
-
throw new ValidationError(
|
|
1531
|
-
`REST backend requires numeric u64-compatible document IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER})`
|
|
1532
|
-
);
|
|
1533
|
-
}
|
|
1823
|
+
requireVector(doc.vector, "Vector");
|
|
1824
|
+
this.validateRestPointId(doc.id);
|
|
1534
1825
|
}
|
|
1535
1826
|
validateRestPointId(id) {
|
|
1536
1827
|
if (this.config.backend === "rest" && (typeof id !== "number" || !Number.isInteger(id) || id < 0 || id > Number.MAX_SAFE_INTEGER)) {
|
|
@@ -1547,12 +1838,10 @@ var VelesDB = class {
|
|
|
1547
1838
|
* @param options - Search options
|
|
1548
1839
|
* @returns Search results sorted by relevance
|
|
1549
1840
|
*/
|
|
1550
|
-
async search(collection,
|
|
1841
|
+
async search(collection, query2, options) {
|
|
1551
1842
|
this.ensureInitialized();
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
}
|
|
1555
|
-
return this.backend.search(collection, query, options);
|
|
1843
|
+
requireVector(query2, "Query");
|
|
1844
|
+
return this.backend.search(collection, query2, options);
|
|
1556
1845
|
}
|
|
1557
1846
|
/**
|
|
1558
1847
|
* Search for multiple vectors in parallel
|
|
@@ -1567,9 +1856,7 @@ var VelesDB = class {
|
|
|
1567
1856
|
throw new ValidationError("Searches must be an array");
|
|
1568
1857
|
}
|
|
1569
1858
|
for (const s of searches) {
|
|
1570
|
-
|
|
1571
|
-
throw new ValidationError("Each search must have a vector (array or Float32Array)");
|
|
1572
|
-
}
|
|
1859
|
+
requireVector(s.vector, "Each search vector");
|
|
1573
1860
|
}
|
|
1574
1861
|
return this.backend.searchBatch(collection, searches);
|
|
1575
1862
|
}
|
|
@@ -1605,12 +1892,10 @@ var VelesDB = class {
|
|
|
1605
1892
|
* @param options - Search options (k, filter)
|
|
1606
1893
|
* @returns Search results sorted by BM25 score
|
|
1607
1894
|
*/
|
|
1608
|
-
async textSearch(collection,
|
|
1895
|
+
async textSearch(collection, query2, options) {
|
|
1609
1896
|
this.ensureInitialized();
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
}
|
|
1613
|
-
return this.backend.textSearch(collection, query, options);
|
|
1897
|
+
requireNonEmptyString(query2, "Query");
|
|
1898
|
+
return this.backend.textSearch(collection, query2, options);
|
|
1614
1899
|
}
|
|
1615
1900
|
/**
|
|
1616
1901
|
* Perform hybrid search combining vector similarity and BM25 text search
|
|
@@ -1623,12 +1908,8 @@ var VelesDB = class {
|
|
|
1623
1908
|
*/
|
|
1624
1909
|
async hybridSearch(collection, vector, textQuery, options) {
|
|
1625
1910
|
this.ensureInitialized();
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
}
|
|
1629
|
-
if (!textQuery || typeof textQuery !== "string") {
|
|
1630
|
-
throw new ValidationError("Text query must be a non-empty string");
|
|
1631
|
-
}
|
|
1911
|
+
requireVector(vector, "Vector");
|
|
1912
|
+
requireNonEmptyString(textQuery, "Text query");
|
|
1632
1913
|
return this.backend.hybridSearch(collection, vector, textQuery, options);
|
|
1633
1914
|
}
|
|
1634
1915
|
/**
|
|
@@ -1649,30 +1930,43 @@ var VelesDB = class {
|
|
|
1649
1930
|
* `, { q: queryVector });
|
|
1650
1931
|
*
|
|
1651
1932
|
* for (const r of response.results) {
|
|
1652
|
-
* console.log(`
|
|
1933
|
+
* console.log(`ID ${r.id}, title: ${r.title}`);
|
|
1653
1934
|
* }
|
|
1654
1935
|
* ```
|
|
1655
1936
|
*/
|
|
1656
1937
|
async query(collection, queryString, params, options) {
|
|
1657
1938
|
this.ensureInitialized();
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
}
|
|
1661
|
-
if (!queryString || typeof queryString !== "string") {
|
|
1662
|
-
throw new ValidationError("Query string must be a non-empty string");
|
|
1663
|
-
}
|
|
1939
|
+
requireNonEmptyString(collection, "Collection name");
|
|
1940
|
+
requireNonEmptyString(queryString, "Query string");
|
|
1664
1941
|
return this.backend.query(collection, queryString, params, options);
|
|
1665
1942
|
}
|
|
1943
|
+
/**
|
|
1944
|
+
* Explain the execution plan for a VelesQL query without running it
|
|
1945
|
+
*
|
|
1946
|
+
* @param queryString - VelesQL query string to explain
|
|
1947
|
+
* @param params - Optional query parameters (vectors, scalars)
|
|
1948
|
+
* @returns Explain response with the query execution plan
|
|
1949
|
+
*/
|
|
1950
|
+
async queryExplain(queryString, params) {
|
|
1951
|
+
this.ensureInitialized();
|
|
1952
|
+
requireNonEmptyString(queryString, "Query string");
|
|
1953
|
+
return this.backend.queryExplain(queryString, params);
|
|
1954
|
+
}
|
|
1955
|
+
async collectionSanity(collection) {
|
|
1956
|
+
this.ensureInitialized();
|
|
1957
|
+
requireNonEmptyString(collection, "Collection name");
|
|
1958
|
+
return this.backend.collectionSanity(collection);
|
|
1959
|
+
}
|
|
1666
1960
|
/**
|
|
1667
1961
|
* Multi-query fusion search combining results from multiple query vectors
|
|
1668
|
-
*
|
|
1962
|
+
*
|
|
1669
1963
|
* Ideal for RAG pipelines using Multiple Query Generation (MQG).
|
|
1670
|
-
*
|
|
1964
|
+
*
|
|
1671
1965
|
* @param collection - Collection name
|
|
1672
1966
|
* @param vectors - Array of query vectors
|
|
1673
1967
|
* @param options - Search options (k, fusion strategy, fusionParams, filter)
|
|
1674
1968
|
* @returns Fused search results
|
|
1675
|
-
*
|
|
1969
|
+
*
|
|
1676
1970
|
* @example
|
|
1677
1971
|
* ```typescript
|
|
1678
1972
|
* // RRF fusion (default)
|
|
@@ -1681,7 +1975,7 @@ var VelesDB = class {
|
|
|
1681
1975
|
* fusion: 'rrf',
|
|
1682
1976
|
* fusionParams: { k: 60 }
|
|
1683
1977
|
* });
|
|
1684
|
-
*
|
|
1978
|
+
*
|
|
1685
1979
|
* // Weighted fusion
|
|
1686
1980
|
* const results = await db.multiQuerySearch('docs', [emb1, emb2], {
|
|
1687
1981
|
* k: 10,
|
|
@@ -1690,29 +1984,13 @@ var VelesDB = class {
|
|
|
1690
1984
|
* });
|
|
1691
1985
|
* ```
|
|
1692
1986
|
*/
|
|
1693
|
-
async queryExplain(queryString, params) {
|
|
1694
|
-
this.ensureInitialized();
|
|
1695
|
-
if (!queryString || typeof queryString !== "string") {
|
|
1696
|
-
throw new ValidationError("Query string must be a non-empty string");
|
|
1697
|
-
}
|
|
1698
|
-
return this.backend.queryExplain(queryString, params);
|
|
1699
|
-
}
|
|
1700
|
-
async collectionSanity(collection) {
|
|
1701
|
-
this.ensureInitialized();
|
|
1702
|
-
if (!collection || typeof collection !== "string") {
|
|
1703
|
-
throw new ValidationError("Collection name must be a non-empty string");
|
|
1704
|
-
}
|
|
1705
|
-
return this.backend.collectionSanity(collection);
|
|
1706
|
-
}
|
|
1707
1987
|
async multiQuerySearch(collection, vectors, options) {
|
|
1708
1988
|
this.ensureInitialized();
|
|
1709
1989
|
if (!Array.isArray(vectors) || vectors.length === 0) {
|
|
1710
1990
|
throw new ValidationError("Vectors must be a non-empty array");
|
|
1711
1991
|
}
|
|
1712
1992
|
for (const v of vectors) {
|
|
1713
|
-
|
|
1714
|
-
throw new ValidationError("Each vector must be an array or Float32Array");
|
|
1715
|
-
}
|
|
1993
|
+
requireVector(v, "Each vector");
|
|
1716
1994
|
}
|
|
1717
1995
|
return this.backend.multiQuerySearch(collection, vectors, options);
|
|
1718
1996
|
}
|
|
@@ -1738,12 +2016,7 @@ var VelesDB = class {
|
|
|
1738
2016
|
*/
|
|
1739
2017
|
async streamInsert(collection, docs) {
|
|
1740
2018
|
this.ensureInitialized();
|
|
1741
|
-
|
|
1742
|
-
throw new ValidationError("Documents must be an array");
|
|
1743
|
-
}
|
|
1744
|
-
for (const doc of docs) {
|
|
1745
|
-
this.validateDocument(doc);
|
|
1746
|
-
}
|
|
2019
|
+
validateDocsBatch(docs, (doc) => this.validateDocument(doc));
|
|
1747
2020
|
await this.backend.streamInsert(collection, docs);
|
|
1748
2021
|
}
|
|
1749
2022
|
/**
|
|
@@ -1942,6 +2215,75 @@ var VelesDB = class {
|
|
|
1942
2215
|
}
|
|
1943
2216
|
return this.backend.getNodeDegree(collection, nodeId);
|
|
1944
2217
|
}
|
|
2218
|
+
// ========================================================================
|
|
2219
|
+
// Graph Collection Management (Phase 8)
|
|
2220
|
+
// ========================================================================
|
|
2221
|
+
/**
|
|
2222
|
+
* Create a graph collection
|
|
2223
|
+
*
|
|
2224
|
+
* @param name - Collection name
|
|
2225
|
+
* @param config - Optional graph collection configuration
|
|
2226
|
+
*/
|
|
2227
|
+
async createGraphCollection(name, config) {
|
|
2228
|
+
this.ensureInitialized();
|
|
2229
|
+
requireNonEmptyString(name, "Collection name");
|
|
2230
|
+
await this.backend.createGraphCollection(name, config);
|
|
2231
|
+
}
|
|
2232
|
+
/**
|
|
2233
|
+
* Get collection statistics (requires prior analyze)
|
|
2234
|
+
*
|
|
2235
|
+
* @param collection - Collection name
|
|
2236
|
+
* @returns Statistics or null if not yet analyzed
|
|
2237
|
+
*/
|
|
2238
|
+
async getCollectionStats(collection) {
|
|
2239
|
+
this.ensureInitialized();
|
|
2240
|
+
return this.backend.getCollectionStats(collection);
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* Analyze a collection to compute statistics
|
|
2244
|
+
*
|
|
2245
|
+
* @param collection - Collection name
|
|
2246
|
+
* @returns Computed statistics
|
|
2247
|
+
*/
|
|
2248
|
+
async analyzeCollection(collection) {
|
|
2249
|
+
this.ensureInitialized();
|
|
2250
|
+
return this.backend.analyzeCollection(collection);
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* Get collection configuration
|
|
2254
|
+
*
|
|
2255
|
+
* @param collection - Collection name
|
|
2256
|
+
* @returns Collection configuration details
|
|
2257
|
+
*/
|
|
2258
|
+
async getCollectionConfig(collection) {
|
|
2259
|
+
this.ensureInitialized();
|
|
2260
|
+
return this.backend.getCollectionConfig(collection);
|
|
2261
|
+
}
|
|
2262
|
+
/**
|
|
2263
|
+
* Search returning only IDs and scores (lightweight)
|
|
2264
|
+
*
|
|
2265
|
+
* @param collection - Collection name
|
|
2266
|
+
* @param query - Query vector
|
|
2267
|
+
* @param options - Search options
|
|
2268
|
+
* @returns Array of id/score pairs
|
|
2269
|
+
*/
|
|
2270
|
+
async searchIds(collection, query2, options) {
|
|
2271
|
+
this.ensureInitialized();
|
|
2272
|
+
return this.backend.searchIds(collection, query2, options);
|
|
2273
|
+
}
|
|
2274
|
+
// ========================================================================
|
|
2275
|
+
// Agent Memory (Phase 8)
|
|
2276
|
+
// ========================================================================
|
|
2277
|
+
/**
|
|
2278
|
+
* Create an agent memory interface
|
|
2279
|
+
*
|
|
2280
|
+
* @param config - Optional agent memory configuration
|
|
2281
|
+
* @returns AgentMemoryClient instance
|
|
2282
|
+
*/
|
|
2283
|
+
agentMemory(config) {
|
|
2284
|
+
this.ensureInitialized();
|
|
2285
|
+
return new AgentMemoryClient(this.backend, config);
|
|
2286
|
+
}
|
|
1945
2287
|
};
|
|
1946
2288
|
|
|
1947
2289
|
// src/query-builder.ts
|
|
@@ -2247,6 +2589,7 @@ function velesql() {
|
|
|
2247
2589
|
}
|
|
2248
2590
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2249
2591
|
0 && (module.exports = {
|
|
2592
|
+
AgentMemoryClient,
|
|
2250
2593
|
BackpressureError,
|
|
2251
2594
|
ConnectionError,
|
|
2252
2595
|
NotFoundError,
|