wolbarg 0.5.2 → 0.5.4
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/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/dist/index.cjs +883 -375
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +883 -375
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -689,6 +689,45 @@ function withEmbeddingCache(provider, store, config) {
|
|
|
689
689
|
};
|
|
690
690
|
}
|
|
691
691
|
|
|
692
|
+
// src/utils/vector.ts
|
|
693
|
+
function cosineDistance(a, b) {
|
|
694
|
+
if (a.length !== b.length) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
`cosineDistance dimension mismatch: a.length=${a.length}, b.length=${b.length}`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
const len = a.length;
|
|
700
|
+
let dot = 0;
|
|
701
|
+
let normA = 0;
|
|
702
|
+
let normB = 0;
|
|
703
|
+
for (let i = 0; i < len; i += 1) {
|
|
704
|
+
const av = a[i] ?? 0;
|
|
705
|
+
const bv = b[i] ?? 0;
|
|
706
|
+
dot += av * bv;
|
|
707
|
+
normA += av * av;
|
|
708
|
+
normB += bv * bv;
|
|
709
|
+
}
|
|
710
|
+
if (normA === 0 || normB === 0) {
|
|
711
|
+
return 1;
|
|
712
|
+
}
|
|
713
|
+
const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
714
|
+
return 1 - similarity;
|
|
715
|
+
}
|
|
716
|
+
function bufferToEmbedding(data) {
|
|
717
|
+
const byteOffset = data.byteOffset ?? 0;
|
|
718
|
+
const byteLength = data.byteLength;
|
|
719
|
+
if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
|
|
720
|
+
return new Float32Array(
|
|
721
|
+
data.buffer,
|
|
722
|
+
byteOffset,
|
|
723
|
+
byteLength / Float32Array.BYTES_PER_ELEMENT
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
const copy = new Uint8Array(byteLength);
|
|
727
|
+
copy.set(data);
|
|
728
|
+
return new Float32Array(copy.buffer);
|
|
729
|
+
}
|
|
730
|
+
|
|
692
731
|
// src/embedding/cache-store.ts
|
|
693
732
|
var TOUCH_FLUSH_THRESHOLD = 64;
|
|
694
733
|
var L1_DEFAULT_MAX = 5e4;
|
|
@@ -773,7 +812,9 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
773
812
|
}
|
|
774
813
|
this.stmts = {
|
|
775
814
|
get: db.prepare(
|
|
776
|
-
`SELECT vector, last_used_at, created_at
|
|
815
|
+
`SELECT model, vector, last_used_at, created_at
|
|
816
|
+
FROM embedding_cache
|
|
817
|
+
WHERE cache_key = ?`
|
|
777
818
|
),
|
|
778
819
|
delete: db.prepare(`DELETE FROM embedding_cache WHERE cache_key = ?`),
|
|
779
820
|
set: db.prepare(
|
|
@@ -808,7 +849,29 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
808
849
|
this.l1.set(cacheKey, pending.model, pending.vector);
|
|
809
850
|
return pending.vector;
|
|
810
851
|
}
|
|
811
|
-
|
|
852
|
+
const db = this.requireDb();
|
|
853
|
+
if (!db) return null;
|
|
854
|
+
try {
|
|
855
|
+
const stmts = this.ensureStatements(db);
|
|
856
|
+
const row = stmts.get.get(cacheKey);
|
|
857
|
+
if (!row) return null;
|
|
858
|
+
if (this.ttlMs !== null) {
|
|
859
|
+
const createdMs = row.created_at ? Date.parse(row.created_at) : 0;
|
|
860
|
+
if (createdMs && Date.now() - createdMs > this.ttlMs) {
|
|
861
|
+
stmts.delete.run(cacheKey);
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const vector = bufferToEmbedding(row.vector);
|
|
866
|
+
this.l1.set(cacheKey, row.model, vector);
|
|
867
|
+
this.pendingTouches.add(cacheKey);
|
|
868
|
+
if (this.pendingTouches.size >= TOUCH_FLUSH_THRESHOLD) {
|
|
869
|
+
this.schedulePersist();
|
|
870
|
+
}
|
|
871
|
+
return vector;
|
|
872
|
+
} catch {
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
812
875
|
}
|
|
813
876
|
async set(cacheKey, model, vector) {
|
|
814
877
|
this.l1.set(cacheKey, model, vector);
|
|
@@ -860,6 +923,16 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
860
923
|
this.pendingSets.clear();
|
|
861
924
|
const touches = [...this.pendingTouches];
|
|
862
925
|
this.pendingTouches.clear();
|
|
926
|
+
const requeue = () => {
|
|
927
|
+
for (const [key, value] of sets) {
|
|
928
|
+
this.pendingSets.set(key, value);
|
|
929
|
+
}
|
|
930
|
+
for (const key of touches) {
|
|
931
|
+
this.pendingTouches.add(key);
|
|
932
|
+
}
|
|
933
|
+
this.stmts = null;
|
|
934
|
+
this.schedulePersist();
|
|
935
|
+
};
|
|
863
936
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
864
937
|
try {
|
|
865
938
|
const stmts = this.ensureStatements(db);
|
|
@@ -894,10 +967,10 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
894
967
|
db.exec("ROLLBACK");
|
|
895
968
|
} catch {
|
|
896
969
|
}
|
|
897
|
-
|
|
970
|
+
requeue();
|
|
898
971
|
}
|
|
899
972
|
} catch {
|
|
900
|
-
|
|
973
|
+
requeue();
|
|
901
974
|
}
|
|
902
975
|
}
|
|
903
976
|
};
|
|
@@ -1206,12 +1279,27 @@ var DocxParser = class {
|
|
|
1206
1279
|
name = "docx";
|
|
1207
1280
|
extensions = [".docx"];
|
|
1208
1281
|
async parse(input) {
|
|
1282
|
+
let mod;
|
|
1283
|
+
try {
|
|
1284
|
+
mod = await import('mammoth');
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
throw new ConfigurationError(
|
|
1287
|
+
'DOCX ingest requires the optional peer package "mammoth". Install it with: npm install mammoth',
|
|
1288
|
+
{
|
|
1289
|
+
cause: error instanceof Error ? error : void 0,
|
|
1290
|
+
suggestion: "npm install mammoth",
|
|
1291
|
+
operation: "parse"
|
|
1292
|
+
}
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
|
|
1296
|
+
if (!extractRawText) {
|
|
1297
|
+
throw new ConfigurationError(
|
|
1298
|
+
"DOCX ingest could not find mammoth.extractRawText. Ensure your mammoth version is compatible.",
|
|
1299
|
+
{ operation: "parse" }
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1209
1302
|
try {
|
|
1210
|
-
const mod = await import('mammoth');
|
|
1211
|
-
const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
|
|
1212
|
-
if (!extractRawText) {
|
|
1213
|
-
throw new Error("mammoth.extractRawText unavailable");
|
|
1214
|
-
}
|
|
1215
1303
|
const result = await extractRawText({ buffer: input.buffer });
|
|
1216
1304
|
return {
|
|
1217
1305
|
text: result.value ?? "",
|
|
@@ -1219,9 +1307,14 @@ var DocxParser = class {
|
|
|
1219
1307
|
filename: input.filename,
|
|
1220
1308
|
isImage: false
|
|
1221
1309
|
};
|
|
1222
|
-
} catch {
|
|
1310
|
+
} catch (error) {
|
|
1223
1311
|
throw new ConfigurationError(
|
|
1224
|
-
|
|
1312
|
+
"DOCX ingest failed to parse the document (it may be corrupt or an unsupported .docx).",
|
|
1313
|
+
{
|
|
1314
|
+
cause: error instanceof Error ? error : void 0,
|
|
1315
|
+
suggestion: "Try a different DOCX file or ensure it is not password-protected.",
|
|
1316
|
+
operation: "parse"
|
|
1317
|
+
}
|
|
1225
1318
|
);
|
|
1226
1319
|
}
|
|
1227
1320
|
}
|
|
@@ -1368,6 +1461,12 @@ function compileComparison(field, op) {
|
|
|
1368
1461
|
const extract = `json_extract(metadata_json, '${path8}')`;
|
|
1369
1462
|
if ("eq" in op) {
|
|
1370
1463
|
const value = op.eq;
|
|
1464
|
+
if (value === null) {
|
|
1465
|
+
return {
|
|
1466
|
+
expression: `json_type(metadata_json, '${path8}') = 'null'`,
|
|
1467
|
+
params: []
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1371
1470
|
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
1372
1471
|
return null;
|
|
1373
1472
|
}
|
|
@@ -1448,6 +1547,59 @@ function compileMetadataFilterToSql(filter) {
|
|
|
1448
1547
|
return compileComparison(filter.field, filter.op);
|
|
1449
1548
|
}
|
|
1450
1549
|
|
|
1550
|
+
// src/telemetry/logger.ts
|
|
1551
|
+
var LEVEL_ORDER = {
|
|
1552
|
+
off: 100,
|
|
1553
|
+
error: 50,
|
|
1554
|
+
warn: 40,
|
|
1555
|
+
info: 30,
|
|
1556
|
+
debug: 20,
|
|
1557
|
+
trace: 10
|
|
1558
|
+
};
|
|
1559
|
+
var WolbargLogger = class {
|
|
1560
|
+
constructor(level = "info") {
|
|
1561
|
+
this.level = level;
|
|
1562
|
+
}
|
|
1563
|
+
level;
|
|
1564
|
+
setLevel(level) {
|
|
1565
|
+
this.level = level;
|
|
1566
|
+
}
|
|
1567
|
+
getLevel() {
|
|
1568
|
+
return this.level;
|
|
1569
|
+
}
|
|
1570
|
+
error(message, extra) {
|
|
1571
|
+
this.write("error", message, extra);
|
|
1572
|
+
}
|
|
1573
|
+
warn(message, extra) {
|
|
1574
|
+
this.write("warn", message, extra);
|
|
1575
|
+
}
|
|
1576
|
+
info(message, extra) {
|
|
1577
|
+
this.write("info", message, extra);
|
|
1578
|
+
}
|
|
1579
|
+
debug(message, extra) {
|
|
1580
|
+
this.write("debug", message, extra);
|
|
1581
|
+
}
|
|
1582
|
+
trace(message, extra) {
|
|
1583
|
+
this.write("trace", message, extra);
|
|
1584
|
+
}
|
|
1585
|
+
write(level, message, extra) {
|
|
1586
|
+
if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
if (this.level === "off") {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
const line = `[wolbarg:${level}] ${message}`;
|
|
1593
|
+
if (level === "error") {
|
|
1594
|
+
console.error(line, extra ?? "");
|
|
1595
|
+
} else if (level === "warn") {
|
|
1596
|
+
console.warn(line, extra ?? "");
|
|
1597
|
+
} else {
|
|
1598
|
+
console.log(line, extra ?? "");
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
|
|
1451
1603
|
// src/schema/index.ts
|
|
1452
1604
|
var SCHEMA_VERSION = 4;
|
|
1453
1605
|
var META_KEYS = {
|
|
@@ -1634,7 +1786,7 @@ var SQL = {
|
|
|
1634
1786
|
AND k = ?
|
|
1635
1787
|
`,
|
|
1636
1788
|
insertEmbeddingBlob: `
|
|
1637
|
-
INSERT INTO memory_embeddings_blob (memory_rowid, embedding) VALUES (?, ?)
|
|
1789
|
+
INSERT OR REPLACE INTO memory_embeddings_blob (memory_rowid, embedding) VALUES (?, ?)
|
|
1638
1790
|
`,
|
|
1639
1791
|
deleteEmbeddingBlob: `
|
|
1640
1792
|
DELETE FROM memory_embeddings_blob WHERE memory_rowid = ?
|
|
@@ -1762,40 +1914,6 @@ var SQL = {
|
|
|
1762
1914
|
`
|
|
1763
1915
|
};
|
|
1764
1916
|
|
|
1765
|
-
// src/utils/vector.ts
|
|
1766
|
-
function cosineDistance(a, b) {
|
|
1767
|
-
const len = Math.min(a.length, b.length);
|
|
1768
|
-
let dot = 0;
|
|
1769
|
-
let normA = 0;
|
|
1770
|
-
let normB = 0;
|
|
1771
|
-
for (let i = 0; i < len; i += 1) {
|
|
1772
|
-
const av = a[i] ?? 0;
|
|
1773
|
-
const bv = b[i] ?? 0;
|
|
1774
|
-
dot += av * bv;
|
|
1775
|
-
normA += av * av;
|
|
1776
|
-
normB += bv * bv;
|
|
1777
|
-
}
|
|
1778
|
-
if (normA === 0 || normB === 0) {
|
|
1779
|
-
return 1;
|
|
1780
|
-
}
|
|
1781
|
-
const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
1782
|
-
return 1 - similarity;
|
|
1783
|
-
}
|
|
1784
|
-
function bufferToEmbedding(data) {
|
|
1785
|
-
const byteOffset = data.byteOffset ?? 0;
|
|
1786
|
-
const byteLength = data.byteLength;
|
|
1787
|
-
if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
|
|
1788
|
-
return new Float32Array(
|
|
1789
|
-
data.buffer,
|
|
1790
|
-
byteOffset,
|
|
1791
|
-
byteLength / Float32Array.BYTES_PER_ELEMENT
|
|
1792
|
-
);
|
|
1793
|
-
}
|
|
1794
|
-
const copy = new Uint8Array(byteLength);
|
|
1795
|
-
copy.set(data);
|
|
1796
|
-
return new Float32Array(copy.buffer);
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
1917
|
// src/utils/vector-index.ts
|
|
1800
1918
|
var InMemoryVectorIndex = class {
|
|
1801
1919
|
dims;
|
|
@@ -1888,24 +2006,33 @@ var InMemoryVectorIndex = class {
|
|
|
1888
2006
|
dot += queryNormalized[d] * data[base + d];
|
|
1889
2007
|
}
|
|
1890
2008
|
const distance = 1 - dot;
|
|
2009
|
+
const rowid = rowids[i];
|
|
1891
2010
|
if (heapSize < k) {
|
|
1892
2011
|
heapDist[heapSize] = distance;
|
|
1893
|
-
heapRow[heapSize] =
|
|
2012
|
+
heapRow[heapSize] = rowid;
|
|
1894
2013
|
heapSize += 1;
|
|
1895
2014
|
if (heapSize === k) {
|
|
1896
2015
|
buildMaxHeap(heapDist, heapRow, k);
|
|
1897
2016
|
}
|
|
1898
|
-
} else
|
|
1899
|
-
heapDist[0]
|
|
1900
|
-
|
|
1901
|
-
|
|
2017
|
+
} else {
|
|
2018
|
+
const worstDist = heapDist[0];
|
|
2019
|
+
const worstRow = heapRow[0];
|
|
2020
|
+
if (distance < worstDist || distance === worstDist && rowid < worstRow) {
|
|
2021
|
+
heapDist[0] = distance;
|
|
2022
|
+
heapRow[0] = rowid;
|
|
2023
|
+
siftDown(heapDist, heapRow, 0, k);
|
|
2024
|
+
}
|
|
1902
2025
|
}
|
|
1903
2026
|
}
|
|
1904
2027
|
const hits = new Array(heapSize);
|
|
1905
2028
|
for (let i = 0; i < heapSize; i += 1) {
|
|
1906
2029
|
hits[i] = { memoryRowid: heapRow[i], distance: heapDist[i] };
|
|
1907
2030
|
}
|
|
1908
|
-
hits.sort((a, b) =>
|
|
2031
|
+
hits.sort((a, b) => {
|
|
2032
|
+
const diff = a.distance - b.distance;
|
|
2033
|
+
if (diff !== 0) return diff;
|
|
2034
|
+
return a.memoryRowid - b.memoryRowid;
|
|
2035
|
+
});
|
|
1909
2036
|
return hits;
|
|
1910
2037
|
}
|
|
1911
2038
|
ensureCapacity(needed) {
|
|
@@ -1951,8 +2078,8 @@ function siftDown(dist, rows, i, n) {
|
|
|
1951
2078
|
let largest = i;
|
|
1952
2079
|
const left = (i << 1) + 1;
|
|
1953
2080
|
const right = left + 1;
|
|
1954
|
-
if (left < n && dist[left] > dist[largest]) largest = left;
|
|
1955
|
-
if (right < n && dist[right] > dist[largest]) largest = right;
|
|
2081
|
+
if (left < n && (dist[left] > dist[largest] || dist[left] === dist[largest] && rows[left] > rows[largest])) largest = left;
|
|
2082
|
+
if (right < n && (dist[right] > dist[largest] || dist[right] === dist[largest] && rows[right] > rows[largest])) largest = right;
|
|
1956
2083
|
if (largest === i) return;
|
|
1957
2084
|
const td = dist[i];
|
|
1958
2085
|
dist[i] = dist[largest];
|
|
@@ -2094,6 +2221,9 @@ var BATCH_INSERT_CHUNK = 96;
|
|
|
2094
2221
|
var MAX_ANN_OVERFETCH = 8192;
|
|
2095
2222
|
var INSERT_COALESCE_THRESHOLD = 24;
|
|
2096
2223
|
var INSERT_COALESCE_MAX = 96;
|
|
2224
|
+
var warnLogger = new WolbargLogger("warn");
|
|
2225
|
+
var warnedSqliteVecFallback = false;
|
|
2226
|
+
var warnedFtsKeywordSearch = false;
|
|
2097
2227
|
var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
2098
2228
|
name = "sqlite";
|
|
2099
2229
|
connectionString;
|
|
@@ -2103,6 +2233,10 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2103
2233
|
vectorDimensions = null;
|
|
2104
2234
|
vectorBackend = null;
|
|
2105
2235
|
sqliteVecLoaded = false;
|
|
2236
|
+
/** Tracks nesting depth for {@link withTransaction} so we can use savepoints. */
|
|
2237
|
+
transactionDepth = 0;
|
|
2238
|
+
/** Incrementing counter for deterministic savepoint names. */
|
|
2239
|
+
savepointCounter = 0;
|
|
2106
2240
|
/** Hot in-process ANN for blob backend (sqlite-vec unavailable platforms). */
|
|
2107
2241
|
memoryIndex = null;
|
|
2108
2242
|
memoryIndexDirty = false;
|
|
@@ -2169,6 +2303,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2169
2303
|
} else if (this.sqliteVecLoaded) {
|
|
2170
2304
|
this.vectorBackend = "sqlite-vec";
|
|
2171
2305
|
} else {
|
|
2306
|
+
if (!warnedSqliteVecFallback) {
|
|
2307
|
+
warnedSqliteVecFallback = true;
|
|
2308
|
+
warnLogger.warn(
|
|
2309
|
+
"sqlite-vec unavailable on this platform; using blob ANN fallback."
|
|
2310
|
+
);
|
|
2311
|
+
}
|
|
2172
2312
|
this.vectorBackend = "blob";
|
|
2173
2313
|
}
|
|
2174
2314
|
if (dims !== null) {
|
|
@@ -2412,6 +2552,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2412
2552
|
async searchKeyword(query, organization, topK) {
|
|
2413
2553
|
const stmts = this.requireStatements();
|
|
2414
2554
|
if (!stmts.searchFts) {
|
|
2555
|
+
if (!warnedFtsKeywordSearch) {
|
|
2556
|
+
warnedFtsKeywordSearch = true;
|
|
2557
|
+
warnLogger.warn(
|
|
2558
|
+
"FTS keyword search is unavailable; returning empty keyword hits."
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2415
2561
|
return [];
|
|
2416
2562
|
}
|
|
2417
2563
|
try {
|
|
@@ -2425,7 +2571,16 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2425
2571
|
// bm25 returns lower (more negative) for better matches — invert to [0, ∞)
|
|
2426
2572
|
score: 1 / (1 + Math.abs(row.rank))
|
|
2427
2573
|
}));
|
|
2428
|
-
} catch {
|
|
2574
|
+
} catch (error) {
|
|
2575
|
+
if (!warnedFtsKeywordSearch) {
|
|
2576
|
+
warnedFtsKeywordSearch = true;
|
|
2577
|
+
warnLogger.warn(
|
|
2578
|
+
"FTS keyword search failed; returning empty keyword hits.",
|
|
2579
|
+
{
|
|
2580
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2581
|
+
}
|
|
2582
|
+
);
|
|
2583
|
+
}
|
|
2429
2584
|
return [];
|
|
2430
2585
|
}
|
|
2431
2586
|
}
|
|
@@ -2590,6 +2745,13 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2590
2745
|
this.deleteEmbeddingsForScope(organization);
|
|
2591
2746
|
this.deleteFtsForScope(organization);
|
|
2592
2747
|
const result = stmts.deleteMemoriesByOrg.run(organization);
|
|
2748
|
+
try {
|
|
2749
|
+
this.requireDb().exec(`
|
|
2750
|
+
DELETE FROM memory_embeddings_blob
|
|
2751
|
+
WHERE memory_rowid NOT IN (SELECT rowid FROM memories)
|
|
2752
|
+
`);
|
|
2753
|
+
} catch {
|
|
2754
|
+
}
|
|
2593
2755
|
return Number(result.changes);
|
|
2594
2756
|
});
|
|
2595
2757
|
}
|
|
@@ -2649,16 +2811,39 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2649
2811
|
}
|
|
2650
2812
|
async withTransaction(fn) {
|
|
2651
2813
|
const db = this.requireDb();
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2814
|
+
if (this.transactionDepth > 0) {
|
|
2815
|
+
const savepointName = `wolbarg_sp_${this.savepointCounter++}`;
|
|
2816
|
+
db.exec(`SAVEPOINT ${savepointName}`);
|
|
2817
|
+
try {
|
|
2818
|
+
this.transactionDepth += 1;
|
|
2819
|
+
const result = await fn();
|
|
2820
|
+
db.exec(`RELEASE SAVEPOINT ${savepointName}`);
|
|
2821
|
+
return result;
|
|
2822
|
+
} catch (error) {
|
|
2823
|
+
try {
|
|
2824
|
+
db.exec(`ROLLBACK TO SAVEPOINT ${savepointName}`);
|
|
2825
|
+
} catch {
|
|
2826
|
+
}
|
|
2827
|
+
throw error;
|
|
2828
|
+
} finally {
|
|
2829
|
+
this.transactionDepth -= 1;
|
|
2660
2830
|
}
|
|
2661
|
-
|
|
2831
|
+
}
|
|
2832
|
+
this.transactionDepth = 1;
|
|
2833
|
+
try {
|
|
2834
|
+
return await withImmediateTransaction(
|
|
2835
|
+
db,
|
|
2836
|
+
this.concurrency,
|
|
2837
|
+
fn,
|
|
2838
|
+
(attempt, delayMs) => {
|
|
2839
|
+
this.retryLog?.(
|
|
2840
|
+
`SQLITE_BUSY retry attempt=${attempt} backoffMs=${Math.round(delayMs)}`
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
);
|
|
2844
|
+
} finally {
|
|
2845
|
+
this.transactionDepth = 0;
|
|
2846
|
+
}
|
|
2662
2847
|
}
|
|
2663
2848
|
// ─── internals ───────────────────────────────────────────────────────────
|
|
2664
2849
|
tryLoadSqliteVec(db) {
|
|
@@ -3104,7 +3289,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
3104
3289
|
if (!stmt) {
|
|
3105
3290
|
const values = Array.from({ length: n }, () => "(?, ?)").join(", ");
|
|
3106
3291
|
stmt = this.requireDb().prepare(
|
|
3107
|
-
`INSERT INTO memory_embeddings_blob (memory_rowid, embedding) VALUES ${values}`
|
|
3292
|
+
`INSERT OR REPLACE INTO memory_embeddings_blob (memory_rowid, embedding) VALUES ${values}`
|
|
3108
3293
|
);
|
|
3109
3294
|
this.batchBlobEmbStatements.set(n, stmt);
|
|
3110
3295
|
}
|
|
@@ -3812,9 +3997,13 @@ function createPostgresListenerFromPool(pool, onError) {
|
|
|
3812
3997
|
|
|
3813
3998
|
// src/storage/providers/postgres.ts
|
|
3814
3999
|
var txStore = new async_hooks.AsyncLocalStorage();
|
|
4000
|
+
var warnLogger2 = new WolbargLogger("warn");
|
|
4001
|
+
var warnedPgvectorFallback = false;
|
|
4002
|
+
var warnedFtsKeywordSearch2 = false;
|
|
4003
|
+
var warnedHnswSoftFail = false;
|
|
3815
4004
|
var STMT = {
|
|
3816
4005
|
insertOne: "Wolbarg_insert_one_v5",
|
|
3817
|
-
insertBatch: "
|
|
4006
|
+
insertBatch: "Wolbarg_insert_batch_v6"
|
|
3818
4007
|
};
|
|
3819
4008
|
function toFloat4Param(embedding) {
|
|
3820
4009
|
const out = new Array(embedding.length);
|
|
@@ -3885,13 +4074,13 @@ var INSERT_ONE_SQL = `WITH mem AS (
|
|
|
3885
4074
|
var INSERT_BATCH_SQL = `WITH mem AS (
|
|
3886
4075
|
INSERT INTO memories (
|
|
3887
4076
|
id, organization, agent, content_text, metadata_json,
|
|
3888
|
-
archived, compressed_into, created_at, updated_at
|
|
4077
|
+
archived, compressed_into, content_hash, created_at, updated_at
|
|
3889
4078
|
)
|
|
3890
|
-
SELECT id, org, agent, txt, meta::jsonb, false, NULL, c, u
|
|
4079
|
+
SELECT id, org, agent, txt, meta::jsonb, false, NULL, h, c, u
|
|
3891
4080
|
FROM unnest(
|
|
3892
4081
|
$1::text[], $2::text[], $3::text[], $4::text[],
|
|
3893
|
-
$5::text[], $6::timestamptz[], $7::timestamptz[]
|
|
3894
|
-
) AS t(id, org, agent, txt, meta, c, u)
|
|
4082
|
+
$5::text[], $6::timestamptz[], $7::timestamptz[], $8::text[]
|
|
4083
|
+
) AS t(id, org, agent, txt, meta, c, u, h)
|
|
3895
4084
|
RETURNING id
|
|
3896
4085
|
),
|
|
3897
4086
|
hist AS (
|
|
@@ -3907,7 +4096,7 @@ var INSERT_BATCH_SQL = `WITH mem AS (
|
|
|
3907
4096
|
emb AS (
|
|
3908
4097
|
INSERT INTO memory_embeddings (memory_id, embedding, organization, agent, archived)
|
|
3909
4098
|
SELECT id, emb::vector, org, agent, false
|
|
3910
|
-
FROM unnest($1::text[], $
|
|
4099
|
+
FROM unnest($1::text[], $9::text[], $2::text[], $3::text[]) AS t(id, emb, org, agent)
|
|
3911
4100
|
)
|
|
3912
4101
|
SELECT id FROM mem`;
|
|
3913
4102
|
var COALESCE_FLUSH_MAX = 128;
|
|
@@ -4176,6 +4365,12 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4176
4365
|
return;
|
|
4177
4366
|
}
|
|
4178
4367
|
this.hnswCreateFailures += 1;
|
|
4368
|
+
if (this.hnswCreateFailures === 1 && !warnedHnswSoftFail) {
|
|
4369
|
+
warnedHnswSoftFail = true;
|
|
4370
|
+
warnLogger2.warn(
|
|
4371
|
+
"HNSW index creation failed; ANN will fall back to sequential scan."
|
|
4372
|
+
);
|
|
4373
|
+
}
|
|
4179
4374
|
if (this.hnswCreateFailures >= 3) {
|
|
4180
4375
|
throw new DatabaseError(
|
|
4181
4376
|
`Failed to create HNSW index after ${this.hnswCreateFailures} attempts: ${this.describe(error)}`,
|
|
@@ -4320,6 +4515,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4320
4515
|
const metas = new Array(inputs.length);
|
|
4321
4516
|
const created = new Array(inputs.length);
|
|
4322
4517
|
const updated = new Array(inputs.length);
|
|
4518
|
+
const contentHashes = new Array(inputs.length);
|
|
4323
4519
|
const vectors = new Array(inputs.length);
|
|
4324
4520
|
for (let i = 0; i < inputs.length; i += 1) {
|
|
4325
4521
|
const input = inputs[i];
|
|
@@ -4330,6 +4526,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4330
4526
|
metas[i] = serializeMetadata(input.metadata);
|
|
4331
4527
|
created[i] = input.createdAt;
|
|
4332
4528
|
updated[i] = input.updatedAt;
|
|
4529
|
+
contentHashes[i] = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
|
|
4333
4530
|
vectors[i] = toVectorLiteral(input.embedding);
|
|
4334
4531
|
}
|
|
4335
4532
|
await this.queryNamed(STMT.insertBatch, INSERT_BATCH_SQL, [
|
|
@@ -4340,9 +4537,10 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4340
4537
|
metas,
|
|
4341
4538
|
created,
|
|
4342
4539
|
updated,
|
|
4540
|
+
contentHashes,
|
|
4343
4541
|
vectors
|
|
4344
4542
|
]);
|
|
4345
|
-
return inputs.map((
|
|
4543
|
+
return inputs.map((_, i) => ({
|
|
4346
4544
|
id: ids[i],
|
|
4347
4545
|
organization: orgs[i],
|
|
4348
4546
|
agent: agents[i],
|
|
@@ -4350,7 +4548,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4350
4548
|
metadata_json: metas[i],
|
|
4351
4549
|
archived: 0,
|
|
4352
4550
|
compressed_into: null,
|
|
4353
|
-
content_hash:
|
|
4551
|
+
content_hash: contentHashes[i] ?? null,
|
|
4354
4552
|
created_at: created[i],
|
|
4355
4553
|
updated_at: updated[i]
|
|
4356
4554
|
}));
|
|
@@ -4391,80 +4589,84 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4391
4589
|
return out;
|
|
4392
4590
|
}
|
|
4393
4591
|
async insertOneBlob(input) {
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4592
|
+
return this.withTransaction(async () => {
|
|
4593
|
+
const buf = Buffer.from(
|
|
4594
|
+
input.embedding.buffer,
|
|
4595
|
+
input.embedding.byteOffset,
|
|
4596
|
+
input.embedding.byteLength
|
|
4597
|
+
);
|
|
4598
|
+
const contentHash = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
|
|
4599
|
+
const inserted = await this.query(
|
|
4600
|
+
`INSERT INTO memories (
|
|
4601
|
+
id, organization, agent, content_text, metadata_json,
|
|
4602
|
+
archived, compressed_into, content_hash, created_at, updated_at
|
|
4603
|
+
) VALUES ($1,$2,$3,$4,$5::jsonb,false,NULL,$8,$6,$7)
|
|
4604
|
+
RETURNING id, organization, agent, content_text, metadata_json,
|
|
4605
|
+
archived::int AS archived, compressed_into, content_hash, created_at, updated_at`,
|
|
4606
|
+
[
|
|
4607
|
+
input.id,
|
|
4608
|
+
input.organization,
|
|
4609
|
+
input.agent,
|
|
4610
|
+
input.contentText,
|
|
4611
|
+
serializeMetadata(input.metadata),
|
|
4612
|
+
input.createdAt,
|
|
4613
|
+
input.updatedAt,
|
|
4614
|
+
contentHash
|
|
4615
|
+
]
|
|
4616
|
+
);
|
|
4617
|
+
const row = this.mapRow(inserted.rows[0]);
|
|
4618
|
+
await this.query(
|
|
4619
|
+
`WITH mapped AS (
|
|
4620
|
+
INSERT INTO memory_row_map (memory_id) VALUES ($1)
|
|
4621
|
+
ON CONFLICT (memory_id) DO NOTHING
|
|
4622
|
+
)
|
|
4623
|
+
INSERT INTO memory_embeddings_blob (memory_id, embedding)
|
|
4624
|
+
VALUES ($1, $2)
|
|
4625
|
+
ON CONFLICT (memory_id) DO UPDATE SET embedding = EXCLUDED.embedding`,
|
|
4626
|
+
[input.id, buf]
|
|
4627
|
+
);
|
|
4628
|
+
await this.query(
|
|
4629
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4630
|
+
VALUES ($1,$2,'created',NULL,$3)`,
|
|
4631
|
+
[crypto.randomUUID(), input.id, input.createdAt]
|
|
4632
|
+
);
|
|
4633
|
+
return row;
|
|
4634
|
+
});
|
|
4435
4635
|
}
|
|
4436
4636
|
async updateMemory(input) {
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4637
|
+
return this.withTransaction(async () => {
|
|
4638
|
+
const existing = await this.getMemoryById(input.id, input.organization);
|
|
4639
|
+
if (!existing) {
|
|
4640
|
+
return null;
|
|
4641
|
+
}
|
|
4642
|
+
const contentHash = input.contentHash !== void 0 ? input.contentHash : input.contentText !== void 0 ? hashMemoryContent(input.contentText) : existing.content_hash ?? null;
|
|
4643
|
+
await this.query(
|
|
4644
|
+
`UPDATE memories SET
|
|
4645
|
+
content_text = COALESCE($1, content_text),
|
|
4646
|
+
metadata_json = COALESCE($2::jsonb, metadata_json),
|
|
4647
|
+
content_hash = COALESCE($3, content_hash),
|
|
4648
|
+
updated_at = $4
|
|
4649
|
+
WHERE id = $5 AND organization = $6`,
|
|
4650
|
+
[
|
|
4651
|
+
input.contentText ?? null,
|
|
4652
|
+
input.metadata !== void 0 ? serializeMetadata(input.metadata) : null,
|
|
4653
|
+
contentHash,
|
|
4654
|
+
input.updatedAt,
|
|
4655
|
+
input.id,
|
|
4656
|
+
input.organization
|
|
4657
|
+
]
|
|
4658
|
+
);
|
|
4659
|
+
if (input.embedding) {
|
|
4660
|
+
await this.deleteEmbedding(input.id);
|
|
4661
|
+
await this.insertEmbedding(input.id, input.embedding);
|
|
4662
|
+
}
|
|
4663
|
+
await this.query(
|
|
4664
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4665
|
+
VALUES ($1,$2,'updated',NULL,$3)`,
|
|
4666
|
+
[crypto.randomUUID(), input.id, input.updatedAt]
|
|
4667
|
+
);
|
|
4668
|
+
return this.getMemoryById(input.id, input.organization);
|
|
4669
|
+
});
|
|
4468
4670
|
}
|
|
4469
4671
|
async findActiveByContentHash(organization, agent, contentHash) {
|
|
4470
4672
|
const result = await this.query(
|
|
@@ -4613,7 +4815,16 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4613
4815
|
memoryId: String(row.memory_id),
|
|
4614
4816
|
score: Number(row.rank)
|
|
4615
4817
|
}));
|
|
4616
|
-
} catch {
|
|
4818
|
+
} catch (error) {
|
|
4819
|
+
if (!warnedFtsKeywordSearch2) {
|
|
4820
|
+
warnedFtsKeywordSearch2 = true;
|
|
4821
|
+
warnLogger2.warn(
|
|
4822
|
+
"FTS keyword search failed; returning empty keyword hits.",
|
|
4823
|
+
{
|
|
4824
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
4825
|
+
}
|
|
4826
|
+
);
|
|
4827
|
+
}
|
|
4617
4828
|
return [];
|
|
4618
4829
|
}
|
|
4619
4830
|
}
|
|
@@ -4731,44 +4942,46 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4731
4942
|
return mapHits(result.rows);
|
|
4732
4943
|
}
|
|
4733
4944
|
async archiveMemories(ids, organization, compressedIntoId, archivedAt) {
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4945
|
+
return this.withTransaction(async () => {
|
|
4946
|
+
if (ids.length === 0) {
|
|
4947
|
+
return [];
|
|
4948
|
+
}
|
|
4949
|
+
const result = await this.query(
|
|
4950
|
+
`UPDATE memories
|
|
4951
|
+
SET archived = true, compressed_into = $1, updated_at = $2
|
|
4952
|
+
WHERE organization = $3 AND archived = false AND id = ANY($4::text[])
|
|
4953
|
+
RETURNING id`,
|
|
4954
|
+
[compressedIntoId, archivedAt, organization, ids]
|
|
4955
|
+
);
|
|
4956
|
+
const archived = result.rows.map((r) => String(r.id));
|
|
4957
|
+
if (archived.length === 0) {
|
|
4958
|
+
return [];
|
|
4959
|
+
}
|
|
4960
|
+
await this.query(
|
|
4961
|
+
`UPDATE memory_embeddings
|
|
4962
|
+
SET archived = true
|
|
4963
|
+
WHERE memory_id = ANY($1::text[])`,
|
|
4964
|
+
[archived]
|
|
4965
|
+
);
|
|
4966
|
+
const histIds = [];
|
|
4967
|
+
const memIds = [];
|
|
4968
|
+
const types = [];
|
|
4969
|
+
const related = [];
|
|
4970
|
+
const times = [];
|
|
4971
|
+
for (const id of archived) {
|
|
4972
|
+
histIds.push(crypto.randomUUID(), crypto.randomUUID());
|
|
4973
|
+
memIds.push(id, compressedIntoId);
|
|
4974
|
+
types.push("archived", "compressed");
|
|
4975
|
+
related.push(compressedIntoId, id);
|
|
4976
|
+
times.push(archivedAt, archivedAt);
|
|
4977
|
+
}
|
|
4978
|
+
await this.query(
|
|
4979
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4980
|
+
SELECT * FROM unnest($1::text[], $2::text[], $3::text[], $4::text[], $5::timestamptz[])`,
|
|
4981
|
+
[histIds, memIds, types, related, times]
|
|
4982
|
+
);
|
|
4983
|
+
return archived;
|
|
4984
|
+
});
|
|
4772
4985
|
}
|
|
4773
4986
|
async deleteMemoryById(id, organization) {
|
|
4774
4987
|
const result = await this.query(
|
|
@@ -4879,6 +5092,11 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4879
5092
|
[META_KEYS.schemaVersion]
|
|
4880
5093
|
).catch(() => ({ rows: [] }));
|
|
4881
5094
|
const current = versionRow.rows[0]?.value !== void 0 ? Number(versionRow.rows[0].value) : null;
|
|
5095
|
+
if (current !== null && current > SCHEMA_VERSION) {
|
|
5096
|
+
throw new InitializationError(
|
|
5097
|
+
`Database schema version ${current} is newer than this Wolbarg SDK (${SCHEMA_VERSION}). Please upgrade Wolbarg to open this database.`
|
|
5098
|
+
);
|
|
5099
|
+
}
|
|
4882
5100
|
if (current === SCHEMA_VERSION) {
|
|
4883
5101
|
const tsvProbe2 = await this.query(
|
|
4884
5102
|
`SELECT 1 FROM information_schema.columns
|
|
@@ -5029,8 +5247,17 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
5029
5247
|
await this.query(`CREATE EXTENSION IF NOT EXISTS vector`);
|
|
5030
5248
|
_PostgresStorageProvider.pgvectorByConn.set(this.connectionString, true);
|
|
5031
5249
|
return true;
|
|
5032
|
-
} catch {
|
|
5250
|
+
} catch (error) {
|
|
5033
5251
|
_PostgresStorageProvider.pgvectorByConn.set(this.connectionString, false);
|
|
5252
|
+
if (!warnedPgvectorFallback) {
|
|
5253
|
+
warnedPgvectorFallback = true;
|
|
5254
|
+
warnLogger2.warn(
|
|
5255
|
+
"pgvector extension is unavailable; using blob cosine search fallback.",
|
|
5256
|
+
{
|
|
5257
|
+
cause: error instanceof Error ? error.message : `unknown error: ${String(error)}`
|
|
5258
|
+
}
|
|
5259
|
+
);
|
|
5260
|
+
}
|
|
5034
5261
|
return false;
|
|
5035
5262
|
}
|
|
5036
5263
|
}
|
|
@@ -5171,11 +5398,12 @@ function createDatabaseProvider(config) {
|
|
|
5171
5398
|
}
|
|
5172
5399
|
|
|
5173
5400
|
// src/version.ts
|
|
5174
|
-
var SDK_VERSION = "0.5.
|
|
5401
|
+
var SDK_VERSION = "0.5.4";
|
|
5175
5402
|
|
|
5176
5403
|
// src/memory/transfer.ts
|
|
5404
|
+
var warnedMissingExportManifest = false;
|
|
5177
5405
|
var SqliteMemoryTransferProvider = class {
|
|
5178
|
-
async exportTo(exportPath, sourcePath, organization) {
|
|
5406
|
+
async exportTo(exportPath, sourcePath, organization, embeddingModel, embeddingDimensions) {
|
|
5179
5407
|
const resolvedSource = resolvePath(sourcePath);
|
|
5180
5408
|
if (resolvedSource === ":memory:") {
|
|
5181
5409
|
throw new ConfigurationError(
|
|
@@ -5202,7 +5430,9 @@ Initialize Wolbarg and verify the database path.`
|
|
|
5202
5430
|
sdkVersion: SDK_VERSION,
|
|
5203
5431
|
provider: "sqlite",
|
|
5204
5432
|
sourcePath: resolvedSource,
|
|
5205
|
-
organization
|
|
5433
|
+
organization,
|
|
5434
|
+
...embeddingModel ? { embeddingModel } : {},
|
|
5435
|
+
...embeddingDimensions ? { embeddingDimensions } : {}
|
|
5206
5436
|
};
|
|
5207
5437
|
fs6__default.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
5208
5438
|
return {
|
|
@@ -5211,7 +5441,7 @@ Initialize Wolbarg and verify the database path.`
|
|
|
5211
5441
|
sizeBytes: fs6__default.default.statSync(dbExportPath).size
|
|
5212
5442
|
};
|
|
5213
5443
|
}
|
|
5214
|
-
async importFrom(exportPath, targetPath) {
|
|
5444
|
+
async importFrom(exportPath, targetPath, expected) {
|
|
5215
5445
|
const resolvedExport = resolvePath(exportPath);
|
|
5216
5446
|
const dbExportPath = resolvedExport.endsWith(".db") ? resolvedExport : fs6__default.default.existsSync(`${resolvedExport}.db`) ? `${resolvedExport}.db` : resolvedExport;
|
|
5217
5447
|
if (!fs6__default.default.existsSync(dbExportPath)) {
|
|
@@ -5229,30 +5459,61 @@ Pass the path returned by export().`
|
|
|
5229
5459
|
manifest = JSON.parse(fs6__default.default.readFileSync(manifestPath, "utf8"));
|
|
5230
5460
|
if (manifest.format !== "wolbarg-export-v1") {
|
|
5231
5461
|
throw new ValidationError(
|
|
5232
|
-
`Unsupported export format: ${String(manifest.format)}`
|
|
5462
|
+
`Unsupported export format: ${String(manifest.format)}`
|
|
5463
|
+
);
|
|
5464
|
+
}
|
|
5465
|
+
} else {
|
|
5466
|
+
if (!warnedMissingExportManifest) {
|
|
5467
|
+
warnedMissingExportManifest = true;
|
|
5468
|
+
console.warn(
|
|
5469
|
+
"[wolbarg:warn] export manifest is missing; skipping manifest-based import validation."
|
|
5470
|
+
);
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
if (manifest) {
|
|
5474
|
+
if (expected?.organization && manifest.organization && expected.organization !== manifest.organization) {
|
|
5475
|
+
throw new ValidationError(
|
|
5476
|
+
"Import failed: export organization does not match the current Wolbarg organization."
|
|
5477
|
+
);
|
|
5478
|
+
}
|
|
5479
|
+
if (expected?.embeddingDimensions != null && manifest.embeddingDimensions != null && expected.embeddingDimensions !== manifest.embeddingDimensions) {
|
|
5480
|
+
throw new ValidationError(
|
|
5481
|
+
`Import failed: embedding dimension mismatch (expected ${expected.embeddingDimensions}, got ${manifest.embeddingDimensions}).`
|
|
5482
|
+
);
|
|
5483
|
+
}
|
|
5484
|
+
if (expected?.embeddingModel && manifest.embeddingModel && expected.embeddingModel !== manifest.embeddingModel) {
|
|
5485
|
+
throw new ValidationError(
|
|
5486
|
+
`Import failed: embedding model mismatch (expected ${expected.embeddingModel}, got ${manifest.embeddingModel}).`
|
|
5233
5487
|
);
|
|
5234
5488
|
}
|
|
5235
|
-
} else {
|
|
5236
|
-
manifest = {
|
|
5237
|
-
format: "wolbarg-export-v1",
|
|
5238
|
-
exportedAt: nowIso(),
|
|
5239
|
-
sdkVersion: SDK_VERSION,
|
|
5240
|
-
provider: "sqlite",
|
|
5241
|
-
sourcePath: dbExportPath
|
|
5242
|
-
};
|
|
5243
5489
|
}
|
|
5244
5490
|
const resolvedTarget = resolvePath(targetPath);
|
|
5245
5491
|
if (resolvedTarget === ":memory:") {
|
|
5246
5492
|
throw new ConfigurationError("Cannot import into an in-memory database.");
|
|
5247
5493
|
}
|
|
5494
|
+
const tmpTarget = `${resolvedTarget}.tmp-import-${Date.now()}`;
|
|
5495
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5496
|
+
const side = `${tmpTarget}${suffix}`;
|
|
5497
|
+
if (fs6__default.default.existsSync(side)) {
|
|
5498
|
+
fs6__default.default.rmSync(side, { force: true });
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
await copySqlite(dbExportPath, tmpTarget);
|
|
5248
5502
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5249
5503
|
const side = `${resolvedTarget}${suffix}`;
|
|
5250
5504
|
if (fs6__default.default.existsSync(side)) {
|
|
5251
5505
|
fs6__default.default.rmSync(side, { force: true });
|
|
5252
5506
|
}
|
|
5253
5507
|
}
|
|
5254
|
-
|
|
5255
|
-
|
|
5508
|
+
fs6__default.default.renameSync(tmpTarget, resolvedTarget);
|
|
5509
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
5510
|
+
const from = `${tmpTarget}${suffix}`;
|
|
5511
|
+
const to = `${resolvedTarget}${suffix}`;
|
|
5512
|
+
if (fs6__default.default.existsSync(from)) {
|
|
5513
|
+
fs6__default.default.renameSync(from, to);
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
return { path: resolvedTarget, manifest: manifest ?? void 0 };
|
|
5256
5517
|
}
|
|
5257
5518
|
};
|
|
5258
5519
|
async function copySqlite(sourcePath, destPath) {
|
|
@@ -5481,7 +5742,8 @@ function applyMmr(candidates, topK, lambda) {
|
|
|
5481
5742
|
);
|
|
5482
5743
|
}
|
|
5483
5744
|
const score = lambda * relevance - (1 - lambda) * maxSim;
|
|
5484
|
-
|
|
5745
|
+
const best = remaining[bestIdx];
|
|
5746
|
+
if (score > bestScore || score === bestScore && candidate.id < best.id) {
|
|
5485
5747
|
bestScore = score;
|
|
5486
5748
|
bestIdx = i;
|
|
5487
5749
|
}
|
|
@@ -6041,24 +6303,26 @@ var Neo4jGraphProvider = class {
|
|
|
6041
6303
|
}
|
|
6042
6304
|
}
|
|
6043
6305
|
async run(cypher, params = {}) {
|
|
6044
|
-
return this.withSession(
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6306
|
+
return this.withSession(
|
|
6307
|
+
(session) => this.runOnSession(session, cypher, params)
|
|
6308
|
+
);
|
|
6309
|
+
}
|
|
6310
|
+
async runOnSession(session, cypher, params = {}) {
|
|
6311
|
+
const result = await session.run(cypher, params);
|
|
6312
|
+
return result.records.map((rec) => {
|
|
6313
|
+
try {
|
|
6314
|
+
return rec.toObject();
|
|
6315
|
+
} catch {
|
|
6316
|
+
const obj = {};
|
|
6317
|
+
for (const key of rec.keys) {
|
|
6318
|
+
obj[key] = unwrapNeo4jValue(rec.get(key));
|
|
6055
6319
|
}
|
|
6056
|
-
|
|
6320
|
+
return obj;
|
|
6321
|
+
}
|
|
6057
6322
|
});
|
|
6058
6323
|
}
|
|
6059
|
-
async ensureMemoryNode(id) {
|
|
6060
|
-
|
|
6061
|
-
`MERGE (m:Memory { id: $id })
|
|
6324
|
+
async ensureMemoryNode(id, session) {
|
|
6325
|
+
const cypher = `MERGE (m:Memory { id: $id })
|
|
6062
6326
|
ON CREATE SET
|
|
6063
6327
|
m.organization = '',
|
|
6064
6328
|
m.agent = '',
|
|
@@ -6067,24 +6331,30 @@ var Neo4jGraphProvider = class {
|
|
|
6067
6331
|
m.archived = false,
|
|
6068
6332
|
m.compressed_into = '',
|
|
6069
6333
|
m.created_at = '',
|
|
6070
|
-
m.updated_at = ''
|
|
6071
|
-
|
|
6072
|
-
|
|
6334
|
+
m.updated_at = ''`;
|
|
6335
|
+
if (session) {
|
|
6336
|
+
await this.runOnSession(session, cypher, { id });
|
|
6337
|
+
return;
|
|
6338
|
+
}
|
|
6339
|
+
await this.run(cypher, { id });
|
|
6073
6340
|
}
|
|
6074
6341
|
async linkMemories(fromId, toId, relation, metadata) {
|
|
6075
|
-
await this.
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6342
|
+
await this.withSession(async (session) => {
|
|
6343
|
+
await this.ensureMemoryNode(fromId, session);
|
|
6344
|
+
await this.ensureMemoryNode(toId, session);
|
|
6345
|
+
await this.runOnSession(
|
|
6346
|
+
session,
|
|
6347
|
+
`MATCH (a:Memory { id: $fromId }), (b:Memory { id: $toId })
|
|
6348
|
+
MERGE (a)-[r:RELATED { relation: $relation }]->(b)
|
|
6349
|
+
SET r.metadata_json = $metadata`,
|
|
6350
|
+
{
|
|
6351
|
+
fromId,
|
|
6352
|
+
toId,
|
|
6353
|
+
relation,
|
|
6354
|
+
metadata: serializeMetadata2(metadata)
|
|
6355
|
+
}
|
|
6356
|
+
);
|
|
6357
|
+
});
|
|
6088
6358
|
}
|
|
6089
6359
|
async unlinkMemories(fromId, toId, relation) {
|
|
6090
6360
|
if (relation !== void 0) {
|
|
@@ -6146,22 +6416,26 @@ var Neo4jGraphProvider = class {
|
|
|
6146
6416
|
return id;
|
|
6147
6417
|
}
|
|
6148
6418
|
async linkEntityToMemory(entityId, memoryId, role) {
|
|
6149
|
-
await this.
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6419
|
+
await this.withSession(async (session) => {
|
|
6420
|
+
await this.ensureMemoryNode(memoryId, session);
|
|
6421
|
+
const found = await this.runOnSession(
|
|
6422
|
+
session,
|
|
6423
|
+
`MATCH (e:Entity { id: $id }) RETURN e.id AS id`,
|
|
6424
|
+
{ id: entityId }
|
|
6425
|
+
);
|
|
6426
|
+
if (found.length === 0) {
|
|
6427
|
+
throw new DatabaseError(`Entity not found: ${entityId}`, {
|
|
6428
|
+
operation: "linkEntityToMemory"
|
|
6429
|
+
});
|
|
6430
|
+
}
|
|
6431
|
+
await this.runOnSession(
|
|
6432
|
+
session,
|
|
6433
|
+
`MATCH (e:Entity { id: $entityId }), (m:Memory { id: $memoryId })
|
|
6434
|
+
MERGE (e)-[r:MENTIONS]->(m)
|
|
6435
|
+
SET r.role = $role`,
|
|
6436
|
+
{ entityId, memoryId, role: role ?? "" }
|
|
6437
|
+
);
|
|
6438
|
+
});
|
|
6165
6439
|
}
|
|
6166
6440
|
async deleteMemory(memoryId) {
|
|
6167
6441
|
await this.run(
|
|
@@ -6234,6 +6508,21 @@ function assertNonEmpty(value, fieldName) {
|
|
|
6234
6508
|
throw new ConfigurationError(`${fieldName} must be a non-empty string`);
|
|
6235
6509
|
}
|
|
6236
6510
|
}
|
|
6511
|
+
function assertFiniteNumber2(value, fieldName) {
|
|
6512
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6513
|
+
throw new ConfigurationError(`${fieldName} must be a finite number`);
|
|
6514
|
+
}
|
|
6515
|
+
}
|
|
6516
|
+
function assertNumberMin(value, fieldName, min) {
|
|
6517
|
+
if (!Number.isFinite(value) || value < min) {
|
|
6518
|
+
throw new ConfigurationError(`${fieldName} must be >= ${min}`);
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
function assertNumberInRange(value, fieldName, min, max) {
|
|
6522
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
6523
|
+
throw new ConfigurationError(`${fieldName} must be between ${min} and ${max}`);
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6237
6526
|
function assertUrl(value, fieldName) {
|
|
6238
6527
|
assertNonEmpty(value, fieldName);
|
|
6239
6528
|
try {
|
|
@@ -6280,6 +6569,135 @@ function validateLlmConfig(config) {
|
|
|
6280
6569
|
...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {}
|
|
6281
6570
|
};
|
|
6282
6571
|
}
|
|
6572
|
+
function validateRetrievalConfig(config) {
|
|
6573
|
+
if (config === null || typeof config !== "object") {
|
|
6574
|
+
throw new ConfigurationError("retrieval must be an object");
|
|
6575
|
+
}
|
|
6576
|
+
const out = {};
|
|
6577
|
+
if (config.overFetchFactor !== void 0) {
|
|
6578
|
+
assertFiniteNumber2(config.overFetchFactor, "retrieval.overFetchFactor");
|
|
6579
|
+
assertNumberMin(config.overFetchFactor, "retrieval.overFetchFactor", 0);
|
|
6580
|
+
if (config.overFetchFactor <= 0) {
|
|
6581
|
+
throw new ConfigurationError("retrieval.overFetchFactor must be > 0");
|
|
6582
|
+
}
|
|
6583
|
+
out.overFetchFactor = config.overFetchFactor;
|
|
6584
|
+
}
|
|
6585
|
+
if (config.hybrid !== void 0) {
|
|
6586
|
+
if (config.hybrid === null || typeof config.hybrid !== "object") {
|
|
6587
|
+
throw new ConfigurationError("retrieval.hybrid must be an object");
|
|
6588
|
+
}
|
|
6589
|
+
const hybrid = config.hybrid;
|
|
6590
|
+
const outHybrid = {};
|
|
6591
|
+
if (hybrid.semanticWeight !== void 0) {
|
|
6592
|
+
assertFiniteNumber2(hybrid.semanticWeight, "retrieval.hybrid.semanticWeight");
|
|
6593
|
+
if (hybrid.semanticWeight < 0) {
|
|
6594
|
+
throw new ConfigurationError(
|
|
6595
|
+
"retrieval.hybrid.semanticWeight must be >= 0"
|
|
6596
|
+
);
|
|
6597
|
+
}
|
|
6598
|
+
outHybrid.semanticWeight = hybrid.semanticWeight;
|
|
6599
|
+
}
|
|
6600
|
+
if (hybrid.keywordWeight !== void 0) {
|
|
6601
|
+
assertFiniteNumber2(hybrid.keywordWeight, "retrieval.hybrid.keywordWeight");
|
|
6602
|
+
if (hybrid.keywordWeight < 0) {
|
|
6603
|
+
throw new ConfigurationError(
|
|
6604
|
+
"retrieval.hybrid.keywordWeight must be >= 0"
|
|
6605
|
+
);
|
|
6606
|
+
}
|
|
6607
|
+
outHybrid.keywordWeight = hybrid.keywordWeight;
|
|
6608
|
+
}
|
|
6609
|
+
if (Object.keys(outHybrid).length > 0) {
|
|
6610
|
+
out.hybrid = outHybrid;
|
|
6611
|
+
}
|
|
6612
|
+
}
|
|
6613
|
+
if (config.mmr !== void 0) {
|
|
6614
|
+
if (config.mmr === null || typeof config.mmr !== "object") {
|
|
6615
|
+
throw new ConfigurationError("retrieval.mmr must be an object");
|
|
6616
|
+
}
|
|
6617
|
+
const mmr = config.mmr;
|
|
6618
|
+
const outMmr = {};
|
|
6619
|
+
if (mmr.lambda !== void 0) {
|
|
6620
|
+
assertFiniteNumber2(mmr.lambda, "retrieval.mmr.lambda");
|
|
6621
|
+
assertNumberInRange(mmr.lambda, "retrieval.mmr.lambda", 0, 1);
|
|
6622
|
+
outMmr.lambda = mmr.lambda;
|
|
6623
|
+
}
|
|
6624
|
+
if (Object.keys(outMmr).length > 0) {
|
|
6625
|
+
out.mmr = outMmr;
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
return out;
|
|
6629
|
+
}
|
|
6630
|
+
function validateMemoryDedupeConfig(config) {
|
|
6631
|
+
if (config === null || typeof config !== "object") {
|
|
6632
|
+
throw new ConfigurationError("memory.dedupe must be an object");
|
|
6633
|
+
}
|
|
6634
|
+
const out = {};
|
|
6635
|
+
if (config.enabled !== void 0) {
|
|
6636
|
+
if (typeof config.enabled !== "boolean") {
|
|
6637
|
+
throw new ConfigurationError("memory.dedupe.enabled must be a boolean");
|
|
6638
|
+
}
|
|
6639
|
+
out.enabled = config.enabled;
|
|
6640
|
+
}
|
|
6641
|
+
if (config.strategy !== void 0) {
|
|
6642
|
+
const allowed = /* @__PURE__ */ new Set(["exact", "near", "exact-or-near"]);
|
|
6643
|
+
if (!allowed.has(config.strategy)) {
|
|
6644
|
+
throw new ConfigurationError(
|
|
6645
|
+
`memory.dedupe.strategy must be one of ${Array.from(allowed).join(", ")}`
|
|
6646
|
+
);
|
|
6647
|
+
}
|
|
6648
|
+
out.strategy = config.strategy;
|
|
6649
|
+
}
|
|
6650
|
+
if (config.nearThreshold !== void 0) {
|
|
6651
|
+
assertFiniteNumber2(config.nearThreshold, "memory.dedupe.nearThreshold");
|
|
6652
|
+
assertNumberInRange(
|
|
6653
|
+
config.nearThreshold,
|
|
6654
|
+
"memory.dedupe.nearThreshold",
|
|
6655
|
+
0,
|
|
6656
|
+
1
|
|
6657
|
+
);
|
|
6658
|
+
out.nearThreshold = config.nearThreshold;
|
|
6659
|
+
}
|
|
6660
|
+
if (config.nearCandidateLimit !== void 0) {
|
|
6661
|
+
assertFiniteNumber2(
|
|
6662
|
+
config.nearCandidateLimit,
|
|
6663
|
+
"memory.dedupe.nearCandidateLimit"
|
|
6664
|
+
);
|
|
6665
|
+
assertNumberMin(
|
|
6666
|
+
config.nearCandidateLimit,
|
|
6667
|
+
"memory.dedupe.nearCandidateLimit",
|
|
6668
|
+
1
|
|
6669
|
+
);
|
|
6670
|
+
out.nearCandidateLimit = config.nearCandidateLimit;
|
|
6671
|
+
}
|
|
6672
|
+
return out;
|
|
6673
|
+
}
|
|
6674
|
+
function validateEmbeddingCacheConfig(config) {
|
|
6675
|
+
if (config === null || typeof config !== "object") {
|
|
6676
|
+
throw new ConfigurationError("embeddingCache must be an object");
|
|
6677
|
+
}
|
|
6678
|
+
const out = {};
|
|
6679
|
+
if (config.enabled !== void 0) {
|
|
6680
|
+
if (typeof config.enabled !== "boolean") {
|
|
6681
|
+
throw new ConfigurationError("embeddingCache.enabled must be a boolean");
|
|
6682
|
+
}
|
|
6683
|
+
out.enabled = config.enabled;
|
|
6684
|
+
}
|
|
6685
|
+
if (config.ttlMs !== void 0) {
|
|
6686
|
+
assertFiniteNumber2(config.ttlMs, "embeddingCache.ttlMs");
|
|
6687
|
+
if (config.ttlMs < 0) {
|
|
6688
|
+
throw new ConfigurationError("embeddingCache.ttlMs must be >= 0");
|
|
6689
|
+
}
|
|
6690
|
+
out.ttlMs = config.ttlMs;
|
|
6691
|
+
}
|
|
6692
|
+
if (config.maxEntries !== void 0) {
|
|
6693
|
+
assertFiniteNumber2(config.maxEntries, "embeddingCache.maxEntries");
|
|
6694
|
+
if (config.maxEntries < 0) {
|
|
6695
|
+
throw new ConfigurationError("embeddingCache.maxEntries must be >= 0");
|
|
6696
|
+
}
|
|
6697
|
+
out.maxEntries = config.maxEntries;
|
|
6698
|
+
}
|
|
6699
|
+
return out;
|
|
6700
|
+
}
|
|
6283
6701
|
function normalizeDatabaseConfig(config) {
|
|
6284
6702
|
const provider = config.provider;
|
|
6285
6703
|
if (provider !== "sqlite" && provider !== "postgres") {
|
|
@@ -6389,11 +6807,22 @@ function validateWolbargOptions(options) {
|
|
|
6389
6807
|
throw new ConfigurationError("Wolbarg options must be an object");
|
|
6390
6808
|
}
|
|
6391
6809
|
assertNonEmpty(options.organization, "organization");
|
|
6810
|
+
const checkpointDirectory = options.checkpointDirectory !== void 0 ? (() => {
|
|
6811
|
+
assertNonEmpty(options.checkpointDirectory, "checkpointDirectory");
|
|
6812
|
+
return options.checkpointDirectory.trim();
|
|
6813
|
+
})() : void 0;
|
|
6392
6814
|
const storageInput = resolveStorageInput(options);
|
|
6393
6815
|
let storage = storageInput;
|
|
6394
6816
|
if (!isStorageProvider(storageInput)) {
|
|
6395
6817
|
storage = normalizeDatabaseConfig(storageInput);
|
|
6396
6818
|
}
|
|
6819
|
+
if (!isStorageProvider(storageInput) && storageInput.provider === "postgres" && storageInput.maxPoolSize !== void 0) {
|
|
6820
|
+
assertFiniteNumber2(
|
|
6821
|
+
storageInput.maxPoolSize,
|
|
6822
|
+
"database.maxPoolSize"
|
|
6823
|
+
);
|
|
6824
|
+
assertNumberMin(storageInput.maxPoolSize, "database.maxPoolSize", 1);
|
|
6825
|
+
}
|
|
6397
6826
|
if (!options.embedding) {
|
|
6398
6827
|
throw new ConfigurationError("embedding is required");
|
|
6399
6828
|
}
|
|
@@ -6411,13 +6840,20 @@ function validateWolbargOptions(options) {
|
|
|
6411
6840
|
if (graph !== void 0) {
|
|
6412
6841
|
graph = resolveGraphInput(graph);
|
|
6413
6842
|
}
|
|
6843
|
+
const retrieval = options.retrieval !== void 0 ? validateRetrievalConfig(options.retrieval) : void 0;
|
|
6844
|
+
const embeddingCache = options.embeddingCache !== void 0 ? validateEmbeddingCacheConfig(options.embeddingCache) : void 0;
|
|
6845
|
+
const memory = options.memory !== void 0 && options.memory.dedupe !== void 0 ? { ...options.memory, dedupe: validateMemoryDedupeConfig(options.memory.dedupe) } : options.memory;
|
|
6414
6846
|
return {
|
|
6415
6847
|
...options,
|
|
6416
6848
|
organization: options.organization.trim(),
|
|
6417
6849
|
storage,
|
|
6418
6850
|
database: void 0,
|
|
6419
6851
|
...telemetry ? { telemetry } : {},
|
|
6420
|
-
...graph !== void 0 ? { graph } : {}
|
|
6852
|
+
...graph !== void 0 ? { graph } : {},
|
|
6853
|
+
...retrieval !== void 0 ? { retrieval } : {},
|
|
6854
|
+
...embeddingCache !== void 0 ? { embeddingCache } : {},
|
|
6855
|
+
...checkpointDirectory !== void 0 ? { checkpointDirectory } : {},
|
|
6856
|
+
...memory !== void 0 ? { memory } : {}
|
|
6421
6857
|
};
|
|
6422
6858
|
}
|
|
6423
6859
|
function resolveGraphInput(input) {
|
|
@@ -6476,59 +6912,6 @@ function resolveGraphInput(input) {
|
|
|
6476
6912
|
);
|
|
6477
6913
|
}
|
|
6478
6914
|
|
|
6479
|
-
// src/telemetry/logger.ts
|
|
6480
|
-
var LEVEL_ORDER = {
|
|
6481
|
-
off: 100,
|
|
6482
|
-
error: 50,
|
|
6483
|
-
warn: 40,
|
|
6484
|
-
info: 30,
|
|
6485
|
-
debug: 20,
|
|
6486
|
-
trace: 10
|
|
6487
|
-
};
|
|
6488
|
-
var WolbargLogger = class {
|
|
6489
|
-
constructor(level = "info") {
|
|
6490
|
-
this.level = level;
|
|
6491
|
-
}
|
|
6492
|
-
level;
|
|
6493
|
-
setLevel(level) {
|
|
6494
|
-
this.level = level;
|
|
6495
|
-
}
|
|
6496
|
-
getLevel() {
|
|
6497
|
-
return this.level;
|
|
6498
|
-
}
|
|
6499
|
-
error(message, extra) {
|
|
6500
|
-
this.write("error", message, extra);
|
|
6501
|
-
}
|
|
6502
|
-
warn(message, extra) {
|
|
6503
|
-
this.write("warn", message, extra);
|
|
6504
|
-
}
|
|
6505
|
-
info(message, extra) {
|
|
6506
|
-
this.write("info", message, extra);
|
|
6507
|
-
}
|
|
6508
|
-
debug(message, extra) {
|
|
6509
|
-
this.write("debug", message, extra);
|
|
6510
|
-
}
|
|
6511
|
-
trace(message, extra) {
|
|
6512
|
-
this.write("trace", message, extra);
|
|
6513
|
-
}
|
|
6514
|
-
write(level, message, extra) {
|
|
6515
|
-
if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
|
|
6516
|
-
return;
|
|
6517
|
-
}
|
|
6518
|
-
if (this.level === "off") {
|
|
6519
|
-
return;
|
|
6520
|
-
}
|
|
6521
|
-
const line = `[wolbarg:${level}] ${message}`;
|
|
6522
|
-
if (level === "error") {
|
|
6523
|
-
console.error(line, extra ?? "");
|
|
6524
|
-
} else if (level === "warn") {
|
|
6525
|
-
console.warn(line, extra ?? "");
|
|
6526
|
-
} else {
|
|
6527
|
-
console.log(line, extra ?? "");
|
|
6528
|
-
}
|
|
6529
|
-
}
|
|
6530
|
-
};
|
|
6531
|
-
|
|
6532
6915
|
// src/telemetry/trace.ts
|
|
6533
6916
|
function createSessionId() {
|
|
6534
6917
|
return createId();
|
|
@@ -6613,14 +6996,14 @@ var TelemetryEmitter = class {
|
|
|
6613
6996
|
const totalMs = performance.now() - startedAt;
|
|
6614
6997
|
const errMessage = error instanceof Error ? error.message : error ? String(error) : fields?.error ?? null;
|
|
6615
6998
|
const errStack = error instanceof Error ? error.stack ?? null : fields?.errorStack ?? null;
|
|
6999
|
+
if (!self.config.enabled) {
|
|
7000
|
+
return;
|
|
7001
|
+
}
|
|
6616
7002
|
if (status === "error") {
|
|
6617
7003
|
self.logger.error(`${operation} failed: ${errMessage ?? "unknown"}`);
|
|
6618
7004
|
} else {
|
|
6619
7005
|
self.logger.debug(`${operation} completed in ${totalMs.toFixed(2)}ms`);
|
|
6620
7006
|
}
|
|
6621
|
-
if (!self.config.enabled) {
|
|
6622
|
-
return;
|
|
6623
|
-
}
|
|
6624
7007
|
const event = {
|
|
6625
7008
|
id: createId(),
|
|
6626
7009
|
timestamp: nowIso(),
|
|
@@ -6922,8 +7305,10 @@ var SqliteEventDatabase = class {
|
|
|
6922
7305
|
}
|
|
6923
7306
|
}
|
|
6924
7307
|
if (options.memoryId) {
|
|
6925
|
-
clauses.push(
|
|
6926
|
-
|
|
7308
|
+
clauses.push(
|
|
7309
|
+
`EXISTS (SELECT 1 FROM json_each(memory_ids_json) WHERE json_each.value = ?)`
|
|
7310
|
+
);
|
|
7311
|
+
params.push(options.memoryId);
|
|
6927
7312
|
}
|
|
6928
7313
|
if (options.queryText) {
|
|
6929
7314
|
clauses.push(`query LIKE ?`);
|
|
@@ -7100,6 +7485,7 @@ function describe2(error) {
|
|
|
7100
7485
|
}
|
|
7101
7486
|
|
|
7102
7487
|
// src/providers/sqlite/sqliteTelemetryProvider.ts
|
|
7488
|
+
var MAX_QUEUE_SIZE = 1e4;
|
|
7103
7489
|
var SqliteTelemetryProvider = class {
|
|
7104
7490
|
name = "sqlite";
|
|
7105
7491
|
db;
|
|
@@ -7107,6 +7493,8 @@ var SqliteTelemetryProvider = class {
|
|
|
7107
7493
|
flushing = null;
|
|
7108
7494
|
closed = false;
|
|
7109
7495
|
openPromise = null;
|
|
7496
|
+
warnedQueueDrop = false;
|
|
7497
|
+
warnedFlushFailure = false;
|
|
7110
7498
|
constructor(options) {
|
|
7111
7499
|
this.db = new SqliteEventDatabase({ url: options.url });
|
|
7112
7500
|
}
|
|
@@ -7127,6 +7515,15 @@ var SqliteTelemetryProvider = class {
|
|
|
7127
7515
|
if (this.closed) {
|
|
7128
7516
|
return;
|
|
7129
7517
|
}
|
|
7518
|
+
if (this.queue.length >= MAX_QUEUE_SIZE) {
|
|
7519
|
+
this.queue.shift();
|
|
7520
|
+
if (!this.warnedQueueDrop) {
|
|
7521
|
+
this.warnedQueueDrop = true;
|
|
7522
|
+
console.warn(
|
|
7523
|
+
`[wolbarg telemetry] event queue capped at ${MAX_QUEUE_SIZE}; dropping oldest events`
|
|
7524
|
+
);
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7130
7527
|
this.queue.push(event);
|
|
7131
7528
|
void this.scheduleFlush();
|
|
7132
7529
|
}
|
|
@@ -7169,7 +7566,13 @@ var SqliteTelemetryProvider = class {
|
|
|
7169
7566
|
await this.db.insertEvent(event);
|
|
7170
7567
|
}
|
|
7171
7568
|
}
|
|
7172
|
-
} catch {
|
|
7569
|
+
} catch (error) {
|
|
7570
|
+
if (!this.warnedFlushFailure) {
|
|
7571
|
+
this.warnedFlushFailure = true;
|
|
7572
|
+
console.warn(
|
|
7573
|
+
`[wolbarg telemetry] flush failed; dropping batch: ${error instanceof Error ? error.message : String(error)}`
|
|
7574
|
+
);
|
|
7575
|
+
}
|
|
7173
7576
|
}
|
|
7174
7577
|
}
|
|
7175
7578
|
};
|
|
@@ -7212,8 +7615,20 @@ Ensure Wolbarg has been initialized and the database path is correct.`
|
|
|
7212
7615
|
);
|
|
7213
7616
|
}
|
|
7214
7617
|
const snapshotPath = this.snapshotPath(name);
|
|
7215
|
-
|
|
7216
|
-
|
|
7618
|
+
const tmpSnapshotPath = `${snapshotPath}.tmp-${Date.now()}`;
|
|
7619
|
+
try {
|
|
7620
|
+
await safeSqliteBackup(resolvedSource, tmpSnapshotPath);
|
|
7621
|
+
} catch (error) {
|
|
7622
|
+
if (fs6__default.default.existsSync(tmpSnapshotPath)) {
|
|
7623
|
+
fs6__default.default.rmSync(tmpSnapshotPath, { force: true });
|
|
7624
|
+
}
|
|
7625
|
+
throw error;
|
|
7626
|
+
}
|
|
7627
|
+
const stats = fs6__default.default.statSync(tmpSnapshotPath);
|
|
7628
|
+
if (fs6__default.default.existsSync(snapshotPath)) {
|
|
7629
|
+
fs6__default.default.rmSync(snapshotPath, { force: true });
|
|
7630
|
+
}
|
|
7631
|
+
fs6__default.default.renameSync(tmpSnapshotPath, snapshotPath);
|
|
7217
7632
|
const meta2 = {
|
|
7218
7633
|
name,
|
|
7219
7634
|
description: options?.description ?? null,
|
|
@@ -7224,7 +7639,9 @@ Ensure Wolbarg has been initialized and the database path is correct.`
|
|
|
7224
7639
|
sourcePath: resolvedSource,
|
|
7225
7640
|
sizeBytes: stats.size
|
|
7226
7641
|
};
|
|
7227
|
-
|
|
7642
|
+
const tmpMetaPath = `${metaPath}.tmp-${Date.now()}`;
|
|
7643
|
+
fs6__default.default.writeFileSync(tmpMetaPath, JSON.stringify(meta2, null, 2), "utf8");
|
|
7644
|
+
fs6__default.default.renameSync(tmpMetaPath, metaPath);
|
|
7228
7645
|
return meta2;
|
|
7229
7646
|
}
|
|
7230
7647
|
async rollback(name, targetPath) {
|
|
@@ -7488,6 +7905,9 @@ var SqliteSubscribeEmitter = class {
|
|
|
7488
7905
|
};
|
|
7489
7906
|
|
|
7490
7907
|
// src/core/wolbarg.ts
|
|
7908
|
+
var warnLogger3 = new WolbargLogger("warn");
|
|
7909
|
+
var warnedHybridNoKeyword = false;
|
|
7910
|
+
var warnedRerankNoProvider = false;
|
|
7491
7911
|
var Wolbarg = class {
|
|
7492
7912
|
initialized = false;
|
|
7493
7913
|
booting = null;
|
|
@@ -7593,6 +8013,7 @@ var Wolbarg = class {
|
|
|
7593
8013
|
this.storage = createStorageProvider(validated.database);
|
|
7594
8014
|
this.memoryDbPath = resolveDatabaseUrl(validated.database);
|
|
7595
8015
|
this.embedding = createEmbeddingProvider(validated.embedding);
|
|
8016
|
+
this.rawEmbedding = this.embedding;
|
|
7596
8017
|
if (validated.llm) {
|
|
7597
8018
|
this.llm = createLlmProvider(validated.llm);
|
|
7598
8019
|
this.compression = createCompressionProvider(this.llm);
|
|
@@ -7914,7 +8335,7 @@ var Wolbarg = class {
|
|
|
7914
8335
|
* Update an existing memory by id (re-embeds when content changes).
|
|
7915
8336
|
*/
|
|
7916
8337
|
async update(options) {
|
|
7917
|
-
const trace = this.telemetry.start("
|
|
8338
|
+
const trace = this.telemetry.start("update");
|
|
7918
8339
|
try {
|
|
7919
8340
|
const { storage, embedding, organization } = await this.requireReady();
|
|
7920
8341
|
assertNonEmptyString(options.id, "id");
|
|
@@ -8440,17 +8861,37 @@ var Wolbarg = class {
|
|
|
8440
8861
|
}
|
|
8441
8862
|
}
|
|
8442
8863
|
}
|
|
8864
|
+
if (hybridWeights && keywordSignal === "disabled" && !warnedHybridNoKeyword) {
|
|
8865
|
+
warnedHybridNoKeyword = true;
|
|
8866
|
+
warnLogger3.warn(
|
|
8867
|
+
"hybrid search requested but no keyword channel is available; using semantic-only results."
|
|
8868
|
+
);
|
|
8869
|
+
}
|
|
8443
8870
|
const tRank = performance.now();
|
|
8444
|
-
let results = [...byId.values()].sort(
|
|
8445
|
-
|
|
8446
|
-
|
|
8871
|
+
let results = [...byId.values()].sort((a, b) => {
|
|
8872
|
+
const diff = b.similarity - a.similarity;
|
|
8873
|
+
if (diff !== 0) return diff;
|
|
8874
|
+
return a.id.localeCompare(b.id);
|
|
8875
|
+
});
|
|
8447
8876
|
const mmrLambda = resolveMmr(options.mmr) ?? resolveMmr(
|
|
8448
8877
|
this.retrievalConfig.mmr ? { lambda: this.retrievalConfig.mmr.lambda } : void 0
|
|
8449
8878
|
);
|
|
8450
8879
|
let rankingReason = "cosine similarity";
|
|
8880
|
+
let mmrApplied = false;
|
|
8451
8881
|
if (mmrLambda !== null) {
|
|
8452
|
-
|
|
8453
|
-
|
|
8882
|
+
mmrApplied = results.length > topK;
|
|
8883
|
+
if (mmrApplied) {
|
|
8884
|
+
results = applyMmr(results, topK, mmrLambda);
|
|
8885
|
+
rankingReason = `MMR(lambda=${mmrLambda})`;
|
|
8886
|
+
} else {
|
|
8887
|
+
rankingReason = `MMR(lambda=${mmrLambda})(skipped:insufficient_candidates)`;
|
|
8888
|
+
}
|
|
8889
|
+
}
|
|
8890
|
+
if (options.rerank === true && !this.reranker && !warnedRerankNoProvider) {
|
|
8891
|
+
warnedRerankNoProvider = true;
|
|
8892
|
+
warnLogger3.warn(
|
|
8893
|
+
"rerank is enabled but no reranker provider is configured; using identity order."
|
|
8894
|
+
);
|
|
8454
8895
|
}
|
|
8455
8896
|
if (options.rerank && this.reranker) {
|
|
8456
8897
|
const reranked = await this.reranker.rerank(
|
|
@@ -8485,14 +8926,16 @@ var Wolbarg = class {
|
|
|
8485
8926
|
agentId: options.filter?.agent ?? commonAgent(serialized.map((result) => result.agent)),
|
|
8486
8927
|
tags: commonTags(serialized.map((result) => result.metadata))
|
|
8487
8928
|
};
|
|
8488
|
-
if (
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8929
|
+
if (options.includeGraph === true && this.graph) {
|
|
8930
|
+
const tGraph = performance.now();
|
|
8931
|
+
await Promise.all(
|
|
8932
|
+
serialized.map(async (hit) => {
|
|
8492
8933
|
hit.related = await this.hydrateRelated(hit.id);
|
|
8493
|
-
}
|
|
8494
|
-
|
|
8495
|
-
|
|
8934
|
+
})
|
|
8935
|
+
);
|
|
8936
|
+
trace.mark("databaseReadMs", performance.now() - tGraph);
|
|
8937
|
+
}
|
|
8938
|
+
if (!explain) {
|
|
8496
8939
|
trace.success(telemetryFields);
|
|
8497
8940
|
return serialized;
|
|
8498
8941
|
}
|
|
@@ -8521,7 +8964,7 @@ var Wolbarg = class {
|
|
|
8521
8964
|
semantic: "enabled",
|
|
8522
8965
|
keyword: keywordSignal,
|
|
8523
8966
|
reranker: options.rerank === true && this.reranker ? "enabled" : "disabled",
|
|
8524
|
-
mmr: mmrLambda === null ? "disabled" : "enabled",
|
|
8967
|
+
mmr: mmrLambda === null ? "disabled" : mmrApplied ? "enabled" : "disabled",
|
|
8525
8968
|
recency: "disabled"
|
|
8526
8969
|
},
|
|
8527
8970
|
results: explanations.map((hit) => ({
|
|
@@ -8630,31 +9073,34 @@ var Wolbarg = class {
|
|
|
8630
9073
|
const timestamp = nowIso();
|
|
8631
9074
|
const result = await this.withWriteLock(async () => {
|
|
8632
9075
|
const tWrite = performance.now();
|
|
8633
|
-
const
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
9076
|
+
const txResult = await storage.withTransaction(async () => {
|
|
9077
|
+
const summaryRow = await storage.insertMemory({
|
|
9078
|
+
id: summaryId,
|
|
9079
|
+
organization,
|
|
9080
|
+
agent: options.agent.trim(),
|
|
9081
|
+
contentText: summaryText,
|
|
9082
|
+
metadata: {
|
|
9083
|
+
compressed: true,
|
|
9084
|
+
sourceCount: records.length,
|
|
9085
|
+
sourceIds: records.map((r) => r.id)
|
|
9086
|
+
},
|
|
9087
|
+
embedding: vector,
|
|
9088
|
+
createdAt: timestamp,
|
|
9089
|
+
updatedAt: timestamp
|
|
9090
|
+
});
|
|
9091
|
+
const archivedIds = await storage.archiveMemories(
|
|
9092
|
+
records.map((r) => r.id),
|
|
9093
|
+
organization,
|
|
9094
|
+
summaryId,
|
|
9095
|
+
timestamp
|
|
9096
|
+
);
|
|
9097
|
+
return {
|
|
9098
|
+
summary: toMemoryRecord(summaryRow),
|
|
9099
|
+
archivedIds
|
|
9100
|
+
};
|
|
8646
9101
|
});
|
|
8647
|
-
const archivedIds = await storage.archiveMemories(
|
|
8648
|
-
records.map((r) => r.id),
|
|
8649
|
-
organization,
|
|
8650
|
-
summaryId,
|
|
8651
|
-
timestamp
|
|
8652
|
-
);
|
|
8653
9102
|
trace.mark("databaseWriteMs", performance.now() - tWrite);
|
|
8654
|
-
return
|
|
8655
|
-
summary: toMemoryRecord(summaryRow),
|
|
8656
|
-
archivedIds
|
|
8657
|
-
};
|
|
9103
|
+
return txResult;
|
|
8658
9104
|
});
|
|
8659
9105
|
trace.success({
|
|
8660
9106
|
provider: storage.name,
|
|
@@ -8886,8 +9332,23 @@ var Wolbarg = class {
|
|
|
8886
9332
|
await this.requireReady();
|
|
8887
9333
|
const graph = this.requireGraph("getRelated");
|
|
8888
9334
|
assertNonEmptyString(memoryId, "memoryId");
|
|
9335
|
+
let validatedOptions = options;
|
|
9336
|
+
if (options?.depth !== void 0) {
|
|
9337
|
+
if (!Number.isFinite(options.depth)) {
|
|
9338
|
+
throw new ConfigurationError("graph.depth must be a finite number");
|
|
9339
|
+
}
|
|
9340
|
+
if (options.depth < 1) {
|
|
9341
|
+
throw new ConfigurationError("graph.depth must be >= 1");
|
|
9342
|
+
}
|
|
9343
|
+
if (options.depth > 16) {
|
|
9344
|
+
validatedOptions = { ...options, depth: 16 };
|
|
9345
|
+
}
|
|
9346
|
+
}
|
|
8889
9347
|
const tGraph = performance.now();
|
|
8890
|
-
const related = await this.hydrateRelated(
|
|
9348
|
+
const related = await this.hydrateRelated(
|
|
9349
|
+
memoryId.trim(),
|
|
9350
|
+
validatedOptions
|
|
9351
|
+
);
|
|
8891
9352
|
trace.mark("databaseReadMs", performance.now() - tGraph);
|
|
8892
9353
|
trace.success({
|
|
8893
9354
|
provider: graph.name,
|
|
@@ -9056,7 +9517,15 @@ var Wolbarg = class {
|
|
|
9056
9517
|
this.assertGraphCheckpointSupported("checkpoint");
|
|
9057
9518
|
const tCheckpoint = performance.now();
|
|
9058
9519
|
const meta2 = await provider.checkpoint(name, source, options);
|
|
9059
|
-
|
|
9520
|
+
try {
|
|
9521
|
+
await this.snapshotGraphAlongside(meta2.snapshotPath, "checkpoint");
|
|
9522
|
+
} catch (error) {
|
|
9523
|
+
const sidecar = this.graphSnapshotDir(meta2.snapshotPath);
|
|
9524
|
+
if (fs6__default.default.existsSync(sidecar)) {
|
|
9525
|
+
fs6__default.default.rmSync(sidecar, { recursive: true, force: true });
|
|
9526
|
+
}
|
|
9527
|
+
throw error;
|
|
9528
|
+
}
|
|
9060
9529
|
trace.mark("databaseWriteMs", performance.now() - tCheckpoint);
|
|
9061
9530
|
trace.success({
|
|
9062
9531
|
provider: provider.name,
|
|
@@ -9120,8 +9589,15 @@ var Wolbarg = class {
|
|
|
9120
9589
|
try {
|
|
9121
9590
|
await this.requireReady();
|
|
9122
9591
|
const provider = this.requireCheckpointProvider();
|
|
9592
|
+
const existing = await provider.getCheckpoint(name);
|
|
9123
9593
|
const tDelete = performance.now();
|
|
9124
9594
|
const removed = await provider.deleteCheckpoint(name);
|
|
9595
|
+
if (removed && existing) {
|
|
9596
|
+
const sidecar = this.graphSnapshotDir(existing.snapshotPath);
|
|
9597
|
+
if (fs6__default.default.existsSync(sidecar)) {
|
|
9598
|
+
fs6__default.default.rmSync(sidecar, { recursive: true, force: true });
|
|
9599
|
+
}
|
|
9600
|
+
}
|
|
9125
9601
|
trace.mark("databaseWriteMs", performance.now() - tDelete);
|
|
9126
9602
|
trace.success({
|
|
9127
9603
|
provider: provider.name,
|
|
@@ -9183,7 +9659,9 @@ var Wolbarg = class {
|
|
|
9183
9659
|
const result = await this.transfer.exportTo(
|
|
9184
9660
|
exportPath,
|
|
9185
9661
|
source,
|
|
9186
|
-
this.organization ?? void 0
|
|
9662
|
+
this.organization ?? void 0,
|
|
9663
|
+
this.embedding?.model,
|
|
9664
|
+
this.embeddingDimensions ?? void 0
|
|
9187
9665
|
);
|
|
9188
9666
|
await this.snapshotGraphAlongside(result.path, "export");
|
|
9189
9667
|
trace.success({
|
|
@@ -9206,16 +9684,21 @@ var Wolbarg = class {
|
|
|
9206
9684
|
let storageClosed = false;
|
|
9207
9685
|
try {
|
|
9208
9686
|
await this.requireReady();
|
|
9687
|
+
this.assertGraphCheckpointSupported("import");
|
|
9209
9688
|
const target = this.requireMemoryDbPath();
|
|
9210
9689
|
if (this.storage) {
|
|
9211
9690
|
await this.storage.close();
|
|
9212
9691
|
storageClosed = true;
|
|
9213
9692
|
}
|
|
9214
9693
|
const graphClosed = await this.closeGraphForSnapshot();
|
|
9215
|
-
this.assertGraphCheckpointSupported("import");
|
|
9216
9694
|
const result = await this.transfer.importFrom(
|
|
9217
9695
|
exportPath,
|
|
9218
|
-
target
|
|
9696
|
+
target,
|
|
9697
|
+
{
|
|
9698
|
+
organization: this.organization ?? void 0,
|
|
9699
|
+
embeddingModel: this.embedding?.model,
|
|
9700
|
+
embeddingDimensions: this.embeddingDimensions ?? void 0
|
|
9701
|
+
}
|
|
9219
9702
|
);
|
|
9220
9703
|
await this.restoreGraphAlongside(result.path, "import");
|
|
9221
9704
|
if (graphClosed) {
|
|
@@ -9410,18 +9893,26 @@ var Wolbarg = class {
|
|
|
9410
9893
|
const graph = this.requireGraph("getRelated");
|
|
9411
9894
|
const related = await graph.getRelated(memoryId, options);
|
|
9412
9895
|
const { storage, organization } = await this.requireReady();
|
|
9896
|
+
const rows = await Promise.all(
|
|
9897
|
+
related.map((stub) => storage.getMemoryById(stub.id, organization))
|
|
9898
|
+
);
|
|
9413
9899
|
const out = [];
|
|
9414
|
-
for (
|
|
9415
|
-
const
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
} else {
|
|
9419
|
-
out.push(stub);
|
|
9420
|
-
}
|
|
9900
|
+
for (let i = 0; i < related.length; i += 1) {
|
|
9901
|
+
const stub = related[i];
|
|
9902
|
+
const row = rows[i];
|
|
9903
|
+
out.push(row ? toMemoryRecord(row) : stub);
|
|
9421
9904
|
}
|
|
9422
9905
|
return out;
|
|
9423
9906
|
}
|
|
9424
9907
|
requireMemoryDbPath() {
|
|
9908
|
+
if (this.storage && !(this.storage instanceof SqliteStorageProvider)) {
|
|
9909
|
+
throw new ConfigurationError(
|
|
9910
|
+
"This operation requires a file-backed SQLite database (checkpoints / export-import are not supported for PostgreSQL).",
|
|
9911
|
+
{
|
|
9912
|
+
suggestion: 'Use database: { provider: "sqlite", url: "./memory.db" }'
|
|
9913
|
+
}
|
|
9914
|
+
);
|
|
9915
|
+
}
|
|
9425
9916
|
if (!this.memoryDbPath || this.memoryDbPath === ":memory:") {
|
|
9426
9917
|
throw new ConfigurationError(
|
|
9427
9918
|
"This operation requires a file-backed SQLite memory database (not :memory:).",
|
|
@@ -9650,7 +10141,14 @@ var HttpRerankerProvider = class {
|
|
|
9650
10141
|
}));
|
|
9651
10142
|
}
|
|
9652
10143
|
const parsed = this.options.parseResults(body, documents);
|
|
9653
|
-
|
|
10144
|
+
const hits = parsed.slice(0, topK);
|
|
10145
|
+
if (hits.length === 0) {
|
|
10146
|
+
return documents.slice(0, topK).map((d, i) => ({
|
|
10147
|
+
id: d.id,
|
|
10148
|
+
score: 1 - i / Math.max(documents.length, 1)
|
|
10149
|
+
}));
|
|
10150
|
+
}
|
|
10151
|
+
return hits;
|
|
9654
10152
|
} catch {
|
|
9655
10153
|
return documents.slice(0, topK).map((d, i) => ({
|
|
9656
10154
|
id: d.id,
|
|
@@ -9835,21 +10333,31 @@ function tesseract() {
|
|
|
9835
10333
|
return {
|
|
9836
10334
|
name: "tesseract",
|
|
9837
10335
|
async recognize(image) {
|
|
10336
|
+
let mod;
|
|
9838
10337
|
try {
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
10338
|
+
mod = await import('tesseract.js');
|
|
10339
|
+
} catch (error) {
|
|
10340
|
+
throw new ConfigurationError(
|
|
10341
|
+
'OCR provider "tesseract" requires the optional peer package "tesseract.js". Install it with: npm install tesseract.js',
|
|
10342
|
+
{
|
|
10343
|
+
cause: error instanceof Error ? error : void 0,
|
|
10344
|
+
suggestion: "npm install tesseract.js",
|
|
10345
|
+
operation: "recognize"
|
|
10346
|
+
}
|
|
10347
|
+
);
|
|
10348
|
+
}
|
|
10349
|
+
const createWorker = mod.createWorker ?? mod.default?.createWorker;
|
|
10350
|
+
if (!createWorker) {
|
|
10351
|
+
throw new ConfigurationError(
|
|
10352
|
+
'OCR provider "tesseract" could not find tesseract.js.createWorker. Ensure your tesseract.js version is compatible.'
|
|
10353
|
+
);
|
|
10354
|
+
}
|
|
10355
|
+
const worker = await createWorker("eng");
|
|
10356
|
+
try {
|
|
10357
|
+
const result = await worker.recognize(image);
|
|
10358
|
+
return { text: result.data.text.trim() };
|
|
10359
|
+
} finally {
|
|
10360
|
+
await worker.terminate();
|
|
9853
10361
|
}
|
|
9854
10362
|
}
|
|
9855
10363
|
};
|