@wiscale/velesdb-sdk 1.17.0 → 2.0.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/LICENSE +356 -20
- package/README.md +169 -11
- package/dist/index.d.mts +203 -44
- package/dist/index.d.ts +203 -44
- package/dist/index.js +391 -97
- package/dist/index.mjs +391 -97
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -199,7 +199,7 @@ var WASM_CAPABILITIES = Object.freeze({
|
|
|
199
199
|
agentMemory: false,
|
|
200
200
|
streamInsert: false,
|
|
201
201
|
pqTraining: false,
|
|
202
|
-
velesqlQuery:
|
|
202
|
+
velesqlQuery: false,
|
|
203
203
|
collectionIntrospection: false
|
|
204
204
|
});
|
|
205
205
|
|
|
@@ -437,20 +437,47 @@ async function wasmMultiQuerySearch(ctx, collectionName, vectors, options) {
|
|
|
437
437
|
);
|
|
438
438
|
return raw.map((r) => mapWasmResult(ctx, collection, r));
|
|
439
439
|
}
|
|
440
|
-
|
|
440
|
+
var PURE_NEAR_QUERY = /^\s*select\s+\*\s+from\s+([a-z_]\w*)\s+where\s+vector\s+near\s+\$([a-z_]\w*)\s*(?:limit\s+(\d+))?\s*;?\s*$/i;
|
|
441
|
+
function parsePureNearQuery(queryString) {
|
|
442
|
+
const match = PURE_NEAR_QUERY.exec(queryString);
|
|
443
|
+
if (!match) {
|
|
444
|
+
throw new VelesDBError(
|
|
445
|
+
`The WASM backend only executes pure top-k NEAR queries of the form "SELECT * FROM <collection> WHERE vector NEAR $param [LIMIT n]". WHERE predicates, JOIN, GROUP BY, MATCH, set operations and FUSION are not evaluated in WASM \u2014 use the REST backend (velesdb-server) for full VelesQL. Received: ${queryString}`,
|
|
446
|
+
"NOT_SUPPORTED"
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
const parsed = { from: match[1], param: match[2] };
|
|
450
|
+
if (match[3] !== void 0) {
|
|
451
|
+
parsed.limit = Number(match[3]);
|
|
452
|
+
}
|
|
453
|
+
return parsed;
|
|
454
|
+
}
|
|
455
|
+
function resolveQueryK(limit, requestedK) {
|
|
456
|
+
if (limit !== void 0) {
|
|
457
|
+
return limit;
|
|
458
|
+
}
|
|
459
|
+
return typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
|
|
460
|
+
}
|
|
461
|
+
async function wasmQuery(ctx, collectionName, queryString, params, _options) {
|
|
441
462
|
const collection = ctx.getCollection(collectionName);
|
|
442
463
|
if (!collection) {
|
|
443
464
|
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
444
465
|
}
|
|
445
|
-
const
|
|
466
|
+
const parsed = parsePureNearQuery(queryString);
|
|
467
|
+
if (parsed.from !== collectionName) {
|
|
468
|
+
throw new VelesDBError(
|
|
469
|
+
`Query targets collection '${parsed.from}' but was executed against '${collectionName}'.`,
|
|
470
|
+
"BAD_REQUEST"
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
const paramsVector = params?.[parsed.param];
|
|
446
474
|
if (!Array.isArray(paramsVector) && !(paramsVector instanceof Float32Array)) {
|
|
447
475
|
throw new VelesDBError(
|
|
448
|
-
|
|
476
|
+
`WASM query() expects params.${parsed.param} to contain the query embedding vector.`,
|
|
449
477
|
"BAD_REQUEST"
|
|
450
478
|
);
|
|
451
479
|
}
|
|
452
|
-
const
|
|
453
|
-
const k = typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
|
|
480
|
+
const k = resolveQueryK(parsed.limit, params?.k);
|
|
454
481
|
const raw = collection.store.query(
|
|
455
482
|
paramsVector instanceof Float32Array ? paramsVector : new Float32Array(paramsVector),
|
|
456
483
|
k
|
|
@@ -950,6 +977,12 @@ async function wasmRecordEpisodicEvent(_collection, _event) {
|
|
|
950
977
|
async function wasmRecallEpisodicEvents(_collection, _embedding, _k) {
|
|
951
978
|
wasmNotSupported("Agent memory");
|
|
952
979
|
}
|
|
980
|
+
async function wasmRecallRecentEvents(_collection, _since) {
|
|
981
|
+
wasmNotSupported("Agent memory");
|
|
982
|
+
}
|
|
983
|
+
async function wasmRecallOlderThanEvents(_collection, _before) {
|
|
984
|
+
wasmNotSupported("Agent memory");
|
|
985
|
+
}
|
|
953
986
|
async function wasmStoreProceduralPattern(_collection, _pattern) {
|
|
954
987
|
wasmNotSupported("Agent memory");
|
|
955
988
|
}
|
|
@@ -997,6 +1030,18 @@ function wasmUpsertNodePayload(_c, _id, _p) {
|
|
|
997
1030
|
function wasmGraphSearch(_c, _r) {
|
|
998
1031
|
return Promise.resolve(wasmNotSupported("Graph search"));
|
|
999
1032
|
}
|
|
1033
|
+
function wasmRelate(_c, _req) {
|
|
1034
|
+
return Promise.resolve(wasmNotSupported("Relation edges"));
|
|
1035
|
+
}
|
|
1036
|
+
function wasmUnrelate(_c, _id) {
|
|
1037
|
+
return Promise.resolve(wasmNotSupported("Relation edge removal"));
|
|
1038
|
+
}
|
|
1039
|
+
function wasmGetRelations(_c, _id) {
|
|
1040
|
+
return Promise.resolve(wasmNotSupported("Relation edges"));
|
|
1041
|
+
}
|
|
1042
|
+
function wasmSetTtlDurable(_c, _id, _ttl) {
|
|
1043
|
+
return Promise.resolve(wasmNotSupported("Durable TTL"));
|
|
1044
|
+
}
|
|
1000
1045
|
|
|
1001
1046
|
// src/backends/wasm.ts
|
|
1002
1047
|
var WasmBackend = class {
|
|
@@ -1351,6 +1396,14 @@ var WasmBackend = class {
|
|
|
1351
1396
|
this.ensureInitialized();
|
|
1352
1397
|
return wasmRecallEpisodicEvents(c, e, k);
|
|
1353
1398
|
}
|
|
1399
|
+
async recallRecentEvents(c, since) {
|
|
1400
|
+
this.ensureInitialized();
|
|
1401
|
+
return wasmRecallRecentEvents(c, since);
|
|
1402
|
+
}
|
|
1403
|
+
async recallOlderThanEvents(c, before) {
|
|
1404
|
+
this.ensureInitialized();
|
|
1405
|
+
return wasmRecallOlderThanEvents(c, before);
|
|
1406
|
+
}
|
|
1354
1407
|
async storeProceduralPattern(c, p) {
|
|
1355
1408
|
this.ensureInitialized();
|
|
1356
1409
|
return wasmStoreProceduralPattern(c, p);
|
|
@@ -1412,16 +1465,43 @@ var WasmBackend = class {
|
|
|
1412
1465
|
this.ensureInitialized();
|
|
1413
1466
|
return wasmSparseSearchNamed(c, q, idx, o);
|
|
1414
1467
|
}
|
|
1468
|
+
async relate(c, req) {
|
|
1469
|
+
this.ensureInitialized();
|
|
1470
|
+
return wasmRelate(c, req);
|
|
1471
|
+
}
|
|
1472
|
+
async unrelate(c, edgeId) {
|
|
1473
|
+
this.ensureInitialized();
|
|
1474
|
+
return wasmUnrelate(c, edgeId);
|
|
1475
|
+
}
|
|
1476
|
+
async getRelations(c, pointId) {
|
|
1477
|
+
this.ensureInitialized();
|
|
1478
|
+
return wasmGetRelations(c, pointId);
|
|
1479
|
+
}
|
|
1480
|
+
async setTtlDurable(c, pointId, ttlSeconds) {
|
|
1481
|
+
this.ensureInitialized();
|
|
1482
|
+
return wasmSetTtlDurable(c, pointId, ttlSeconds);
|
|
1483
|
+
}
|
|
1415
1484
|
};
|
|
1416
1485
|
|
|
1417
1486
|
// src/backends/crud-backend.ts
|
|
1487
|
+
var U64_MAX = 18446744073709551615n;
|
|
1488
|
+
function coerceDecimalStringId(id) {
|
|
1489
|
+
if (!/^\d+$/.test(id)) return NaN;
|
|
1490
|
+
const big = BigInt(id);
|
|
1491
|
+
if (big > U64_MAX) return NaN;
|
|
1492
|
+
return big > BigInt(Number.MAX_SAFE_INTEGER) ? id : Number(id);
|
|
1493
|
+
}
|
|
1418
1494
|
function parseRestPointId(id) {
|
|
1419
|
-
|
|
1495
|
+
const coerced = typeof id === "string" ? coerceDecimalStringId(id) : id;
|
|
1496
|
+
if (typeof coerced === "string") {
|
|
1497
|
+
return coerced;
|
|
1498
|
+
}
|
|
1499
|
+
if (!Number.isFinite(coerced) || coerced < 0 || !Number.isInteger(coerced) || coerced > Number.MAX_SAFE_INTEGER) {
|
|
1420
1500
|
throw new ValidationError(
|
|
1421
|
-
`REST backend requires numeric u64-compatible IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER}). Received: ${String(id)}`
|
|
1501
|
+
`REST backend requires numeric u64-compatible IDs: a non-negative integer in the JS safe integer range (0..${Number.MAX_SAFE_INTEGER}) or a decimal string up to u64::MAX (${U64_MAX}). Received: ${String(id)}`
|
|
1422
1502
|
);
|
|
1423
1503
|
}
|
|
1424
|
-
return
|
|
1504
|
+
return coerced;
|
|
1425
1505
|
}
|
|
1426
1506
|
function sparseVectorToRestFormat(sv) {
|
|
1427
1507
|
const result = {};
|
|
@@ -1815,16 +1895,13 @@ async function getEdgeCount(transport, collection) {
|
|
|
1815
1895
|
return response.data?.count ?? 0;
|
|
1816
1896
|
}
|
|
1817
1897
|
async function listNodes(transport, collection) {
|
|
1818
|
-
const response = await transport.requestJson(
|
|
1819
|
-
"GET",
|
|
1820
|
-
`${collectionPath(collection)}/graph/nodes`
|
|
1821
|
-
);
|
|
1898
|
+
const response = await transport.requestJson("GET", `${collectionPath(collection)}/graph/nodes`);
|
|
1822
1899
|
throwOnError(response, `Collection '${collection}'`);
|
|
1823
1900
|
const data = response.data;
|
|
1824
1901
|
return { nodeIds: data.node_ids, count: data.count };
|
|
1825
1902
|
}
|
|
1826
|
-
function
|
|
1827
|
-
return
|
|
1903
|
+
function idToGraphId(id) {
|
|
1904
|
+
return id;
|
|
1828
1905
|
}
|
|
1829
1906
|
async function getNodeEdges(transport, collection, nodeId, options) {
|
|
1830
1907
|
const params = new URLSearchParams();
|
|
@@ -1835,9 +1912,9 @@ async function getNodeEdges(transport, collection, nodeId, options) {
|
|
|
1835
1912
|
const response = await transport.requestJson("GET", url);
|
|
1836
1913
|
throwOnError(response, `Collection '${collection}'`);
|
|
1837
1914
|
return (response.data?.edges ?? []).map((e) => ({
|
|
1838
|
-
id:
|
|
1839
|
-
source:
|
|
1840
|
-
target:
|
|
1915
|
+
id: e.id,
|
|
1916
|
+
source: e.source,
|
|
1917
|
+
target: e.target,
|
|
1841
1918
|
label: e.label,
|
|
1842
1919
|
properties: e.properties
|
|
1843
1920
|
}));
|
|
@@ -1847,7 +1924,7 @@ async function getNodePayload(transport, collection, nodeId) {
|
|
|
1847
1924
|
throwOnError(response, `Collection '${collection}'`);
|
|
1848
1925
|
const data = response.data;
|
|
1849
1926
|
return {
|
|
1850
|
-
nodeId:
|
|
1927
|
+
nodeId: idToGraphId(data.node_id),
|
|
1851
1928
|
payload: data.payload
|
|
1852
1929
|
};
|
|
1853
1930
|
}
|
|
@@ -1871,7 +1948,7 @@ async function graphSearch(transport, collection, request2) {
|
|
|
1871
1948
|
throwOnError(response, `Collection '${collection}'`);
|
|
1872
1949
|
const items = (response.data?.results ?? []).map(
|
|
1873
1950
|
(r) => ({
|
|
1874
|
-
id:
|
|
1951
|
+
id: idToGraphId(r.id),
|
|
1875
1952
|
score: r.score,
|
|
1876
1953
|
payload: r.payload
|
|
1877
1954
|
})
|
|
@@ -1879,6 +1956,31 @@ async function graphSearch(transport, collection, request2) {
|
|
|
1879
1956
|
return { results: items };
|
|
1880
1957
|
}
|
|
1881
1958
|
|
|
1959
|
+
// src/backends/scroll-backend.ts
|
|
1960
|
+
async function scroll(transport, collection, request2) {
|
|
1961
|
+
const body = {};
|
|
1962
|
+
if (request2?.cursor !== void 0) {
|
|
1963
|
+
body.cursor = request2.cursor;
|
|
1964
|
+
}
|
|
1965
|
+
if (request2?.batchSize !== void 0) {
|
|
1966
|
+
body.batch_size = request2.batchSize;
|
|
1967
|
+
}
|
|
1968
|
+
if (request2?.filter !== void 0) {
|
|
1969
|
+
body.filter = request2.filter;
|
|
1970
|
+
}
|
|
1971
|
+
const response = await transport.requestJson(
|
|
1972
|
+
"POST",
|
|
1973
|
+
`${collectionPath(collection)}/points/scroll`,
|
|
1974
|
+
body
|
|
1975
|
+
);
|
|
1976
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
1977
|
+
const data = response.data;
|
|
1978
|
+
return {
|
|
1979
|
+
points: data.points,
|
|
1980
|
+
nextCursor: data.next_cursor
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1882
1984
|
// src/backends/agent-memory-backend.ts
|
|
1883
1985
|
var _idCounter = 0;
|
|
1884
1986
|
var _lastTimestamp = 0;
|
|
@@ -1896,18 +1998,28 @@ function generateUniqueId() {
|
|
|
1896
1998
|
}
|
|
1897
1999
|
return _lastTimestamp * 1e3 + _idCounter;
|
|
1898
2000
|
}
|
|
2001
|
+
function memoryIdToString(id) {
|
|
2002
|
+
return String(id);
|
|
2003
|
+
}
|
|
2004
|
+
function nowUnixSeconds() {
|
|
2005
|
+
return Math.floor(Date.now() / 1e3);
|
|
2006
|
+
}
|
|
1899
2007
|
async function storeSemanticFact(transport, collection, entry) {
|
|
1900
2008
|
const response = await transport.requestJson(
|
|
1901
2009
|
"POST",
|
|
1902
2010
|
`${collectionPath(collection)}/points`,
|
|
1903
2011
|
{
|
|
1904
2012
|
points: [{
|
|
1905
|
-
id: entry.id,
|
|
2013
|
+
id: parseRestPointId(entry.id),
|
|
1906
2014
|
vector: entry.embedding,
|
|
1907
2015
|
payload: {
|
|
2016
|
+
// Caller metadata is spread first so the reserved keys below
|
|
2017
|
+
// (`_memory_type`, `content`) always win and cannot be clobbered.
|
|
2018
|
+
...entry.metadata,
|
|
1908
2019
|
_memory_type: "semantic",
|
|
1909
|
-
|
|
1910
|
-
|
|
2020
|
+
// `content` matches the core semantic store and the server/Python
|
|
2021
|
+
// payload field (BREAKING: was `text` before this change).
|
|
2022
|
+
content: entry.text
|
|
1911
2023
|
}
|
|
1912
2024
|
}]
|
|
1913
2025
|
}
|
|
@@ -1918,7 +2030,8 @@ async function searchSemanticMemory(transport, collection, embedding, k = 5) {
|
|
|
1918
2030
|
return transport.searchVectors(collection, embedding, k, { _memory_type: "semantic" });
|
|
1919
2031
|
}
|
|
1920
2032
|
async function recordEpisodicEvent(transport, collection, event) {
|
|
1921
|
-
const id = generateUniqueId();
|
|
2033
|
+
const id = event.id !== void 0 ? parseRestPointId(event.id) : generateUniqueId();
|
|
2034
|
+
const timestamp = event.timestamp ?? nowUnixSeconds();
|
|
1922
2035
|
const response = await transport.requestJson(
|
|
1923
2036
|
"POST",
|
|
1924
2037
|
`${collectionPath(collection)}/points`,
|
|
@@ -1927,42 +2040,83 @@ async function recordEpisodicEvent(transport, collection, event) {
|
|
|
1927
2040
|
id,
|
|
1928
2041
|
vector: event.embedding,
|
|
1929
2042
|
payload: {
|
|
2043
|
+
// Caller-supplied data/metadata is spread first so the reserved
|
|
2044
|
+
// keys below (`_memory_type`, `event_type`, `timestamp`) always
|
|
2045
|
+
// win and cannot be clobbered.
|
|
2046
|
+
...event.data,
|
|
2047
|
+
...event.metadata,
|
|
1930
2048
|
_memory_type: "episodic",
|
|
1931
2049
|
event_type: event.eventType,
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2050
|
+
// NUMERIC unix-seconds, mirroring the core episodic store so
|
|
2051
|
+
// recallRecent/recallOlderThan can range-filter on it.
|
|
2052
|
+
timestamp
|
|
1935
2053
|
}
|
|
1936
2054
|
}]
|
|
1937
2055
|
}
|
|
1938
2056
|
);
|
|
1939
2057
|
throwOnError(response);
|
|
2058
|
+
return memoryIdToString(id);
|
|
1940
2059
|
}
|
|
1941
2060
|
async function recallEpisodicEvents(transport, collection, embedding, k = 5) {
|
|
1942
2061
|
return transport.searchVectors(collection, embedding, k, { _memory_type: "episodic" });
|
|
1943
2062
|
}
|
|
1944
2063
|
async function storeProceduralPattern(transport, collection, pattern) {
|
|
1945
|
-
const id = generateUniqueId();
|
|
2064
|
+
const id = pattern.id !== void 0 ? parseRestPointId(pattern.id) : generateUniqueId();
|
|
1946
2065
|
const response = await transport.requestJson(
|
|
1947
2066
|
"POST",
|
|
1948
2067
|
`${collectionPath(collection)}/points`,
|
|
1949
2068
|
{
|
|
1950
2069
|
points: [{
|
|
1951
2070
|
id,
|
|
2071
|
+
vector: pattern.embedding,
|
|
1952
2072
|
payload: {
|
|
2073
|
+
// Caller metadata is spread first so the reserved keys below
|
|
2074
|
+
// (`_memory_type`, `name`, `steps`) always win and cannot be
|
|
2075
|
+
// clobbered.
|
|
2076
|
+
...pattern.metadata,
|
|
1953
2077
|
_memory_type: "procedural",
|
|
1954
2078
|
name: pattern.name,
|
|
1955
|
-
steps: pattern.steps
|
|
1956
|
-
...pattern.metadata
|
|
2079
|
+
steps: pattern.steps
|
|
1957
2080
|
}
|
|
1958
2081
|
}]
|
|
1959
2082
|
}
|
|
1960
2083
|
);
|
|
1961
2084
|
throwOnError(response);
|
|
2085
|
+
return memoryIdToString(id);
|
|
1962
2086
|
}
|
|
1963
2087
|
async function matchProceduralPatterns(transport, collection, embedding, k = 5) {
|
|
1964
2088
|
return transport.searchVectors(collection, embedding, k, { _memory_type: "procedural" });
|
|
1965
2089
|
}
|
|
2090
|
+
function toEpisodicRecord(point) {
|
|
2091
|
+
const payload = point.payload ?? {};
|
|
2092
|
+
if (payload._memory_type !== "episodic") return void 0;
|
|
2093
|
+
if (typeof payload.timestamp !== "number") return void 0;
|
|
2094
|
+
return { id: String(point.id), timestamp: payload.timestamp, payload };
|
|
2095
|
+
}
|
|
2096
|
+
async function scrollEpisodicRecords(transport, collection) {
|
|
2097
|
+
const records = [];
|
|
2098
|
+
let cursor = null;
|
|
2099
|
+
do {
|
|
2100
|
+
const page = await scroll(transport, collection, {
|
|
2101
|
+
cursor: cursor ?? void 0,
|
|
2102
|
+
filter: { _memory_type: "episodic" }
|
|
2103
|
+
});
|
|
2104
|
+
for (const point of page.points) {
|
|
2105
|
+
const record = toEpisodicRecord(point);
|
|
2106
|
+
if (record !== void 0) records.push(record);
|
|
2107
|
+
}
|
|
2108
|
+
cursor = page.nextCursor;
|
|
2109
|
+
} while (cursor !== null && cursor !== void 0);
|
|
2110
|
+
return records;
|
|
2111
|
+
}
|
|
2112
|
+
async function recallRecentEvents(transport, collection, since) {
|
|
2113
|
+
const records = await scrollEpisodicRecords(transport, collection);
|
|
2114
|
+
return records.filter((r) => since === void 0 || r.timestamp >= since).sort((a, b) => b.timestamp - a.timestamp);
|
|
2115
|
+
}
|
|
2116
|
+
async function recallOlderThanEvents(transport, collection, before) {
|
|
2117
|
+
const records = await scrollEpisodicRecords(transport, collection);
|
|
2118
|
+
return records.filter((r) => r.timestamp < before).sort((a, b) => b.timestamp - a.timestamp);
|
|
2119
|
+
}
|
|
1966
2120
|
|
|
1967
2121
|
// src/search-quality.ts
|
|
1968
2122
|
function searchQualityToMode(quality) {
|
|
@@ -2112,15 +2266,29 @@ async function addEdge(transport, collection, edge) {
|
|
|
2112
2266
|
throwOnError(response, `Collection '${collection}'`);
|
|
2113
2267
|
}
|
|
2114
2268
|
function toGraphEdge(e) {
|
|
2115
|
-
const toNum = (v) => typeof v === "string" ? Number(v) : v;
|
|
2116
2269
|
return {
|
|
2117
|
-
id:
|
|
2118
|
-
source:
|
|
2119
|
-
target:
|
|
2270
|
+
id: e.id,
|
|
2271
|
+
source: e.source,
|
|
2272
|
+
target: e.target,
|
|
2120
2273
|
label: e.label,
|
|
2121
2274
|
properties: e.properties
|
|
2122
2275
|
};
|
|
2123
2276
|
}
|
|
2277
|
+
function toTraverseResponse(data) {
|
|
2278
|
+
return {
|
|
2279
|
+
results: data.results.map((r) => ({
|
|
2280
|
+
targetId: r.target_id,
|
|
2281
|
+
depth: r.depth,
|
|
2282
|
+
path: r.path
|
|
2283
|
+
})),
|
|
2284
|
+
nextCursor: data.next_cursor ?? void 0,
|
|
2285
|
+
hasMore: data.has_more,
|
|
2286
|
+
stats: {
|
|
2287
|
+
visited: data.stats.visited,
|
|
2288
|
+
depthReached: data.stats.depth_reached
|
|
2289
|
+
}
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2124
2292
|
async function getEdges(transport, collection, options) {
|
|
2125
2293
|
const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
|
|
2126
2294
|
const response = await transport.requestJson(
|
|
@@ -2144,20 +2312,7 @@ async function traverseGraph(transport, collection, request2) {
|
|
|
2144
2312
|
}
|
|
2145
2313
|
);
|
|
2146
2314
|
throwOnError(response, `Collection '${collection}'`);
|
|
2147
|
-
|
|
2148
|
-
return {
|
|
2149
|
-
results: data.results.map((r) => ({
|
|
2150
|
-
targetId: r.target_id,
|
|
2151
|
-
depth: r.depth,
|
|
2152
|
-
path: r.path
|
|
2153
|
-
})),
|
|
2154
|
-
nextCursor: data.next_cursor ?? void 0,
|
|
2155
|
-
hasMore: data.has_more,
|
|
2156
|
-
stats: {
|
|
2157
|
-
visited: data.stats.visited,
|
|
2158
|
-
depthReached: data.stats.depth_reached
|
|
2159
|
-
}
|
|
2160
|
-
};
|
|
2315
|
+
return toTraverseResponse(response.data);
|
|
2161
2316
|
}
|
|
2162
2317
|
async function getNodeDegree(transport, collection, nodeId) {
|
|
2163
2318
|
const response = await transport.requestJson(
|
|
@@ -2192,21 +2347,66 @@ async function traverseParallel(transport, collection, request2) {
|
|
|
2192
2347
|
}
|
|
2193
2348
|
);
|
|
2194
2349
|
throwOnError(response, `Collection '${collection}'`);
|
|
2195
|
-
|
|
2350
|
+
return toTraverseResponse(response.data);
|
|
2351
|
+
}
|
|
2352
|
+
async function relate(transport, collection, req) {
|
|
2353
|
+
const response = await transport.requestJson(
|
|
2354
|
+
"POST",
|
|
2355
|
+
`${collectionPath(collection)}/relations`,
|
|
2356
|
+
{
|
|
2357
|
+
source: req.source,
|
|
2358
|
+
target: req.target,
|
|
2359
|
+
rel_type: req.relType,
|
|
2360
|
+
properties: req.properties ?? {}
|
|
2361
|
+
}
|
|
2362
|
+
);
|
|
2363
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
2364
|
+
return { edgeId: response.data.edge_id };
|
|
2365
|
+
}
|
|
2366
|
+
async function unrelate(transport, collection, edgeId) {
|
|
2367
|
+
const response = await transport.requestJson(
|
|
2368
|
+
"DELETE",
|
|
2369
|
+
`${collectionPath(collection)}/relations/${encodeURIComponent(String(edgeId))}`
|
|
2370
|
+
);
|
|
2371
|
+
if (response.error !== void 0) {
|
|
2372
|
+
const { code, message } = response.error;
|
|
2373
|
+
const err = parseVelesError(code, message);
|
|
2374
|
+
if (err instanceof EdgeNotFoundError) {
|
|
2375
|
+
return false;
|
|
2376
|
+
}
|
|
2377
|
+
if (code === "NOT_FOUND") {
|
|
2378
|
+
return false;
|
|
2379
|
+
}
|
|
2380
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
2381
|
+
}
|
|
2382
|
+
return true;
|
|
2383
|
+
}
|
|
2384
|
+
async function getRelations(transport, collection, pointId) {
|
|
2385
|
+
const response = await transport.requestJson(
|
|
2386
|
+
"GET",
|
|
2387
|
+
`${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/relations`
|
|
2388
|
+
);
|
|
2389
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
2390
|
+
const raw = response.data;
|
|
2196
2391
|
return {
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2392
|
+
edges: raw.edges.map((e) => ({
|
|
2393
|
+
id: e.id,
|
|
2394
|
+
source: e.source,
|
|
2395
|
+
target: e.target,
|
|
2396
|
+
relType: e.rel_type,
|
|
2397
|
+
properties: e.properties
|
|
2201
2398
|
})),
|
|
2202
|
-
|
|
2203
|
-
hasMore: data.has_more,
|
|
2204
|
-
stats: {
|
|
2205
|
-
visited: data.stats.visited,
|
|
2206
|
-
depthReached: data.stats.depth_reached
|
|
2207
|
-
}
|
|
2399
|
+
count: raw.count
|
|
2208
2400
|
};
|
|
2209
2401
|
}
|
|
2402
|
+
async function setTtlDurable(transport, collection, pointId, ttlSeconds) {
|
|
2403
|
+
const response = await transport.requestJson(
|
|
2404
|
+
"PATCH",
|
|
2405
|
+
`${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/ttl`,
|
|
2406
|
+
{ ttl_seconds: ttlSeconds }
|
|
2407
|
+
);
|
|
2408
|
+
throwOnError(response, `Collection '${collection}'`);
|
|
2409
|
+
}
|
|
2210
2410
|
|
|
2211
2411
|
// src/backends/query-backend.ts
|
|
2212
2412
|
function isLikelyAggregationQuery(queryString) {
|
|
@@ -2339,31 +2539,6 @@ async function collectionSanity(transport, collection) {
|
|
|
2339
2539
|
};
|
|
2340
2540
|
}
|
|
2341
2541
|
|
|
2342
|
-
// src/backends/scroll-backend.ts
|
|
2343
|
-
async function scroll(transport, collection, request2) {
|
|
2344
|
-
const body = {};
|
|
2345
|
-
if (request2?.cursor !== void 0) {
|
|
2346
|
-
body.cursor = request2.cursor;
|
|
2347
|
-
}
|
|
2348
|
-
if (request2?.batchSize !== void 0) {
|
|
2349
|
-
body.batch_size = request2.batchSize;
|
|
2350
|
-
}
|
|
2351
|
-
if (request2?.filter !== void 0) {
|
|
2352
|
-
body.filter = request2.filter;
|
|
2353
|
-
}
|
|
2354
|
-
const response = await transport.requestJson(
|
|
2355
|
-
"POST",
|
|
2356
|
-
`${collectionPath(collection)}/points/scroll`,
|
|
2357
|
-
body
|
|
2358
|
-
);
|
|
2359
|
-
throwOnError(response, `Collection '${collection}'`);
|
|
2360
|
-
const data = response.data;
|
|
2361
|
-
return {
|
|
2362
|
-
points: data.points,
|
|
2363
|
-
nextCursor: data.next_cursor
|
|
2364
|
-
};
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
2542
|
// src/backends/admin-backend.ts
|
|
2368
2543
|
function mapStatsResponse(data) {
|
|
2369
2544
|
let columnStats;
|
|
@@ -2549,9 +2724,17 @@ async function streamInsert(transport, collection, docs) {
|
|
|
2549
2724
|
}
|
|
2550
2725
|
}
|
|
2551
2726
|
}
|
|
2727
|
+
function requireSafeRangeId(restId) {
|
|
2728
|
+
if (typeof restId === "string") {
|
|
2729
|
+
throw new ValidationError(
|
|
2730
|
+
`streamUpsertPoints requires ids in the JS safe integer range (0..${Number.MAX_SAFE_INTEGER}); use upsert/upsertBatch for string ids above it. Received: ${restId}`
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
return restId;
|
|
2734
|
+
}
|
|
2552
2735
|
async function streamUpsertPoints(transport, collection, docs) {
|
|
2553
2736
|
const ndjsonLines = docs.map((doc) => {
|
|
2554
|
-
const restId = transport.parseRestPointId(doc.id);
|
|
2737
|
+
const restId = requireSafeRangeId(transport.parseRestPointId(doc.id));
|
|
2555
2738
|
const vector = toNumberArray(doc.vector);
|
|
2556
2739
|
const point = {
|
|
2557
2740
|
id: restId,
|
|
@@ -2742,6 +2925,22 @@ var RestBackend = class {
|
|
|
2742
2925
|
this.ensureInitialized();
|
|
2743
2926
|
return graphSearch(buildBaseTransport(this.httpConfig), c, r);
|
|
2744
2927
|
}
|
|
2928
|
+
async relate(c, req) {
|
|
2929
|
+
this.ensureInitialized();
|
|
2930
|
+
return relate(buildCrudTransport(this.httpConfig), c, req);
|
|
2931
|
+
}
|
|
2932
|
+
async unrelate(c, edgeId) {
|
|
2933
|
+
this.ensureInitialized();
|
|
2934
|
+
return unrelate(buildCrudTransport(this.httpConfig), c, edgeId);
|
|
2935
|
+
}
|
|
2936
|
+
async getRelations(c, pointId) {
|
|
2937
|
+
this.ensureInitialized();
|
|
2938
|
+
return getRelations(buildCrudTransport(this.httpConfig), c, pointId);
|
|
2939
|
+
}
|
|
2940
|
+
async setTtlDurable(c, pointId, ttlSeconds) {
|
|
2941
|
+
this.ensureInitialized();
|
|
2942
|
+
return setTtlDurable(buildCrudTransport(this.httpConfig), c, pointId, ttlSeconds);
|
|
2943
|
+
}
|
|
2745
2944
|
// Search
|
|
2746
2945
|
async search(c, q, o) {
|
|
2747
2946
|
this.ensureInitialized();
|
|
@@ -2874,6 +3073,14 @@ var RestBackend = class {
|
|
|
2874
3073
|
this.ensureInitialized();
|
|
2875
3074
|
return recallEpisodicEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, e, k);
|
|
2876
3075
|
}
|
|
3076
|
+
async recallRecentEvents(c, since) {
|
|
3077
|
+
this.ensureInitialized();
|
|
3078
|
+
return recallRecentEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, since);
|
|
3079
|
+
}
|
|
3080
|
+
async recallOlderThanEvents(c, before) {
|
|
3081
|
+
this.ensureInitialized();
|
|
3082
|
+
return recallOlderThanEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, before);
|
|
3083
|
+
}
|
|
2877
3084
|
async storeProceduralPattern(c, p) {
|
|
2878
3085
|
this.ensureInitialized();
|
|
2879
3086
|
return storeProceduralPattern(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, p);
|
|
@@ -2890,7 +3097,16 @@ var AgentMemoryClient = class {
|
|
|
2890
3097
|
this.backend = backend;
|
|
2891
3098
|
this.config = config;
|
|
2892
3099
|
}
|
|
2893
|
-
/**
|
|
3100
|
+
/**
|
|
3101
|
+
* Advisory embedding dimension passed at construction (default: 384).
|
|
3102
|
+
*
|
|
3103
|
+
* This value is **not** enforced and does not create or size any
|
|
3104
|
+
* collection — the dimension that actually governs storage and search
|
|
3105
|
+
* is the one fixed when the collection was created
|
|
3106
|
+
* (`db.createCollection(name, { dimension, metric: 'cosine' })`).
|
|
3107
|
+
* Embeddings you pass to `storeFact` / `recordEvent` / `learnProcedure`
|
|
3108
|
+
* must match that collection dimension.
|
|
3109
|
+
*/
|
|
2894
3110
|
get dimension() {
|
|
2895
3111
|
return this.config?.dimension ?? 384;
|
|
2896
3112
|
}
|
|
@@ -2902,15 +3118,29 @@ var AgentMemoryClient = class {
|
|
|
2902
3118
|
async searchFacts(collection, embedding, k = 5) {
|
|
2903
3119
|
return this.backend.searchSemanticMemory(collection, embedding, k);
|
|
2904
3120
|
}
|
|
2905
|
-
/** Record an episodic event */
|
|
3121
|
+
/** Record an episodic event. Returns the point ID (string, u64-safe). */
|
|
2906
3122
|
async recordEvent(collection, event) {
|
|
2907
3123
|
return this.backend.recordEpisodicEvent(collection, event);
|
|
2908
3124
|
}
|
|
2909
|
-
/** Recall episodic events */
|
|
3125
|
+
/** Recall episodic events by vector similarity. */
|
|
2910
3126
|
async recallEvents(collection, embedding, k = 5) {
|
|
2911
3127
|
return this.backend.recallEpisodicEvents(collection, embedding, k);
|
|
2912
3128
|
}
|
|
2913
|
-
/**
|
|
3129
|
+
/**
|
|
3130
|
+
* Recall episodic events most-recent-first, optionally bounded below by
|
|
3131
|
+
* `since` (inclusive unix-seconds). Mirrors core `episodic.recent(since)`.
|
|
3132
|
+
*/
|
|
3133
|
+
async recallRecent(collection, since) {
|
|
3134
|
+
return this.backend.recallRecentEvents(collection, since);
|
|
3135
|
+
}
|
|
3136
|
+
/**
|
|
3137
|
+
* Recall episodic events strictly older than `before` (unix-seconds),
|
|
3138
|
+
* most-recent-first. Mirrors core `episodic.older_than(before)`.
|
|
3139
|
+
*/
|
|
3140
|
+
async recallOlderThan(collection, before) {
|
|
3141
|
+
return this.backend.recallOlderThanEvents(collection, before);
|
|
3142
|
+
}
|
|
3143
|
+
/** Store a procedural pattern. Returns the point ID (string, u64-safe). */
|
|
2914
3144
|
async learnProcedure(collection, pattern) {
|
|
2915
3145
|
return this.backend.storeProceduralPattern(collection, pattern);
|
|
2916
3146
|
}
|
|
@@ -2918,6 +3148,15 @@ var AgentMemoryClient = class {
|
|
|
2918
3148
|
async recallProcedures(collection, embedding, k = 5) {
|
|
2919
3149
|
return this.backend.matchProceduralPatterns(collection, embedding, k);
|
|
2920
3150
|
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Delete a memory entry (fact, event, or procedure) by its point ID.
|
|
3153
|
+
*
|
|
3154
|
+
* Accepts the `string` ids returned by `recordEvent` / `learnProcedure`
|
|
3155
|
+
* (u64-safe decimal strings) as well as numeric ids.
|
|
3156
|
+
*/
|
|
3157
|
+
async deleteMemory(collection, id) {
|
|
3158
|
+
return this.backend.delete(collection, id);
|
|
3159
|
+
}
|
|
2921
3160
|
};
|
|
2922
3161
|
|
|
2923
3162
|
// src/client/validation.ts
|
|
@@ -2948,11 +3187,10 @@ function validateDocument(doc, config) {
|
|
|
2948
3187
|
validateRestPointId(doc.id, config);
|
|
2949
3188
|
}
|
|
2950
3189
|
function validateRestPointId(id, config) {
|
|
2951
|
-
if (config.backend
|
|
2952
|
-
|
|
2953
|
-
`REST backend requires numeric u64-compatible document IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER})`
|
|
2954
|
-
);
|
|
3190
|
+
if (config.backend !== "rest") {
|
|
3191
|
+
return;
|
|
2955
3192
|
}
|
|
3193
|
+
parseRestPointId(id);
|
|
2956
3194
|
}
|
|
2957
3195
|
|
|
2958
3196
|
// src/client/search-methods.ts
|
|
@@ -3057,12 +3295,15 @@ function aggregate2(backend, queryString, params, options) {
|
|
|
3057
3295
|
}
|
|
3058
3296
|
|
|
3059
3297
|
// src/client/graph-methods.ts
|
|
3298
|
+
function isGraphNodeId(value) {
|
|
3299
|
+
return typeof value === "number" || typeof value === "string";
|
|
3300
|
+
}
|
|
3060
3301
|
function addEdge2(backend, collection, edge) {
|
|
3061
3302
|
if (!edge.label || typeof edge.label !== "string") {
|
|
3062
3303
|
throw new ValidationError("Edge label is required and must be a string");
|
|
3063
3304
|
}
|
|
3064
|
-
if (
|
|
3065
|
-
throw new ValidationError("Edge source and target must be numbers");
|
|
3305
|
+
if (!isGraphNodeId(edge.source) || !isGraphNodeId(edge.target)) {
|
|
3306
|
+
throw new ValidationError("Edge source and target must be numbers or strings");
|
|
3066
3307
|
}
|
|
3067
3308
|
return backend.addEdge(collection, edge);
|
|
3068
3309
|
}
|
|
@@ -3070,8 +3311,8 @@ function getEdges2(backend, collection, options) {
|
|
|
3070
3311
|
return backend.getEdges(collection, options);
|
|
3071
3312
|
}
|
|
3072
3313
|
function traverseGraph2(backend, collection, request2) {
|
|
3073
|
-
if (
|
|
3074
|
-
throw new ValidationError("Source node ID must be a number");
|
|
3314
|
+
if (!isGraphNodeId(request2.source)) {
|
|
3315
|
+
throw new ValidationError("Source node ID must be a number or string");
|
|
3075
3316
|
}
|
|
3076
3317
|
if (request2.strategy && !["bfs", "dfs"].includes(request2.strategy)) {
|
|
3077
3318
|
throw new ValidationError("Strategy must be 'bfs' or 'dfs'");
|
|
@@ -3082,6 +3323,9 @@ function traverseParallel2(backend, collection, request2) {
|
|
|
3082
3323
|
if (!Array.isArray(request2.sources) || request2.sources.length === 0) {
|
|
3083
3324
|
throw new ValidationError("At least one source node ID is required");
|
|
3084
3325
|
}
|
|
3326
|
+
if (!request2.sources.every(isGraphNodeId)) {
|
|
3327
|
+
throw new ValidationError("Source node IDs must be numbers or strings");
|
|
3328
|
+
}
|
|
3085
3329
|
return backend.traverseParallel(collection, request2);
|
|
3086
3330
|
}
|
|
3087
3331
|
function getNodeDegree2(backend, collection, nodeId) {
|
|
@@ -3127,6 +3371,40 @@ function graphSearch2(backend, collection, request2) {
|
|
|
3127
3371
|
requireNonEmptyString(collection, "Collection");
|
|
3128
3372
|
return backend.graphSearch(collection, request2);
|
|
3129
3373
|
}
|
|
3374
|
+
function relate2(backend, collection, req) {
|
|
3375
|
+
requireNonEmptyString(collection, "Collection");
|
|
3376
|
+
if (!req.relType || typeof req.relType !== "string") {
|
|
3377
|
+
throw new ValidationError("Relation type is required and must be a string");
|
|
3378
|
+
}
|
|
3379
|
+
if (!isGraphNodeId(req.source) || !isGraphNodeId(req.target)) {
|
|
3380
|
+
throw new ValidationError("Source and target must be numbers or strings");
|
|
3381
|
+
}
|
|
3382
|
+
return backend.relate(collection, req);
|
|
3383
|
+
}
|
|
3384
|
+
function unrelate2(backend, collection, edgeId) {
|
|
3385
|
+
requireNonEmptyString(collection, "Collection");
|
|
3386
|
+
if (!isGraphNodeId(edgeId)) {
|
|
3387
|
+
throw new ValidationError("Edge ID must be a number or string");
|
|
3388
|
+
}
|
|
3389
|
+
return backend.unrelate(collection, edgeId);
|
|
3390
|
+
}
|
|
3391
|
+
function getRelations2(backend, collection, pointId) {
|
|
3392
|
+
requireNonEmptyString(collection, "Collection");
|
|
3393
|
+
if (!isGraphNodeId(pointId)) {
|
|
3394
|
+
throw new ValidationError("Point ID must be a number or string");
|
|
3395
|
+
}
|
|
3396
|
+
return backend.getRelations(collection, pointId);
|
|
3397
|
+
}
|
|
3398
|
+
function setTtlDurable2(backend, collection, pointId, ttlSeconds) {
|
|
3399
|
+
requireNonEmptyString(collection, "Collection");
|
|
3400
|
+
if (!isGraphNodeId(pointId)) {
|
|
3401
|
+
throw new ValidationError("Point ID must be a number or string");
|
|
3402
|
+
}
|
|
3403
|
+
if (typeof ttlSeconds !== "number" || ttlSeconds < 0) {
|
|
3404
|
+
throw new ValidationError("ttlSeconds must be a non-negative number");
|
|
3405
|
+
}
|
|
3406
|
+
return backend.setTtlDurable(collection, pointId, ttlSeconds);
|
|
3407
|
+
}
|
|
3130
3408
|
|
|
3131
3409
|
// src/client.ts
|
|
3132
3410
|
var VelesDB = class {
|
|
@@ -3424,6 +3702,22 @@ var VelesDB = class {
|
|
|
3424
3702
|
this.ensureInitialized();
|
|
3425
3703
|
return graphSearch2(this.backend, collection, request2);
|
|
3426
3704
|
}
|
|
3705
|
+
async relate(collection, req) {
|
|
3706
|
+
this.ensureInitialized();
|
|
3707
|
+
return relate2(this.backend, collection, req);
|
|
3708
|
+
}
|
|
3709
|
+
async unrelate(collection, edgeId) {
|
|
3710
|
+
this.ensureInitialized();
|
|
3711
|
+
return unrelate2(this.backend, collection, edgeId);
|
|
3712
|
+
}
|
|
3713
|
+
async getRelations(collection, pointId) {
|
|
3714
|
+
this.ensureInitialized();
|
|
3715
|
+
return getRelations2(this.backend, collection, pointId);
|
|
3716
|
+
}
|
|
3717
|
+
async setTtlDurable(collection, pointId, ttlSeconds) {
|
|
3718
|
+
this.ensureInitialized();
|
|
3719
|
+
return setTtlDurable2(this.backend, collection, pointId, ttlSeconds);
|
|
3720
|
+
}
|
|
3427
3721
|
// ========================================================================
|
|
3428
3722
|
// Capabilities & Backend Info
|
|
3429
3723
|
// ========================================================================
|