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.js
CHANGED
|
@@ -662,6 +662,45 @@ function withEmbeddingCache(provider, store, config) {
|
|
|
662
662
|
};
|
|
663
663
|
}
|
|
664
664
|
|
|
665
|
+
// src/utils/vector.ts
|
|
666
|
+
function cosineDistance(a, b) {
|
|
667
|
+
if (a.length !== b.length) {
|
|
668
|
+
throw new Error(
|
|
669
|
+
`cosineDistance dimension mismatch: a.length=${a.length}, b.length=${b.length}`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
const len = a.length;
|
|
673
|
+
let dot = 0;
|
|
674
|
+
let normA = 0;
|
|
675
|
+
let normB = 0;
|
|
676
|
+
for (let i = 0; i < len; i += 1) {
|
|
677
|
+
const av = a[i] ?? 0;
|
|
678
|
+
const bv = b[i] ?? 0;
|
|
679
|
+
dot += av * bv;
|
|
680
|
+
normA += av * av;
|
|
681
|
+
normB += bv * bv;
|
|
682
|
+
}
|
|
683
|
+
if (normA === 0 || normB === 0) {
|
|
684
|
+
return 1;
|
|
685
|
+
}
|
|
686
|
+
const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
687
|
+
return 1 - similarity;
|
|
688
|
+
}
|
|
689
|
+
function bufferToEmbedding(data) {
|
|
690
|
+
const byteOffset = data.byteOffset ?? 0;
|
|
691
|
+
const byteLength = data.byteLength;
|
|
692
|
+
if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
|
|
693
|
+
return new Float32Array(
|
|
694
|
+
data.buffer,
|
|
695
|
+
byteOffset,
|
|
696
|
+
byteLength / Float32Array.BYTES_PER_ELEMENT
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
const copy = new Uint8Array(byteLength);
|
|
700
|
+
copy.set(data);
|
|
701
|
+
return new Float32Array(copy.buffer);
|
|
702
|
+
}
|
|
703
|
+
|
|
665
704
|
// src/embedding/cache-store.ts
|
|
666
705
|
var TOUCH_FLUSH_THRESHOLD = 64;
|
|
667
706
|
var L1_DEFAULT_MAX = 5e4;
|
|
@@ -746,7 +785,9 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
746
785
|
}
|
|
747
786
|
this.stmts = {
|
|
748
787
|
get: db.prepare(
|
|
749
|
-
`SELECT vector, last_used_at, created_at
|
|
788
|
+
`SELECT model, vector, last_used_at, created_at
|
|
789
|
+
FROM embedding_cache
|
|
790
|
+
WHERE cache_key = ?`
|
|
750
791
|
),
|
|
751
792
|
delete: db.prepare(`DELETE FROM embedding_cache WHERE cache_key = ?`),
|
|
752
793
|
set: db.prepare(
|
|
@@ -781,7 +822,29 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
781
822
|
this.l1.set(cacheKey, pending.model, pending.vector);
|
|
782
823
|
return pending.vector;
|
|
783
824
|
}
|
|
784
|
-
|
|
825
|
+
const db = this.requireDb();
|
|
826
|
+
if (!db) return null;
|
|
827
|
+
try {
|
|
828
|
+
const stmts = this.ensureStatements(db);
|
|
829
|
+
const row = stmts.get.get(cacheKey);
|
|
830
|
+
if (!row) return null;
|
|
831
|
+
if (this.ttlMs !== null) {
|
|
832
|
+
const createdMs = row.created_at ? Date.parse(row.created_at) : 0;
|
|
833
|
+
if (createdMs && Date.now() - createdMs > this.ttlMs) {
|
|
834
|
+
stmts.delete.run(cacheKey);
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
const vector = bufferToEmbedding(row.vector);
|
|
839
|
+
this.l1.set(cacheKey, row.model, vector);
|
|
840
|
+
this.pendingTouches.add(cacheKey);
|
|
841
|
+
if (this.pendingTouches.size >= TOUCH_FLUSH_THRESHOLD) {
|
|
842
|
+
this.schedulePersist();
|
|
843
|
+
}
|
|
844
|
+
return vector;
|
|
845
|
+
} catch {
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
785
848
|
}
|
|
786
849
|
async set(cacheKey, model, vector) {
|
|
787
850
|
this.l1.set(cacheKey, model, vector);
|
|
@@ -833,6 +896,16 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
833
896
|
this.pendingSets.clear();
|
|
834
897
|
const touches = [...this.pendingTouches];
|
|
835
898
|
this.pendingTouches.clear();
|
|
899
|
+
const requeue = () => {
|
|
900
|
+
for (const [key, value] of sets) {
|
|
901
|
+
this.pendingSets.set(key, value);
|
|
902
|
+
}
|
|
903
|
+
for (const key of touches) {
|
|
904
|
+
this.pendingTouches.add(key);
|
|
905
|
+
}
|
|
906
|
+
this.stmts = null;
|
|
907
|
+
this.schedulePersist();
|
|
908
|
+
};
|
|
836
909
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
837
910
|
try {
|
|
838
911
|
const stmts = this.ensureStatements(db);
|
|
@@ -867,10 +940,10 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
867
940
|
db.exec("ROLLBACK");
|
|
868
941
|
} catch {
|
|
869
942
|
}
|
|
870
|
-
|
|
943
|
+
requeue();
|
|
871
944
|
}
|
|
872
945
|
} catch {
|
|
873
|
-
|
|
946
|
+
requeue();
|
|
874
947
|
}
|
|
875
948
|
}
|
|
876
949
|
};
|
|
@@ -1179,12 +1252,27 @@ var DocxParser = class {
|
|
|
1179
1252
|
name = "docx";
|
|
1180
1253
|
extensions = [".docx"];
|
|
1181
1254
|
async parse(input) {
|
|
1255
|
+
let mod;
|
|
1256
|
+
try {
|
|
1257
|
+
mod = await import('mammoth');
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
throw new ConfigurationError(
|
|
1260
|
+
'DOCX ingest requires the optional peer package "mammoth". Install it with: npm install mammoth',
|
|
1261
|
+
{
|
|
1262
|
+
cause: error instanceof Error ? error : void 0,
|
|
1263
|
+
suggestion: "npm install mammoth",
|
|
1264
|
+
operation: "parse"
|
|
1265
|
+
}
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
|
|
1269
|
+
if (!extractRawText) {
|
|
1270
|
+
throw new ConfigurationError(
|
|
1271
|
+
"DOCX ingest could not find mammoth.extractRawText. Ensure your mammoth version is compatible.",
|
|
1272
|
+
{ operation: "parse" }
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1182
1275
|
try {
|
|
1183
|
-
const mod = await import('mammoth');
|
|
1184
|
-
const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
|
|
1185
|
-
if (!extractRawText) {
|
|
1186
|
-
throw new Error("mammoth.extractRawText unavailable");
|
|
1187
|
-
}
|
|
1188
1276
|
const result = await extractRawText({ buffer: input.buffer });
|
|
1189
1277
|
return {
|
|
1190
1278
|
text: result.value ?? "",
|
|
@@ -1192,9 +1280,14 @@ var DocxParser = class {
|
|
|
1192
1280
|
filename: input.filename,
|
|
1193
1281
|
isImage: false
|
|
1194
1282
|
};
|
|
1195
|
-
} catch {
|
|
1283
|
+
} catch (error) {
|
|
1196
1284
|
throw new ConfigurationError(
|
|
1197
|
-
|
|
1285
|
+
"DOCX ingest failed to parse the document (it may be corrupt or an unsupported .docx).",
|
|
1286
|
+
{
|
|
1287
|
+
cause: error instanceof Error ? error : void 0,
|
|
1288
|
+
suggestion: "Try a different DOCX file or ensure it is not password-protected.",
|
|
1289
|
+
operation: "parse"
|
|
1290
|
+
}
|
|
1198
1291
|
);
|
|
1199
1292
|
}
|
|
1200
1293
|
}
|
|
@@ -1341,6 +1434,12 @@ function compileComparison(field, op) {
|
|
|
1341
1434
|
const extract = `json_extract(metadata_json, '${path8}')`;
|
|
1342
1435
|
if ("eq" in op) {
|
|
1343
1436
|
const value = op.eq;
|
|
1437
|
+
if (value === null) {
|
|
1438
|
+
return {
|
|
1439
|
+
expression: `json_type(metadata_json, '${path8}') = 'null'`,
|
|
1440
|
+
params: []
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1344
1443
|
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
1345
1444
|
return null;
|
|
1346
1445
|
}
|
|
@@ -1421,6 +1520,59 @@ function compileMetadataFilterToSql(filter) {
|
|
|
1421
1520
|
return compileComparison(filter.field, filter.op);
|
|
1422
1521
|
}
|
|
1423
1522
|
|
|
1523
|
+
// src/telemetry/logger.ts
|
|
1524
|
+
var LEVEL_ORDER = {
|
|
1525
|
+
off: 100,
|
|
1526
|
+
error: 50,
|
|
1527
|
+
warn: 40,
|
|
1528
|
+
info: 30,
|
|
1529
|
+
debug: 20,
|
|
1530
|
+
trace: 10
|
|
1531
|
+
};
|
|
1532
|
+
var WolbargLogger = class {
|
|
1533
|
+
constructor(level = "info") {
|
|
1534
|
+
this.level = level;
|
|
1535
|
+
}
|
|
1536
|
+
level;
|
|
1537
|
+
setLevel(level) {
|
|
1538
|
+
this.level = level;
|
|
1539
|
+
}
|
|
1540
|
+
getLevel() {
|
|
1541
|
+
return this.level;
|
|
1542
|
+
}
|
|
1543
|
+
error(message, extra) {
|
|
1544
|
+
this.write("error", message, extra);
|
|
1545
|
+
}
|
|
1546
|
+
warn(message, extra) {
|
|
1547
|
+
this.write("warn", message, extra);
|
|
1548
|
+
}
|
|
1549
|
+
info(message, extra) {
|
|
1550
|
+
this.write("info", message, extra);
|
|
1551
|
+
}
|
|
1552
|
+
debug(message, extra) {
|
|
1553
|
+
this.write("debug", message, extra);
|
|
1554
|
+
}
|
|
1555
|
+
trace(message, extra) {
|
|
1556
|
+
this.write("trace", message, extra);
|
|
1557
|
+
}
|
|
1558
|
+
write(level, message, extra) {
|
|
1559
|
+
if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
if (this.level === "off") {
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const line = `[wolbarg:${level}] ${message}`;
|
|
1566
|
+
if (level === "error") {
|
|
1567
|
+
console.error(line, extra ?? "");
|
|
1568
|
+
} else if (level === "warn") {
|
|
1569
|
+
console.warn(line, extra ?? "");
|
|
1570
|
+
} else {
|
|
1571
|
+
console.log(line, extra ?? "");
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
|
|
1424
1576
|
// src/schema/index.ts
|
|
1425
1577
|
var SCHEMA_VERSION = 4;
|
|
1426
1578
|
var META_KEYS = {
|
|
@@ -1607,7 +1759,7 @@ var SQL = {
|
|
|
1607
1759
|
AND k = ?
|
|
1608
1760
|
`,
|
|
1609
1761
|
insertEmbeddingBlob: `
|
|
1610
|
-
INSERT INTO memory_embeddings_blob (memory_rowid, embedding) VALUES (?, ?)
|
|
1762
|
+
INSERT OR REPLACE INTO memory_embeddings_blob (memory_rowid, embedding) VALUES (?, ?)
|
|
1611
1763
|
`,
|
|
1612
1764
|
deleteEmbeddingBlob: `
|
|
1613
1765
|
DELETE FROM memory_embeddings_blob WHERE memory_rowid = ?
|
|
@@ -1735,40 +1887,6 @@ var SQL = {
|
|
|
1735
1887
|
`
|
|
1736
1888
|
};
|
|
1737
1889
|
|
|
1738
|
-
// src/utils/vector.ts
|
|
1739
|
-
function cosineDistance(a, b) {
|
|
1740
|
-
const len = Math.min(a.length, b.length);
|
|
1741
|
-
let dot = 0;
|
|
1742
|
-
let normA = 0;
|
|
1743
|
-
let normB = 0;
|
|
1744
|
-
for (let i = 0; i < len; i += 1) {
|
|
1745
|
-
const av = a[i] ?? 0;
|
|
1746
|
-
const bv = b[i] ?? 0;
|
|
1747
|
-
dot += av * bv;
|
|
1748
|
-
normA += av * av;
|
|
1749
|
-
normB += bv * bv;
|
|
1750
|
-
}
|
|
1751
|
-
if (normA === 0 || normB === 0) {
|
|
1752
|
-
return 1;
|
|
1753
|
-
}
|
|
1754
|
-
const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
1755
|
-
return 1 - similarity;
|
|
1756
|
-
}
|
|
1757
|
-
function bufferToEmbedding(data) {
|
|
1758
|
-
const byteOffset = data.byteOffset ?? 0;
|
|
1759
|
-
const byteLength = data.byteLength;
|
|
1760
|
-
if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
|
|
1761
|
-
return new Float32Array(
|
|
1762
|
-
data.buffer,
|
|
1763
|
-
byteOffset,
|
|
1764
|
-
byteLength / Float32Array.BYTES_PER_ELEMENT
|
|
1765
|
-
);
|
|
1766
|
-
}
|
|
1767
|
-
const copy = new Uint8Array(byteLength);
|
|
1768
|
-
copy.set(data);
|
|
1769
|
-
return new Float32Array(copy.buffer);
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
1890
|
// src/utils/vector-index.ts
|
|
1773
1891
|
var InMemoryVectorIndex = class {
|
|
1774
1892
|
dims;
|
|
@@ -1861,24 +1979,33 @@ var InMemoryVectorIndex = class {
|
|
|
1861
1979
|
dot += queryNormalized[d] * data[base + d];
|
|
1862
1980
|
}
|
|
1863
1981
|
const distance = 1 - dot;
|
|
1982
|
+
const rowid = rowids[i];
|
|
1864
1983
|
if (heapSize < k) {
|
|
1865
1984
|
heapDist[heapSize] = distance;
|
|
1866
|
-
heapRow[heapSize] =
|
|
1985
|
+
heapRow[heapSize] = rowid;
|
|
1867
1986
|
heapSize += 1;
|
|
1868
1987
|
if (heapSize === k) {
|
|
1869
1988
|
buildMaxHeap(heapDist, heapRow, k);
|
|
1870
1989
|
}
|
|
1871
|
-
} else
|
|
1872
|
-
heapDist[0]
|
|
1873
|
-
|
|
1874
|
-
|
|
1990
|
+
} else {
|
|
1991
|
+
const worstDist = heapDist[0];
|
|
1992
|
+
const worstRow = heapRow[0];
|
|
1993
|
+
if (distance < worstDist || distance === worstDist && rowid < worstRow) {
|
|
1994
|
+
heapDist[0] = distance;
|
|
1995
|
+
heapRow[0] = rowid;
|
|
1996
|
+
siftDown(heapDist, heapRow, 0, k);
|
|
1997
|
+
}
|
|
1875
1998
|
}
|
|
1876
1999
|
}
|
|
1877
2000
|
const hits = new Array(heapSize);
|
|
1878
2001
|
for (let i = 0; i < heapSize; i += 1) {
|
|
1879
2002
|
hits[i] = { memoryRowid: heapRow[i], distance: heapDist[i] };
|
|
1880
2003
|
}
|
|
1881
|
-
hits.sort((a, b) =>
|
|
2004
|
+
hits.sort((a, b) => {
|
|
2005
|
+
const diff = a.distance - b.distance;
|
|
2006
|
+
if (diff !== 0) return diff;
|
|
2007
|
+
return a.memoryRowid - b.memoryRowid;
|
|
2008
|
+
});
|
|
1882
2009
|
return hits;
|
|
1883
2010
|
}
|
|
1884
2011
|
ensureCapacity(needed) {
|
|
@@ -1924,8 +2051,8 @@ function siftDown(dist, rows, i, n) {
|
|
|
1924
2051
|
let largest = i;
|
|
1925
2052
|
const left = (i << 1) + 1;
|
|
1926
2053
|
const right = left + 1;
|
|
1927
|
-
if (left < n && dist[left] > dist[largest]) largest = left;
|
|
1928
|
-
if (right < n && dist[right] > dist[largest]) largest = right;
|
|
2054
|
+
if (left < n && (dist[left] > dist[largest] || dist[left] === dist[largest] && rows[left] > rows[largest])) largest = left;
|
|
2055
|
+
if (right < n && (dist[right] > dist[largest] || dist[right] === dist[largest] && rows[right] > rows[largest])) largest = right;
|
|
1929
2056
|
if (largest === i) return;
|
|
1930
2057
|
const td = dist[i];
|
|
1931
2058
|
dist[i] = dist[largest];
|
|
@@ -2067,6 +2194,9 @@ var BATCH_INSERT_CHUNK = 96;
|
|
|
2067
2194
|
var MAX_ANN_OVERFETCH = 8192;
|
|
2068
2195
|
var INSERT_COALESCE_THRESHOLD = 24;
|
|
2069
2196
|
var INSERT_COALESCE_MAX = 96;
|
|
2197
|
+
var warnLogger = new WolbargLogger("warn");
|
|
2198
|
+
var warnedSqliteVecFallback = false;
|
|
2199
|
+
var warnedFtsKeywordSearch = false;
|
|
2070
2200
|
var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
2071
2201
|
name = "sqlite";
|
|
2072
2202
|
connectionString;
|
|
@@ -2076,6 +2206,10 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2076
2206
|
vectorDimensions = null;
|
|
2077
2207
|
vectorBackend = null;
|
|
2078
2208
|
sqliteVecLoaded = false;
|
|
2209
|
+
/** Tracks nesting depth for {@link withTransaction} so we can use savepoints. */
|
|
2210
|
+
transactionDepth = 0;
|
|
2211
|
+
/** Incrementing counter for deterministic savepoint names. */
|
|
2212
|
+
savepointCounter = 0;
|
|
2079
2213
|
/** Hot in-process ANN for blob backend (sqlite-vec unavailable platforms). */
|
|
2080
2214
|
memoryIndex = null;
|
|
2081
2215
|
memoryIndexDirty = false;
|
|
@@ -2142,6 +2276,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2142
2276
|
} else if (this.sqliteVecLoaded) {
|
|
2143
2277
|
this.vectorBackend = "sqlite-vec";
|
|
2144
2278
|
} else {
|
|
2279
|
+
if (!warnedSqliteVecFallback) {
|
|
2280
|
+
warnedSqliteVecFallback = true;
|
|
2281
|
+
warnLogger.warn(
|
|
2282
|
+
"sqlite-vec unavailable on this platform; using blob ANN fallback."
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2145
2285
|
this.vectorBackend = "blob";
|
|
2146
2286
|
}
|
|
2147
2287
|
if (dims !== null) {
|
|
@@ -2385,6 +2525,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2385
2525
|
async searchKeyword(query, organization, topK) {
|
|
2386
2526
|
const stmts = this.requireStatements();
|
|
2387
2527
|
if (!stmts.searchFts) {
|
|
2528
|
+
if (!warnedFtsKeywordSearch) {
|
|
2529
|
+
warnedFtsKeywordSearch = true;
|
|
2530
|
+
warnLogger.warn(
|
|
2531
|
+
"FTS keyword search is unavailable; returning empty keyword hits."
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2388
2534
|
return [];
|
|
2389
2535
|
}
|
|
2390
2536
|
try {
|
|
@@ -2398,7 +2544,16 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2398
2544
|
// bm25 returns lower (more negative) for better matches — invert to [0, ∞)
|
|
2399
2545
|
score: 1 / (1 + Math.abs(row.rank))
|
|
2400
2546
|
}));
|
|
2401
|
-
} catch {
|
|
2547
|
+
} catch (error) {
|
|
2548
|
+
if (!warnedFtsKeywordSearch) {
|
|
2549
|
+
warnedFtsKeywordSearch = true;
|
|
2550
|
+
warnLogger.warn(
|
|
2551
|
+
"FTS keyword search failed; returning empty keyword hits.",
|
|
2552
|
+
{
|
|
2553
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2554
|
+
}
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2402
2557
|
return [];
|
|
2403
2558
|
}
|
|
2404
2559
|
}
|
|
@@ -2563,6 +2718,13 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2563
2718
|
this.deleteEmbeddingsForScope(organization);
|
|
2564
2719
|
this.deleteFtsForScope(organization);
|
|
2565
2720
|
const result = stmts.deleteMemoriesByOrg.run(organization);
|
|
2721
|
+
try {
|
|
2722
|
+
this.requireDb().exec(`
|
|
2723
|
+
DELETE FROM memory_embeddings_blob
|
|
2724
|
+
WHERE memory_rowid NOT IN (SELECT rowid FROM memories)
|
|
2725
|
+
`);
|
|
2726
|
+
} catch {
|
|
2727
|
+
}
|
|
2566
2728
|
return Number(result.changes);
|
|
2567
2729
|
});
|
|
2568
2730
|
}
|
|
@@ -2622,16 +2784,39 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2622
2784
|
}
|
|
2623
2785
|
async withTransaction(fn) {
|
|
2624
2786
|
const db = this.requireDb();
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2787
|
+
if (this.transactionDepth > 0) {
|
|
2788
|
+
const savepointName = `wolbarg_sp_${this.savepointCounter++}`;
|
|
2789
|
+
db.exec(`SAVEPOINT ${savepointName}`);
|
|
2790
|
+
try {
|
|
2791
|
+
this.transactionDepth += 1;
|
|
2792
|
+
const result = await fn();
|
|
2793
|
+
db.exec(`RELEASE SAVEPOINT ${savepointName}`);
|
|
2794
|
+
return result;
|
|
2795
|
+
} catch (error) {
|
|
2796
|
+
try {
|
|
2797
|
+
db.exec(`ROLLBACK TO SAVEPOINT ${savepointName}`);
|
|
2798
|
+
} catch {
|
|
2799
|
+
}
|
|
2800
|
+
throw error;
|
|
2801
|
+
} finally {
|
|
2802
|
+
this.transactionDepth -= 1;
|
|
2633
2803
|
}
|
|
2634
|
-
|
|
2804
|
+
}
|
|
2805
|
+
this.transactionDepth = 1;
|
|
2806
|
+
try {
|
|
2807
|
+
return await withImmediateTransaction(
|
|
2808
|
+
db,
|
|
2809
|
+
this.concurrency,
|
|
2810
|
+
fn,
|
|
2811
|
+
(attempt, delayMs) => {
|
|
2812
|
+
this.retryLog?.(
|
|
2813
|
+
`SQLITE_BUSY retry attempt=${attempt} backoffMs=${Math.round(delayMs)}`
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
);
|
|
2817
|
+
} finally {
|
|
2818
|
+
this.transactionDepth = 0;
|
|
2819
|
+
}
|
|
2635
2820
|
}
|
|
2636
2821
|
// ─── internals ───────────────────────────────────────────────────────────
|
|
2637
2822
|
tryLoadSqliteVec(db) {
|
|
@@ -3077,7 +3262,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
3077
3262
|
if (!stmt) {
|
|
3078
3263
|
const values = Array.from({ length: n }, () => "(?, ?)").join(", ");
|
|
3079
3264
|
stmt = this.requireDb().prepare(
|
|
3080
|
-
`INSERT INTO memory_embeddings_blob (memory_rowid, embedding) VALUES ${values}`
|
|
3265
|
+
`INSERT OR REPLACE INTO memory_embeddings_blob (memory_rowid, embedding) VALUES ${values}`
|
|
3081
3266
|
);
|
|
3082
3267
|
this.batchBlobEmbStatements.set(n, stmt);
|
|
3083
3268
|
}
|
|
@@ -3785,9 +3970,13 @@ function createPostgresListenerFromPool(pool, onError) {
|
|
|
3785
3970
|
|
|
3786
3971
|
// src/storage/providers/postgres.ts
|
|
3787
3972
|
var txStore = new AsyncLocalStorage();
|
|
3973
|
+
var warnLogger2 = new WolbargLogger("warn");
|
|
3974
|
+
var warnedPgvectorFallback = false;
|
|
3975
|
+
var warnedFtsKeywordSearch2 = false;
|
|
3976
|
+
var warnedHnswSoftFail = false;
|
|
3788
3977
|
var STMT = {
|
|
3789
3978
|
insertOne: "Wolbarg_insert_one_v5",
|
|
3790
|
-
insertBatch: "
|
|
3979
|
+
insertBatch: "Wolbarg_insert_batch_v6"
|
|
3791
3980
|
};
|
|
3792
3981
|
function toFloat4Param(embedding) {
|
|
3793
3982
|
const out = new Array(embedding.length);
|
|
@@ -3858,13 +4047,13 @@ var INSERT_ONE_SQL = `WITH mem AS (
|
|
|
3858
4047
|
var INSERT_BATCH_SQL = `WITH mem AS (
|
|
3859
4048
|
INSERT INTO memories (
|
|
3860
4049
|
id, organization, agent, content_text, metadata_json,
|
|
3861
|
-
archived, compressed_into, created_at, updated_at
|
|
4050
|
+
archived, compressed_into, content_hash, created_at, updated_at
|
|
3862
4051
|
)
|
|
3863
|
-
SELECT id, org, agent, txt, meta::jsonb, false, NULL, c, u
|
|
4052
|
+
SELECT id, org, agent, txt, meta::jsonb, false, NULL, h, c, u
|
|
3864
4053
|
FROM unnest(
|
|
3865
4054
|
$1::text[], $2::text[], $3::text[], $4::text[],
|
|
3866
|
-
$5::text[], $6::timestamptz[], $7::timestamptz[]
|
|
3867
|
-
) AS t(id, org, agent, txt, meta, c, u)
|
|
4055
|
+
$5::text[], $6::timestamptz[], $7::timestamptz[], $8::text[]
|
|
4056
|
+
) AS t(id, org, agent, txt, meta, c, u, h)
|
|
3868
4057
|
RETURNING id
|
|
3869
4058
|
),
|
|
3870
4059
|
hist AS (
|
|
@@ -3880,7 +4069,7 @@ var INSERT_BATCH_SQL = `WITH mem AS (
|
|
|
3880
4069
|
emb AS (
|
|
3881
4070
|
INSERT INTO memory_embeddings (memory_id, embedding, organization, agent, archived)
|
|
3882
4071
|
SELECT id, emb::vector, org, agent, false
|
|
3883
|
-
FROM unnest($1::text[], $
|
|
4072
|
+
FROM unnest($1::text[], $9::text[], $2::text[], $3::text[]) AS t(id, emb, org, agent)
|
|
3884
4073
|
)
|
|
3885
4074
|
SELECT id FROM mem`;
|
|
3886
4075
|
var COALESCE_FLUSH_MAX = 128;
|
|
@@ -4149,6 +4338,12 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4149
4338
|
return;
|
|
4150
4339
|
}
|
|
4151
4340
|
this.hnswCreateFailures += 1;
|
|
4341
|
+
if (this.hnswCreateFailures === 1 && !warnedHnswSoftFail) {
|
|
4342
|
+
warnedHnswSoftFail = true;
|
|
4343
|
+
warnLogger2.warn(
|
|
4344
|
+
"HNSW index creation failed; ANN will fall back to sequential scan."
|
|
4345
|
+
);
|
|
4346
|
+
}
|
|
4152
4347
|
if (this.hnswCreateFailures >= 3) {
|
|
4153
4348
|
throw new DatabaseError(
|
|
4154
4349
|
`Failed to create HNSW index after ${this.hnswCreateFailures} attempts: ${this.describe(error)}`,
|
|
@@ -4293,6 +4488,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4293
4488
|
const metas = new Array(inputs.length);
|
|
4294
4489
|
const created = new Array(inputs.length);
|
|
4295
4490
|
const updated = new Array(inputs.length);
|
|
4491
|
+
const contentHashes = new Array(inputs.length);
|
|
4296
4492
|
const vectors = new Array(inputs.length);
|
|
4297
4493
|
for (let i = 0; i < inputs.length; i += 1) {
|
|
4298
4494
|
const input = inputs[i];
|
|
@@ -4303,6 +4499,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4303
4499
|
metas[i] = serializeMetadata(input.metadata);
|
|
4304
4500
|
created[i] = input.createdAt;
|
|
4305
4501
|
updated[i] = input.updatedAt;
|
|
4502
|
+
contentHashes[i] = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
|
|
4306
4503
|
vectors[i] = toVectorLiteral(input.embedding);
|
|
4307
4504
|
}
|
|
4308
4505
|
await this.queryNamed(STMT.insertBatch, INSERT_BATCH_SQL, [
|
|
@@ -4313,9 +4510,10 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4313
4510
|
metas,
|
|
4314
4511
|
created,
|
|
4315
4512
|
updated,
|
|
4513
|
+
contentHashes,
|
|
4316
4514
|
vectors
|
|
4317
4515
|
]);
|
|
4318
|
-
return inputs.map((
|
|
4516
|
+
return inputs.map((_, i) => ({
|
|
4319
4517
|
id: ids[i],
|
|
4320
4518
|
organization: orgs[i],
|
|
4321
4519
|
agent: agents[i],
|
|
@@ -4323,7 +4521,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4323
4521
|
metadata_json: metas[i],
|
|
4324
4522
|
archived: 0,
|
|
4325
4523
|
compressed_into: null,
|
|
4326
|
-
content_hash:
|
|
4524
|
+
content_hash: contentHashes[i] ?? null,
|
|
4327
4525
|
created_at: created[i],
|
|
4328
4526
|
updated_at: updated[i]
|
|
4329
4527
|
}));
|
|
@@ -4364,80 +4562,84 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4364
4562
|
return out;
|
|
4365
4563
|
}
|
|
4366
4564
|
async insertOneBlob(input) {
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4565
|
+
return this.withTransaction(async () => {
|
|
4566
|
+
const buf = Buffer.from(
|
|
4567
|
+
input.embedding.buffer,
|
|
4568
|
+
input.embedding.byteOffset,
|
|
4569
|
+
input.embedding.byteLength
|
|
4570
|
+
);
|
|
4571
|
+
const contentHash = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
|
|
4572
|
+
const inserted = await this.query(
|
|
4573
|
+
`INSERT INTO memories (
|
|
4574
|
+
id, organization, agent, content_text, metadata_json,
|
|
4575
|
+
archived, compressed_into, content_hash, created_at, updated_at
|
|
4576
|
+
) VALUES ($1,$2,$3,$4,$5::jsonb,false,NULL,$8,$6,$7)
|
|
4577
|
+
RETURNING id, organization, agent, content_text, metadata_json,
|
|
4578
|
+
archived::int AS archived, compressed_into, content_hash, created_at, updated_at`,
|
|
4579
|
+
[
|
|
4580
|
+
input.id,
|
|
4581
|
+
input.organization,
|
|
4582
|
+
input.agent,
|
|
4583
|
+
input.contentText,
|
|
4584
|
+
serializeMetadata(input.metadata),
|
|
4585
|
+
input.createdAt,
|
|
4586
|
+
input.updatedAt,
|
|
4587
|
+
contentHash
|
|
4588
|
+
]
|
|
4589
|
+
);
|
|
4590
|
+
const row = this.mapRow(inserted.rows[0]);
|
|
4591
|
+
await this.query(
|
|
4592
|
+
`WITH mapped AS (
|
|
4593
|
+
INSERT INTO memory_row_map (memory_id) VALUES ($1)
|
|
4594
|
+
ON CONFLICT (memory_id) DO NOTHING
|
|
4595
|
+
)
|
|
4596
|
+
INSERT INTO memory_embeddings_blob (memory_id, embedding)
|
|
4597
|
+
VALUES ($1, $2)
|
|
4598
|
+
ON CONFLICT (memory_id) DO UPDATE SET embedding = EXCLUDED.embedding`,
|
|
4599
|
+
[input.id, buf]
|
|
4600
|
+
);
|
|
4601
|
+
await this.query(
|
|
4602
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4603
|
+
VALUES ($1,$2,'created',NULL,$3)`,
|
|
4604
|
+
[crypto.randomUUID(), input.id, input.createdAt]
|
|
4605
|
+
);
|
|
4606
|
+
return row;
|
|
4607
|
+
});
|
|
4408
4608
|
}
|
|
4409
4609
|
async updateMemory(input) {
|
|
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
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4610
|
+
return this.withTransaction(async () => {
|
|
4611
|
+
const existing = await this.getMemoryById(input.id, input.organization);
|
|
4612
|
+
if (!existing) {
|
|
4613
|
+
return null;
|
|
4614
|
+
}
|
|
4615
|
+
const contentHash = input.contentHash !== void 0 ? input.contentHash : input.contentText !== void 0 ? hashMemoryContent(input.contentText) : existing.content_hash ?? null;
|
|
4616
|
+
await this.query(
|
|
4617
|
+
`UPDATE memories SET
|
|
4618
|
+
content_text = COALESCE($1, content_text),
|
|
4619
|
+
metadata_json = COALESCE($2::jsonb, metadata_json),
|
|
4620
|
+
content_hash = COALESCE($3, content_hash),
|
|
4621
|
+
updated_at = $4
|
|
4622
|
+
WHERE id = $5 AND organization = $6`,
|
|
4623
|
+
[
|
|
4624
|
+
input.contentText ?? null,
|
|
4625
|
+
input.metadata !== void 0 ? serializeMetadata(input.metadata) : null,
|
|
4626
|
+
contentHash,
|
|
4627
|
+
input.updatedAt,
|
|
4628
|
+
input.id,
|
|
4629
|
+
input.organization
|
|
4630
|
+
]
|
|
4631
|
+
);
|
|
4632
|
+
if (input.embedding) {
|
|
4633
|
+
await this.deleteEmbedding(input.id);
|
|
4634
|
+
await this.insertEmbedding(input.id, input.embedding);
|
|
4635
|
+
}
|
|
4636
|
+
await this.query(
|
|
4637
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4638
|
+
VALUES ($1,$2,'updated',NULL,$3)`,
|
|
4639
|
+
[crypto.randomUUID(), input.id, input.updatedAt]
|
|
4640
|
+
);
|
|
4641
|
+
return this.getMemoryById(input.id, input.organization);
|
|
4642
|
+
});
|
|
4441
4643
|
}
|
|
4442
4644
|
async findActiveByContentHash(organization, agent, contentHash) {
|
|
4443
4645
|
const result = await this.query(
|
|
@@ -4586,7 +4788,16 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4586
4788
|
memoryId: String(row.memory_id),
|
|
4587
4789
|
score: Number(row.rank)
|
|
4588
4790
|
}));
|
|
4589
|
-
} catch {
|
|
4791
|
+
} catch (error) {
|
|
4792
|
+
if (!warnedFtsKeywordSearch2) {
|
|
4793
|
+
warnedFtsKeywordSearch2 = true;
|
|
4794
|
+
warnLogger2.warn(
|
|
4795
|
+
"FTS keyword search failed; returning empty keyword hits.",
|
|
4796
|
+
{
|
|
4797
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
4798
|
+
}
|
|
4799
|
+
);
|
|
4800
|
+
}
|
|
4590
4801
|
return [];
|
|
4591
4802
|
}
|
|
4592
4803
|
}
|
|
@@ -4704,44 +4915,46 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4704
4915
|
return mapHits(result.rows);
|
|
4705
4916
|
}
|
|
4706
4917
|
async archiveMemories(ids, organization, compressedIntoId, archivedAt) {
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4918
|
+
return this.withTransaction(async () => {
|
|
4919
|
+
if (ids.length === 0) {
|
|
4920
|
+
return [];
|
|
4921
|
+
}
|
|
4922
|
+
const result = await this.query(
|
|
4923
|
+
`UPDATE memories
|
|
4924
|
+
SET archived = true, compressed_into = $1, updated_at = $2
|
|
4925
|
+
WHERE organization = $3 AND archived = false AND id = ANY($4::text[])
|
|
4926
|
+
RETURNING id`,
|
|
4927
|
+
[compressedIntoId, archivedAt, organization, ids]
|
|
4928
|
+
);
|
|
4929
|
+
const archived = result.rows.map((r) => String(r.id));
|
|
4930
|
+
if (archived.length === 0) {
|
|
4931
|
+
return [];
|
|
4932
|
+
}
|
|
4933
|
+
await this.query(
|
|
4934
|
+
`UPDATE memory_embeddings
|
|
4935
|
+
SET archived = true
|
|
4936
|
+
WHERE memory_id = ANY($1::text[])`,
|
|
4937
|
+
[archived]
|
|
4938
|
+
);
|
|
4939
|
+
const histIds = [];
|
|
4940
|
+
const memIds = [];
|
|
4941
|
+
const types = [];
|
|
4942
|
+
const related = [];
|
|
4943
|
+
const times = [];
|
|
4944
|
+
for (const id of archived) {
|
|
4945
|
+
histIds.push(crypto.randomUUID(), crypto.randomUUID());
|
|
4946
|
+
memIds.push(id, compressedIntoId);
|
|
4947
|
+
types.push("archived", "compressed");
|
|
4948
|
+
related.push(compressedIntoId, id);
|
|
4949
|
+
times.push(archivedAt, archivedAt);
|
|
4950
|
+
}
|
|
4951
|
+
await this.query(
|
|
4952
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4953
|
+
SELECT * FROM unnest($1::text[], $2::text[], $3::text[], $4::text[], $5::timestamptz[])`,
|
|
4954
|
+
[histIds, memIds, types, related, times]
|
|
4955
|
+
);
|
|
4956
|
+
return archived;
|
|
4957
|
+
});
|
|
4745
4958
|
}
|
|
4746
4959
|
async deleteMemoryById(id, organization) {
|
|
4747
4960
|
const result = await this.query(
|
|
@@ -4852,6 +5065,11 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4852
5065
|
[META_KEYS.schemaVersion]
|
|
4853
5066
|
).catch(() => ({ rows: [] }));
|
|
4854
5067
|
const current = versionRow.rows[0]?.value !== void 0 ? Number(versionRow.rows[0].value) : null;
|
|
5068
|
+
if (current !== null && current > SCHEMA_VERSION) {
|
|
5069
|
+
throw new InitializationError(
|
|
5070
|
+
`Database schema version ${current} is newer than this Wolbarg SDK (${SCHEMA_VERSION}). Please upgrade Wolbarg to open this database.`
|
|
5071
|
+
);
|
|
5072
|
+
}
|
|
4855
5073
|
if (current === SCHEMA_VERSION) {
|
|
4856
5074
|
const tsvProbe2 = await this.query(
|
|
4857
5075
|
`SELECT 1 FROM information_schema.columns
|
|
@@ -5002,8 +5220,17 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
5002
5220
|
await this.query(`CREATE EXTENSION IF NOT EXISTS vector`);
|
|
5003
5221
|
_PostgresStorageProvider.pgvectorByConn.set(this.connectionString, true);
|
|
5004
5222
|
return true;
|
|
5005
|
-
} catch {
|
|
5223
|
+
} catch (error) {
|
|
5006
5224
|
_PostgresStorageProvider.pgvectorByConn.set(this.connectionString, false);
|
|
5225
|
+
if (!warnedPgvectorFallback) {
|
|
5226
|
+
warnedPgvectorFallback = true;
|
|
5227
|
+
warnLogger2.warn(
|
|
5228
|
+
"pgvector extension is unavailable; using blob cosine search fallback.",
|
|
5229
|
+
{
|
|
5230
|
+
cause: error instanceof Error ? error.message : `unknown error: ${String(error)}`
|
|
5231
|
+
}
|
|
5232
|
+
);
|
|
5233
|
+
}
|
|
5007
5234
|
return false;
|
|
5008
5235
|
}
|
|
5009
5236
|
}
|
|
@@ -5144,11 +5371,12 @@ function createDatabaseProvider(config) {
|
|
|
5144
5371
|
}
|
|
5145
5372
|
|
|
5146
5373
|
// src/version.ts
|
|
5147
|
-
var SDK_VERSION = "0.5.
|
|
5374
|
+
var SDK_VERSION = "0.5.4";
|
|
5148
5375
|
|
|
5149
5376
|
// src/memory/transfer.ts
|
|
5377
|
+
var warnedMissingExportManifest = false;
|
|
5150
5378
|
var SqliteMemoryTransferProvider = class {
|
|
5151
|
-
async exportTo(exportPath, sourcePath, organization) {
|
|
5379
|
+
async exportTo(exportPath, sourcePath, organization, embeddingModel, embeddingDimensions) {
|
|
5152
5380
|
const resolvedSource = resolvePath(sourcePath);
|
|
5153
5381
|
if (resolvedSource === ":memory:") {
|
|
5154
5382
|
throw new ConfigurationError(
|
|
@@ -5175,7 +5403,9 @@ Initialize Wolbarg and verify the database path.`
|
|
|
5175
5403
|
sdkVersion: SDK_VERSION,
|
|
5176
5404
|
provider: "sqlite",
|
|
5177
5405
|
sourcePath: resolvedSource,
|
|
5178
|
-
organization
|
|
5406
|
+
organization,
|
|
5407
|
+
...embeddingModel ? { embeddingModel } : {},
|
|
5408
|
+
...embeddingDimensions ? { embeddingDimensions } : {}
|
|
5179
5409
|
};
|
|
5180
5410
|
fs6.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
5181
5411
|
return {
|
|
@@ -5184,7 +5414,7 @@ Initialize Wolbarg and verify the database path.`
|
|
|
5184
5414
|
sizeBytes: fs6.statSync(dbExportPath).size
|
|
5185
5415
|
};
|
|
5186
5416
|
}
|
|
5187
|
-
async importFrom(exportPath, targetPath) {
|
|
5417
|
+
async importFrom(exportPath, targetPath, expected) {
|
|
5188
5418
|
const resolvedExport = resolvePath(exportPath);
|
|
5189
5419
|
const dbExportPath = resolvedExport.endsWith(".db") ? resolvedExport : fs6.existsSync(`${resolvedExport}.db`) ? `${resolvedExport}.db` : resolvedExport;
|
|
5190
5420
|
if (!fs6.existsSync(dbExportPath)) {
|
|
@@ -5202,30 +5432,61 @@ Pass the path returned by export().`
|
|
|
5202
5432
|
manifest = JSON.parse(fs6.readFileSync(manifestPath, "utf8"));
|
|
5203
5433
|
if (manifest.format !== "wolbarg-export-v1") {
|
|
5204
5434
|
throw new ValidationError(
|
|
5205
|
-
`Unsupported export format: ${String(manifest.format)}`
|
|
5435
|
+
`Unsupported export format: ${String(manifest.format)}`
|
|
5436
|
+
);
|
|
5437
|
+
}
|
|
5438
|
+
} else {
|
|
5439
|
+
if (!warnedMissingExportManifest) {
|
|
5440
|
+
warnedMissingExportManifest = true;
|
|
5441
|
+
console.warn(
|
|
5442
|
+
"[wolbarg:warn] export manifest is missing; skipping manifest-based import validation."
|
|
5443
|
+
);
|
|
5444
|
+
}
|
|
5445
|
+
}
|
|
5446
|
+
if (manifest) {
|
|
5447
|
+
if (expected?.organization && manifest.organization && expected.organization !== manifest.organization) {
|
|
5448
|
+
throw new ValidationError(
|
|
5449
|
+
"Import failed: export organization does not match the current Wolbarg organization."
|
|
5450
|
+
);
|
|
5451
|
+
}
|
|
5452
|
+
if (expected?.embeddingDimensions != null && manifest.embeddingDimensions != null && expected.embeddingDimensions !== manifest.embeddingDimensions) {
|
|
5453
|
+
throw new ValidationError(
|
|
5454
|
+
`Import failed: embedding dimension mismatch (expected ${expected.embeddingDimensions}, got ${manifest.embeddingDimensions}).`
|
|
5455
|
+
);
|
|
5456
|
+
}
|
|
5457
|
+
if (expected?.embeddingModel && manifest.embeddingModel && expected.embeddingModel !== manifest.embeddingModel) {
|
|
5458
|
+
throw new ValidationError(
|
|
5459
|
+
`Import failed: embedding model mismatch (expected ${expected.embeddingModel}, got ${manifest.embeddingModel}).`
|
|
5206
5460
|
);
|
|
5207
5461
|
}
|
|
5208
|
-
} else {
|
|
5209
|
-
manifest = {
|
|
5210
|
-
format: "wolbarg-export-v1",
|
|
5211
|
-
exportedAt: nowIso(),
|
|
5212
|
-
sdkVersion: SDK_VERSION,
|
|
5213
|
-
provider: "sqlite",
|
|
5214
|
-
sourcePath: dbExportPath
|
|
5215
|
-
};
|
|
5216
5462
|
}
|
|
5217
5463
|
const resolvedTarget = resolvePath(targetPath);
|
|
5218
5464
|
if (resolvedTarget === ":memory:") {
|
|
5219
5465
|
throw new ConfigurationError("Cannot import into an in-memory database.");
|
|
5220
5466
|
}
|
|
5467
|
+
const tmpTarget = `${resolvedTarget}.tmp-import-${Date.now()}`;
|
|
5468
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5469
|
+
const side = `${tmpTarget}${suffix}`;
|
|
5470
|
+
if (fs6.existsSync(side)) {
|
|
5471
|
+
fs6.rmSync(side, { force: true });
|
|
5472
|
+
}
|
|
5473
|
+
}
|
|
5474
|
+
await copySqlite(dbExportPath, tmpTarget);
|
|
5221
5475
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5222
5476
|
const side = `${resolvedTarget}${suffix}`;
|
|
5223
5477
|
if (fs6.existsSync(side)) {
|
|
5224
5478
|
fs6.rmSync(side, { force: true });
|
|
5225
5479
|
}
|
|
5226
5480
|
}
|
|
5227
|
-
|
|
5228
|
-
|
|
5481
|
+
fs6.renameSync(tmpTarget, resolvedTarget);
|
|
5482
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
5483
|
+
const from = `${tmpTarget}${suffix}`;
|
|
5484
|
+
const to = `${resolvedTarget}${suffix}`;
|
|
5485
|
+
if (fs6.existsSync(from)) {
|
|
5486
|
+
fs6.renameSync(from, to);
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
return { path: resolvedTarget, manifest: manifest ?? void 0 };
|
|
5229
5490
|
}
|
|
5230
5491
|
};
|
|
5231
5492
|
async function copySqlite(sourcePath, destPath) {
|
|
@@ -5454,7 +5715,8 @@ function applyMmr(candidates, topK, lambda) {
|
|
|
5454
5715
|
);
|
|
5455
5716
|
}
|
|
5456
5717
|
const score = lambda * relevance - (1 - lambda) * maxSim;
|
|
5457
|
-
|
|
5718
|
+
const best = remaining[bestIdx];
|
|
5719
|
+
if (score > bestScore || score === bestScore && candidate.id < best.id) {
|
|
5458
5720
|
bestScore = score;
|
|
5459
5721
|
bestIdx = i;
|
|
5460
5722
|
}
|
|
@@ -6014,24 +6276,26 @@ var Neo4jGraphProvider = class {
|
|
|
6014
6276
|
}
|
|
6015
6277
|
}
|
|
6016
6278
|
async run(cypher, params = {}) {
|
|
6017
|
-
return this.withSession(
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6279
|
+
return this.withSession(
|
|
6280
|
+
(session) => this.runOnSession(session, cypher, params)
|
|
6281
|
+
);
|
|
6282
|
+
}
|
|
6283
|
+
async runOnSession(session, cypher, params = {}) {
|
|
6284
|
+
const result = await session.run(cypher, params);
|
|
6285
|
+
return result.records.map((rec) => {
|
|
6286
|
+
try {
|
|
6287
|
+
return rec.toObject();
|
|
6288
|
+
} catch {
|
|
6289
|
+
const obj = {};
|
|
6290
|
+
for (const key of rec.keys) {
|
|
6291
|
+
obj[key] = unwrapNeo4jValue(rec.get(key));
|
|
6028
6292
|
}
|
|
6029
|
-
|
|
6293
|
+
return obj;
|
|
6294
|
+
}
|
|
6030
6295
|
});
|
|
6031
6296
|
}
|
|
6032
|
-
async ensureMemoryNode(id) {
|
|
6033
|
-
|
|
6034
|
-
`MERGE (m:Memory { id: $id })
|
|
6297
|
+
async ensureMemoryNode(id, session) {
|
|
6298
|
+
const cypher = `MERGE (m:Memory { id: $id })
|
|
6035
6299
|
ON CREATE SET
|
|
6036
6300
|
m.organization = '',
|
|
6037
6301
|
m.agent = '',
|
|
@@ -6040,24 +6304,30 @@ var Neo4jGraphProvider = class {
|
|
|
6040
6304
|
m.archived = false,
|
|
6041
6305
|
m.compressed_into = '',
|
|
6042
6306
|
m.created_at = '',
|
|
6043
|
-
m.updated_at = ''
|
|
6044
|
-
|
|
6045
|
-
|
|
6307
|
+
m.updated_at = ''`;
|
|
6308
|
+
if (session) {
|
|
6309
|
+
await this.runOnSession(session, cypher, { id });
|
|
6310
|
+
return;
|
|
6311
|
+
}
|
|
6312
|
+
await this.run(cypher, { id });
|
|
6046
6313
|
}
|
|
6047
6314
|
async linkMemories(fromId, toId, relation, metadata) {
|
|
6048
|
-
await this.
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6315
|
+
await this.withSession(async (session) => {
|
|
6316
|
+
await this.ensureMemoryNode(fromId, session);
|
|
6317
|
+
await this.ensureMemoryNode(toId, session);
|
|
6318
|
+
await this.runOnSession(
|
|
6319
|
+
session,
|
|
6320
|
+
`MATCH (a:Memory { id: $fromId }), (b:Memory { id: $toId })
|
|
6321
|
+
MERGE (a)-[r:RELATED { relation: $relation }]->(b)
|
|
6322
|
+
SET r.metadata_json = $metadata`,
|
|
6323
|
+
{
|
|
6324
|
+
fromId,
|
|
6325
|
+
toId,
|
|
6326
|
+
relation,
|
|
6327
|
+
metadata: serializeMetadata2(metadata)
|
|
6328
|
+
}
|
|
6329
|
+
);
|
|
6330
|
+
});
|
|
6061
6331
|
}
|
|
6062
6332
|
async unlinkMemories(fromId, toId, relation) {
|
|
6063
6333
|
if (relation !== void 0) {
|
|
@@ -6119,22 +6389,26 @@ var Neo4jGraphProvider = class {
|
|
|
6119
6389
|
return id;
|
|
6120
6390
|
}
|
|
6121
6391
|
async linkEntityToMemory(entityId, memoryId, role) {
|
|
6122
|
-
await this.
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6392
|
+
await this.withSession(async (session) => {
|
|
6393
|
+
await this.ensureMemoryNode(memoryId, session);
|
|
6394
|
+
const found = await this.runOnSession(
|
|
6395
|
+
session,
|
|
6396
|
+
`MATCH (e:Entity { id: $id }) RETURN e.id AS id`,
|
|
6397
|
+
{ id: entityId }
|
|
6398
|
+
);
|
|
6399
|
+
if (found.length === 0) {
|
|
6400
|
+
throw new DatabaseError(`Entity not found: ${entityId}`, {
|
|
6401
|
+
operation: "linkEntityToMemory"
|
|
6402
|
+
});
|
|
6403
|
+
}
|
|
6404
|
+
await this.runOnSession(
|
|
6405
|
+
session,
|
|
6406
|
+
`MATCH (e:Entity { id: $entityId }), (m:Memory { id: $memoryId })
|
|
6407
|
+
MERGE (e)-[r:MENTIONS]->(m)
|
|
6408
|
+
SET r.role = $role`,
|
|
6409
|
+
{ entityId, memoryId, role: role ?? "" }
|
|
6410
|
+
);
|
|
6411
|
+
});
|
|
6138
6412
|
}
|
|
6139
6413
|
async deleteMemory(memoryId) {
|
|
6140
6414
|
await this.run(
|
|
@@ -6207,6 +6481,21 @@ function assertNonEmpty(value, fieldName) {
|
|
|
6207
6481
|
throw new ConfigurationError(`${fieldName} must be a non-empty string`);
|
|
6208
6482
|
}
|
|
6209
6483
|
}
|
|
6484
|
+
function assertFiniteNumber2(value, fieldName) {
|
|
6485
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6486
|
+
throw new ConfigurationError(`${fieldName} must be a finite number`);
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6489
|
+
function assertNumberMin(value, fieldName, min) {
|
|
6490
|
+
if (!Number.isFinite(value) || value < min) {
|
|
6491
|
+
throw new ConfigurationError(`${fieldName} must be >= ${min}`);
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6494
|
+
function assertNumberInRange(value, fieldName, min, max) {
|
|
6495
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
6496
|
+
throw new ConfigurationError(`${fieldName} must be between ${min} and ${max}`);
|
|
6497
|
+
}
|
|
6498
|
+
}
|
|
6210
6499
|
function assertUrl(value, fieldName) {
|
|
6211
6500
|
assertNonEmpty(value, fieldName);
|
|
6212
6501
|
try {
|
|
@@ -6253,6 +6542,135 @@ function validateLlmConfig(config) {
|
|
|
6253
6542
|
...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {}
|
|
6254
6543
|
};
|
|
6255
6544
|
}
|
|
6545
|
+
function validateRetrievalConfig(config) {
|
|
6546
|
+
if (config === null || typeof config !== "object") {
|
|
6547
|
+
throw new ConfigurationError("retrieval must be an object");
|
|
6548
|
+
}
|
|
6549
|
+
const out = {};
|
|
6550
|
+
if (config.overFetchFactor !== void 0) {
|
|
6551
|
+
assertFiniteNumber2(config.overFetchFactor, "retrieval.overFetchFactor");
|
|
6552
|
+
assertNumberMin(config.overFetchFactor, "retrieval.overFetchFactor", 0);
|
|
6553
|
+
if (config.overFetchFactor <= 0) {
|
|
6554
|
+
throw new ConfigurationError("retrieval.overFetchFactor must be > 0");
|
|
6555
|
+
}
|
|
6556
|
+
out.overFetchFactor = config.overFetchFactor;
|
|
6557
|
+
}
|
|
6558
|
+
if (config.hybrid !== void 0) {
|
|
6559
|
+
if (config.hybrid === null || typeof config.hybrid !== "object") {
|
|
6560
|
+
throw new ConfigurationError("retrieval.hybrid must be an object");
|
|
6561
|
+
}
|
|
6562
|
+
const hybrid = config.hybrid;
|
|
6563
|
+
const outHybrid = {};
|
|
6564
|
+
if (hybrid.semanticWeight !== void 0) {
|
|
6565
|
+
assertFiniteNumber2(hybrid.semanticWeight, "retrieval.hybrid.semanticWeight");
|
|
6566
|
+
if (hybrid.semanticWeight < 0) {
|
|
6567
|
+
throw new ConfigurationError(
|
|
6568
|
+
"retrieval.hybrid.semanticWeight must be >= 0"
|
|
6569
|
+
);
|
|
6570
|
+
}
|
|
6571
|
+
outHybrid.semanticWeight = hybrid.semanticWeight;
|
|
6572
|
+
}
|
|
6573
|
+
if (hybrid.keywordWeight !== void 0) {
|
|
6574
|
+
assertFiniteNumber2(hybrid.keywordWeight, "retrieval.hybrid.keywordWeight");
|
|
6575
|
+
if (hybrid.keywordWeight < 0) {
|
|
6576
|
+
throw new ConfigurationError(
|
|
6577
|
+
"retrieval.hybrid.keywordWeight must be >= 0"
|
|
6578
|
+
);
|
|
6579
|
+
}
|
|
6580
|
+
outHybrid.keywordWeight = hybrid.keywordWeight;
|
|
6581
|
+
}
|
|
6582
|
+
if (Object.keys(outHybrid).length > 0) {
|
|
6583
|
+
out.hybrid = outHybrid;
|
|
6584
|
+
}
|
|
6585
|
+
}
|
|
6586
|
+
if (config.mmr !== void 0) {
|
|
6587
|
+
if (config.mmr === null || typeof config.mmr !== "object") {
|
|
6588
|
+
throw new ConfigurationError("retrieval.mmr must be an object");
|
|
6589
|
+
}
|
|
6590
|
+
const mmr = config.mmr;
|
|
6591
|
+
const outMmr = {};
|
|
6592
|
+
if (mmr.lambda !== void 0) {
|
|
6593
|
+
assertFiniteNumber2(mmr.lambda, "retrieval.mmr.lambda");
|
|
6594
|
+
assertNumberInRange(mmr.lambda, "retrieval.mmr.lambda", 0, 1);
|
|
6595
|
+
outMmr.lambda = mmr.lambda;
|
|
6596
|
+
}
|
|
6597
|
+
if (Object.keys(outMmr).length > 0) {
|
|
6598
|
+
out.mmr = outMmr;
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6601
|
+
return out;
|
|
6602
|
+
}
|
|
6603
|
+
function validateMemoryDedupeConfig(config) {
|
|
6604
|
+
if (config === null || typeof config !== "object") {
|
|
6605
|
+
throw new ConfigurationError("memory.dedupe must be an object");
|
|
6606
|
+
}
|
|
6607
|
+
const out = {};
|
|
6608
|
+
if (config.enabled !== void 0) {
|
|
6609
|
+
if (typeof config.enabled !== "boolean") {
|
|
6610
|
+
throw new ConfigurationError("memory.dedupe.enabled must be a boolean");
|
|
6611
|
+
}
|
|
6612
|
+
out.enabled = config.enabled;
|
|
6613
|
+
}
|
|
6614
|
+
if (config.strategy !== void 0) {
|
|
6615
|
+
const allowed = /* @__PURE__ */ new Set(["exact", "near", "exact-or-near"]);
|
|
6616
|
+
if (!allowed.has(config.strategy)) {
|
|
6617
|
+
throw new ConfigurationError(
|
|
6618
|
+
`memory.dedupe.strategy must be one of ${Array.from(allowed).join(", ")}`
|
|
6619
|
+
);
|
|
6620
|
+
}
|
|
6621
|
+
out.strategy = config.strategy;
|
|
6622
|
+
}
|
|
6623
|
+
if (config.nearThreshold !== void 0) {
|
|
6624
|
+
assertFiniteNumber2(config.nearThreshold, "memory.dedupe.nearThreshold");
|
|
6625
|
+
assertNumberInRange(
|
|
6626
|
+
config.nearThreshold,
|
|
6627
|
+
"memory.dedupe.nearThreshold",
|
|
6628
|
+
0,
|
|
6629
|
+
1
|
|
6630
|
+
);
|
|
6631
|
+
out.nearThreshold = config.nearThreshold;
|
|
6632
|
+
}
|
|
6633
|
+
if (config.nearCandidateLimit !== void 0) {
|
|
6634
|
+
assertFiniteNumber2(
|
|
6635
|
+
config.nearCandidateLimit,
|
|
6636
|
+
"memory.dedupe.nearCandidateLimit"
|
|
6637
|
+
);
|
|
6638
|
+
assertNumberMin(
|
|
6639
|
+
config.nearCandidateLimit,
|
|
6640
|
+
"memory.dedupe.nearCandidateLimit",
|
|
6641
|
+
1
|
|
6642
|
+
);
|
|
6643
|
+
out.nearCandidateLimit = config.nearCandidateLimit;
|
|
6644
|
+
}
|
|
6645
|
+
return out;
|
|
6646
|
+
}
|
|
6647
|
+
function validateEmbeddingCacheConfig(config) {
|
|
6648
|
+
if (config === null || typeof config !== "object") {
|
|
6649
|
+
throw new ConfigurationError("embeddingCache must be an object");
|
|
6650
|
+
}
|
|
6651
|
+
const out = {};
|
|
6652
|
+
if (config.enabled !== void 0) {
|
|
6653
|
+
if (typeof config.enabled !== "boolean") {
|
|
6654
|
+
throw new ConfigurationError("embeddingCache.enabled must be a boolean");
|
|
6655
|
+
}
|
|
6656
|
+
out.enabled = config.enabled;
|
|
6657
|
+
}
|
|
6658
|
+
if (config.ttlMs !== void 0) {
|
|
6659
|
+
assertFiniteNumber2(config.ttlMs, "embeddingCache.ttlMs");
|
|
6660
|
+
if (config.ttlMs < 0) {
|
|
6661
|
+
throw new ConfigurationError("embeddingCache.ttlMs must be >= 0");
|
|
6662
|
+
}
|
|
6663
|
+
out.ttlMs = config.ttlMs;
|
|
6664
|
+
}
|
|
6665
|
+
if (config.maxEntries !== void 0) {
|
|
6666
|
+
assertFiniteNumber2(config.maxEntries, "embeddingCache.maxEntries");
|
|
6667
|
+
if (config.maxEntries < 0) {
|
|
6668
|
+
throw new ConfigurationError("embeddingCache.maxEntries must be >= 0");
|
|
6669
|
+
}
|
|
6670
|
+
out.maxEntries = config.maxEntries;
|
|
6671
|
+
}
|
|
6672
|
+
return out;
|
|
6673
|
+
}
|
|
6256
6674
|
function normalizeDatabaseConfig(config) {
|
|
6257
6675
|
const provider = config.provider;
|
|
6258
6676
|
if (provider !== "sqlite" && provider !== "postgres") {
|
|
@@ -6362,11 +6780,22 @@ function validateWolbargOptions(options) {
|
|
|
6362
6780
|
throw new ConfigurationError("Wolbarg options must be an object");
|
|
6363
6781
|
}
|
|
6364
6782
|
assertNonEmpty(options.organization, "organization");
|
|
6783
|
+
const checkpointDirectory = options.checkpointDirectory !== void 0 ? (() => {
|
|
6784
|
+
assertNonEmpty(options.checkpointDirectory, "checkpointDirectory");
|
|
6785
|
+
return options.checkpointDirectory.trim();
|
|
6786
|
+
})() : void 0;
|
|
6365
6787
|
const storageInput = resolveStorageInput(options);
|
|
6366
6788
|
let storage = storageInput;
|
|
6367
6789
|
if (!isStorageProvider(storageInput)) {
|
|
6368
6790
|
storage = normalizeDatabaseConfig(storageInput);
|
|
6369
6791
|
}
|
|
6792
|
+
if (!isStorageProvider(storageInput) && storageInput.provider === "postgres" && storageInput.maxPoolSize !== void 0) {
|
|
6793
|
+
assertFiniteNumber2(
|
|
6794
|
+
storageInput.maxPoolSize,
|
|
6795
|
+
"database.maxPoolSize"
|
|
6796
|
+
);
|
|
6797
|
+
assertNumberMin(storageInput.maxPoolSize, "database.maxPoolSize", 1);
|
|
6798
|
+
}
|
|
6370
6799
|
if (!options.embedding) {
|
|
6371
6800
|
throw new ConfigurationError("embedding is required");
|
|
6372
6801
|
}
|
|
@@ -6384,13 +6813,20 @@ function validateWolbargOptions(options) {
|
|
|
6384
6813
|
if (graph !== void 0) {
|
|
6385
6814
|
graph = resolveGraphInput(graph);
|
|
6386
6815
|
}
|
|
6816
|
+
const retrieval = options.retrieval !== void 0 ? validateRetrievalConfig(options.retrieval) : void 0;
|
|
6817
|
+
const embeddingCache = options.embeddingCache !== void 0 ? validateEmbeddingCacheConfig(options.embeddingCache) : void 0;
|
|
6818
|
+
const memory = options.memory !== void 0 && options.memory.dedupe !== void 0 ? { ...options.memory, dedupe: validateMemoryDedupeConfig(options.memory.dedupe) } : options.memory;
|
|
6387
6819
|
return {
|
|
6388
6820
|
...options,
|
|
6389
6821
|
organization: options.organization.trim(),
|
|
6390
6822
|
storage,
|
|
6391
6823
|
database: void 0,
|
|
6392
6824
|
...telemetry ? { telemetry } : {},
|
|
6393
|
-
...graph !== void 0 ? { graph } : {}
|
|
6825
|
+
...graph !== void 0 ? { graph } : {},
|
|
6826
|
+
...retrieval !== void 0 ? { retrieval } : {},
|
|
6827
|
+
...embeddingCache !== void 0 ? { embeddingCache } : {},
|
|
6828
|
+
...checkpointDirectory !== void 0 ? { checkpointDirectory } : {},
|
|
6829
|
+
...memory !== void 0 ? { memory } : {}
|
|
6394
6830
|
};
|
|
6395
6831
|
}
|
|
6396
6832
|
function resolveGraphInput(input) {
|
|
@@ -6449,59 +6885,6 @@ function resolveGraphInput(input) {
|
|
|
6449
6885
|
);
|
|
6450
6886
|
}
|
|
6451
6887
|
|
|
6452
|
-
// src/telemetry/logger.ts
|
|
6453
|
-
var LEVEL_ORDER = {
|
|
6454
|
-
off: 100,
|
|
6455
|
-
error: 50,
|
|
6456
|
-
warn: 40,
|
|
6457
|
-
info: 30,
|
|
6458
|
-
debug: 20,
|
|
6459
|
-
trace: 10
|
|
6460
|
-
};
|
|
6461
|
-
var WolbargLogger = class {
|
|
6462
|
-
constructor(level = "info") {
|
|
6463
|
-
this.level = level;
|
|
6464
|
-
}
|
|
6465
|
-
level;
|
|
6466
|
-
setLevel(level) {
|
|
6467
|
-
this.level = level;
|
|
6468
|
-
}
|
|
6469
|
-
getLevel() {
|
|
6470
|
-
return this.level;
|
|
6471
|
-
}
|
|
6472
|
-
error(message, extra) {
|
|
6473
|
-
this.write("error", message, extra);
|
|
6474
|
-
}
|
|
6475
|
-
warn(message, extra) {
|
|
6476
|
-
this.write("warn", message, extra);
|
|
6477
|
-
}
|
|
6478
|
-
info(message, extra) {
|
|
6479
|
-
this.write("info", message, extra);
|
|
6480
|
-
}
|
|
6481
|
-
debug(message, extra) {
|
|
6482
|
-
this.write("debug", message, extra);
|
|
6483
|
-
}
|
|
6484
|
-
trace(message, extra) {
|
|
6485
|
-
this.write("trace", message, extra);
|
|
6486
|
-
}
|
|
6487
|
-
write(level, message, extra) {
|
|
6488
|
-
if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
|
|
6489
|
-
return;
|
|
6490
|
-
}
|
|
6491
|
-
if (this.level === "off") {
|
|
6492
|
-
return;
|
|
6493
|
-
}
|
|
6494
|
-
const line = `[wolbarg:${level}] ${message}`;
|
|
6495
|
-
if (level === "error") {
|
|
6496
|
-
console.error(line, extra ?? "");
|
|
6497
|
-
} else if (level === "warn") {
|
|
6498
|
-
console.warn(line, extra ?? "");
|
|
6499
|
-
} else {
|
|
6500
|
-
console.log(line, extra ?? "");
|
|
6501
|
-
}
|
|
6502
|
-
}
|
|
6503
|
-
};
|
|
6504
|
-
|
|
6505
6888
|
// src/telemetry/trace.ts
|
|
6506
6889
|
function createSessionId() {
|
|
6507
6890
|
return createId();
|
|
@@ -6586,14 +6969,14 @@ var TelemetryEmitter = class {
|
|
|
6586
6969
|
const totalMs = performance.now() - startedAt;
|
|
6587
6970
|
const errMessage = error instanceof Error ? error.message : error ? String(error) : fields?.error ?? null;
|
|
6588
6971
|
const errStack = error instanceof Error ? error.stack ?? null : fields?.errorStack ?? null;
|
|
6972
|
+
if (!self.config.enabled) {
|
|
6973
|
+
return;
|
|
6974
|
+
}
|
|
6589
6975
|
if (status === "error") {
|
|
6590
6976
|
self.logger.error(`${operation} failed: ${errMessage ?? "unknown"}`);
|
|
6591
6977
|
} else {
|
|
6592
6978
|
self.logger.debug(`${operation} completed in ${totalMs.toFixed(2)}ms`);
|
|
6593
6979
|
}
|
|
6594
|
-
if (!self.config.enabled) {
|
|
6595
|
-
return;
|
|
6596
|
-
}
|
|
6597
6980
|
const event = {
|
|
6598
6981
|
id: createId(),
|
|
6599
6982
|
timestamp: nowIso(),
|
|
@@ -6895,8 +7278,10 @@ var SqliteEventDatabase = class {
|
|
|
6895
7278
|
}
|
|
6896
7279
|
}
|
|
6897
7280
|
if (options.memoryId) {
|
|
6898
|
-
clauses.push(
|
|
6899
|
-
|
|
7281
|
+
clauses.push(
|
|
7282
|
+
`EXISTS (SELECT 1 FROM json_each(memory_ids_json) WHERE json_each.value = ?)`
|
|
7283
|
+
);
|
|
7284
|
+
params.push(options.memoryId);
|
|
6900
7285
|
}
|
|
6901
7286
|
if (options.queryText) {
|
|
6902
7287
|
clauses.push(`query LIKE ?`);
|
|
@@ -7073,6 +7458,7 @@ function describe2(error) {
|
|
|
7073
7458
|
}
|
|
7074
7459
|
|
|
7075
7460
|
// src/providers/sqlite/sqliteTelemetryProvider.ts
|
|
7461
|
+
var MAX_QUEUE_SIZE = 1e4;
|
|
7076
7462
|
var SqliteTelemetryProvider = class {
|
|
7077
7463
|
name = "sqlite";
|
|
7078
7464
|
db;
|
|
@@ -7080,6 +7466,8 @@ var SqliteTelemetryProvider = class {
|
|
|
7080
7466
|
flushing = null;
|
|
7081
7467
|
closed = false;
|
|
7082
7468
|
openPromise = null;
|
|
7469
|
+
warnedQueueDrop = false;
|
|
7470
|
+
warnedFlushFailure = false;
|
|
7083
7471
|
constructor(options) {
|
|
7084
7472
|
this.db = new SqliteEventDatabase({ url: options.url });
|
|
7085
7473
|
}
|
|
@@ -7100,6 +7488,15 @@ var SqliteTelemetryProvider = class {
|
|
|
7100
7488
|
if (this.closed) {
|
|
7101
7489
|
return;
|
|
7102
7490
|
}
|
|
7491
|
+
if (this.queue.length >= MAX_QUEUE_SIZE) {
|
|
7492
|
+
this.queue.shift();
|
|
7493
|
+
if (!this.warnedQueueDrop) {
|
|
7494
|
+
this.warnedQueueDrop = true;
|
|
7495
|
+
console.warn(
|
|
7496
|
+
`[wolbarg telemetry] event queue capped at ${MAX_QUEUE_SIZE}; dropping oldest events`
|
|
7497
|
+
);
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7103
7500
|
this.queue.push(event);
|
|
7104
7501
|
void this.scheduleFlush();
|
|
7105
7502
|
}
|
|
@@ -7142,7 +7539,13 @@ var SqliteTelemetryProvider = class {
|
|
|
7142
7539
|
await this.db.insertEvent(event);
|
|
7143
7540
|
}
|
|
7144
7541
|
}
|
|
7145
|
-
} catch {
|
|
7542
|
+
} catch (error) {
|
|
7543
|
+
if (!this.warnedFlushFailure) {
|
|
7544
|
+
this.warnedFlushFailure = true;
|
|
7545
|
+
console.warn(
|
|
7546
|
+
`[wolbarg telemetry] flush failed; dropping batch: ${error instanceof Error ? error.message : String(error)}`
|
|
7547
|
+
);
|
|
7548
|
+
}
|
|
7146
7549
|
}
|
|
7147
7550
|
}
|
|
7148
7551
|
};
|
|
@@ -7185,8 +7588,20 @@ Ensure Wolbarg has been initialized and the database path is correct.`
|
|
|
7185
7588
|
);
|
|
7186
7589
|
}
|
|
7187
7590
|
const snapshotPath = this.snapshotPath(name);
|
|
7188
|
-
|
|
7189
|
-
|
|
7591
|
+
const tmpSnapshotPath = `${snapshotPath}.tmp-${Date.now()}`;
|
|
7592
|
+
try {
|
|
7593
|
+
await safeSqliteBackup(resolvedSource, tmpSnapshotPath);
|
|
7594
|
+
} catch (error) {
|
|
7595
|
+
if (fs6.existsSync(tmpSnapshotPath)) {
|
|
7596
|
+
fs6.rmSync(tmpSnapshotPath, { force: true });
|
|
7597
|
+
}
|
|
7598
|
+
throw error;
|
|
7599
|
+
}
|
|
7600
|
+
const stats = fs6.statSync(tmpSnapshotPath);
|
|
7601
|
+
if (fs6.existsSync(snapshotPath)) {
|
|
7602
|
+
fs6.rmSync(snapshotPath, { force: true });
|
|
7603
|
+
}
|
|
7604
|
+
fs6.renameSync(tmpSnapshotPath, snapshotPath);
|
|
7190
7605
|
const meta2 = {
|
|
7191
7606
|
name,
|
|
7192
7607
|
description: options?.description ?? null,
|
|
@@ -7197,7 +7612,9 @@ Ensure Wolbarg has been initialized and the database path is correct.`
|
|
|
7197
7612
|
sourcePath: resolvedSource,
|
|
7198
7613
|
sizeBytes: stats.size
|
|
7199
7614
|
};
|
|
7200
|
-
|
|
7615
|
+
const tmpMetaPath = `${metaPath}.tmp-${Date.now()}`;
|
|
7616
|
+
fs6.writeFileSync(tmpMetaPath, JSON.stringify(meta2, null, 2), "utf8");
|
|
7617
|
+
fs6.renameSync(tmpMetaPath, metaPath);
|
|
7201
7618
|
return meta2;
|
|
7202
7619
|
}
|
|
7203
7620
|
async rollback(name, targetPath) {
|
|
@@ -7461,6 +7878,9 @@ var SqliteSubscribeEmitter = class {
|
|
|
7461
7878
|
};
|
|
7462
7879
|
|
|
7463
7880
|
// src/core/wolbarg.ts
|
|
7881
|
+
var warnLogger3 = new WolbargLogger("warn");
|
|
7882
|
+
var warnedHybridNoKeyword = false;
|
|
7883
|
+
var warnedRerankNoProvider = false;
|
|
7464
7884
|
var Wolbarg = class {
|
|
7465
7885
|
initialized = false;
|
|
7466
7886
|
booting = null;
|
|
@@ -7566,6 +7986,7 @@ var Wolbarg = class {
|
|
|
7566
7986
|
this.storage = createStorageProvider(validated.database);
|
|
7567
7987
|
this.memoryDbPath = resolveDatabaseUrl(validated.database);
|
|
7568
7988
|
this.embedding = createEmbeddingProvider(validated.embedding);
|
|
7989
|
+
this.rawEmbedding = this.embedding;
|
|
7569
7990
|
if (validated.llm) {
|
|
7570
7991
|
this.llm = createLlmProvider(validated.llm);
|
|
7571
7992
|
this.compression = createCompressionProvider(this.llm);
|
|
@@ -7887,7 +8308,7 @@ var Wolbarg = class {
|
|
|
7887
8308
|
* Update an existing memory by id (re-embeds when content changes).
|
|
7888
8309
|
*/
|
|
7889
8310
|
async update(options) {
|
|
7890
|
-
const trace = this.telemetry.start("
|
|
8311
|
+
const trace = this.telemetry.start("update");
|
|
7891
8312
|
try {
|
|
7892
8313
|
const { storage, embedding, organization } = await this.requireReady();
|
|
7893
8314
|
assertNonEmptyString(options.id, "id");
|
|
@@ -8413,17 +8834,37 @@ var Wolbarg = class {
|
|
|
8413
8834
|
}
|
|
8414
8835
|
}
|
|
8415
8836
|
}
|
|
8837
|
+
if (hybridWeights && keywordSignal === "disabled" && !warnedHybridNoKeyword) {
|
|
8838
|
+
warnedHybridNoKeyword = true;
|
|
8839
|
+
warnLogger3.warn(
|
|
8840
|
+
"hybrid search requested but no keyword channel is available; using semantic-only results."
|
|
8841
|
+
);
|
|
8842
|
+
}
|
|
8416
8843
|
const tRank = performance.now();
|
|
8417
|
-
let results = [...byId.values()].sort(
|
|
8418
|
-
|
|
8419
|
-
|
|
8844
|
+
let results = [...byId.values()].sort((a, b) => {
|
|
8845
|
+
const diff = b.similarity - a.similarity;
|
|
8846
|
+
if (diff !== 0) return diff;
|
|
8847
|
+
return a.id.localeCompare(b.id);
|
|
8848
|
+
});
|
|
8420
8849
|
const mmrLambda = resolveMmr(options.mmr) ?? resolveMmr(
|
|
8421
8850
|
this.retrievalConfig.mmr ? { lambda: this.retrievalConfig.mmr.lambda } : void 0
|
|
8422
8851
|
);
|
|
8423
8852
|
let rankingReason = "cosine similarity";
|
|
8853
|
+
let mmrApplied = false;
|
|
8424
8854
|
if (mmrLambda !== null) {
|
|
8425
|
-
|
|
8426
|
-
|
|
8855
|
+
mmrApplied = results.length > topK;
|
|
8856
|
+
if (mmrApplied) {
|
|
8857
|
+
results = applyMmr(results, topK, mmrLambda);
|
|
8858
|
+
rankingReason = `MMR(lambda=${mmrLambda})`;
|
|
8859
|
+
} else {
|
|
8860
|
+
rankingReason = `MMR(lambda=${mmrLambda})(skipped:insufficient_candidates)`;
|
|
8861
|
+
}
|
|
8862
|
+
}
|
|
8863
|
+
if (options.rerank === true && !this.reranker && !warnedRerankNoProvider) {
|
|
8864
|
+
warnedRerankNoProvider = true;
|
|
8865
|
+
warnLogger3.warn(
|
|
8866
|
+
"rerank is enabled but no reranker provider is configured; using identity order."
|
|
8867
|
+
);
|
|
8427
8868
|
}
|
|
8428
8869
|
if (options.rerank && this.reranker) {
|
|
8429
8870
|
const reranked = await this.reranker.rerank(
|
|
@@ -8458,14 +8899,16 @@ var Wolbarg = class {
|
|
|
8458
8899
|
agentId: options.filter?.agent ?? commonAgent(serialized.map((result) => result.agent)),
|
|
8459
8900
|
tags: commonTags(serialized.map((result) => result.metadata))
|
|
8460
8901
|
};
|
|
8461
|
-
if (
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8902
|
+
if (options.includeGraph === true && this.graph) {
|
|
8903
|
+
const tGraph = performance.now();
|
|
8904
|
+
await Promise.all(
|
|
8905
|
+
serialized.map(async (hit) => {
|
|
8465
8906
|
hit.related = await this.hydrateRelated(hit.id);
|
|
8466
|
-
}
|
|
8467
|
-
|
|
8468
|
-
|
|
8907
|
+
})
|
|
8908
|
+
);
|
|
8909
|
+
trace.mark("databaseReadMs", performance.now() - tGraph);
|
|
8910
|
+
}
|
|
8911
|
+
if (!explain) {
|
|
8469
8912
|
trace.success(telemetryFields);
|
|
8470
8913
|
return serialized;
|
|
8471
8914
|
}
|
|
@@ -8494,7 +8937,7 @@ var Wolbarg = class {
|
|
|
8494
8937
|
semantic: "enabled",
|
|
8495
8938
|
keyword: keywordSignal,
|
|
8496
8939
|
reranker: options.rerank === true && this.reranker ? "enabled" : "disabled",
|
|
8497
|
-
mmr: mmrLambda === null ? "disabled" : "enabled",
|
|
8940
|
+
mmr: mmrLambda === null ? "disabled" : mmrApplied ? "enabled" : "disabled",
|
|
8498
8941
|
recency: "disabled"
|
|
8499
8942
|
},
|
|
8500
8943
|
results: explanations.map((hit) => ({
|
|
@@ -8603,31 +9046,34 @@ var Wolbarg = class {
|
|
|
8603
9046
|
const timestamp = nowIso();
|
|
8604
9047
|
const result = await this.withWriteLock(async () => {
|
|
8605
9048
|
const tWrite = performance.now();
|
|
8606
|
-
const
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
9049
|
+
const txResult = await storage.withTransaction(async () => {
|
|
9050
|
+
const summaryRow = await storage.insertMemory({
|
|
9051
|
+
id: summaryId,
|
|
9052
|
+
organization,
|
|
9053
|
+
agent: options.agent.trim(),
|
|
9054
|
+
contentText: summaryText,
|
|
9055
|
+
metadata: {
|
|
9056
|
+
compressed: true,
|
|
9057
|
+
sourceCount: records.length,
|
|
9058
|
+
sourceIds: records.map((r) => r.id)
|
|
9059
|
+
},
|
|
9060
|
+
embedding: vector,
|
|
9061
|
+
createdAt: timestamp,
|
|
9062
|
+
updatedAt: timestamp
|
|
9063
|
+
});
|
|
9064
|
+
const archivedIds = await storage.archiveMemories(
|
|
9065
|
+
records.map((r) => r.id),
|
|
9066
|
+
organization,
|
|
9067
|
+
summaryId,
|
|
9068
|
+
timestamp
|
|
9069
|
+
);
|
|
9070
|
+
return {
|
|
9071
|
+
summary: toMemoryRecord(summaryRow),
|
|
9072
|
+
archivedIds
|
|
9073
|
+
};
|
|
8619
9074
|
});
|
|
8620
|
-
const archivedIds = await storage.archiveMemories(
|
|
8621
|
-
records.map((r) => r.id),
|
|
8622
|
-
organization,
|
|
8623
|
-
summaryId,
|
|
8624
|
-
timestamp
|
|
8625
|
-
);
|
|
8626
9075
|
trace.mark("databaseWriteMs", performance.now() - tWrite);
|
|
8627
|
-
return
|
|
8628
|
-
summary: toMemoryRecord(summaryRow),
|
|
8629
|
-
archivedIds
|
|
8630
|
-
};
|
|
9076
|
+
return txResult;
|
|
8631
9077
|
});
|
|
8632
9078
|
trace.success({
|
|
8633
9079
|
provider: storage.name,
|
|
@@ -8859,8 +9305,23 @@ var Wolbarg = class {
|
|
|
8859
9305
|
await this.requireReady();
|
|
8860
9306
|
const graph = this.requireGraph("getRelated");
|
|
8861
9307
|
assertNonEmptyString(memoryId, "memoryId");
|
|
9308
|
+
let validatedOptions = options;
|
|
9309
|
+
if (options?.depth !== void 0) {
|
|
9310
|
+
if (!Number.isFinite(options.depth)) {
|
|
9311
|
+
throw new ConfigurationError("graph.depth must be a finite number");
|
|
9312
|
+
}
|
|
9313
|
+
if (options.depth < 1) {
|
|
9314
|
+
throw new ConfigurationError("graph.depth must be >= 1");
|
|
9315
|
+
}
|
|
9316
|
+
if (options.depth > 16) {
|
|
9317
|
+
validatedOptions = { ...options, depth: 16 };
|
|
9318
|
+
}
|
|
9319
|
+
}
|
|
8862
9320
|
const tGraph = performance.now();
|
|
8863
|
-
const related = await this.hydrateRelated(
|
|
9321
|
+
const related = await this.hydrateRelated(
|
|
9322
|
+
memoryId.trim(),
|
|
9323
|
+
validatedOptions
|
|
9324
|
+
);
|
|
8864
9325
|
trace.mark("databaseReadMs", performance.now() - tGraph);
|
|
8865
9326
|
trace.success({
|
|
8866
9327
|
provider: graph.name,
|
|
@@ -9029,7 +9490,15 @@ var Wolbarg = class {
|
|
|
9029
9490
|
this.assertGraphCheckpointSupported("checkpoint");
|
|
9030
9491
|
const tCheckpoint = performance.now();
|
|
9031
9492
|
const meta2 = await provider.checkpoint(name, source, options);
|
|
9032
|
-
|
|
9493
|
+
try {
|
|
9494
|
+
await this.snapshotGraphAlongside(meta2.snapshotPath, "checkpoint");
|
|
9495
|
+
} catch (error) {
|
|
9496
|
+
const sidecar = this.graphSnapshotDir(meta2.snapshotPath);
|
|
9497
|
+
if (fs6.existsSync(sidecar)) {
|
|
9498
|
+
fs6.rmSync(sidecar, { recursive: true, force: true });
|
|
9499
|
+
}
|
|
9500
|
+
throw error;
|
|
9501
|
+
}
|
|
9033
9502
|
trace.mark("databaseWriteMs", performance.now() - tCheckpoint);
|
|
9034
9503
|
trace.success({
|
|
9035
9504
|
provider: provider.name,
|
|
@@ -9093,8 +9562,15 @@ var Wolbarg = class {
|
|
|
9093
9562
|
try {
|
|
9094
9563
|
await this.requireReady();
|
|
9095
9564
|
const provider = this.requireCheckpointProvider();
|
|
9565
|
+
const existing = await provider.getCheckpoint(name);
|
|
9096
9566
|
const tDelete = performance.now();
|
|
9097
9567
|
const removed = await provider.deleteCheckpoint(name);
|
|
9568
|
+
if (removed && existing) {
|
|
9569
|
+
const sidecar = this.graphSnapshotDir(existing.snapshotPath);
|
|
9570
|
+
if (fs6.existsSync(sidecar)) {
|
|
9571
|
+
fs6.rmSync(sidecar, { recursive: true, force: true });
|
|
9572
|
+
}
|
|
9573
|
+
}
|
|
9098
9574
|
trace.mark("databaseWriteMs", performance.now() - tDelete);
|
|
9099
9575
|
trace.success({
|
|
9100
9576
|
provider: provider.name,
|
|
@@ -9156,7 +9632,9 @@ var Wolbarg = class {
|
|
|
9156
9632
|
const result = await this.transfer.exportTo(
|
|
9157
9633
|
exportPath,
|
|
9158
9634
|
source,
|
|
9159
|
-
this.organization ?? void 0
|
|
9635
|
+
this.organization ?? void 0,
|
|
9636
|
+
this.embedding?.model,
|
|
9637
|
+
this.embeddingDimensions ?? void 0
|
|
9160
9638
|
);
|
|
9161
9639
|
await this.snapshotGraphAlongside(result.path, "export");
|
|
9162
9640
|
trace.success({
|
|
@@ -9179,16 +9657,21 @@ var Wolbarg = class {
|
|
|
9179
9657
|
let storageClosed = false;
|
|
9180
9658
|
try {
|
|
9181
9659
|
await this.requireReady();
|
|
9660
|
+
this.assertGraphCheckpointSupported("import");
|
|
9182
9661
|
const target = this.requireMemoryDbPath();
|
|
9183
9662
|
if (this.storage) {
|
|
9184
9663
|
await this.storage.close();
|
|
9185
9664
|
storageClosed = true;
|
|
9186
9665
|
}
|
|
9187
9666
|
const graphClosed = await this.closeGraphForSnapshot();
|
|
9188
|
-
this.assertGraphCheckpointSupported("import");
|
|
9189
9667
|
const result = await this.transfer.importFrom(
|
|
9190
9668
|
exportPath,
|
|
9191
|
-
target
|
|
9669
|
+
target,
|
|
9670
|
+
{
|
|
9671
|
+
organization: this.organization ?? void 0,
|
|
9672
|
+
embeddingModel: this.embedding?.model,
|
|
9673
|
+
embeddingDimensions: this.embeddingDimensions ?? void 0
|
|
9674
|
+
}
|
|
9192
9675
|
);
|
|
9193
9676
|
await this.restoreGraphAlongside(result.path, "import");
|
|
9194
9677
|
if (graphClosed) {
|
|
@@ -9383,18 +9866,26 @@ var Wolbarg = class {
|
|
|
9383
9866
|
const graph = this.requireGraph("getRelated");
|
|
9384
9867
|
const related = await graph.getRelated(memoryId, options);
|
|
9385
9868
|
const { storage, organization } = await this.requireReady();
|
|
9869
|
+
const rows = await Promise.all(
|
|
9870
|
+
related.map((stub) => storage.getMemoryById(stub.id, organization))
|
|
9871
|
+
);
|
|
9386
9872
|
const out = [];
|
|
9387
|
-
for (
|
|
9388
|
-
const
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
} else {
|
|
9392
|
-
out.push(stub);
|
|
9393
|
-
}
|
|
9873
|
+
for (let i = 0; i < related.length; i += 1) {
|
|
9874
|
+
const stub = related[i];
|
|
9875
|
+
const row = rows[i];
|
|
9876
|
+
out.push(row ? toMemoryRecord(row) : stub);
|
|
9394
9877
|
}
|
|
9395
9878
|
return out;
|
|
9396
9879
|
}
|
|
9397
9880
|
requireMemoryDbPath() {
|
|
9881
|
+
if (this.storage && !(this.storage instanceof SqliteStorageProvider)) {
|
|
9882
|
+
throw new ConfigurationError(
|
|
9883
|
+
"This operation requires a file-backed SQLite database (checkpoints / export-import are not supported for PostgreSQL).",
|
|
9884
|
+
{
|
|
9885
|
+
suggestion: 'Use database: { provider: "sqlite", url: "./memory.db" }'
|
|
9886
|
+
}
|
|
9887
|
+
);
|
|
9888
|
+
}
|
|
9398
9889
|
if (!this.memoryDbPath || this.memoryDbPath === ":memory:") {
|
|
9399
9890
|
throw new ConfigurationError(
|
|
9400
9891
|
"This operation requires a file-backed SQLite memory database (not :memory:).",
|
|
@@ -9623,7 +10114,14 @@ var HttpRerankerProvider = class {
|
|
|
9623
10114
|
}));
|
|
9624
10115
|
}
|
|
9625
10116
|
const parsed = this.options.parseResults(body, documents);
|
|
9626
|
-
|
|
10117
|
+
const hits = parsed.slice(0, topK);
|
|
10118
|
+
if (hits.length === 0) {
|
|
10119
|
+
return documents.slice(0, topK).map((d, i) => ({
|
|
10120
|
+
id: d.id,
|
|
10121
|
+
score: 1 - i / Math.max(documents.length, 1)
|
|
10122
|
+
}));
|
|
10123
|
+
}
|
|
10124
|
+
return hits;
|
|
9627
10125
|
} catch {
|
|
9628
10126
|
return documents.slice(0, topK).map((d, i) => ({
|
|
9629
10127
|
id: d.id,
|
|
@@ -9808,21 +10306,31 @@ function tesseract() {
|
|
|
9808
10306
|
return {
|
|
9809
10307
|
name: "tesseract",
|
|
9810
10308
|
async recognize(image) {
|
|
10309
|
+
let mod;
|
|
9811
10310
|
try {
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
10311
|
+
mod = await import('tesseract.js');
|
|
10312
|
+
} catch (error) {
|
|
10313
|
+
throw new ConfigurationError(
|
|
10314
|
+
'OCR provider "tesseract" requires the optional peer package "tesseract.js". Install it with: npm install tesseract.js',
|
|
10315
|
+
{
|
|
10316
|
+
cause: error instanceof Error ? error : void 0,
|
|
10317
|
+
suggestion: "npm install tesseract.js",
|
|
10318
|
+
operation: "recognize"
|
|
10319
|
+
}
|
|
10320
|
+
);
|
|
10321
|
+
}
|
|
10322
|
+
const createWorker = mod.createWorker ?? mod.default?.createWorker;
|
|
10323
|
+
if (!createWorker) {
|
|
10324
|
+
throw new ConfigurationError(
|
|
10325
|
+
'OCR provider "tesseract" could not find tesseract.js.createWorker. Ensure your tesseract.js version is compatible.'
|
|
10326
|
+
);
|
|
10327
|
+
}
|
|
10328
|
+
const worker = await createWorker("eng");
|
|
10329
|
+
try {
|
|
10330
|
+
const result = await worker.recognize(image);
|
|
10331
|
+
return { text: result.data.text.trim() };
|
|
10332
|
+
} finally {
|
|
10333
|
+
await worker.terminate();
|
|
9826
10334
|
}
|
|
9827
10335
|
}
|
|
9828
10336
|
};
|